Compare commits
	
		
			67 Commits
		
	
	
		
			feature/wo
			...
			feat/persi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					810d3b2524 | ||
| 
						 | 
					a3393ded1e | ||
| 
						 | 
					17ede265b1 | ||
| 
						 | 
					629070edad | ||
| 
						 | 
					cc83924c27 | ||
| 
						 | 
					e3e964f72a | ||
| 
						 | 
					bdb2c0cf8f | ||
| 
						 | 
					3e8dbc9793 | ||
| 
						 | 
					d735cd3833 | ||
| 
						 | 
					eddf228283 | ||
| 
						 | 
					f9d617efdc | ||
| 
						 | 
					43796021da | ||
| 
						 | 
					241c21782d | ||
| 
						 | 
					1a3f5d144c | ||
| 
						 | 
					66fb68d52e | ||
| 
						 | 
					aaeb32a576 | ||
| 
						 | 
					78b5dcceb6 | ||
| 
						 | 
					ce81c11c29 | ||
| 
						 | 
					3a98b95e3d | ||
| 
						 | 
					8bc36655e2 | ||
| 
						 | 
					b6ab536a60 | ||
| 
						 | 
					37a3d2b207 | ||
| 
						 | 
					cd0c5f8a0d | ||
| 
						 | 
					8dde89fa9a | ||
| 
						 | 
					ca3d60cfb8 | ||
| 
						 | 
					1a4d53cfbc | ||
| 
						 | 
					94e126782b | ||
| 
						 | 
					cc03c64f0a | ||
| 
						 | 
					3647aa6274 | ||
| 
						 | 
					a2a58f0ba9 | ||
| 
						 | 
					c544a03be4 | ||
| 
						 | 
					9a09da88ec | ||
| 
						 | 
					5850551906 | ||
| 
						 | 
					e35e520d24 | ||
| 
						 | 
					8077fc5865 | ||
| 
						 | 
					bff01b4a9f | ||
| 
						 | 
					de5380d6f3 | ||
| 
						 | 
					eda2a9596a | ||
| 
						 | 
					195d33b1db | ||
| 
						 | 
					4f042ef3b7 | ||
| 
						 | 
					17c67822a9 | ||
| 
						 | 
					e6f613ae0b | ||
| 
						 | 
					9b822cfda4 | ||
| 
						 | 
					b5b918d877 | ||
| 
						 | 
					739918647d | ||
| 
						 | 
					1f334d6253 | ||
| 
						 | 
					0f15a85c45 | ||
| 
						 | 
					0c4330e329 | ||
| 
						 | 
					a9676288ea | ||
| 
						 | 
					7354474c70 | ||
| 
						 | 
					ce5b307a8b | ||
| 
						 | 
					b28bcfdd0a | ||
| 
						 | 
					7f06876e3e | ||
| 
						 | 
					fd479d8671 | ||
| 
						 | 
					938b567256 | ||
| 
						 | 
					779f5aab0a | ||
| 
						 | 
					02194d8a98 | ||
| 
						 | 
					5677fe34dc | ||
| 
						 | 
					895da89325 | ||
| 
						 | 
					b138cc8d5b | ||
| 
						 | 
					027b2c8ed4 | ||
| 
						 | 
					d85cc71817 | ||
| 
						 | 
					bac3522078 | ||
| 
						 | 
					b2c6aa7871 | ||
| 
						 | 
					b4ca360661 | ||
| 
						 | 
					ad947be0bc | ||
| 
						 | 
					f739d4da34 | 
							
								
								
									
										75
									
								
								README.md
									
									
									
									
									
								
							
							
						
						@@ -26,6 +26,75 @@ You can start editing the page by modifying `pages/index.tsx`. The page auto-upd
 | 
			
		||||
 | 
			
		||||
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
 | 
			
		||||
 | 
			
		||||
## Github Login
 | 
			
		||||
 | 
			
		||||
If you want to use your Github app to provide login, here's the guide to do that.
 | 
			
		||||
 | 
			
		||||
- First go to https://github.com/settings/profile -> Developer Settings -> OAuth Apps
 | 
			
		||||
- Click "New OAuth App" button
 | 
			
		||||
- Give application some name eg. Xrpl Hooks Development
 | 
			
		||||
- Give some homepage url eg. localhost:3000
 | 
			
		||||
- Give some optional description (these values will show up on the popup when you login)
 | 
			
		||||
- Authorization callback URL should be http://localhost:3000/api/auth/callback (if you're creating the app for local development)
 | 
			
		||||
- Click register application
 | 
			
		||||
- Then a page should open up where you can get client id and client secret values. Copy paste those to .env.local to use them:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
GITHUB_SECRET="client-secret-here"
 | 
			
		||||
GITHUB_ID="client-id-here"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Login should now work through your own Github OAuth app.
 | 
			
		||||
 | 
			
		||||
## Styling and Theming
 | 
			
		||||
 | 
			
		||||
This project uses Stitches (https://stitches.dev) for theming and styling the components. You should be quite familiar with the API if you have used for example styled-components earlier. Stitches should provide better performance, near zero runtime.
 | 
			
		||||
 | 
			
		||||
For components we try to use Radix-UI (https://www.radix-ui.com/) as much as possible. It may not provide all the necessary components so you're free to use other components/libraries if those makes sense. For colors we're using Radix-UI Colors (https://radix-ui.com/colors).
 | 
			
		||||
 | 
			
		||||
Theme file can be found under `./stitches.config.ts` file. When you're creating new components remeber to import `styled` from that file and not `@stitches` directly. That way it will provide the correct theme for you automatically.
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
```tsx
 | 
			
		||||
// Use our stitches.config instead of @stitches/react
 | 
			
		||||
import { styled } from "../stitches.config";
 | 
			
		||||
 | 
			
		||||
const Box = styled("div", {
 | 
			
		||||
  boxSizing: "border-box",
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export default Box;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Custom components can be found from `./components` folder.
 | 
			
		||||
 | 
			
		||||
## Monaco Editor
 | 
			
		||||
 | 
			
		||||
Project is relying on Monaco editor heavily. Instead of using Monaco editor directly we're using `@monaco-editor/react` which provides little helpers to use Monaco editor.
 | 
			
		||||
 | 
			
		||||
On the Develop page we're using following loader for Monaco editor:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
loader.config({
 | 
			
		||||
  paths: {
 | 
			
		||||
    vs: "https://cdn.jsdelivr.net/npm/monaco-editor@0.30.1/min/vs",
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
By default `@monaco-editor/react` was using 0.29.? version of Monaco editor. @codingame/monaco-languageclient library (connects to clangd language server) was using API methods that were introduced in Monaco Editor 0.30 so that's why we're loading certain version of it.
 | 
			
		||||
 | 
			
		||||
Monaco Languageclient related stuff is found from `./utils/languageClient.ts`. Basically we're connecting the editor to clangd language server which lives on separate backend. That project can be found from https://github.com/eqlabs/xrpl-hooks-compiler/. If you need access to that project ask permissions from @vbar (Vaclav Barta) on GitHub.
 | 
			
		||||
 | 
			
		||||
## Global state management
 | 
			
		||||
 | 
			
		||||
Global state management is handled with library called Valtio (https://github.com/pmndrs/valtio). Initial state can be found from `./state/index.ts` file. All the actions which updates the state is found under `./state/actions/` folder.
 | 
			
		||||
 | 
			
		||||
## Special notes
 | 
			
		||||
 | 
			
		||||
Since we are dealing with greenfield tech and one of the dependencies (ripple-binary-codec) doesn't yet support signing `SetHook` transactions we had to monkey patch the library with patch-package (https://www.npmjs.com/package/patch-package). We modified the definitions.json file of the ripple-binary-codec library and then ran `yarn patch-package ripple-binary-codec` which created `patches/ripple-binary-codec+1.2.0.patch` file to this project. This file contains the modifications to `ripple-binary-codec` library. package.json contains postinstall hook which runs patch-package, and it will add the patch on the file mentioned earlier. This happens automatically after running patch package.
 | 
			
		||||
 | 
			
		||||
## Learn More
 | 
			
		||||
 | 
			
		||||
To learn more about Next.js, take a look at the following resources:
 | 
			
		||||
@@ -34,9 +103,3 @@ To learn more about Next.js, take a look at the following resources:
 | 
			
		||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
 | 
			
		||||
 | 
			
		||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
 | 
			
		||||
 | 
			
		||||
## Deploy on Vercel
 | 
			
		||||
 | 
			
		||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
 | 
			
		||||
 | 
			
		||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +1,14 @@
 | 
			
		||||
import toast from "react-hot-toast";
 | 
			
		||||
import { useSnapshot } from "valtio";
 | 
			
		||||
import { ArrowSquareOut, Copy, Wallet, X } from "phosphor-react";
 | 
			
		||||
import React, { useEffect, useState } from "react";
 | 
			
		||||
import React, { useEffect, useState, FC } from "react";
 | 
			
		||||
import Dinero from "dinero.js";
 | 
			
		||||
 | 
			
		||||
import Button from "./Button";
 | 
			
		||||
import { addFaucetAccount, deployHook, importAccount } from "../state/actions";
 | 
			
		||||
import state from "../state";
 | 
			
		||||
import Box from "./Box";
 | 
			
		||||
import Container from "./Container";
 | 
			
		||||
import Heading from "./Heading";
 | 
			
		||||
import Stack from "./Stack";
 | 
			
		||||
import Text from "./Text";
 | 
			
		||||
import Flex from "./Flex";
 | 
			
		||||
import { Container, Heading, Stack, Text, Flex } from ".";
 | 
			
		||||
import {
 | 
			
		||||
  Dialog,
 | 
			
		||||
  DialogContent,
 | 
			
		||||
@@ -40,13 +36,11 @@ const AccountDialog = ({
 | 
			
		||||
}) => {
 | 
			
		||||
  const snap = useSnapshot(state);
 | 
			
		||||
  const [showSecret, setShowSecret] = useState(false);
 | 
			
		||||
  const activeAccount = snap.accounts.find(
 | 
			
		||||
    (account) => account.address === activeAccountAddress
 | 
			
		||||
  );
 | 
			
		||||
  const activeAccount = snap.accounts.find(account => account.address === activeAccountAddress);
 | 
			
		||||
  return (
 | 
			
		||||
    <Dialog
 | 
			
		||||
      open={Boolean(activeAccountAddress)}
 | 
			
		||||
      onOpenChange={(open) => {
 | 
			
		||||
      onOpenChange={open => {
 | 
			
		||||
        setShowSecret(false);
 | 
			
		||||
        !open && setActiveAccountAddress(null);
 | 
			
		||||
      }}
 | 
			
		||||
@@ -141,7 +135,7 @@ const AccountDialog = ({
 | 
			
		||||
                    }}
 | 
			
		||||
                    ghost
 | 
			
		||||
                    size="xs"
 | 
			
		||||
                    onClick={() => setShowSecret((curr) => !curr)}
 | 
			
		||||
                    onClick={() => setShowSecret(curr => !curr)}
 | 
			
		||||
                  >
 | 
			
		||||
                    {showSecret ? "Hide" : "Show"}
 | 
			
		||||
                  </Button>
 | 
			
		||||
@@ -190,7 +184,7 @@ const AccountDialog = ({
 | 
			
		||||
                  <Button
 | 
			
		||||
                    size="sm"
 | 
			
		||||
                    ghost
 | 
			
		||||
                    css={{ color: "$green11 !important", mt: "$3" }}
 | 
			
		||||
                    css={{ color: "$grass11 !important", mt: "$3" }}
 | 
			
		||||
                  >
 | 
			
		||||
                    <ArrowSquareOut size="15px" />
 | 
			
		||||
                  </Button>
 | 
			
		||||
@@ -205,15 +199,7 @@ const AccountDialog = ({
 | 
			
		||||
                    fontFamily: "$monospace",
 | 
			
		||||
                  }}
 | 
			
		||||
                >
 | 
			
		||||
                  {activeAccount && activeAccount?.hooks?.length > 0
 | 
			
		||||
                    ? activeAccount?.hooks
 | 
			
		||||
                        .map((i) => {
 | 
			
		||||
                          return `${i?.substring(0, 6)}...${i?.substring(
 | 
			
		||||
                            i.length - 4
 | 
			
		||||
                          )}`;
 | 
			
		||||
                        })
 | 
			
		||||
                        .join(", ")
 | 
			
		||||
                    : "–"}
 | 
			
		||||
                  {activeAccount && activeAccount.hooks.length}
 | 
			
		||||
                </Text>
 | 
			
		||||
              </Flex>
 | 
			
		||||
            </Flex>
 | 
			
		||||
@@ -229,15 +215,19 @@ const AccountDialog = ({
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Accounts = () => {
 | 
			
		||||
interface AccountProps {
 | 
			
		||||
  card?: boolean;
 | 
			
		||||
  hideDeployBtn?: boolean;
 | 
			
		||||
  showHookStats?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Accounts: FC<AccountProps> = props => {
 | 
			
		||||
  const snap = useSnapshot(state);
 | 
			
		||||
  const [activeAccountAddress, setActiveAccountAddress] = useState<
 | 
			
		||||
    string | null
 | 
			
		||||
  >(null);
 | 
			
		||||
  const [activeAccountAddress, setActiveAccountAddress] = useState<string | null>(null);
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const fetchAccInfo = async () => {
 | 
			
		||||
      if (snap.clientStatus === "online") {
 | 
			
		||||
        const requests = snap.accounts.map((acc) =>
 | 
			
		||||
        const requests = snap.accounts.map(acc =>
 | 
			
		||||
          snap.client?.send({
 | 
			
		||||
            id: acc.address,
 | 
			
		||||
            command: "account_info",
 | 
			
		||||
@@ -249,15 +239,13 @@ const Accounts = () => {
 | 
			
		||||
          const address = res?.account_data?.Account as string;
 | 
			
		||||
          const balance = res?.account_data?.Balance as string;
 | 
			
		||||
          const sequence = res?.account_data?.Sequence as number;
 | 
			
		||||
          const accountToUpdate = state.accounts.find(
 | 
			
		||||
            (acc) => acc.address === address
 | 
			
		||||
          );
 | 
			
		||||
          const accountToUpdate = state.accounts.find(acc => acc.address === address);
 | 
			
		||||
          if (accountToUpdate) {
 | 
			
		||||
            accountToUpdate.xrp = balance;
 | 
			
		||||
            accountToUpdate.sequence = sequence;
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        const objectRequests = snap.accounts.map((acc) => {
 | 
			
		||||
        const objectRequests = snap.accounts.map(acc => {
 | 
			
		||||
          return snap.client?.send({
 | 
			
		||||
            id: `${acc.address}-hooks`,
 | 
			
		||||
            command: "account_objects",
 | 
			
		||||
@@ -267,9 +255,7 @@ const Accounts = () => {
 | 
			
		||||
        const objectResponses = await Promise.all(objectRequests);
 | 
			
		||||
        objectResponses.forEach((res: any) => {
 | 
			
		||||
          const address = res?.account as string;
 | 
			
		||||
          const accountToUpdate = state.accounts.find(
 | 
			
		||||
            (acc) => acc.address === address
 | 
			
		||||
          );
 | 
			
		||||
          const accountToUpdate = state.accounts.find(acc => acc.address === address);
 | 
			
		||||
          if (accountToUpdate) {
 | 
			
		||||
            accountToUpdate.hooks = res.account_objects
 | 
			
		||||
              .filter((ac: any) => ac?.LedgerEntryType === "Hook")
 | 
			
		||||
@@ -299,16 +285,21 @@ const Accounts = () => {
 | 
			
		||||
      as="div"
 | 
			
		||||
      css={{
 | 
			
		||||
        display: "flex",
 | 
			
		||||
        borderTop: "1px solid $mauve6",
 | 
			
		||||
        background: "$mauve1",
 | 
			
		||||
        backgroundColor: props.card ? "$deep" : "$mauve1",
 | 
			
		||||
        position: "relative",
 | 
			
		||||
        width: "50%",
 | 
			
		||||
        flexShrink: 0,
 | 
			
		||||
        borderRight: "1px solid $mauve6",
 | 
			
		||||
        flex: "1",
 | 
			
		||||
        height: "100%",
 | 
			
		||||
        border: "1px solid $mauve6",
 | 
			
		||||
        borderRadius: props.card ? "$md" : undefined,
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      <Container css={{ px: 0, flexShrink: 1 }}>
 | 
			
		||||
        <Flex css={{ py: "$3" }}>
 | 
			
		||||
      <Container css={{ p: 0, flexShrink: 1, height: "100%" }}>
 | 
			
		||||
        <Flex
 | 
			
		||||
          css={{
 | 
			
		||||
            py: "$3",
 | 
			
		||||
            borderBottom: props.card ? "1px solid $mauve6" : undefined,
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          <Heading
 | 
			
		||||
            as="h3"
 | 
			
		||||
            css={{
 | 
			
		||||
@@ -326,79 +317,99 @@ const Accounts = () => {
 | 
			
		||||
            <Wallet size="15px" /> <Text css={{ lineHeight: 1 }}>Accounts</Text>
 | 
			
		||||
          </Heading>
 | 
			
		||||
          <Flex css={{ ml: "auto", gap: "$3", marginRight: "$3" }}>
 | 
			
		||||
            <Button ghost size="xs" onClick={() => addFaucetAccount(true)}>
 | 
			
		||||
            <Button ghost size="sm" onClick={() => addFaucetAccount(true)}>
 | 
			
		||||
              Create
 | 
			
		||||
            </Button>
 | 
			
		||||
            <ImportAccountDialog />
 | 
			
		||||
          </Flex>
 | 
			
		||||
        </Flex>
 | 
			
		||||
        <Box
 | 
			
		||||
          as="div"
 | 
			
		||||
        <Stack
 | 
			
		||||
          css={{
 | 
			
		||||
            display: "flex",
 | 
			
		||||
            flexDirection: "column",
 | 
			
		||||
            width: "100%",
 | 
			
		||||
            height: "160px",
 | 
			
		||||
            fontSize: "13px",
 | 
			
		||||
            wordWrap: "break-word",
 | 
			
		||||
            fontWeight: "$body",
 | 
			
		||||
            fontFamily: "$monospace",
 | 
			
		||||
            gap: 0,
 | 
			
		||||
            height: "calc(100% - 52px)",
 | 
			
		||||
            flexWrap: "nowrap",
 | 
			
		||||
            overflowY: "auto",
 | 
			
		||||
            wordWrap: "break-word",
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          <Stack css={{ flexDirection: "column", gap: 0 }}>
 | 
			
		||||
            {snap.accounts.map((account) => (
 | 
			
		||||
              <Flex
 | 
			
		||||
                key={account.address + account.name}
 | 
			
		||||
                onClick={() => setActiveAccountAddress(account.address)}
 | 
			
		||||
                css={{
 | 
			
		||||
                  gap: "$3",
 | 
			
		||||
                  p: "$2 $3",
 | 
			
		||||
                  justifyContent: "center",
 | 
			
		||||
                  cursor: "pointer",
 | 
			
		||||
                  "@hover": {
 | 
			
		||||
                    "&:hover": {
 | 
			
		||||
                      background: "$mauve3",
 | 
			
		||||
                    },
 | 
			
		||||
          {snap.accounts.map(account => (
 | 
			
		||||
            <Flex
 | 
			
		||||
              column
 | 
			
		||||
              key={account.address + account.name}
 | 
			
		||||
              onClick={() => setActiveAccountAddress(account.address)}
 | 
			
		||||
              css={{
 | 
			
		||||
                px: "$3",
 | 
			
		||||
                py: props.card ? "$3" : "$2",
 | 
			
		||||
                cursor: "pointer",
 | 
			
		||||
                borderBottom: props.card ? "1px solid $mauve6" : undefined,
 | 
			
		||||
                "@hover": {
 | 
			
		||||
                  "&:hover": {
 | 
			
		||||
                    background: "$backgroundAlt",
 | 
			
		||||
                  },
 | 
			
		||||
                },
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              <Flex
 | 
			
		||||
                row
 | 
			
		||||
                css={{
 | 
			
		||||
                  justifyContent: "space-between",
 | 
			
		||||
                }}
 | 
			
		||||
              >
 | 
			
		||||
                <Text>{account.name} </Text>
 | 
			
		||||
                <Text css={{ color: "$mauve9" }}>
 | 
			
		||||
                  {account.address} (
 | 
			
		||||
                  {Dinero({
 | 
			
		||||
                    amount: Number(account?.xrp || "0"),
 | 
			
		||||
                    precision: 6,
 | 
			
		||||
                  })
 | 
			
		||||
                    .toUnit()
 | 
			
		||||
                    .toLocaleString(undefined, {
 | 
			
		||||
                      style: "currency",
 | 
			
		||||
                      currency: "XRP",
 | 
			
		||||
                      currencyDisplay: "name",
 | 
			
		||||
                    })}
 | 
			
		||||
                  )
 | 
			
		||||
                </Text>
 | 
			
		||||
                <Button
 | 
			
		||||
                  css={{ ml: "auto" }}
 | 
			
		||||
                  size="xs"
 | 
			
		||||
                  uppercase
 | 
			
		||||
                  isLoading={account.isLoading}
 | 
			
		||||
                  disabled={
 | 
			
		||||
                    account.isLoading ||
 | 
			
		||||
                    !snap.files.filter((file) => file.compiledWatContent).length
 | 
			
		||||
                  }
 | 
			
		||||
                  variant="secondary"
 | 
			
		||||
                  onClick={(e) => {
 | 
			
		||||
                    e.stopPropagation();
 | 
			
		||||
                    deployHook(account);
 | 
			
		||||
                  }}
 | 
			
		||||
                >
 | 
			
		||||
                  Deploy
 | 
			
		||||
                </Button>
 | 
			
		||||
                <Box>
 | 
			
		||||
                  <Text>{account.name} </Text>
 | 
			
		||||
                  <Text
 | 
			
		||||
                    css={{
 | 
			
		||||
                      color: "$mauve9",
 | 
			
		||||
                      wordBreak: "break-word",
 | 
			
		||||
                    }}
 | 
			
		||||
                  >
 | 
			
		||||
                    {account.address} (
 | 
			
		||||
                    {Dinero({
 | 
			
		||||
                      amount: Number(account?.xrp || "0"),
 | 
			
		||||
                      precision: 6,
 | 
			
		||||
                    })
 | 
			
		||||
                      .toUnit()
 | 
			
		||||
                      .toLocaleString(undefined, {
 | 
			
		||||
                        style: "currency",
 | 
			
		||||
                        currency: "XRP",
 | 
			
		||||
                        currencyDisplay: "name",
 | 
			
		||||
                      })}
 | 
			
		||||
                    )
 | 
			
		||||
                  </Text>
 | 
			
		||||
                </Box>
 | 
			
		||||
                {!props.hideDeployBtn && (
 | 
			
		||||
                  <Button
 | 
			
		||||
                    css={{ ml: "auto" }}
 | 
			
		||||
                    size="xs"
 | 
			
		||||
                    uppercase
 | 
			
		||||
                    isLoading={account.isLoading}
 | 
			
		||||
                    disabled={
 | 
			
		||||
                      account.isLoading ||
 | 
			
		||||
                      !snap.files.filter(file => file.compiledWatContent).length
 | 
			
		||||
                    }
 | 
			
		||||
                    variant="secondary"
 | 
			
		||||
                    onClick={e => {
 | 
			
		||||
                      e.stopPropagation();
 | 
			
		||||
                      deployHook(account);
 | 
			
		||||
                    }}
 | 
			
		||||
                  >
 | 
			
		||||
                    Deploy
 | 
			
		||||
                  </Button>
 | 
			
		||||
                )}
 | 
			
		||||
              </Flex>
 | 
			
		||||
            ))}
 | 
			
		||||
          </Stack>
 | 
			
		||||
        </Box>
 | 
			
		||||
              {props.showHookStats && (
 | 
			
		||||
                <Text muted small css={{ mt: "$2" }}>
 | 
			
		||||
                  {account.hooks.length} hook{account.hooks.length === 1 ? "" : "s"} installed
 | 
			
		||||
                </Text>
 | 
			
		||||
              )}
 | 
			
		||||
            </Flex>
 | 
			
		||||
          ))}
 | 
			
		||||
        </Stack>
 | 
			
		||||
      </Container>
 | 
			
		||||
      <AccountDialog
 | 
			
		||||
        activeAccountAddress={activeAccountAddress}
 | 
			
		||||
@@ -413,7 +424,7 @@ const ImportAccountDialog = () => {
 | 
			
		||||
  return (
 | 
			
		||||
    <Dialog>
 | 
			
		||||
      <DialogTrigger asChild>
 | 
			
		||||
        <Button ghost size="xs">
 | 
			
		||||
        <Button ghost size="sm">
 | 
			
		||||
          Import
 | 
			
		||||
        </Button>
 | 
			
		||||
      </DialogTrigger>
 | 
			
		||||
@@ -425,7 +436,7 @@ const ImportAccountDialog = () => {
 | 
			
		||||
            name="secret"
 | 
			
		||||
            type="password"
 | 
			
		||||
            value={value}
 | 
			
		||||
            onChange={(e) => setValue(e.target.value)}
 | 
			
		||||
            onChange={e => setValue(e.target.value)}
 | 
			
		||||
          />
 | 
			
		||||
        </DialogDescription>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -91,26 +91,26 @@ export const StyledButton = styled("button", {
 | 
			
		||||
          },
 | 
			
		||||
      },
 | 
			
		||||
      primary: {
 | 
			
		||||
        backgroundColor: `$pink9`,
 | 
			
		||||
        boxShadow: "inset 0 0 0 1px $colors$pink9",
 | 
			
		||||
        backgroundColor: `$accent`,
 | 
			
		||||
        boxShadow: "inset 0 0 0 1px $colors$purple9",
 | 
			
		||||
        color: "$white",
 | 
			
		||||
        "@hover": {
 | 
			
		||||
          "&:hover": {
 | 
			
		||||
            backgroundColor: "$pink10",
 | 
			
		||||
            boxShadow: "inset 0 0 0 1px $colors$pink11",
 | 
			
		||||
            backgroundColor: "$purple10",
 | 
			
		||||
            boxShadow: "inset 0 0 0 1px $colors$purple11",
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        "&:active": {
 | 
			
		||||
          backgroundColor: "$pink8",
 | 
			
		||||
          boxShadow: "inset 0 0 0 1px $colors$pink8",
 | 
			
		||||
          backgroundColor: "$purple8",
 | 
			
		||||
          boxShadow: "inset 0 0 0 1px $colors$purple8",
 | 
			
		||||
        },
 | 
			
		||||
        "&:focus": {
 | 
			
		||||
          boxShadow: "inset 0 0 0 2px $colors$pink12",
 | 
			
		||||
          boxShadow: "inset 0 0 0 2px $colors$purple12",
 | 
			
		||||
        },
 | 
			
		||||
        '&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
 | 
			
		||||
          {
 | 
			
		||||
            backgroundColor: "$mauve4",
 | 
			
		||||
            boxShadow: "inset 0 0 0 1px $colors$pink8",
 | 
			
		||||
            boxShadow: "inset 0 0 0 1px $colors$purple8",
 | 
			
		||||
          },
 | 
			
		||||
      },
 | 
			
		||||
      secondary: {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										86
									
								
								components/DebugStream.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,86 @@
 | 
			
		||||
import { useEffect, useState } from "react";
 | 
			
		||||
import { useSnapshot } from "valtio";
 | 
			
		||||
import { Select } from ".";
 | 
			
		||||
import state from "../state";
 | 
			
		||||
import LogBox from "./LogBox";
 | 
			
		||||
import Text from "./Text";
 | 
			
		||||
 | 
			
		||||
const DebugStream = () => {
 | 
			
		||||
  const snap = useSnapshot(state);
 | 
			
		||||
 | 
			
		||||
  const accountOptions = snap.accounts.map(acc => ({
 | 
			
		||||
    label: acc.name,
 | 
			
		||||
    value: acc.address,
 | 
			
		||||
  }));
 | 
			
		||||
  const [selectedAccount, setSelectedAccount] = useState<typeof accountOptions[0] | null>(null);
 | 
			
		||||
 | 
			
		||||
  const renderNav = () => (
 | 
			
		||||
    <>
 | 
			
		||||
      <Text css={{ mx: "$2", fontSize: "inherit" }}>Account: </Text>
 | 
			
		||||
      <Select
 | 
			
		||||
        instanceId="debugStreamAccount"
 | 
			
		||||
        placeholder="Select account"
 | 
			
		||||
        options={accountOptions}
 | 
			
		||||
        hideSelectedOptions
 | 
			
		||||
        value={selectedAccount}
 | 
			
		||||
        onChange={acc => setSelectedAccount(acc as any)}
 | 
			
		||||
        css={{ width: "30%" }}
 | 
			
		||||
      />
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const account = selectedAccount?.value;
 | 
			
		||||
    if (!account) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const socket = new WebSocket(`wss://hooks-testnet-debugstream.xrpl-labs.com/${account}`);
 | 
			
		||||
 | 
			
		||||
    const onOpen = () => {
 | 
			
		||||
      state.debugLogs = [];
 | 
			
		||||
      state.debugLogs.push({
 | 
			
		||||
        type: "success",
 | 
			
		||||
        message: `Debug stream opened for account ${account}`,
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
    const onError = () => {
 | 
			
		||||
      state.debugLogs.push({
 | 
			
		||||
        type: "error",
 | 
			
		||||
        message: "Something went wrong in establishing connection!",
 | 
			
		||||
      });
 | 
			
		||||
      setSelectedAccount(null);
 | 
			
		||||
    };
 | 
			
		||||
    const onMessage = (event: any) => {
 | 
			
		||||
      if (!event.data) return;
 | 
			
		||||
      state.debugLogs.push({
 | 
			
		||||
        type: "log",
 | 
			
		||||
        message: event.data,
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    socket.addEventListener("open", onOpen);
 | 
			
		||||
    socket.addEventListener("close", onError);
 | 
			
		||||
    socket.addEventListener("error", onError);
 | 
			
		||||
    socket.addEventListener("message", onMessage);
 | 
			
		||||
 | 
			
		||||
    return () => {
 | 
			
		||||
      socket.removeEventListener("open", onOpen);
 | 
			
		||||
      socket.removeEventListener("close", onError);
 | 
			
		||||
      socket.removeEventListener("message", onMessage);
 | 
			
		||||
 | 
			
		||||
      socket.close();
 | 
			
		||||
    };
 | 
			
		||||
  }, [selectedAccount]);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <LogBox
 | 
			
		||||
      enhanced
 | 
			
		||||
      renderNav={renderNav}
 | 
			
		||||
      title="Debug stream"
 | 
			
		||||
      logs={snap.debugLogs}
 | 
			
		||||
      clearLog={() => (state.debugLogs = [])}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default DebugStream;
 | 
			
		||||
@@ -34,7 +34,7 @@ const DeployEditor = () => {
 | 
			
		||||
        display: "flex",
 | 
			
		||||
        position: "relative",
 | 
			
		||||
        flexDirection: "column",
 | 
			
		||||
        backgroundColor: "$mauve3",
 | 
			
		||||
        backgroundColor: "$mauve2",
 | 
			
		||||
        width: "100%",
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
 
 | 
			
		||||
@@ -71,7 +71,7 @@ const itemStyles = {
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  "&:focus": {
 | 
			
		||||
    backgroundColor: "$pink9",
 | 
			
		||||
    backgroundColor: "$purple9",
 | 
			
		||||
    color: "$white",
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
@@ -85,8 +85,8 @@ const StyledRadioItem = styled(DropdownMenuPrimitive.RadioItem, {
 | 
			
		||||
});
 | 
			
		||||
const StyledTriggerItem = styled(DropdownMenuPrimitive.TriggerItem, {
 | 
			
		||||
  '&[data-state="open"]': {
 | 
			
		||||
    backgroundColor: "$pink9",
 | 
			
		||||
    color: "$pink9",
 | 
			
		||||
    backgroundColor: "$purple9",
 | 
			
		||||
    color: "$purple9",
 | 
			
		||||
  },
 | 
			
		||||
  ...itemStyles,
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,12 @@ import NewWindow from "react-new-window";
 | 
			
		||||
import { signOut, useSession } from "next-auth/react";
 | 
			
		||||
import { useSnapshot } from "valtio";
 | 
			
		||||
 | 
			
		||||
import { createNewFile, syncToGist, updateEditorSettings, downloadAsZip } from "../state/actions";
 | 
			
		||||
import {
 | 
			
		||||
  createNewFile,
 | 
			
		||||
  syncToGist,
 | 
			
		||||
  updateEditorSettings,
 | 
			
		||||
  downloadAsZip,
 | 
			
		||||
} from "../state/actions";
 | 
			
		||||
import state from "../state";
 | 
			
		||||
import Box from "./Box";
 | 
			
		||||
import Button from "./Button";
 | 
			
		||||
@@ -57,7 +62,7 @@ import { styled } from "../stitches.config";
 | 
			
		||||
const DEFAULT_EXTENSION = ".c";
 | 
			
		||||
 | 
			
		||||
const ErrorText = styled(Text, {
 | 
			
		||||
  color: "$red9",
 | 
			
		||||
  color: "$crimson9",
 | 
			
		||||
  mt: "$1",
 | 
			
		||||
  display: "block",
 | 
			
		||||
});
 | 
			
		||||
@@ -85,9 +90,16 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
 | 
			
		||||
  const validateFilename = useCallback(
 | 
			
		||||
    (filename: string): { error: string | null } => {
 | 
			
		||||
      // check if filename already exists
 | 
			
		||||
      if (snap.files.find(file => file.name === filename)) {
 | 
			
		||||
        return { error: "Filename already exists." };
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // check for illegal characters
 | 
			
		||||
      const ILLEGAL_REGEX = /[/]/gi;
 | 
			
		||||
      if (filename.match(ILLEGAL_REGEX)) {
 | 
			
		||||
        return { error: "Filename contains illegal characters" };
 | 
			
		||||
      }
 | 
			
		||||
      // More checks in future
 | 
			
		||||
      return { error: null };
 | 
			
		||||
    },
 | 
			
		||||
@@ -95,7 +107,9 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
  );
 | 
			
		||||
  const handleConfirm = useCallback(() => {
 | 
			
		||||
    // add default extension in case omitted
 | 
			
		||||
    let _filename = filename.includes(".") ? filename : filename + DEFAULT_EXTENSION;
 | 
			
		||||
    let _filename = filename.includes(".")
 | 
			
		||||
      ? filename
 | 
			
		||||
      : filename + DEFAULT_EXTENSION;
 | 
			
		||||
    const chk = validateFilename(_filename);
 | 
			
		||||
    if (chk.error) {
 | 
			
		||||
      setNewfileError(`Error: ${chk.error}`);
 | 
			
		||||
@@ -139,7 +153,9 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
                return (
 | 
			
		||||
                  <Button
 | 
			
		||||
                    size="sm"
 | 
			
		||||
                    outline={showWat ? snap.activeWat !== index : snap.active !== index}
 | 
			
		||||
                    outline={
 | 
			
		||||
                      showWat ? snap.activeWat !== index : snap.active !== index
 | 
			
		||||
                    }
 | 
			
		||||
                    onClick={() => (state.active = index)}
 | 
			
		||||
                    key={file.name + index}
 | 
			
		||||
                    css={{
 | 
			
		||||
@@ -174,7 +190,8 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
                          // If deleted file is behind active tab
 | 
			
		||||
                          // we keep the current state otherwise
 | 
			
		||||
                          // select previous file on the list
 | 
			
		||||
                          state.active = index > snap.active ? snap.active : snap.active - 1;
 | 
			
		||||
                          state.active =
 | 
			
		||||
                            index > snap.active ? snap.active : snap.active - 1;
 | 
			
		||||
                        }}
 | 
			
		||||
                      >
 | 
			
		||||
                        <X size="9px" weight="bold" />
 | 
			
		||||
@@ -184,10 +201,18 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
                );
 | 
			
		||||
              })}
 | 
			
		||||
            {!showWat && (
 | 
			
		||||
              <Dialog open={isNewfileDialogOpen} onOpenChange={setIsNewfileDialogOpen}>
 | 
			
		||||
              <Dialog
 | 
			
		||||
                open={isNewfileDialogOpen}
 | 
			
		||||
                onOpenChange={setIsNewfileDialogOpen}
 | 
			
		||||
              >
 | 
			
		||||
                <DialogTrigger asChild>
 | 
			
		||||
                  <Button ghost size="sm" css={{ alignItems: "center", px: "$2", mr: "$3" }}>
 | 
			
		||||
                    <Plus size="16px" /> {snap.files.length === 0 && "Add new file"}
 | 
			
		||||
                  <Button
 | 
			
		||||
                    ghost
 | 
			
		||||
                    size="sm"
 | 
			
		||||
                    css={{ alignItems: "center", px: "$2", mr: "$3" }}
 | 
			
		||||
                  >
 | 
			
		||||
                    <Plus size="16px" />{" "}
 | 
			
		||||
                    {snap.files.length === 0 && "Add new file"}
 | 
			
		||||
                  </Button>
 | 
			
		||||
                </DialogTrigger>
 | 
			
		||||
                <DialogContent>
 | 
			
		||||
@@ -196,8 +221,8 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
                    <label>Filename</label>
 | 
			
		||||
                    <Input
 | 
			
		||||
                      value={filename}
 | 
			
		||||
                      onChange={e => setFilename(e.target.value)}
 | 
			
		||||
                      onKeyPress={e => {
 | 
			
		||||
                      onChange={(e) => setFilename(e.target.value)}
 | 
			
		||||
                      onKeyPress={(e) => {
 | 
			
		||||
                        if (e.key === "Enter") {
 | 
			
		||||
                          handleConfirm();
 | 
			
		||||
                        }
 | 
			
		||||
@@ -216,10 +241,7 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
                    <DialogClose asChild>
 | 
			
		||||
                      <Button outline>Cancel</Button>
 | 
			
		||||
                    </DialogClose>
 | 
			
		||||
                    <Button
 | 
			
		||||
                      variant="primary"
 | 
			
		||||
                      onClick={handleConfirm}
 | 
			
		||||
                    >
 | 
			
		||||
                    <Button variant="primary" onClick={handleConfirm}>
 | 
			
		||||
                      Create file
 | 
			
		||||
                    </Button>
 | 
			
		||||
                  </Flex>
 | 
			
		||||
@@ -237,11 +259,13 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
      <Flex
 | 
			
		||||
        css={{
 | 
			
		||||
          py: "$3",
 | 
			
		||||
          backgroundColor: "$mauve3",
 | 
			
		||||
          backgroundColor: "$mauve2",
 | 
			
		||||
          zIndex: 1,
 | 
			
		||||
        }}
 | 
			
		||||
      >
 | 
			
		||||
        <Container css={{ width: "unset", display: "flex", alignItems: "center" }}>
 | 
			
		||||
        <Container
 | 
			
		||||
          css={{ width: "unset", display: "flex", alignItems: "center" }}
 | 
			
		||||
        >
 | 
			
		||||
          {status === "authenticated" ? (
 | 
			
		||||
            <DropdownMenu>
 | 
			
		||||
              <DropdownMenuTrigger asChild>
 | 
			
		||||
@@ -274,10 +298,15 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
              </DropdownMenuTrigger>
 | 
			
		||||
              <DropdownMenuContent>
 | 
			
		||||
                <DropdownMenuItem disabled onClick={() => signOut()}>
 | 
			
		||||
                  <User size="16px" /> {session?.user?.username} ({session?.user.name})
 | 
			
		||||
                  <User size="16px" /> {session?.user?.username} (
 | 
			
		||||
                  {session?.user.name})
 | 
			
		||||
                </DropdownMenuItem>
 | 
			
		||||
                <DropdownMenuItem
 | 
			
		||||
                  onClick={() => window.open(`http://gist.github.com/${session?.user.username}`)}
 | 
			
		||||
                  onClick={() =>
 | 
			
		||||
                    window.open(
 | 
			
		||||
                      `http://gist.github.com/${session?.user.username}`
 | 
			
		||||
                    )
 | 
			
		||||
                  }
 | 
			
		||||
                >
 | 
			
		||||
                  <ArrowSquareOut size="16px" />
 | 
			
		||||
                  Go to your Gist
 | 
			
		||||
@@ -291,7 +320,12 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
              </DropdownMenuContent>
 | 
			
		||||
            </DropdownMenu>
 | 
			
		||||
          ) : (
 | 
			
		||||
            <Button outline size="sm" css={{ mr: "$3" }} onClick={() => setPopUp(true)}>
 | 
			
		||||
            <Button
 | 
			
		||||
              outline
 | 
			
		||||
              size="sm"
 | 
			
		||||
              css={{ mr: "$3" }}
 | 
			
		||||
              onClick={() => setPopUp(true)}
 | 
			
		||||
            >
 | 
			
		||||
              <GithubLogo size="16px" /> Login
 | 
			
		||||
            </Button>
 | 
			
		||||
          )}
 | 
			
		||||
@@ -330,7 +364,13 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
              },
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            <Button isLoading={snap.zipLoading} onClick={downloadAsZip} outline size="sm" css={{ alignItems: "center" }}>
 | 
			
		||||
            <Button
 | 
			
		||||
              isLoading={snap.zipLoading}
 | 
			
		||||
              onClick={downloadAsZip}
 | 
			
		||||
              outline
 | 
			
		||||
              size="sm"
 | 
			
		||||
              css={{ alignItems: "center" }}
 | 
			
		||||
            >
 | 
			
		||||
              <DownloadSimple size="16px" />
 | 
			
		||||
            </Button>
 | 
			
		||||
            <Button
 | 
			
		||||
@@ -338,7 +378,9 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
              size="sm"
 | 
			
		||||
              css={{ alignItems: "center" }}
 | 
			
		||||
              onClick={() => {
 | 
			
		||||
                navigator.clipboard.writeText(`${window.location.origin}/develop/${snap.gistId}`);
 | 
			
		||||
                navigator.clipboard.writeText(
 | 
			
		||||
                  `${window.location.origin}/develop/${snap.gistId}`
 | 
			
		||||
                );
 | 
			
		||||
                toast.success("Copied share link to clipboard!");
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
@@ -368,7 +410,10 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
                </Button>
 | 
			
		||||
              </DropdownMenuTrigger>
 | 
			
		||||
              <DropdownMenuContent>
 | 
			
		||||
                <DropdownMenuItem disabled={snap.zipLoading} onClick={downloadAsZip}>
 | 
			
		||||
                <DropdownMenuItem
 | 
			
		||||
                  disabled={snap.zipLoading}
 | 
			
		||||
                  onClick={downloadAsZip}
 | 
			
		||||
                >
 | 
			
		||||
                  <DownloadSimple size="16px" /> Download as ZIP
 | 
			
		||||
                </DropdownMenuItem>
 | 
			
		||||
                <DropdownMenuItem
 | 
			
		||||
@@ -383,7 +428,9 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
                  Copy share link to clipboard
 | 
			
		||||
                </DropdownMenuItem>
 | 
			
		||||
                <DropdownMenuItem
 | 
			
		||||
                  disabled={session?.user.username !== snap.gistOwner || !snap.gistId}
 | 
			
		||||
                  disabled={
 | 
			
		||||
                    session?.user.username !== snap.gistOwner || !snap.gistId
 | 
			
		||||
                  }
 | 
			
		||||
                  onClick={() => {
 | 
			
		||||
                    syncToGist(session);
 | 
			
		||||
                  }}
 | 
			
		||||
@@ -409,15 +456,21 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
            </DropdownMenu>
 | 
			
		||||
          </Stack>
 | 
			
		||||
 | 
			
		||||
          {popup && !session ? <NewWindow center="parent" url="/sign-in" /> : null}
 | 
			
		||||
          {popup && !session ? (
 | 
			
		||||
            <NewWindow center="parent" url="/sign-in" />
 | 
			
		||||
          ) : null}
 | 
			
		||||
        </Container>
 | 
			
		||||
      </Flex>
 | 
			
		||||
      <AlertDialog open={createNewAlertOpen} onOpenChange={value => setCreateNewAlertOpen(value)}>
 | 
			
		||||
      <AlertDialog
 | 
			
		||||
        open={createNewAlertOpen}
 | 
			
		||||
        onOpenChange={(value) => setCreateNewAlertOpen(value)}
 | 
			
		||||
      >
 | 
			
		||||
        <AlertDialogContent>
 | 
			
		||||
          <AlertDialogTitle>Are you sure?</AlertDialogTitle>
 | 
			
		||||
          <AlertDialogDescription>
 | 
			
		||||
            This action will create new <strong>public</strong> Github Gist from your current saved
 | 
			
		||||
            files. You can delete gist anytime from your GitHub Gists page.
 | 
			
		||||
            This action will create new <strong>public</strong> Github Gist from
 | 
			
		||||
            your current saved files. You can delete gist anytime from your
 | 
			
		||||
            GitHub Gists page.
 | 
			
		||||
          </AlertDialogDescription>
 | 
			
		||||
          <Flex css={{ justifyContent: "flex-end", gap: "$3" }}>
 | 
			
		||||
            <AlertDialogCancel asChild>
 | 
			
		||||
@@ -451,8 +504,8 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
              type="number"
 | 
			
		||||
              min="1"
 | 
			
		||||
              value={editorSettings.tabSize}
 | 
			
		||||
              onChange={e =>
 | 
			
		||||
                setEditorSettings(curr => ({
 | 
			
		||||
              onChange={(e) =>
 | 
			
		||||
                setEditorSettings((curr) => ({
 | 
			
		||||
                  ...curr,
 | 
			
		||||
                  tabSize: Number(e.target.value),
 | 
			
		||||
                }))
 | 
			
		||||
@@ -462,12 +515,18 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
 | 
			
		||||
          <Flex css={{ marginTop: 25, justifyContent: "flex-end", gap: "$3" }}>
 | 
			
		||||
            <DialogClose asChild>
 | 
			
		||||
              <Button outline onClick={() => updateEditorSettings(editorSettings)}>
 | 
			
		||||
              <Button
 | 
			
		||||
                outline
 | 
			
		||||
                onClick={() => updateEditorSettings(editorSettings)}
 | 
			
		||||
              >
 | 
			
		||||
                Cancel
 | 
			
		||||
              </Button>
 | 
			
		||||
            </DialogClose>
 | 
			
		||||
            <DialogClose asChild>
 | 
			
		||||
              <Button variant="primary" onClick={() => updateEditorSettings(editorSettings)}>
 | 
			
		||||
              <Button
 | 
			
		||||
                variant="primary"
 | 
			
		||||
                onClick={() => updateEditorSettings(editorSettings)}
 | 
			
		||||
              >
 | 
			
		||||
                Save changes
 | 
			
		||||
              </Button>
 | 
			
		||||
            </DialogClose>
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,23 @@ import Box from "./Box";
 | 
			
		||||
 | 
			
		||||
const Flex = styled(Box, {
 | 
			
		||||
  display: "flex",
 | 
			
		||||
  variants: {
 | 
			
		||||
    row: {
 | 
			
		||||
      true: {
 | 
			
		||||
        flexDirection: "row",
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    column: {
 | 
			
		||||
      true: {
 | 
			
		||||
        flexDirection: "column",
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    fluid: {
 | 
			
		||||
      true: {
 | 
			
		||||
        width: '100%'
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export default Flex;
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ import Container from "./Container";
 | 
			
		||||
import dark from "../theme/editor/amy.json";
 | 
			
		||||
import light from "../theme/editor/xcode_default.json";
 | 
			
		||||
import { saveFile } from "../state/actions";
 | 
			
		||||
import { apiHeaderFiles } from "../state/constants";
 | 
			
		||||
import state from "../state";
 | 
			
		||||
 | 
			
		||||
import EditorNavigation from "./EditorNavigation";
 | 
			
		||||
@@ -26,12 +27,26 @@ loader.config({
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const validateWritability = (editor: monaco.editor.IStandaloneCodeEditor) => {
 | 
			
		||||
  const currPath = editor.getModel()?.uri.path;
 | 
			
		||||
  if (apiHeaderFiles.find((h) => currPath?.endsWith(h))) {
 | 
			
		||||
    editor.updateOptions({ readOnly: true });
 | 
			
		||||
  } else {
 | 
			
		||||
    editor.updateOptions({ readOnly: false });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const HooksEditor = () => {
 | 
			
		||||
  const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>();
 | 
			
		||||
  const subscriptionRef = useRef<ReconnectingWebSocket | null>(null);
 | 
			
		||||
  const snap = useSnapshot(state);
 | 
			
		||||
  const router = useRouter();
 | 
			
		||||
  const { theme } = useTheme();
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (editorRef.current) validateWritability(editorRef.current);
 | 
			
		||||
  }, [snap.active]);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    return () => {
 | 
			
		||||
      subscriptionRef?.current?.close();
 | 
			
		||||
@@ -45,7 +60,7 @@ const HooksEditor = () => {
 | 
			
		||||
        display: "flex",
 | 
			
		||||
        position: "relative",
 | 
			
		||||
        flexDirection: "column",
 | 
			
		||||
        backgroundColor: "$mauve3",
 | 
			
		||||
        backgroundColor: "$mauve2",
 | 
			
		||||
        width: "100%",
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
@@ -130,6 +145,7 @@ const HooksEditor = () => {
 | 
			
		||||
                saveFile();
 | 
			
		||||
              }
 | 
			
		||||
            );
 | 
			
		||||
            validateWritability(editor);
 | 
			
		||||
          }}
 | 
			
		||||
          theme={theme === "dark" ? "dark" : "light"}
 | 
			
		||||
        />
 | 
			
		||||
@@ -157,7 +173,7 @@ const HooksEditor = () => {
 | 
			
		||||
                    fontFamily: "$monospace",
 | 
			
		||||
                  }}
 | 
			
		||||
                >
 | 
			
		||||
                  Click the link above to create a your file
 | 
			
		||||
                  Click the link above to create your file
 | 
			
		||||
                </Text>
 | 
			
		||||
              </Box>
 | 
			
		||||
            </Box>
 | 
			
		||||
 
 | 
			
		||||
@@ -110,20 +110,24 @@ export const Input = styled("input", {
 | 
			
		||||
          backgroundColor: "transparent",
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      deep: {
 | 
			
		||||
        backgroundColor: "$deep",
 | 
			
		||||
        boxShadow: "none",
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    state: {
 | 
			
		||||
      invalid: {
 | 
			
		||||
        boxShadow: "inset 0 0 0 1px $colors$red7",
 | 
			
		||||
        boxShadow: "inset 0 0 0 1px $colors$crimson7",
 | 
			
		||||
        "&:focus": {
 | 
			
		||||
          boxShadow:
 | 
			
		||||
            "inset 0px 0px 0px 1px $colors$red8, 0px 0px 0px 1px $colors$red8",
 | 
			
		||||
            "inset 0px 0px 0px 1px $colors$crimson8, 0px 0px 0px 1px $colors$crimson8",
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      valid: {
 | 
			
		||||
        boxShadow: "inset 0 0 0 1px $colors$green7",
 | 
			
		||||
        boxShadow: "inset 0 0 0 1px $colors$grass7",
 | 
			
		||||
        "&:focus": {
 | 
			
		||||
          boxShadow:
 | 
			
		||||
            "inset 0px 0px 0px 1px $colors$green8, 0px 0px 0px 1px $colors$green8",
 | 
			
		||||
            "inset 0px 0px 0px 1px $colors$grass8, 0px 0px 0px 1px $colors$grass8",
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import React, { useRef, useLayoutEffect } from "react";
 | 
			
		||||
import React, { useRef, useLayoutEffect, ReactNode } from "react";
 | 
			
		||||
import { Notepad, Prohibit } from "phosphor-react";
 | 
			
		||||
import useStayScrolled from "react-stay-scrolled";
 | 
			
		||||
import NextLink from "next/link";
 | 
			
		||||
@@ -17,9 +17,18 @@ interface ILogBox {
 | 
			
		||||
  title: string;
 | 
			
		||||
  clearLog?: () => void;
 | 
			
		||||
  logs: ILog[];
 | 
			
		||||
  renderNav?: () => ReactNode;
 | 
			
		||||
  enhanced?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children }) => {
 | 
			
		||||
const LogBox: React.FC<ILogBox> = ({
 | 
			
		||||
  title,
 | 
			
		||||
  clearLog,
 | 
			
		||||
  logs,
 | 
			
		||||
  children,
 | 
			
		||||
  renderNav,
 | 
			
		||||
  enhanced,
 | 
			
		||||
}) => {
 | 
			
		||||
  const logRef = useRef<HTMLPreElement>(null);
 | 
			
		||||
  const { stayScrolled /*, scrollBottom*/ } = useStayScrolled(logRef);
 | 
			
		||||
 | 
			
		||||
@@ -36,10 +45,23 @@ const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children }) => {
 | 
			
		||||
        background: "$mauve1",
 | 
			
		||||
        position: "relative",
 | 
			
		||||
        flex: 1,
 | 
			
		||||
        height: "100%",
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      <Container css={{ px: 0, flexShrink: 1 }}>
 | 
			
		||||
        <Flex css={{ py: "$3" }}>
 | 
			
		||||
      <Container
 | 
			
		||||
        css={{
 | 
			
		||||
          px: 0,
 | 
			
		||||
          height: "100%",
 | 
			
		||||
        }}
 | 
			
		||||
      >
 | 
			
		||||
        <Flex
 | 
			
		||||
          css={{
 | 
			
		||||
            height: "48px",
 | 
			
		||||
            alignItems: "center",
 | 
			
		||||
            fontSize: "$sm",
 | 
			
		||||
            fontWeight: 300,
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          <Heading
 | 
			
		||||
            as="h3"
 | 
			
		||||
            css={{
 | 
			
		||||
@@ -56,6 +78,7 @@ const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children }) => {
 | 
			
		||||
          >
 | 
			
		||||
            <Notepad size="15px" /> <Text css={{ lineHeight: 1 }}>{title}</Text>
 | 
			
		||||
          </Heading>
 | 
			
		||||
          {renderNav?.()}
 | 
			
		||||
          <Flex css={{ ml: "auto", gap: "$3", marginRight: "$3" }}>
 | 
			
		||||
            {clearLog && (
 | 
			
		||||
              <Button ghost size="xs" onClick={clearLog}>
 | 
			
		||||
@@ -64,6 +87,7 @@ const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children }) => {
 | 
			
		||||
            )}
 | 
			
		||||
          </Flex>
 | 
			
		||||
        </Flex>
 | 
			
		||||
 | 
			
		||||
        <Box
 | 
			
		||||
          as="pre"
 | 
			
		||||
          ref={logRef}
 | 
			
		||||
@@ -73,21 +97,29 @@ const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children }) => {
 | 
			
		||||
            display: "flex",
 | 
			
		||||
            flexDirection: "column",
 | 
			
		||||
            width: "100%",
 | 
			
		||||
            height: "160px",
 | 
			
		||||
            height: "calc(100% - 48px)", // 100% minus the logbox header height
 | 
			
		||||
            overflowY: "auto",
 | 
			
		||||
            fontSize: "13px",
 | 
			
		||||
            fontWeight: "$body",
 | 
			
		||||
            fontFamily: "$monospace",
 | 
			
		||||
            px: "$3",
 | 
			
		||||
            pb: "$2",
 | 
			
		||||
            whiteSpace: "normal",
 | 
			
		||||
            overflowY: "auto",
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          {logs?.map((log, index) => (
 | 
			
		||||
            <Box as="span" key={log.type + index}>
 | 
			
		||||
              {/* <LogText capitalize variant={log.type}>
 | 
			
		||||
                {log.type}:{" "}
 | 
			
		||||
              </LogText> */}
 | 
			
		||||
            <Box
 | 
			
		||||
              as="span"
 | 
			
		||||
              key={log.type + index}
 | 
			
		||||
              css={{
 | 
			
		||||
                "@hover": {
 | 
			
		||||
                  "&:hover": {
 | 
			
		||||
                    backgroundColor: enhanced ? "$backgroundAlt" : undefined,
 | 
			
		||||
                  },
 | 
			
		||||
                },
 | 
			
		||||
                p: enhanced ? "$2 $1" : undefined,
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              <LogText variant={log.type}>
 | 
			
		||||
                {log.message}{" "}
 | 
			
		||||
                {log.link && (
 | 
			
		||||
 
 | 
			
		||||
@@ -4,19 +4,20 @@ const Text = styled("span", {
 | 
			
		||||
  fontFamily: "$monospace",
 | 
			
		||||
  lineHeight: "$body",
 | 
			
		||||
  color: "$text",
 | 
			
		||||
  wordWrap: "break-word",
 | 
			
		||||
  variants: {
 | 
			
		||||
    variant: {
 | 
			
		||||
      log: {
 | 
			
		||||
        color: "$text",
 | 
			
		||||
      },
 | 
			
		||||
      warning: {
 | 
			
		||||
        color: "$yellow11",
 | 
			
		||||
        color: "$amber11",
 | 
			
		||||
      },
 | 
			
		||||
      error: {
 | 
			
		||||
        color: "$red11",
 | 
			
		||||
        color: "$crimson11",
 | 
			
		||||
      },
 | 
			
		||||
      success: {
 | 
			
		||||
        color: "$green11",
 | 
			
		||||
        color: "$grass11",
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    capitalize: {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
import { styled } from "../stitches.config";
 | 
			
		||||
 | 
			
		||||
const SVG = styled("svg", {
 | 
			
		||||
  "& #path-one, & #path-two": {
 | 
			
		||||
    fill: "$text",
 | 
			
		||||
  "& #path": {
 | 
			
		||||
    fill: "$accent",
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
function Logo({
 | 
			
		||||
@@ -14,21 +14,18 @@ function Logo({
 | 
			
		||||
}) {
 | 
			
		||||
  return (
 | 
			
		||||
    <SVG
 | 
			
		||||
      width={width || "1em"}
 | 
			
		||||
      height={height || "1em"}
 | 
			
		||||
      viewBox="0 0 28 22"
 | 
			
		||||
      width={width || "1.1em"}
 | 
			
		||||
      height={height || "1.1em"}
 | 
			
		||||
      viewBox="0 0 294 283"
 | 
			
		||||
      fill="none"
 | 
			
		||||
      xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
    >
 | 
			
		||||
      <path
 | 
			
		||||
        id="path-one"
 | 
			
		||||
        d="M19.603 3.87h2.3l-4.786 4.747a4.466 4.466 0 01-6.276 0L6.054 3.871h2.3l3.636 3.605a2.828 2.828 0 003.975 0l3.638-3.605zM8.325 17.069h-2.3l4.816-4.776a4.466 4.466 0 016.276 0l4.816 4.776h-2.3l-3.665-3.635a2.828 2.828 0 00-3.975 0l-3.668 3.635z"
 | 
			
		||||
      />
 | 
			
		||||
      <path
 | 
			
		||||
        id="path-two"
 | 
			
		||||
        fillRule="evenodd"
 | 
			
		||||
        clipRule="evenodd"
 | 
			
		||||
        d="M1.556 9.769h4.751v1.555H1.556v10.072H0V0h1.556v9.769zM26.444 9.769h-4.751v1.555h4.751v10.072H28V0h-1.556v9.769z"
 | 
			
		||||
        id="path"
 | 
			
		||||
        d="M265.827 235L172.416 141.589L265.005 49H226.822L147.732 128.089H53.5514L27.4824 155.089H147.732L227.643 235H265.827Z"
 | 
			
		||||
        fill="#9D2DFF"
 | 
			
		||||
      />
 | 
			
		||||
    </SVG>
 | 
			
		||||
  );
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@ import {
 | 
			
		||||
  DialogTrigger,
 | 
			
		||||
} from "./Dialog";
 | 
			
		||||
import PanelBox from "./PanelBox";
 | 
			
		||||
import { templateFileIds } from "../state/constants";
 | 
			
		||||
 | 
			
		||||
const Navigation = () => {
 | 
			
		||||
  const router = useRouter();
 | 
			
		||||
@@ -39,9 +40,11 @@ const Navigation = () => {
 | 
			
		||||
      as="nav"
 | 
			
		||||
      css={{
 | 
			
		||||
        display: "flex",
 | 
			
		||||
        backgroundColor: "$mauve1",
 | 
			
		||||
        borderBottom: "1px solid $mauve6",
 | 
			
		||||
        position: "relative",
 | 
			
		||||
        zIndex: 2003,
 | 
			
		||||
        height: "60px",
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      <Container
 | 
			
		||||
@@ -59,7 +62,7 @@ const Navigation = () => {
 | 
			
		||||
            pr: "$4",
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          <Link href="/" passHref>
 | 
			
		||||
          <Link href={gistId ? `/develop/${gistId}` : "/develop"} passHref>
 | 
			
		||||
            <Box
 | 
			
		||||
              as="a"
 | 
			
		||||
              css={{
 | 
			
		||||
@@ -68,7 +71,7 @@ const Navigation = () => {
 | 
			
		||||
                color: "$textColor",
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              <Logo width="30px" height="30px" />
 | 
			
		||||
              <Logo width="32px" height="32px" />
 | 
			
		||||
            </Box>
 | 
			
		||||
          </Link>
 | 
			
		||||
          <Flex
 | 
			
		||||
@@ -89,10 +92,25 @@ const Navigation = () => {
 | 
			
		||||
                  css={{ fontSize: "$xs", color: "$mauve10", lineHeight: 1 }}
 | 
			
		||||
                >
 | 
			
		||||
                  {snap.files.length > 0 ? "Gist: " : "Playground"}
 | 
			
		||||
                  <Text css={{ color: "$mauve12" }}>
 | 
			
		||||
                    {snap.files.length > 0 &&
 | 
			
		||||
                      `${snap.gistOwner || "-"}/${truncate(snap.gistId || "")}`}
 | 
			
		||||
                  </Text>
 | 
			
		||||
                  {snap.files.length > 0 && (
 | 
			
		||||
                    <Link
 | 
			
		||||
                      href={`https://gist.github.com/${snap.gistOwner || ""}/${
 | 
			
		||||
                        snap.gistId || ""
 | 
			
		||||
                      }`}
 | 
			
		||||
                      passHref
 | 
			
		||||
                    >
 | 
			
		||||
                      <Text
 | 
			
		||||
                        as="a"
 | 
			
		||||
                        target="_blank"
 | 
			
		||||
                        rel="noreferrer noopener"
 | 
			
		||||
                        css={{ color: "$mauve12" }}
 | 
			
		||||
                      >
 | 
			
		||||
                        {`${snap.gistOwner || "-"}/${truncate(
 | 
			
		||||
                          snap.gistId || ""
 | 
			
		||||
                        )}`}
 | 
			
		||||
                      </Text>
 | 
			
		||||
                    </Link>
 | 
			
		||||
                  )}
 | 
			
		||||
                </Text>
 | 
			
		||||
              </>
 | 
			
		||||
            )}
 | 
			
		||||
@@ -136,10 +154,12 @@ const Navigation = () => {
 | 
			
		||||
                        flexDirection: "column",
 | 
			
		||||
                        p: "$7",
 | 
			
		||||
                        height: "100%",
 | 
			
		||||
                        backgroundColor: "$mauve2",
 | 
			
		||||
                        "@md": {
 | 
			
		||||
                          width: "30%",
 | 
			
		||||
                          maxWidth: "300px",
 | 
			
		||||
                          borderBottom: "0px",
 | 
			
		||||
                          borderRight: "1px solid $colors$mauve5",
 | 
			
		||||
                          borderRight: "1px solid $colors$mauve6",
 | 
			
		||||
                        },
 | 
			
		||||
                      }}
 | 
			
		||||
                    >
 | 
			
		||||
@@ -150,9 +170,11 @@ const Navigation = () => {
 | 
			
		||||
                          alignItems: "center",
 | 
			
		||||
                          gap: "$3",
 | 
			
		||||
                          fontSize: "$xl",
 | 
			
		||||
                          lineHeight: "$one",
 | 
			
		||||
                          fontWeight: "$bold",
 | 
			
		||||
                        }}
 | 
			
		||||
                      >
 | 
			
		||||
                        <Logo width="30px" height="30px" /> XRPL Hooks Editor
 | 
			
		||||
                        <Logo width="48px" height="48px" /> XRPL Hooks Builder
 | 
			
		||||
                      </DialogTitle>
 | 
			
		||||
                      <DialogDescription as="div">
 | 
			
		||||
                        <Text
 | 
			
		||||
@@ -174,9 +196,9 @@ const Navigation = () => {
 | 
			
		||||
                              display: "inline-flex",
 | 
			
		||||
                              alignItems: "center",
 | 
			
		||||
                              gap: "$3",
 | 
			
		||||
                              color: "$green9",
 | 
			
		||||
                              color: "$purple10",
 | 
			
		||||
                              "&:hover": {
 | 
			
		||||
                                color: "$green11 !important",
 | 
			
		||||
                                color: "$purple11",
 | 
			
		||||
                              },
 | 
			
		||||
                              "&:focus": {
 | 
			
		||||
                                outline: 0,
 | 
			
		||||
@@ -187,7 +209,7 @@ const Navigation = () => {
 | 
			
		||||
                            target="_blank"
 | 
			
		||||
                            href="https://github.com/XRPL-Labs/xrpld-hooks"
 | 
			
		||||
                          >
 | 
			
		||||
                            <ArrowUpRight size="15px" /> Developing Hooks
 | 
			
		||||
                            <ArrowUpRight size="15px" /> Hooks Github
 | 
			
		||||
                          </Text>
 | 
			
		||||
 | 
			
		||||
                          <Text
 | 
			
		||||
@@ -195,9 +217,9 @@ const Navigation = () => {
 | 
			
		||||
                              display: "inline-flex",
 | 
			
		||||
                              alignItems: "center",
 | 
			
		||||
                              gap: "$3",
 | 
			
		||||
                              color: "$green9",
 | 
			
		||||
                              color: "$purple10",
 | 
			
		||||
                              "&:hover": {
 | 
			
		||||
                                color: "$green11 !important",
 | 
			
		||||
                                color: "$purple11",
 | 
			
		||||
                              },
 | 
			
		||||
                              "&:focus": {
 | 
			
		||||
                                outline: 0,
 | 
			
		||||
@@ -215,9 +237,9 @@ const Navigation = () => {
 | 
			
		||||
                              display: "inline-flex",
 | 
			
		||||
                              alignItems: "center",
 | 
			
		||||
                              gap: "$3",
 | 
			
		||||
                              color: "$green9",
 | 
			
		||||
                              color: "$purple10",
 | 
			
		||||
                              "&:hover": {
 | 
			
		||||
                                color: "$green11 !important",
 | 
			
		||||
                                color: "$purple11",
 | 
			
		||||
                              },
 | 
			
		||||
                              "&:focus": {
 | 
			
		||||
                                outline: 0,
 | 
			
		||||
@@ -244,9 +266,7 @@ const Navigation = () => {
 | 
			
		||||
                        gap: "$3",
 | 
			
		||||
                        alignItems: "flex-start",
 | 
			
		||||
                        flexWrap: "wrap",
 | 
			
		||||
                        backgroundImage: `url('/pattern.svg'), url('/pattern-2.svg')`,
 | 
			
		||||
                        backgroundRepeat: "no-repeat",
 | 
			
		||||
                        backgroundPosition: "bottom left, top right",
 | 
			
		||||
                        backgroundColor: "$mauve1",
 | 
			
		||||
                        "@md": {
 | 
			
		||||
                          gridTemplateColumns: "1fr 1fr 1fr",
 | 
			
		||||
                          gridTemplateRows: "max-content",
 | 
			
		||||
@@ -255,16 +275,16 @@ const Navigation = () => {
 | 
			
		||||
                    >
 | 
			
		||||
                      <PanelBox
 | 
			
		||||
                        as="a"
 | 
			
		||||
                        href="/develop/be088224fb37c0075e84491da0e602c1"
 | 
			
		||||
                        href={`/develop/${templateFileIds.starter}`}
 | 
			
		||||
                      >
 | 
			
		||||
                        <Heading>Starter</Heading>
 | 
			
		||||
                        <Text>
 | 
			
		||||
                          Just an empty starter with essential imports
 | 
			
		||||
                          Just a basic starter with essential imports
 | 
			
		||||
                        </Text>
 | 
			
		||||
                      </PanelBox>
 | 
			
		||||
                      <PanelBox
 | 
			
		||||
                        as="a"
 | 
			
		||||
                        href="/develop/be088224fb37c0075e84491da0e602c1"
 | 
			
		||||
                        href={`/develop/${templateFileIds.firewall}`}
 | 
			
		||||
                      >
 | 
			
		||||
                        <Heading>Firewall</Heading>
 | 
			
		||||
                        <Text>
 | 
			
		||||
@@ -273,23 +293,26 @@ const Navigation = () => {
 | 
			
		||||
                      </PanelBox>
 | 
			
		||||
                      <PanelBox
 | 
			
		||||
                        as="a"
 | 
			
		||||
                        href="/develop/be088224fb37c0075e84491da0e602c1"
 | 
			
		||||
                        href={`/develop/${templateFileIds.notary}`}
 | 
			
		||||
                      >
 | 
			
		||||
                        <Heading>Accept</Heading>
 | 
			
		||||
                        <Heading>Notary</Heading>
 | 
			
		||||
                        <Text>
 | 
			
		||||
                          This hook just accepts any transaction coming through
 | 
			
		||||
                          it
 | 
			
		||||
                          Collecting signatures for multi-sign transactions
 | 
			
		||||
                        </Text>
 | 
			
		||||
                      </PanelBox>
 | 
			
		||||
                      <PanelBox
 | 
			
		||||
                        as="a"
 | 
			
		||||
                        href="/develop/be088224fb37c0075e84491da0e602c1"
 | 
			
		||||
                        href={`/develop/${templateFileIds.carbon}`}
 | 
			
		||||
                      >
 | 
			
		||||
                        <Heading>Accept</Heading>
 | 
			
		||||
                        <Text>
 | 
			
		||||
                          This hook just accepts any transaction coming through
 | 
			
		||||
                          it
 | 
			
		||||
                        </Text>
 | 
			
		||||
                        <Heading>Carbon</Heading>
 | 
			
		||||
                        <Text>Send a percentage of sum to an address</Text>
 | 
			
		||||
                      </PanelBox>
 | 
			
		||||
                      <PanelBox
 | 
			
		||||
                        as="a"
 | 
			
		||||
                        href={`/develop/${templateFileIds.peggy}`}
 | 
			
		||||
                      >
 | 
			
		||||
                        <Heading>Peggy</Heading>
 | 
			
		||||
                        <Text>An oracle based stable coin hook</Text>
 | 
			
		||||
                      </PanelBox>
 | 
			
		||||
                    </Flex>
 | 
			
		||||
                  </Flex>
 | 
			
		||||
@@ -376,9 +399,13 @@ const Navigation = () => {
 | 
			
		||||
                </Button>
 | 
			
		||||
              </Link>
 | 
			
		||||
            </ButtonGroup>
 | 
			
		||||
            <Button outline disabled>
 | 
			
		||||
              <BookOpen size="15px" />
 | 
			
		||||
            </Button>
 | 
			
		||||
            <Link href="https://xrpl-hooks.readme.io/" passHref>
 | 
			
		||||
              <a target="_blank" rel="noreferrer noopener">
 | 
			
		||||
                <Button outline>
 | 
			
		||||
                  <BookOpen size="15px" />
 | 
			
		||||
                </Button>
 | 
			
		||||
              </a>
 | 
			
		||||
            </Link>
 | 
			
		||||
          </Stack>
 | 
			
		||||
        </Flex>
 | 
			
		||||
      </Container>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,8 +5,8 @@ import Text from "./Text";
 | 
			
		||||
const PanelBox = styled("div", {
 | 
			
		||||
  display: "flex",
 | 
			
		||||
  flexDirection: "column",
 | 
			
		||||
  border: "1px solid $colors$mauve5",
 | 
			
		||||
  backgroundColor: "$mauve1",
 | 
			
		||||
  border: "1px solid $colors$mauve6",
 | 
			
		||||
  backgroundColor: "$mauve2",
 | 
			
		||||
  padding: "$3",
 | 
			
		||||
  borderRadius: "$sm",
 | 
			
		||||
  fontWeight: "lighter",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										56
									
								
								components/Select.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,56 @@
 | 
			
		||||
import { FC } from "react";
 | 
			
		||||
import { mauve, mauveDark } from "@radix-ui/colors";
 | 
			
		||||
import { useTheme } from "next-themes";
 | 
			
		||||
import { styled } from '../stitches.config';
 | 
			
		||||
import dynamic from 'next/dynamic';
 | 
			
		||||
import type { Props } from "react-select";
 | 
			
		||||
const SelectInput = dynamic(() => import("react-select"), { ssr: false });
 | 
			
		||||
 | 
			
		||||
const Select: FC<Props> = props => {
 | 
			
		||||
  const { theme } = useTheme();
 | 
			
		||||
  const isDark = theme === "dark";
 | 
			
		||||
  const colors: any = {
 | 
			
		||||
    // primary: pink.pink9,
 | 
			
		||||
    primary: isDark ? mauveDark.mauve4 : mauve.mauve4,
 | 
			
		||||
    secondary: isDark ? mauveDark.mauve8 : mauve.mauve8,
 | 
			
		||||
    background: isDark ? "rgb(10, 10, 10)" : "rgb(245, 245, 245)",
 | 
			
		||||
    searchText: isDark ? mauveDark.mauve12 : mauve.mauve12,
 | 
			
		||||
    placeholder: isDark ? mauveDark.mauve11 : mauve.mauve11,
 | 
			
		||||
  };
 | 
			
		||||
  colors.outline = colors.background;
 | 
			
		||||
  colors.selected = colors.secondary;
 | 
			
		||||
  return (
 | 
			
		||||
    <SelectInput
 | 
			
		||||
      menuPosition="fixed"
 | 
			
		||||
      theme={theme => ({
 | 
			
		||||
        ...theme,
 | 
			
		||||
        spacing: {
 | 
			
		||||
          ...theme.spacing,
 | 
			
		||||
          controlHeight: 30
 | 
			
		||||
        },
 | 
			
		||||
        colors: {
 | 
			
		||||
          primary: colors.selected,
 | 
			
		||||
          primary25: colors.primary,
 | 
			
		||||
          primary50: colors.primary,
 | 
			
		||||
          primary75: colors.primary,
 | 
			
		||||
          danger: colors.primary,
 | 
			
		||||
          dangerLight: colors.primary,
 | 
			
		||||
          neutral0: colors.background,
 | 
			
		||||
          neutral5: colors.primary,
 | 
			
		||||
          neutral10: colors.primary,
 | 
			
		||||
          neutral20: colors.outline,
 | 
			
		||||
          neutral30: colors.primary,
 | 
			
		||||
          neutral40: colors.primary,
 | 
			
		||||
          neutral50: colors.placeholder,
 | 
			
		||||
          neutral60: colors.primary,
 | 
			
		||||
          neutral70: colors.primary,
 | 
			
		||||
          neutral80: colors.searchText,
 | 
			
		||||
          neutral90: colors.primary,
 | 
			
		||||
        },
 | 
			
		||||
      })}
 | 
			
		||||
      {...props}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default styled(Select, {});
 | 
			
		||||
							
								
								
									
										257
									
								
								components/Tabs.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,257 @@
 | 
			
		||||
import React, {
 | 
			
		||||
  useEffect,
 | 
			
		||||
  useState,
 | 
			
		||||
  Fragment,
 | 
			
		||||
  isValidElement,
 | 
			
		||||
  useCallback,
 | 
			
		||||
} from "react";
 | 
			
		||||
import type { ReactNode, ReactElement } from "react";
 | 
			
		||||
import { Box, Button, Flex, Input, Stack, Text } from ".";
 | 
			
		||||
import {
 | 
			
		||||
  Dialog,
 | 
			
		||||
  DialogTrigger,
 | 
			
		||||
  DialogContent,
 | 
			
		||||
  DialogTitle,
 | 
			
		||||
  DialogDescription,
 | 
			
		||||
  DialogClose,
 | 
			
		||||
} from "./Dialog";
 | 
			
		||||
import { Plus, X } from "phosphor-react";
 | 
			
		||||
import { styled } from "../stitches.config";
 | 
			
		||||
 | 
			
		||||
const ErrorText = styled(Text, {
 | 
			
		||||
  color: "$crimson9",
 | 
			
		||||
  mt: "$1",
 | 
			
		||||
  display: "block",
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
interface TabProps {
 | 
			
		||||
  header?: string;
 | 
			
		||||
  children: ReactNode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO customise strings shown
 | 
			
		||||
interface Props {
 | 
			
		||||
  activeIndex?: number;
 | 
			
		||||
  activeHeader?: string;
 | 
			
		||||
  headless?: boolean;
 | 
			
		||||
  children: ReactElement<TabProps>[];
 | 
			
		||||
  keepAllAlive?: boolean;
 | 
			
		||||
  defaultExtension?: string;
 | 
			
		||||
  forceDefaultExtension?: boolean;
 | 
			
		||||
  onCreateNewTab?: (name: string) => any;
 | 
			
		||||
  onCloseTab?: (index: number, header?: string) => any;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const Tab = (props: TabProps) => null;
 | 
			
		||||
 | 
			
		||||
export const Tabs = ({
 | 
			
		||||
  children,
 | 
			
		||||
  activeIndex,
 | 
			
		||||
  activeHeader,
 | 
			
		||||
  headless,
 | 
			
		||||
  keepAllAlive = false,
 | 
			
		||||
  onCreateNewTab,
 | 
			
		||||
  onCloseTab,
 | 
			
		||||
  defaultExtension = "",
 | 
			
		||||
  forceDefaultExtension,
 | 
			
		||||
}: Props) => {
 | 
			
		||||
  const [active, setActive] = useState(activeIndex || 0);
 | 
			
		||||
  const tabs: TabProps[] = children.map((elem) => elem.props);
 | 
			
		||||
 | 
			
		||||
  const [isNewtabDialogOpen, setIsNewtabDialogOpen] = useState(false);
 | 
			
		||||
  const [tabname, setTabname] = useState("");
 | 
			
		||||
  const [newtabError, setNewtabError] = useState<string | null>(null);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (activeIndex) setActive(activeIndex);
 | 
			
		||||
  }, [activeIndex]);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (activeHeader) {
 | 
			
		||||
      const idx = tabs.findIndex((tab) => tab.header === activeHeader);
 | 
			
		||||
      setActive(idx);
 | 
			
		||||
    }
 | 
			
		||||
  }, [activeHeader, tabs]);
 | 
			
		||||
 | 
			
		||||
  // when filename changes, reset error
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setNewtabError(null);
 | 
			
		||||
  }, [tabname, setNewtabError]);
 | 
			
		||||
 | 
			
		||||
  const validateTabname = useCallback(
 | 
			
		||||
    (tabname: string): { error: string | null } => {
 | 
			
		||||
      if (tabs.find((tab) => tab.header === tabname)) {
 | 
			
		||||
        return { error: "Name already exists." };
 | 
			
		||||
      }
 | 
			
		||||
      return { error: null };
 | 
			
		||||
    },
 | 
			
		||||
    [tabs]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const handleCreateTab = useCallback(() => {
 | 
			
		||||
    // add default extension in case omitted
 | 
			
		||||
    let _tabname = tabname.includes(".") ? tabname : tabname + defaultExtension;
 | 
			
		||||
    if (forceDefaultExtension && !_tabname.endsWith(defaultExtension)) {
 | 
			
		||||
      _tabname = _tabname + defaultExtension;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const chk = validateTabname(_tabname);
 | 
			
		||||
    if (chk.error) {
 | 
			
		||||
      setNewtabError(`Error: ${chk.error}`);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setIsNewtabDialogOpen(false);
 | 
			
		||||
    setTabname("");
 | 
			
		||||
    // switch to new tab?
 | 
			
		||||
    setActive(tabs.length);
 | 
			
		||||
 | 
			
		||||
    onCreateNewTab?.(_tabname);
 | 
			
		||||
  }, [tabname, defaultExtension, validateTabname, onCreateNewTab, tabs.length]);
 | 
			
		||||
 | 
			
		||||
  const handleCloseTab = useCallback(
 | 
			
		||||
    (idx: number) => {
 | 
			
		||||
      if (idx <= active && active !== 0) {
 | 
			
		||||
        setActive(active - 1);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      onCloseTab?.(idx, tabs[idx].header);
 | 
			
		||||
    },
 | 
			
		||||
    [active, onCloseTab, tabs]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      {!headless && (
 | 
			
		||||
        <Stack
 | 
			
		||||
          css={{
 | 
			
		||||
            gap: "$3",
 | 
			
		||||
            flex: 1,
 | 
			
		||||
            flexWrap: "nowrap",
 | 
			
		||||
            marginBottom: "-1px",
 | 
			
		||||
            width: "100%",
 | 
			
		||||
            overflow: "auto",
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          {tabs.map((tab, idx) => (
 | 
			
		||||
            <Button
 | 
			
		||||
              key={tab.header}
 | 
			
		||||
              role="tab"
 | 
			
		||||
              tabIndex={idx}
 | 
			
		||||
              onClick={() => setActive(idx)}
 | 
			
		||||
              onKeyPress={() => setActive(idx)}
 | 
			
		||||
              outline={active !== idx}
 | 
			
		||||
              size="sm"
 | 
			
		||||
              css={{
 | 
			
		||||
                "&:hover": {
 | 
			
		||||
                  span: {
 | 
			
		||||
                    visibility: "visible",
 | 
			
		||||
                  },
 | 
			
		||||
                },
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              {tab.header || idx}
 | 
			
		||||
              {onCloseTab && (
 | 
			
		||||
                <Box
 | 
			
		||||
                  as="span"
 | 
			
		||||
                  css={{
 | 
			
		||||
                    display: "flex",
 | 
			
		||||
                    p: "2px",
 | 
			
		||||
                    borderRadius: "$full",
 | 
			
		||||
                    mr: "-4px",
 | 
			
		||||
                    "&:hover": {
 | 
			
		||||
                      // boxSizing: "0px 0px 1px",
 | 
			
		||||
                      backgroundColor: "$mauve2",
 | 
			
		||||
                      color: "$mauve12",
 | 
			
		||||
                    },
 | 
			
		||||
                  }}
 | 
			
		||||
                  onClick={(ev: React.MouseEvent<HTMLElement>) => {
 | 
			
		||||
                    ev.stopPropagation();
 | 
			
		||||
                    handleCloseTab(idx);
 | 
			
		||||
                  }}
 | 
			
		||||
                >
 | 
			
		||||
                  <X size="9px" weight="bold" />
 | 
			
		||||
                </Box>
 | 
			
		||||
              )}
 | 
			
		||||
            </Button>
 | 
			
		||||
          ))}
 | 
			
		||||
          {onCreateNewTab && (
 | 
			
		||||
            <Dialog
 | 
			
		||||
              open={isNewtabDialogOpen}
 | 
			
		||||
              onOpenChange={setIsNewtabDialogOpen}
 | 
			
		||||
            >
 | 
			
		||||
              <DialogTrigger asChild>
 | 
			
		||||
                <Button
 | 
			
		||||
                  ghost
 | 
			
		||||
                  size="sm"
 | 
			
		||||
                  css={{ alignItems: "center", px: "$2", mr: "$3" }}
 | 
			
		||||
                >
 | 
			
		||||
                  <Plus size="16px" /> {tabs.length === 0 && "Add new tab"}
 | 
			
		||||
                </Button>
 | 
			
		||||
              </DialogTrigger>
 | 
			
		||||
              <DialogContent>
 | 
			
		||||
                <DialogTitle>Create new tab</DialogTitle>
 | 
			
		||||
                <DialogDescription>
 | 
			
		||||
                  <label>Tabname</label>
 | 
			
		||||
                  <Input
 | 
			
		||||
                    value={tabname}
 | 
			
		||||
                    onChange={(e) => setTabname(e.target.value)}
 | 
			
		||||
                    onKeyPress={(e) => {
 | 
			
		||||
                      if (e.key === "Enter") {
 | 
			
		||||
                        handleCreateTab();
 | 
			
		||||
                      }
 | 
			
		||||
                    }}
 | 
			
		||||
                  />
 | 
			
		||||
                  <ErrorText>{newtabError}</ErrorText>
 | 
			
		||||
                </DialogDescription>
 | 
			
		||||
 | 
			
		||||
                <Flex
 | 
			
		||||
                  css={{
 | 
			
		||||
                    marginTop: 25,
 | 
			
		||||
                    justifyContent: "flex-end",
 | 
			
		||||
                    gap: "$3",
 | 
			
		||||
                  }}
 | 
			
		||||
                >
 | 
			
		||||
                  <DialogClose asChild>
 | 
			
		||||
                    <Button outline>Cancel</Button>
 | 
			
		||||
                  </DialogClose>
 | 
			
		||||
                  <Button variant="primary" onClick={handleCreateTab}>
 | 
			
		||||
                    Create
 | 
			
		||||
                  </Button>
 | 
			
		||||
                </Flex>
 | 
			
		||||
                <DialogClose asChild>
 | 
			
		||||
                  <Box css={{ position: "absolute", top: "$3", right: "$3" }}>
 | 
			
		||||
                    <X size="20px" />
 | 
			
		||||
                  </Box>
 | 
			
		||||
                </DialogClose>
 | 
			
		||||
              </DialogContent>
 | 
			
		||||
            </Dialog>
 | 
			
		||||
          )}
 | 
			
		||||
        </Stack>
 | 
			
		||||
      )}
 | 
			
		||||
      {keepAllAlive ? (
 | 
			
		||||
        tabs.map((tab, idx) => {
 | 
			
		||||
          // TODO Maybe rule out fragments as children
 | 
			
		||||
          if (!isValidElement(tab.children)) {
 | 
			
		||||
            if (active !== idx) return null;
 | 
			
		||||
            return tab.children;
 | 
			
		||||
          }
 | 
			
		||||
          let key = tab.children.key || tab.header || idx;
 | 
			
		||||
          let { children } = tab;
 | 
			
		||||
          let { style, ...props } = children.props;
 | 
			
		||||
          return (
 | 
			
		||||
            <children.type
 | 
			
		||||
              key={key}
 | 
			
		||||
              {...props}
 | 
			
		||||
              style={{ ...style, display: active !== idx ? "none" : undefined }}
 | 
			
		||||
            />
 | 
			
		||||
          );
 | 
			
		||||
        })
 | 
			
		||||
      ) : (
 | 
			
		||||
        <Fragment key={tabs[active].header || active}>
 | 
			
		||||
          {tabs[active].children}
 | 
			
		||||
        </Fragment>
 | 
			
		||||
      )}
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
@@ -4,6 +4,18 @@ const Text = styled("span", {
 | 
			
		||||
  fontFamily: "$body",
 | 
			
		||||
  lineHeight: "$body",
 | 
			
		||||
  color: "$text",
 | 
			
		||||
  variants: {
 | 
			
		||||
    small: {
 | 
			
		||||
      true: {
 | 
			
		||||
        fontSize: '$xs'
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    muted: {
 | 
			
		||||
      true: {
 | 
			
		||||
        color: '$mauve9'
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export default Text;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								components/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,15 @@
 | 
			
		||||
export { default as Flex } from './Flex'
 | 
			
		||||
export { default as Container } from './Container'
 | 
			
		||||
export { default as Heading } from './Heading'
 | 
			
		||||
export { default as Stack } from './Stack'
 | 
			
		||||
export { default as Text } from './Text'
 | 
			
		||||
export { default as Input } from './Input'
 | 
			
		||||
export { default as Select } from './Select'
 | 
			
		||||
export * from './Tabs'
 | 
			
		||||
export * from './AlertDialog'
 | 
			
		||||
export { default as Box } from './Box'
 | 
			
		||||
export { default as Button } from './Button'
 | 
			
		||||
export { default as ButtonGroup } from './ButtonGroup'
 | 
			
		||||
export { default as DeployFooter } from './DeployFooter'
 | 
			
		||||
export * from './Dialog'
 | 
			
		||||
export * from './DropdownMenu'
 | 
			
		||||
							
								
								
									
										221
									
								
								content/transactions.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,221 @@
 | 
			
		||||
[
 | 
			
		||||
  {
 | 
			
		||||
    "TransactionType": "AccountDelete",
 | 
			
		||||
    "Account": "rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm",
 | 
			
		||||
    "Destination": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
 | 
			
		||||
    "DestinationTag": 13,
 | 
			
		||||
    "Fee": "2000000",
 | 
			
		||||
    "Sequence": 2470665,
 | 
			
		||||
    "Flags": 2147483648
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "TransactionType": "AccountSet",
 | 
			
		||||
    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
			
		||||
    "Fee": "12",
 | 
			
		||||
    "Sequence": 5,
 | 
			
		||||
    "Domain": "6578616D706C652E636F6D",
 | 
			
		||||
    "SetFlag": 5,
 | 
			
		||||
    "MessageKey": "03AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Account": "rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo",
 | 
			
		||||
    "TransactionType": "CheckCancel",
 | 
			
		||||
    "CheckID": "49647F0D748DC3FE26BDACBC57F251AADEFFF391403EC9BF87C97F67E9977FB0",
 | 
			
		||||
    "Fee": "12"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Account": "rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy",
 | 
			
		||||
    "TransactionType": "CheckCash",
 | 
			
		||||
    "Amount": {
 | 
			
		||||
      "value": "100",
 | 
			
		||||
      "type": "currency"
 | 
			
		||||
    },
 | 
			
		||||
    "CheckID": "838766BA2B995C00744175F69A1B11E32C3DBC40E64801A4056FCBD657F57334",
 | 
			
		||||
    "Fee": "12"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "TransactionType": "CheckCreate",
 | 
			
		||||
    "Account": "rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo",
 | 
			
		||||
    "Destination": "rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy",
 | 
			
		||||
    "SendMax": "100000000",
 | 
			
		||||
    "Expiration": 570113521,
 | 
			
		||||
    "InvoiceID": "6F1DFD1D0FE8A32E40E1F2C05CF1C15545BAB56B617F9C6C2D63A6B704BEF59B",
 | 
			
		||||
    "DestinationTag": 1,
 | 
			
		||||
    "Fee": "12"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "TransactionType": "DepositPreauth",
 | 
			
		||||
    "Account": "rsUiUMpnrgxQp24dJYZDhmV4bE3aBtQyt8",
 | 
			
		||||
    "Authorize": "rEhxGqkqPPSxQ3P25J66ft5TwpzV14k2de",
 | 
			
		||||
    "Fee": "10",
 | 
			
		||||
    "Flags": 2147483648,
 | 
			
		||||
    "Sequence": 2
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
			
		||||
    "TransactionType": "EscrowCancel",
 | 
			
		||||
    "Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
			
		||||
    "OfferSequence": 7
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
			
		||||
    "TransactionType": "EscrowCreate",
 | 
			
		||||
    "Amount": {
 | 
			
		||||
      "value": "100",
 | 
			
		||||
      "type": "currency"
 | 
			
		||||
    },
 | 
			
		||||
    "Destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
 | 
			
		||||
    "CancelAfter": 533257958,
 | 
			
		||||
    "FinishAfter": 533171558,
 | 
			
		||||
    "Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
 | 
			
		||||
    "DestinationTag": 23480,
 | 
			
		||||
    "SourceTag": 11747
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
			
		||||
    "TransactionType": "EscrowFinish",
 | 
			
		||||
    "Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
			
		||||
    "OfferSequence": 7,
 | 
			
		||||
    "Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
 | 
			
		||||
    "Fulfillment": "A0028000"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "TransactionType": "NFTokenBurn",
 | 
			
		||||
    "Account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
 | 
			
		||||
    "Fee": "10",
 | 
			
		||||
    "TokenID": "000B013A95F14B0044F78A264E41713C64B5F89242540EE208C3098E00000D65"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "TransactionType": "NFTokenAcceptOffer",
 | 
			
		||||
    "Fee": "10"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "TransactionType": "NFTokenCancelOffer",
 | 
			
		||||
    "Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
 | 
			
		||||
    "TokenIDs": "000100001E962F495F07A990F4ED55ACCFEEF365DBAA76B6A048C0A200000007"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "TransactionType": "NFTokenCreateOffer",
 | 
			
		||||
    "Account": "rs8jBmmfpwgmrSPgwMsh7CvKRmRt1JTVSX",
 | 
			
		||||
    "TokenID": "000100001E962F495F07A990F4ED55ACCFEEF365DBAA76B6A048C0A200000007",
 | 
			
		||||
    "Amount": {
 | 
			
		||||
      "value": "100",
 | 
			
		||||
      "type": "currency"
 | 
			
		||||
    },
 | 
			
		||||
    "Flags": 1
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "TransactionType": "OfferCancel",
 | 
			
		||||
    "Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
 | 
			
		||||
    "Fee": "12",
 | 
			
		||||
    "Flags": 0,
 | 
			
		||||
    "LastLedgerSequence": 7108629,
 | 
			
		||||
    "OfferSequence": 6,
 | 
			
		||||
    "Sequence": 7
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "TransactionType": "OfferCreate",
 | 
			
		||||
    "Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
 | 
			
		||||
    "Fee": "12",
 | 
			
		||||
    "Flags": 0,
 | 
			
		||||
    "LastLedgerSequence": 7108682,
 | 
			
		||||
    "Sequence": 8,
 | 
			
		||||
    "TakerGets": "6000000",
 | 
			
		||||
    "Amount": {
 | 
			
		||||
      "value": "100",
 | 
			
		||||
      "type": "currency"
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "TransactionType": "Payment",
 | 
			
		||||
    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
			
		||||
    "Destination": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
 | 
			
		||||
    "Amount": {
 | 
			
		||||
      "value": "100",
 | 
			
		||||
      "type": "currency"
 | 
			
		||||
    },
 | 
			
		||||
    "Fee": "12",
 | 
			
		||||
    "Flags": 2147483648,
 | 
			
		||||
    "Sequence": 2
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
			
		||||
    "TransactionType": "PaymentChannelCreate",
 | 
			
		||||
    "Amount": {
 | 
			
		||||
      "value": "100",
 | 
			
		||||
      "type": "currency"
 | 
			
		||||
    },
 | 
			
		||||
    "Destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
 | 
			
		||||
    "SettleDelay": 86400,
 | 
			
		||||
    "PublicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A",
 | 
			
		||||
    "CancelAfter": 533171558,
 | 
			
		||||
    "DestinationTag": 23480,
 | 
			
		||||
    "SourceTag": 11747
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
			
		||||
    "TransactionType": "PaymentChannelFund",
 | 
			
		||||
    "Channel": "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198",
 | 
			
		||||
    "Amount": {
 | 
			
		||||
      "value": "200",
 | 
			
		||||
      "type": "currency"
 | 
			
		||||
    },
 | 
			
		||||
    "Expiration": 543171558
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Flags": 0,
 | 
			
		||||
    "TransactionType": "SetRegularKey",
 | 
			
		||||
    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
			
		||||
    "Fee": "12",
 | 
			
		||||
    "RegularKey": "rAR8rR8sUkBoCZFawhkWzY4Y5YoyuznwD"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Flags": 0,
 | 
			
		||||
    "TransactionType": "SignerListSet",
 | 
			
		||||
    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
			
		||||
    "Fee": "12",
 | 
			
		||||
    "SignerQuorum": 3,
 | 
			
		||||
    "SignerEntries": {
 | 
			
		||||
      "type": "json",
 | 
			
		||||
      "value": [
 | 
			
		||||
        {
 | 
			
		||||
          "SignerEntry": {
 | 
			
		||||
            "Account": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
 | 
			
		||||
            "SignerWeight": 2
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "SignerEntry": {
 | 
			
		||||
            "Account": "rUpy3eEg8rqjqfUoLeBnZkscbKbFsKXC3v",
 | 
			
		||||
            "SignerWeight": 1
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "SignerEntry": {
 | 
			
		||||
            "Account": "raKEEVSGnKSD9Zyvxu4z6Pqpm4ABH8FS6n",
 | 
			
		||||
            "SignerWeight": 1
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "TransactionType": "TicketCreate",
 | 
			
		||||
    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
			
		||||
    "Fee": "10",
 | 
			
		||||
    "Sequence": 381,
 | 
			
		||||
    "TicketCount": 10
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "TransactionType": "TrustSet",
 | 
			
		||||
    "Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
 | 
			
		||||
    "Fee": "12",
 | 
			
		||||
    "Flags": 262144,
 | 
			
		||||
    "LastLedgerSequence": 8007750,
 | 
			
		||||
    "Amount": {
 | 
			
		||||
      "value": "100",
 | 
			
		||||
      "type": "currency"
 | 
			
		||||
    },
 | 
			
		||||
    "Sequence": 12
 | 
			
		||||
  }
 | 
			
		||||
]
 | 
			
		||||
@@ -20,10 +20,10 @@
 | 
			
		||||
    "@radix-ui/react-dropdown-menu": "^0.1.1",
 | 
			
		||||
    "@radix-ui/react-id": "^0.1.1",
 | 
			
		||||
    "@stitches/react": "^1.2.6-0",
 | 
			
		||||
    "file-saver": "^2.0.5",
 | 
			
		||||
    "jszip": "^3.7.1",
 | 
			
		||||
    "base64-js": "^1.5.1",
 | 
			
		||||
    "dinero.js": "^1.9.1",
 | 
			
		||||
    "file-saver": "^2.0.5",
 | 
			
		||||
    "jszip": "^3.7.1",
 | 
			
		||||
    "monaco-editor": "^0.30.1",
 | 
			
		||||
    "next": "^12.0.4",
 | 
			
		||||
    "next-auth": "^4.0.0-beta.5",
 | 
			
		||||
@@ -40,6 +40,8 @@
 | 
			
		||||
    "react-hot-keys": "^2.7.1",
 | 
			
		||||
    "react-hot-toast": "^2.1.1",
 | 
			
		||||
    "react-new-window": "^0.2.1",
 | 
			
		||||
    "react-select": "^5.2.1",
 | 
			
		||||
    "react-split": "^2.0.14",
 | 
			
		||||
    "react-stay-scrolled": "^7.4.0",
 | 
			
		||||
    "reconnecting-websocket": "^4.4.0",
 | 
			
		||||
    "valtio": "^1.2.5",
 | 
			
		||||
@@ -50,8 +52,8 @@
 | 
			
		||||
    "xrpl-client": "^1.9.3"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@types/file-saver": "^2.0.4",
 | 
			
		||||
    "@types/dinero.js": "^1.9.0",
 | 
			
		||||
    "@types/file-saver": "^2.0.4",
 | 
			
		||||
    "@types/pako": "^1.0.2",
 | 
			
		||||
    "@types/react": "17.0.31",
 | 
			
		||||
    "eslint": "7.32.0",
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,10 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
 | 
			
		||||
  const router = useRouter();
 | 
			
		||||
  const slug = router.query?.slug;
 | 
			
		||||
  const gistId = (Array.isArray(slug) && slug[0]) ?? null;
 | 
			
		||||
 | 
			
		||||
  const origin = "https://xrpl-hooks-ide.vercel.app"; // TODO: Change when site is deployed
 | 
			
		||||
  const shareImg = "/share-image.png";
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (gistId && router.isReady) {
 | 
			
		||||
      fetchFiles(gistId);
 | 
			
		||||
@@ -30,7 +34,73 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <Head>
 | 
			
		||||
        <title>XRPL Hooks Playground</title>
 | 
			
		||||
        <meta charSet="utf-8" />
 | 
			
		||||
        <meta name="viewport" content="initial-scale=1.0, width=device-width" />
 | 
			
		||||
        <meta name="format-detection" content="telephone=no" />
 | 
			
		||||
        <meta property="og:url" content={`${origin}${router.asPath}`} />
 | 
			
		||||
 | 
			
		||||
        <title>XRPL Hooks Editor</title>
 | 
			
		||||
        <meta property="og:title" content="XRPL Hooks Editor" />
 | 
			
		||||
        <meta name="twitter:title" content="XRPL Hooks Editor" />
 | 
			
		||||
        <meta name="twitter:card" content="summary_large_image" />
 | 
			
		||||
        <meta name="twitter:site" content="@xrpllabs" />
 | 
			
		||||
        <meta
 | 
			
		||||
          name="description"
 | 
			
		||||
          content="Playground for buildings Hooks, that add smart contract functionality to the XRP Ledger."
 | 
			
		||||
        />
 | 
			
		||||
        <meta
 | 
			
		||||
          property="og:description"
 | 
			
		||||
          content="Playground for buildings Hooks, that add smart contract functionality to the XRP Ledger."
 | 
			
		||||
        />
 | 
			
		||||
        <meta
 | 
			
		||||
          name="twitter:description"
 | 
			
		||||
          content="Playground for buildings Hooks, that add smart contract functionality to the XRP Ledger.."
 | 
			
		||||
        />
 | 
			
		||||
        <meta property="og:image" content={`${origin}${shareImg}`} />
 | 
			
		||||
        <meta property="og:image:width" content="1200" />
 | 
			
		||||
        <meta property="og:image:height" content="630" />
 | 
			
		||||
        <meta name="twitter:image" content={`${origin}${shareImg}`} />
 | 
			
		||||
        <link
 | 
			
		||||
          rel="apple-touch-icon"
 | 
			
		||||
          sizes="180x180"
 | 
			
		||||
          href="/apple-touch-icon.png"
 | 
			
		||||
        />
 | 
			
		||||
        <link
 | 
			
		||||
          rel="icon"
 | 
			
		||||
          type="image/png"
 | 
			
		||||
          sizes="32x32"
 | 
			
		||||
          href="/favicon-32x32.png"
 | 
			
		||||
        />
 | 
			
		||||
        <link
 | 
			
		||||
          rel="icon"
 | 
			
		||||
          type="image/png"
 | 
			
		||||
          sizes="16x16"
 | 
			
		||||
          href="/favicon-16x16.png"
 | 
			
		||||
        />
 | 
			
		||||
        <link rel="manifest" href="/site.webmanifest" />
 | 
			
		||||
        <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#161618" />
 | 
			
		||||
        <meta name="application-name" content="XRPL Hooks Editor" />
 | 
			
		||||
        <meta name="msapplication-TileColor" content="#c10ad0" />
 | 
			
		||||
        <meta
 | 
			
		||||
          name="theme-color"
 | 
			
		||||
          content="#161618"
 | 
			
		||||
          media="(prefers-color-scheme: dark)"
 | 
			
		||||
        />
 | 
			
		||||
        <meta
 | 
			
		||||
          name="theme-color"
 | 
			
		||||
          content="#FDFCFD"
 | 
			
		||||
          media="(prefers-color-scheme: light)"
 | 
			
		||||
        />
 | 
			
		||||
        <link rel="preconnect" href="https://fonts.googleapis.com" />
 | 
			
		||||
        <link
 | 
			
		||||
          rel="preconnect"
 | 
			
		||||
          href="https://fonts.gstatic.com"
 | 
			
		||||
          crossOrigin=""
 | 
			
		||||
        />
 | 
			
		||||
        <link
 | 
			
		||||
          href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital@0;1&family=Work+Sans:wght@400;600;700&display=swap"
 | 
			
		||||
          rel="stylesheet"
 | 
			
		||||
        />
 | 
			
		||||
      </Head>
 | 
			
		||||
      <IdProvider>
 | 
			
		||||
        <SessionProvider session={session}>
 | 
			
		||||
 
 | 
			
		||||
@@ -16,21 +16,10 @@ class MyDocument extends Document {
 | 
			
		||||
  }
 | 
			
		||||
  render() {
 | 
			
		||||
    globalStyles();
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <Html>
 | 
			
		||||
        <Head>
 | 
			
		||||
          <meta name="description" content="Playground for XRPL Hooks" />
 | 
			
		||||
          <link rel="icon" href="/favicon.ico" />
 | 
			
		||||
          <link rel="preconnect" href="https://fonts.googleapis.com" />
 | 
			
		||||
          <link
 | 
			
		||||
            rel="preconnect"
 | 
			
		||||
            href="https://fonts.gstatic.com"
 | 
			
		||||
            crossOrigin=""
 | 
			
		||||
          />
 | 
			
		||||
          <link
 | 
			
		||||
            href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital@0;1&family=Work+Sans:wght@400;600;700&display=swap"
 | 
			
		||||
            rel="stylesheet"
 | 
			
		||||
          />
 | 
			
		||||
          <style
 | 
			
		||||
            id="stitches"
 | 
			
		||||
            dangerouslySetInnerHTML={{ __html: getCssText() }}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,9 @@
 | 
			
		||||
import React from "react";
 | 
			
		||||
import dynamic from "next/dynamic";
 | 
			
		||||
import Flex from "../../components/Flex";
 | 
			
		||||
import React from "react";
 | 
			
		||||
import Split from "react-split";
 | 
			
		||||
import { useSnapshot } from "valtio";
 | 
			
		||||
import state from "../../state";
 | 
			
		||||
import { getSplit, saveSplit } from "../../state/actions/persistSplits";
 | 
			
		||||
 | 
			
		||||
const DeployEditor = dynamic(() => import("../../components/DeployEditor"), {
 | 
			
		||||
  ssr: false,
 | 
			
		||||
@@ -17,21 +18,45 @@ const LogBox = dynamic(() => import("../../components/LogBox"), {
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const Deploy = () => {
 | 
			
		||||
  const snap = useSnapshot(state);
 | 
			
		||||
  const { deployLogs } = useSnapshot(state);
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <main style={{ display: "flex", flex: 1 }}>
 | 
			
		||||
    <Split
 | 
			
		||||
      direction="vertical"
 | 
			
		||||
      gutterSize={4}
 | 
			
		||||
      gutterAlign="center"
 | 
			
		||||
      sizes={getSplit("deployVertical") || [40, 60]}
 | 
			
		||||
      style={{ height: "calc(100vh - 60px)" }}
 | 
			
		||||
      onDragEnd={(e) => saveSplit("deployVertical", e)}
 | 
			
		||||
    >
 | 
			
		||||
      <main style={{ display: "flex", flex: 1, position: "relative" }}>
 | 
			
		||||
        <DeployEditor />
 | 
			
		||||
      </main>
 | 
			
		||||
      <Flex css={{ flexDirection: "row", width: "100%" }}>
 | 
			
		||||
        <Accounts />
 | 
			
		||||
        <LogBox
 | 
			
		||||
          title="Deploy Log"
 | 
			
		||||
          logs={snap.deployLogs}
 | 
			
		||||
          clearLog={() => (state.deployLogs = [])}
 | 
			
		||||
        />
 | 
			
		||||
      </Flex>
 | 
			
		||||
    </>
 | 
			
		||||
      <Split
 | 
			
		||||
        direction="horizontal"
 | 
			
		||||
        sizes={getSplit("deployHorizontal") || [50, 50]}
 | 
			
		||||
        minSize={[320, 160]}
 | 
			
		||||
        gutterSize={4}
 | 
			
		||||
        gutterAlign="center"
 | 
			
		||||
        style={{
 | 
			
		||||
          display: "flex",
 | 
			
		||||
          flexDirection: "row",
 | 
			
		||||
          width: "100%",
 | 
			
		||||
          height: "100%",
 | 
			
		||||
        }}
 | 
			
		||||
        onDragEnd={(e) => saveSplit("deployHorizontal", e)}
 | 
			
		||||
      >
 | 
			
		||||
        <div style={{ alignItems: "stretch", display: "flex" }}>
 | 
			
		||||
          <Accounts />
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>
 | 
			
		||||
          <LogBox
 | 
			
		||||
            title="Deploy Log"
 | 
			
		||||
            logs={deployLogs}
 | 
			
		||||
            clearLog={() => (state.deployLogs = [])}
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
      </Split>
 | 
			
		||||
    </Split>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,15 @@
 | 
			
		||||
import dynamic from "next/dynamic";
 | 
			
		||||
import { useSnapshot } from "valtio";
 | 
			
		||||
import Hotkeys from "react-hot-keys";
 | 
			
		||||
import { Play } from "phosphor-react";
 | 
			
		||||
 | 
			
		||||
import type { NextPage } from "next";
 | 
			
		||||
import { compileCode } from "../../state/actions";
 | 
			
		||||
import state from "../../state";
 | 
			
		||||
import Button from "../../components/Button";
 | 
			
		||||
import dynamic from "next/dynamic";
 | 
			
		||||
import { Play } from "phosphor-react";
 | 
			
		||||
import Hotkeys from "react-hot-keys";
 | 
			
		||||
import Split from "react-split";
 | 
			
		||||
import { useSnapshot } from "valtio";
 | 
			
		||||
import Box from "../../components/Box";
 | 
			
		||||
import Button from "../../components/Button";
 | 
			
		||||
import state from "../../state";
 | 
			
		||||
import { compileCode } from "../../state/actions";
 | 
			
		||||
import { getSplit, saveSplit } from "../../state/actions/persistSplits";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const HooksEditor = dynamic(() => import("../../components/HooksEditor"), {
 | 
			
		||||
  ssr: false,
 | 
			
		||||
@@ -19,8 +21,17 @@ const LogBox = dynamic(() => import("../../components/LogBox"), {
 | 
			
		||||
 | 
			
		||||
const Home: NextPage = () => {
 | 
			
		||||
  const snap = useSnapshot(state);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
    <Split
 | 
			
		||||
      direction="vertical"
 | 
			
		||||
      sizes={getSplit("developVertical") || [70, 30]}
 | 
			
		||||
      minSize={[100, 100]}
 | 
			
		||||
      gutterAlign="center"
 | 
			
		||||
      gutterSize={4}
 | 
			
		||||
      style={{ height: "calc(100vh - 60px)" }}
 | 
			
		||||
      onDragEnd={(e) => saveSplit("developVertical", e)}
 | 
			
		||||
    >
 | 
			
		||||
      <main style={{ display: "flex", flex: 1, position: "relative" }}>
 | 
			
		||||
        <HooksEditor />
 | 
			
		||||
        {snap.files[snap.active]?.name?.split(".")?.[1].toLowerCase() ===
 | 
			
		||||
@@ -65,7 +76,7 @@ const Home: NextPage = () => {
 | 
			
		||||
          logs={snap.logs}
 | 
			
		||||
        />
 | 
			
		||||
      </Box>
 | 
			
		||||
    </>
 | 
			
		||||
    </Split>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,442 @@
 | 
			
		||||
import Container from "../../components/Container";
 | 
			
		||||
import dynamic from "next/dynamic";
 | 
			
		||||
import { Play } from "phosphor-react";
 | 
			
		||||
import { FC, useCallback, useEffect, useState } from "react";
 | 
			
		||||
import Split from "react-split";
 | 
			
		||||
import { useSnapshot } from "valtio";
 | 
			
		||||
import {
 | 
			
		||||
  Box, Button, Container,
 | 
			
		||||
  Flex, Input,
 | 
			
		||||
  Select, Tab, Tabs, Text
 | 
			
		||||
} from "../../components";
 | 
			
		||||
import transactionsData from "../../content/transactions.json";
 | 
			
		||||
import state from "../../state";
 | 
			
		||||
import { sendTransaction } from "../../state/actions";
 | 
			
		||||
import { getSplit, saveSplit } from "../../state/actions/persistSplits";
 | 
			
		||||
 | 
			
		||||
const DebugStream = dynamic(() => import("../../components/DebugStream"), {
 | 
			
		||||
  ssr: false,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const LogBox = dynamic(() => import("../../components/LogBox"), {
 | 
			
		||||
  ssr: false,
 | 
			
		||||
});
 | 
			
		||||
const Accounts = dynamic(() => import("../../components/Accounts"), {
 | 
			
		||||
  ssr: false,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// type SelectOption<T> = { value: T, label: string };
 | 
			
		||||
type TxFields = Omit<
 | 
			
		||||
  typeof transactionsData[0],
 | 
			
		||||
  "Account" | "Sequence" | "TransactionType"
 | 
			
		||||
>;
 | 
			
		||||
type OtherFields = (keyof Omit<TxFields, "Destination">)[];
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
  header?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Transaction: FC<Props> = ({ header, ...props }) => {
 | 
			
		||||
  const snap = useSnapshot(state);
 | 
			
		||||
 | 
			
		||||
  const transactionsOptions = transactionsData.map((tx) => ({
 | 
			
		||||
    value: tx.TransactionType,
 | 
			
		||||
    label: tx.TransactionType,
 | 
			
		||||
  }));
 | 
			
		||||
  const [selectedTransaction, setSelectedTransaction] = useState<
 | 
			
		||||
    typeof transactionsOptions[0] | null
 | 
			
		||||
  >(null);
 | 
			
		||||
 | 
			
		||||
  const accountOptions = snap.accounts.map((acc) => ({
 | 
			
		||||
    label: acc.name,
 | 
			
		||||
    value: acc.address,
 | 
			
		||||
  }));
 | 
			
		||||
  const [selectedAccount, setSelectedAccount] = useState<
 | 
			
		||||
    typeof accountOptions[0] | null
 | 
			
		||||
  >(null);
 | 
			
		||||
 | 
			
		||||
  const destAccountOptions = snap.accounts
 | 
			
		||||
    .map((acc) => ({
 | 
			
		||||
      label: acc.name,
 | 
			
		||||
      value: acc.address,
 | 
			
		||||
    }))
 | 
			
		||||
    .filter((acc) => acc.value !== selectedAccount?.value);
 | 
			
		||||
  const [selectedDestAccount, setSelectedDestAccount] = useState<
 | 
			
		||||
    typeof destAccountOptions[0] | null
 | 
			
		||||
  >(null);
 | 
			
		||||
 | 
			
		||||
  const [txIsLoading, setTxIsLoading] = useState(false);
 | 
			
		||||
  const [txIsDisabled, setTxIsDisabled] = useState(false);
 | 
			
		||||
  const [txFields, setTxFields] = useState<TxFields>({});
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const transactionType = selectedTransaction?.value;
 | 
			
		||||
    const account = snap.accounts.find(
 | 
			
		||||
      (acc) => acc.address === selectedAccount?.value
 | 
			
		||||
    );
 | 
			
		||||
    if (!account || !transactionType || txIsLoading) {
 | 
			
		||||
      setTxIsDisabled(true);
 | 
			
		||||
    } else {
 | 
			
		||||
      setTxIsDisabled(false);
 | 
			
		||||
    }
 | 
			
		||||
  }, [txIsLoading, selectedTransaction, selectedAccount, snap.accounts]);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    let _txFields: TxFields | undefined = transactionsData.find(
 | 
			
		||||
      (tx) => tx.TransactionType === selectedTransaction?.value
 | 
			
		||||
    );
 | 
			
		||||
    if (!_txFields) return setTxFields({});
 | 
			
		||||
    _txFields = { ..._txFields } as TxFields;
 | 
			
		||||
 | 
			
		||||
    setSelectedDestAccount(null);
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    delete _txFields.TransactionType;
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    delete _txFields.Account;
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    delete _txFields.Sequence;
 | 
			
		||||
    setTxFields(_txFields);
 | 
			
		||||
  }, [selectedTransaction, setSelectedDestAccount]);
 | 
			
		||||
 | 
			
		||||
  const submitTest = useCallback(async () => {
 | 
			
		||||
    const account = snap.accounts.find(
 | 
			
		||||
      (acc) => acc.address === selectedAccount?.value
 | 
			
		||||
    );
 | 
			
		||||
    const TransactionType = selectedTransaction?.value;
 | 
			
		||||
    if (!account || !TransactionType || txIsDisabled) return;
 | 
			
		||||
 | 
			
		||||
    setTxIsLoading(true);
 | 
			
		||||
    // setTxIsError(null)
 | 
			
		||||
    try {
 | 
			
		||||
      let options = { ...txFields };
 | 
			
		||||
 | 
			
		||||
      options.Destination = selectedDestAccount?.value;
 | 
			
		||||
      (Object.keys(options) as (keyof TxFields)[]).forEach((field) => {
 | 
			
		||||
        let _value = options[field];
 | 
			
		||||
        // convert currency
 | 
			
		||||
        if (typeof _value === "object" && _value.type === "currency") {
 | 
			
		||||
          if (+_value.value) {
 | 
			
		||||
            options[field] = (+_value.value * 1000000 + "") as any;
 | 
			
		||||
          } else {
 | 
			
		||||
            options[field] = undefined; // 👇 💀
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        // handle type: `json`
 | 
			
		||||
        if (typeof _value === "object" && _value.type === "json") {
 | 
			
		||||
          if (typeof _value.value === "object") {
 | 
			
		||||
            options[field] = _value.value as any;
 | 
			
		||||
          } else {
 | 
			
		||||
            try {
 | 
			
		||||
              options[field] = JSON.parse(_value.value);
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
              const message = `Input error for json field '${field}': ${
 | 
			
		||||
                error instanceof Error ? error.message : ""
 | 
			
		||||
              }`;
 | 
			
		||||
              throw Error(message);
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // delete unneccesary fields
 | 
			
		||||
        if (!options[field]) {
 | 
			
		||||
          delete options[field];
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      const logPrefix = header ? `${header.split(".")[0]}: ` : undefined;
 | 
			
		||||
      await sendTransaction(
 | 
			
		||||
        account,
 | 
			
		||||
        {
 | 
			
		||||
          TransactionType,
 | 
			
		||||
          ...options,
 | 
			
		||||
        },
 | 
			
		||||
        { logPrefix }
 | 
			
		||||
      );
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      console.error(error);
 | 
			
		||||
      if (error instanceof Error) {
 | 
			
		||||
        state.transactionLogs.push({ type: "error", message: error.message });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    setTxIsLoading(false);
 | 
			
		||||
  }, [
 | 
			
		||||
    header,
 | 
			
		||||
    selectedAccount?.value,
 | 
			
		||||
    selectedDestAccount?.value,
 | 
			
		||||
    selectedTransaction?.value,
 | 
			
		||||
    snap.accounts,
 | 
			
		||||
    txFields,
 | 
			
		||||
    txIsDisabled,
 | 
			
		||||
  ]);
 | 
			
		||||
 | 
			
		||||
  const resetState = useCallback(() => {
 | 
			
		||||
    setSelectedAccount(null);
 | 
			
		||||
    setSelectedDestAccount(null);
 | 
			
		||||
    setSelectedTransaction(null);
 | 
			
		||||
    setTxFields({});
 | 
			
		||||
    setTxIsDisabled(false);
 | 
			
		||||
    setTxIsLoading(false);
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  const usualFields = ["TransactionType", "Amount", "Account", "Destination"];
 | 
			
		||||
  const otherFields = Object.keys(txFields).filter(
 | 
			
		||||
    (k) => !usualFields.includes(k)
 | 
			
		||||
  ) as OtherFields;
 | 
			
		||||
  return (
 | 
			
		||||
    <Box css={{ position: "relative", height: "calc(100% - 28px)" }} {...props}>
 | 
			
		||||
      <Container
 | 
			
		||||
        css={{ p: "$3 0", fontSize: "$sm", height: "calc(100% - 28px)" }}
 | 
			
		||||
      >
 | 
			
		||||
        <Flex column fluid css={{ height: "100%", overflowY: "auto" }}>
 | 
			
		||||
          <Flex
 | 
			
		||||
            row
 | 
			
		||||
            fluid
 | 
			
		||||
            css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}
 | 
			
		||||
          >
 | 
			
		||||
            <Text muted css={{ mr: "$3" }}>
 | 
			
		||||
              Transaction type:{" "}
 | 
			
		||||
            </Text>
 | 
			
		||||
            <Select
 | 
			
		||||
              instanceId="transactionsType"
 | 
			
		||||
              placeholder="Select transaction type"
 | 
			
		||||
              options={transactionsOptions}
 | 
			
		||||
              hideSelectedOptions
 | 
			
		||||
              css={{ width: "70%" }}
 | 
			
		||||
              value={selectedTransaction}
 | 
			
		||||
              onChange={(tt) => setSelectedTransaction(tt as any)}
 | 
			
		||||
            />
 | 
			
		||||
          </Flex>
 | 
			
		||||
          <Flex
 | 
			
		||||
            row
 | 
			
		||||
            fluid
 | 
			
		||||
            css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}
 | 
			
		||||
          >
 | 
			
		||||
            <Text muted css={{ mr: "$3" }}>
 | 
			
		||||
              Account:{" "}
 | 
			
		||||
            </Text>
 | 
			
		||||
            <Select
 | 
			
		||||
              instanceId="from-account"
 | 
			
		||||
              placeholder="Select your account"
 | 
			
		||||
              css={{ width: "70%" }}
 | 
			
		||||
              options={accountOptions}
 | 
			
		||||
              value={selectedAccount}
 | 
			
		||||
              onChange={(acc) => setSelectedAccount(acc as any)}
 | 
			
		||||
            />
 | 
			
		||||
          </Flex>
 | 
			
		||||
          {txFields.Amount !== undefined && (
 | 
			
		||||
            <Flex
 | 
			
		||||
              row
 | 
			
		||||
              fluid
 | 
			
		||||
              css={{
 | 
			
		||||
                justifyContent: "flex-end",
 | 
			
		||||
                alignItems: "center",
 | 
			
		||||
                mb: "$3",
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              <Text muted css={{ mr: "$3" }}>
 | 
			
		||||
                Amount (XRP):{" "}
 | 
			
		||||
              </Text>
 | 
			
		||||
              <Input
 | 
			
		||||
                value={txFields.Amount.value}
 | 
			
		||||
                onChange={(e) =>
 | 
			
		||||
                  setTxFields({
 | 
			
		||||
                    ...txFields,
 | 
			
		||||
                    Amount: { type: "currency", value: e.target.value },
 | 
			
		||||
                  })
 | 
			
		||||
                }
 | 
			
		||||
                variant="deep"
 | 
			
		||||
                css={{ width: "70%", flex: "inherit", height: "$9" }}
 | 
			
		||||
              />
 | 
			
		||||
            </Flex>
 | 
			
		||||
          )}
 | 
			
		||||
          {txFields.Destination !== undefined && (
 | 
			
		||||
            <Flex
 | 
			
		||||
              row
 | 
			
		||||
              fluid
 | 
			
		||||
              css={{
 | 
			
		||||
                justifyContent: "flex-end",
 | 
			
		||||
                alignItems: "center",
 | 
			
		||||
                mb: "$3",
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              <Text muted css={{ mr: "$3" }}>
 | 
			
		||||
                Destination account:{" "}
 | 
			
		||||
              </Text>
 | 
			
		||||
              <Select
 | 
			
		||||
                instanceId="to-account"
 | 
			
		||||
                placeholder="Select the destination account"
 | 
			
		||||
                css={{ width: "70%" }}
 | 
			
		||||
                options={destAccountOptions}
 | 
			
		||||
                value={selectedDestAccount}
 | 
			
		||||
                isClearable
 | 
			
		||||
                onChange={(acc) => setSelectedDestAccount(acc as any)}
 | 
			
		||||
              />
 | 
			
		||||
            </Flex>
 | 
			
		||||
          )}
 | 
			
		||||
          {otherFields.map((field) => {
 | 
			
		||||
            let _value = txFields[field];
 | 
			
		||||
            let value = typeof _value === "object" ? _value.value : _value;
 | 
			
		||||
            value =
 | 
			
		||||
              typeof value === "object"
 | 
			
		||||
                ? JSON.stringify(value)
 | 
			
		||||
                : value?.toLocaleString();
 | 
			
		||||
            let isCurrency =
 | 
			
		||||
              typeof _value === "object" && _value.type === "currency";
 | 
			
		||||
            return (
 | 
			
		||||
              <Flex
 | 
			
		||||
                key={field}
 | 
			
		||||
                row
 | 
			
		||||
                fluid
 | 
			
		||||
                css={{
 | 
			
		||||
                  justifyContent: "flex-end",
 | 
			
		||||
                  alignItems: "center",
 | 
			
		||||
                  mb: "$3",
 | 
			
		||||
                }}
 | 
			
		||||
              >
 | 
			
		||||
                <Text muted css={{ mr: "$3" }}>
 | 
			
		||||
                  {field + (isCurrency ? " (XRP)" : "")}:{" "}
 | 
			
		||||
                </Text>
 | 
			
		||||
                <Input
 | 
			
		||||
                  value={value}
 | 
			
		||||
                  onChange={(e) =>
 | 
			
		||||
                    setTxFields({
 | 
			
		||||
                      ...txFields,
 | 
			
		||||
                      [field]:
 | 
			
		||||
                        typeof _value === "object"
 | 
			
		||||
                          ? { ..._value, value: e.target.value }
 | 
			
		||||
                          : e.target.value,
 | 
			
		||||
                    })
 | 
			
		||||
                  }
 | 
			
		||||
                  variant="deep"
 | 
			
		||||
                  css={{ width: "70%", flex: "inherit", height: "$9" }}
 | 
			
		||||
                />
 | 
			
		||||
              </Flex>
 | 
			
		||||
            );
 | 
			
		||||
          })}
 | 
			
		||||
        </Flex>
 | 
			
		||||
      </Container>
 | 
			
		||||
      <Flex
 | 
			
		||||
        row
 | 
			
		||||
        css={{
 | 
			
		||||
          justifyContent: "space-between",
 | 
			
		||||
          position: "absolute",
 | 
			
		||||
          left: 0,
 | 
			
		||||
          bottom: 0,
 | 
			
		||||
          width: "100%",
 | 
			
		||||
        }}
 | 
			
		||||
      >
 | 
			
		||||
        <Button outline>VIEW AS JSON</Button>
 | 
			
		||||
        <Flex row>
 | 
			
		||||
          <Button onClick={resetState} outline css={{ mr: "$3" }}>
 | 
			
		||||
            RESET
 | 
			
		||||
          </Button>
 | 
			
		||||
          <Button
 | 
			
		||||
            variant="primary"
 | 
			
		||||
            onClick={submitTest}
 | 
			
		||||
            isLoading={txIsLoading}
 | 
			
		||||
            disabled={txIsDisabled}
 | 
			
		||||
          >
 | 
			
		||||
            <Play weight="bold" size="16px" />
 | 
			
		||||
            RUN TEST
 | 
			
		||||
          </Button>
 | 
			
		||||
        </Flex>
 | 
			
		||||
      </Flex>
 | 
			
		||||
    </Box>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Test = () => {
 | 
			
		||||
  return <Container css={{ py: "$10" }}>This will be the test page</Container>;
 | 
			
		||||
  const { transactionLogs } = useSnapshot(state);
 | 
			
		||||
  const [tabHeaders, setTabHeaders] = useState<string[]>(["test1.json"]);
 | 
			
		||||
  return (
 | 
			
		||||
    <Container css={{ px: 0 }}>
 | 
			
		||||
      <Split
 | 
			
		||||
        direction="vertical"
 | 
			
		||||
        sizes={getSplit("testVertical") || [50, 50]}
 | 
			
		||||
        gutterSize={4}
 | 
			
		||||
        gutterAlign="center"
 | 
			
		||||
        style={{ height: "calc(100vh - 60px)" }}
 | 
			
		||||
        onDragEnd={(e) => saveSplit("testVertical", e)}
 | 
			
		||||
      >
 | 
			
		||||
        <Flex
 | 
			
		||||
          row
 | 
			
		||||
          fluid
 | 
			
		||||
          css={{
 | 
			
		||||
            justifyContent: "center",
 | 
			
		||||
            p: "$3 $2",
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          <Split
 | 
			
		||||
            direction="horizontal"
 | 
			
		||||
            sizes={getSplit("testHorizontal") || [50, 50]}
 | 
			
		||||
            minSize={[180, 320]}
 | 
			
		||||
            gutterSize={4}
 | 
			
		||||
            gutterAlign="center"
 | 
			
		||||
            style={{
 | 
			
		||||
              display: "flex",
 | 
			
		||||
              flexDirection: "row",
 | 
			
		||||
              width: "100%",
 | 
			
		||||
              height: "100%",
 | 
			
		||||
            }}
 | 
			
		||||
            onDragEnd={(e) => saveSplit("testHorizontal", e)}
 | 
			
		||||
          >
 | 
			
		||||
            <Box css={{ width: "55%", px: "$2" }}>
 | 
			
		||||
              <Tabs
 | 
			
		||||
                keepAllAlive
 | 
			
		||||
                forceDefaultExtension
 | 
			
		||||
                defaultExtension=".json"
 | 
			
		||||
                onCreateNewTab={(name) =>
 | 
			
		||||
                  setTabHeaders(tabHeaders.concat(name))
 | 
			
		||||
                }
 | 
			
		||||
                onCloseTab={(index) =>
 | 
			
		||||
                  setTabHeaders(tabHeaders.filter((_, idx) => idx !== index))
 | 
			
		||||
                }
 | 
			
		||||
              >
 | 
			
		||||
                {tabHeaders.map((header) => (
 | 
			
		||||
                  <Tab key={header} header={header}>
 | 
			
		||||
                    <Transaction header={header} />
 | 
			
		||||
                  </Tab>
 | 
			
		||||
                ))}
 | 
			
		||||
              </Tabs>
 | 
			
		||||
            </Box>
 | 
			
		||||
            <Box css={{ width: "45%", mx: "$2", height: "100%" }}>
 | 
			
		||||
              <Accounts card hideDeployBtn showHookStats />
 | 
			
		||||
            </Box>
 | 
			
		||||
          </Split>
 | 
			
		||||
        </Flex>
 | 
			
		||||
 | 
			
		||||
        <Flex row fluid>
 | 
			
		||||
          <Split
 | 
			
		||||
            direction="horizontal"
 | 
			
		||||
            sizes={[50, 50]}
 | 
			
		||||
            minSize={[320, 160]}
 | 
			
		||||
            gutterSize={4}
 | 
			
		||||
            gutterAlign="center"
 | 
			
		||||
            style={{
 | 
			
		||||
              display: "flex",
 | 
			
		||||
              flexDirection: "row",
 | 
			
		||||
              width: "100%",
 | 
			
		||||
              height: "100%",
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            <Box
 | 
			
		||||
              css={{
 | 
			
		||||
                borderRight: "1px solid $mauve8",
 | 
			
		||||
                height: "100%",
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              <LogBox
 | 
			
		||||
                title="Development Log"
 | 
			
		||||
                logs={transactionLogs}
 | 
			
		||||
                clearLog={() => (state.transactionLogs = [])}
 | 
			
		||||
              />
 | 
			
		||||
            </Box>
 | 
			
		||||
            <Box css={{ height: "100%" }}>
 | 
			
		||||
              <DebugStream />
 | 
			
		||||
            </Box>
 | 
			
		||||
          </Split>
 | 
			
		||||
        </Flex>
 | 
			
		||||
      </Split>
 | 
			
		||||
    </Container>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Test;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
diff --git a/node_modules/ripple-binary-codec/dist/enums/definitions.json b/node_modules/ripple-binary-codec/dist/enums/definitions.json
 | 
			
		||||
index 2333c42..99557cb 100644
 | 
			
		||||
index 2333c42..b8f8eab 100644
 | 
			
		||||
--- a/node_modules/ripple-binary-codec/dist/enums/definitions.json
 | 
			
		||||
+++ b/node_modules/ripple-binary-codec/dist/enums/definitions.json
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
@@ -7,17 +7,18 @@ index 2333c42..99557cb 100644
 | 
			
		||||
 {
 | 
			
		||||
   "TYPES": {
 | 
			
		||||
     "Validation": 10003,
 | 
			
		||||
@@ -40,8 +41,7 @@
 | 
			
		||||
@@ -40,9 +41,7 @@
 | 
			
		||||
     "Check": 67,
 | 
			
		||||
     "Nickname": 110,
 | 
			
		||||
     "Contract": 99,
 | 
			
		||||
-    "NFTokenPage": 80,
 | 
			
		||||
-    "NFTokenOffer": 55,
 | 
			
		||||
+    "GeneratorMap": 103,
 | 
			
		||||
     "NegativeUNL": 78
 | 
			
		||||
-    "NegativeUNL": 78
 | 
			
		||||
+    "GeneratorMap": 103
 | 
			
		||||
   },
 | 
			
		||||
   "FIELDS": [
 | 
			
		||||
@@ -95,16 +95,6 @@
 | 
			
		||||
     [
 | 
			
		||||
@@ -95,16 +94,6 @@
 | 
			
		||||
         "type": "UInt16"
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
@@ -34,7 +35,7 @@ index 2333c42..99557cb 100644
 | 
			
		||||
     [
 | 
			
		||||
       "Flags",
 | 
			
		||||
       {
 | 
			
		||||
@@ -455,6 +445,16 @@
 | 
			
		||||
@@ -455,6 +444,16 @@
 | 
			
		||||
         "type": "UInt32"
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
@@ -51,7 +52,7 @@ index 2333c42..99557cb 100644
 | 
			
		||||
     [
 | 
			
		||||
       "IndexNext",
 | 
			
		||||
       {
 | 
			
		||||
@@ -635,16 +635,6 @@
 | 
			
		||||
@@ -635,16 +634,6 @@
 | 
			
		||||
         "type": "Hash256"
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
@@ -68,7 +69,7 @@ index 2333c42..99557cb 100644
 | 
			
		||||
     [
 | 
			
		||||
       "BookDirectory",
 | 
			
		||||
       {
 | 
			
		||||
@@ -916,7 +906,7 @@
 | 
			
		||||
@@ -916,7 +905,7 @@
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
     [
 | 
			
		||||
@@ -77,7 +78,44 @@ index 2333c42..99557cb 100644
 | 
			
		||||
       {
 | 
			
		||||
         "nth": 5,
 | 
			
		||||
         "isVLEncoded": true,
 | 
			
		||||
@@ -1156,7 +1146,7 @@
 | 
			
		||||
@@ -1045,36 +1034,6 @@
 | 
			
		||||
         "type": "Blob"
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
-    [
 | 
			
		||||
-      "UNLModifyValidator",
 | 
			
		||||
-      {
 | 
			
		||||
-        "nth": 19,
 | 
			
		||||
-        "isVLEncoded": true,
 | 
			
		||||
-        "isSerialized": true,
 | 
			
		||||
-        "isSigningField": true,
 | 
			
		||||
-        "type": "Blob"
 | 
			
		||||
-      }
 | 
			
		||||
-    ],
 | 
			
		||||
-    [
 | 
			
		||||
-      "ValidatorToDisable",
 | 
			
		||||
-      {
 | 
			
		||||
-        "nth": 20,
 | 
			
		||||
-        "isVLEncoded": true,
 | 
			
		||||
-        "isSerialized": true,
 | 
			
		||||
-        "isSigningField": true,
 | 
			
		||||
-        "type": "Blob"
 | 
			
		||||
-      }
 | 
			
		||||
-    ],
 | 
			
		||||
-    [
 | 
			
		||||
-      "ValidatorToReEnable",
 | 
			
		||||
-      {
 | 
			
		||||
-        "nth": 21,
 | 
			
		||||
-        "isVLEncoded": true,
 | 
			
		||||
-        "isSerialized": true,
 | 
			
		||||
-        "isSigningField": true,
 | 
			
		||||
-        "type": "Blob"
 | 
			
		||||
-      }
 | 
			
		||||
-    ],
 | 
			
		||||
     [
 | 
			
		||||
       "Account",
 | 
			
		||||
       {
 | 
			
		||||
@@ -1156,7 +1115,7 @@
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
     [
 | 
			
		||||
@@ -86,7 +124,7 @@ index 2333c42..99557cb 100644
 | 
			
		||||
       {
 | 
			
		||||
         "nth": 9,
 | 
			
		||||
         "isVLEncoded": true,
 | 
			
		||||
@@ -1276,9 +1266,9 @@
 | 
			
		||||
@@ -1276,9 +1235,9 @@
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
     [
 | 
			
		||||
@@ -98,7 +136,7 @@ index 2333c42..99557cb 100644
 | 
			
		||||
         "isVLEncoded": false,
 | 
			
		||||
         "isSerialized": true,
 | 
			
		||||
         "isSigningField": true,
 | 
			
		||||
@@ -1286,9 +1276,9 @@
 | 
			
		||||
@@ -1286,9 +1245,9 @@
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
     [
 | 
			
		||||
@@ -110,7 +148,7 @@ index 2333c42..99557cb 100644
 | 
			
		||||
         "isVLEncoded": false,
 | 
			
		||||
         "isSerialized": true,
 | 
			
		||||
         "isSigningField": true,
 | 
			
		||||
@@ -1296,9 +1286,9 @@
 | 
			
		||||
@@ -1296,9 +1255,9 @@
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
     [
 | 
			
		||||
@@ -122,7 +160,7 @@ index 2333c42..99557cb 100644
 | 
			
		||||
         "isVLEncoded": false,
 | 
			
		||||
         "isSerialized": true,
 | 
			
		||||
         "isSigningField": true,
 | 
			
		||||
@@ -1306,9 +1296,9 @@
 | 
			
		||||
@@ -1306,9 +1265,9 @@
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
     [
 | 
			
		||||
@@ -134,7 +172,7 @@ index 2333c42..99557cb 100644
 | 
			
		||||
         "isVLEncoded": false,
 | 
			
		||||
         "isSerialized": true,
 | 
			
		||||
         "isSigningField": true,
 | 
			
		||||
@@ -1395,16 +1385,6 @@
 | 
			
		||||
@@ -1395,16 +1354,6 @@
 | 
			
		||||
         "type": "STArray"
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
@@ -151,7 +189,24 @@ index 2333c42..99557cb 100644
 | 
			
		||||
     [
 | 
			
		||||
       "Majorities",
 | 
			
		||||
       {
 | 
			
		||||
@@ -1535,16 +1515,6 @@
 | 
			
		||||
@@ -1415,16 +1364,6 @@
 | 
			
		||||
         "type": "STArray"
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
-    [
 | 
			
		||||
-      "DisabledValidators",
 | 
			
		||||
-      {
 | 
			
		||||
-        "nth": 17,
 | 
			
		||||
-        "isVLEncoded": false,
 | 
			
		||||
-        "isSerialized": true,
 | 
			
		||||
-        "isSigningField": true,
 | 
			
		||||
-        "type": "STArray"
 | 
			
		||||
-      }
 | 
			
		||||
-    ],
 | 
			
		||||
     [
 | 
			
		||||
       "CloseResolution",
 | 
			
		||||
       {
 | 
			
		||||
@@ -1535,16 +1474,6 @@
 | 
			
		||||
         "type": "Vector256"
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
@@ -168,129 +223,96 @@ index 2333c42..99557cb 100644
 | 
			
		||||
     [
 | 
			
		||||
       "Transaction",
 | 
			
		||||
       {
 | 
			
		||||
@@ -1616,9 +1586,9 @@
 | 
			
		||||
@@ -1596,7 +1525,7 @@
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
     [
 | 
			
		||||
-      "TicketCount",
 | 
			
		||||
+      "HookStateCount",
 | 
			
		||||
       {
 | 
			
		||||
         "nth": 40,
 | 
			
		||||
         "isVLEncoded": false,
 | 
			
		||||
@@ -1606,7 +1535,7 @@
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
     [
 | 
			
		||||
-      "TicketSequence",
 | 
			
		||||
+      "HookReserveCount",
 | 
			
		||||
       {
 | 
			
		||||
         "nth": 41,
 | 
			
		||||
         "isVLEncoded": false,
 | 
			
		||||
@@ -1616,7 +1545,7 @@
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
     [
 | 
			
		||||
-      "TokenTaxon",
 | 
			
		||||
+      "HookStateCount",
 | 
			
		||||
+      "HookDataMaxSize",
 | 
			
		||||
       {
 | 
			
		||||
-        "nth": 42,
 | 
			
		||||
+        "nth": 40,
 | 
			
		||||
         "nth": 42,
 | 
			
		||||
         "isVLEncoded": false,
 | 
			
		||||
         "isSerialized": true,
 | 
			
		||||
         "isSigningField": true,
 | 
			
		||||
@@ -1626,9 +1596,9 @@
 | 
			
		||||
@@ -1626,23 +1555,23 @@
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
     [
 | 
			
		||||
-      "MintedTokens",
 | 
			
		||||
+      "HookReserveCount",
 | 
			
		||||
       {
 | 
			
		||||
-        "nth": 43,
 | 
			
		||||
+        "nth": 41,
 | 
			
		||||
         "isVLEncoded": false,
 | 
			
		||||
         "isSerialized": true,
 | 
			
		||||
         "isSigningField": true,
 | 
			
		||||
@@ -1636,9 +1606,9 @@
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
     [
 | 
			
		||||
-      "BurnedTokens",
 | 
			
		||||
+      "HookDataMaxSize",
 | 
			
		||||
       {
 | 
			
		||||
-        "nth": 44,
 | 
			
		||||
+        "nth": 42,
 | 
			
		||||
         "isVLEncoded": false,
 | 
			
		||||
         "isSerialized": true,
 | 
			
		||||
         "isSigningField": true,
 | 
			
		||||
@@ -1646,29 +1616,29 @@
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
     [
 | 
			
		||||
-      "Channel",
 | 
			
		||||
+      "HookOn",
 | 
			
		||||
       {
 | 
			
		||||
-        "nth": 22,
 | 
			
		||||
-        "nth": 43,
 | 
			
		||||
+        "nth": 16,
 | 
			
		||||
         "isVLEncoded": false,
 | 
			
		||||
         "isSerialized": true,
 | 
			
		||||
         "isSigningField": true,
 | 
			
		||||
-        "type": "Hash256"
 | 
			
		||||
-        "type": "UInt32"
 | 
			
		||||
+        "type": "UInt64"
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
     [
 | 
			
		||||
-      "ConsensusHash",
 | 
			
		||||
-      "BurnedTokens",
 | 
			
		||||
+      "EmitBurden",
 | 
			
		||||
       {
 | 
			
		||||
-        "nth": 23,
 | 
			
		||||
-        "nth": 44,
 | 
			
		||||
+        "nth": 12,
 | 
			
		||||
         "isVLEncoded": false,
 | 
			
		||||
         "isSerialized": true,
 | 
			
		||||
         "isSigningField": true,
 | 
			
		||||
-        "type": "Hash256"
 | 
			
		||||
-        "type": "UInt32"
 | 
			
		||||
+        "type": "UInt64"
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
     [
 | 
			
		||||
-      "CheckID",
 | 
			
		||||
+      "Channel",
 | 
			
		||||
       {
 | 
			
		||||
-        "nth": 24,
 | 
			
		||||
+        "nth": 22,
 | 
			
		||||
         "isVLEncoded": false,
 | 
			
		||||
         "isSerialized": true,
 | 
			
		||||
         "isSigningField": true,
 | 
			
		||||
@@ -1676,9 +1646,9 @@
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
     [
 | 
			
		||||
-      "ValidatedHash",
 | 
			
		||||
+      "ConsensusHash",
 | 
			
		||||
       {
 | 
			
		||||
-        "nth": 25,
 | 
			
		||||
+        "nth": 23,
 | 
			
		||||
         "isVLEncoded": false,
 | 
			
		||||
         "isSerialized": true,
 | 
			
		||||
         "isSigningField": true,
 | 
			
		||||
@@ -1686,9 +1656,9 @@
 | 
			
		||||
@@ -1686,29 +1615,9 @@
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
     [
 | 
			
		||||
-      "PreviousPageMin",
 | 
			
		||||
+      "CheckID",
 | 
			
		||||
       {
 | 
			
		||||
-        "nth": 26,
 | 
			
		||||
+        "nth": 24,
 | 
			
		||||
         "isVLEncoded": false,
 | 
			
		||||
         "isSerialized": true,
 | 
			
		||||
         "isSigningField": true,
 | 
			
		||||
@@ -1696,9 +1666,9 @@
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
     [
 | 
			
		||||
-      "NextPageMin",
 | 
			
		||||
+      "ValidatedHash",
 | 
			
		||||
       {
 | 
			
		||||
-        "nth": 27,
 | 
			
		||||
+        "nth": 25,
 | 
			
		||||
         "isVLEncoded": false,
 | 
			
		||||
         "isSerialized": true,
 | 
			
		||||
         "isSigningField": true,
 | 
			
		||||
@@ -1706,9 +1676,9 @@
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
     [
 | 
			
		||||
-      "BuyOffer",
 | 
			
		||||
+      "EmitParentTxnID",
 | 
			
		||||
       {
 | 
			
		||||
-        "nth": 26,
 | 
			
		||||
-        "isVLEncoded": false,
 | 
			
		||||
-        "isSerialized": true,
 | 
			
		||||
-        "isSigningField": true,
 | 
			
		||||
-        "type": "Hash256"
 | 
			
		||||
-      }
 | 
			
		||||
-    ],
 | 
			
		||||
-    [
 | 
			
		||||
-      "NextPageMin",
 | 
			
		||||
-      {
 | 
			
		||||
-        "nth": 27,
 | 
			
		||||
-        "isVLEncoded": false,
 | 
			
		||||
-        "isSerialized": true,
 | 
			
		||||
-        "isSigningField": true,
 | 
			
		||||
-        "type": "Hash256"
 | 
			
		||||
-      }
 | 
			
		||||
-    ],
 | 
			
		||||
-    [
 | 
			
		||||
-      "BuyOffer",
 | 
			
		||||
-      {
 | 
			
		||||
-        "nth": 28,
 | 
			
		||||
+        "nth": 10,
 | 
			
		||||
         "isVLEncoded": false,
 | 
			
		||||
         "isSerialized": true,
 | 
			
		||||
         "isSigningField": true,
 | 
			
		||||
@@ -1716,9 +1686,9 @@
 | 
			
		||||
@@ -1716,9 +1625,9 @@
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
     [
 | 
			
		||||
@@ -302,7 +324,24 @@ index 2333c42..99557cb 100644
 | 
			
		||||
         "isVLEncoded": false,
 | 
			
		||||
         "isSerialized": true,
 | 
			
		||||
         "isSigningField": true,
 | 
			
		||||
@@ -1754,36 +1724,6 @@
 | 
			
		||||
@@ -1735,16 +1644,6 @@
 | 
			
		||||
         "type": "UInt8"
 | 
			
		||||
       }
 | 
			
		||||
     ],
 | 
			
		||||
-    [
 | 
			
		||||
-      "UNLModifyDisabling",
 | 
			
		||||
-      {
 | 
			
		||||
-        "nth": 17,
 | 
			
		||||
-        "isVLEncoded": false,
 | 
			
		||||
-        "isSerialized": true,
 | 
			
		||||
-        "isSigningField": true,
 | 
			
		||||
-        "type": "UInt8"
 | 
			
		||||
-      }
 | 
			
		||||
-    ],
 | 
			
		||||
     [
 | 
			
		||||
       "DestinationNode",
 | 
			
		||||
       {
 | 
			
		||||
@@ -1754,36 +1653,6 @@
 | 
			
		||||
         "isSigningField": true,
 | 
			
		||||
         "type": "UInt64"
 | 
			
		||||
       }
 | 
			
		||||
@@ -339,7 +378,7 @@ index 2333c42..99557cb 100644
 | 
			
		||||
     ]
 | 
			
		||||
   ],
 | 
			
		||||
   "TRANSACTION_RESULTS": {
 | 
			
		||||
@@ -1908,18 +1848,7 @@
 | 
			
		||||
@@ -1908,18 +1777,7 @@
 | 
			
		||||
     "tecDUPLICATE": 149,
 | 
			
		||||
     "tecKILLED": 150,
 | 
			
		||||
     "tecHAS_OBLIGATIONS": 151,
 | 
			
		||||
@@ -359,7 +398,7 @@ index 2333c42..99557cb 100644
 | 
			
		||||
   },
 | 
			
		||||
   "TRANSACTION_TYPES": {
 | 
			
		||||
     "Invalid": -1,
 | 
			
		||||
@@ -1946,11 +1875,10 @@
 | 
			
		||||
@@ -1946,13 +1804,11 @@
 | 
			
		||||
     "DepositPreauth": 19,
 | 
			
		||||
     "TrustSet": 20,
 | 
			
		||||
     "AccountDelete": 21,
 | 
			
		||||
@@ -373,5 +412,8 @@ index 2333c42..99557cb 100644
 | 
			
		||||
+    "Batch": 24,
 | 
			
		||||
+
 | 
			
		||||
     "EnableAmendment": 100,
 | 
			
		||||
     "SetFee": 101,
 | 
			
		||||
     "UNLModify": 102
 | 
			
		||||
-    "SetFee": 101,
 | 
			
		||||
-    "UNLModify": 102
 | 
			
		||||
+    "SetFee": 101
 | 
			
		||||
   }
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								public/android-chrome-192x192.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.0 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								public/android-chrome-512x512.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								public/apple-touch-icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.1 KiB  | 
							
								
								
									
										9
									
								
								public/browserconfig.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,9 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<browserconfig>
 | 
			
		||||
    <msapplication>
 | 
			
		||||
        <tile>
 | 
			
		||||
            <square150x150logo src="/mstile-150x150.png"/>
 | 
			
		||||
            <TileColor>#161618</TileColor>
 | 
			
		||||
        </tile>
 | 
			
		||||
    </msapplication>
 | 
			
		||||
</browserconfig>
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								public/favicon-16x16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 737 B  | 
							
								
								
									
										
											BIN
										
									
								
								public/favicon-32x32.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.1 KiB  | 
| 
		 Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 15 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								public/mstile-150x150.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.2 KiB  | 
							
								
								
									
										18
									
								
								public/safari-pinned-tab.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,18 @@
 | 
			
		||||
<?xml version="1.0" standalone="no"?>
 | 
			
		||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
 | 
			
		||||
 "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
 | 
			
		||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
 width="588.000000pt" height="588.000000pt" viewBox="0 0 588.000000 588.000000"
 | 
			
		||||
 preserveAspectRatio="xMidYMid meet">
 | 
			
		||||
<metadata>
 | 
			
		||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
 | 
			
		||||
</metadata>
 | 
			
		||||
<g transform="translate(0.000000,588.000000) scale(0.100000,-0.100000)"
 | 
			
		||||
fill="#000000" stroke="none">
 | 
			
		||||
<path d="M3843 4097 c-381 -380 -737 -736 -791 -790 l-99 -99 -940 1 -940 0
 | 
			
		||||
-204 -211 c-112 -116 -228 -235 -257 -265 -29 -30 -51 -57 -48 -60 2 -3 542
 | 
			
		||||
-5 1198 -5 l1193 0 799 -799 799 -799 380 0 c209 0 378 2 376 5 -2 2 -422 423
 | 
			
		||||
-933 934 l-928 929 921 920 c507 507 921 923 921 927 0 3 -170 5 -377 5 l-378
 | 
			
		||||
0 -692 -693z"/>
 | 
			
		||||
</g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 837 B  | 
							
								
								
									
										
											BIN
										
									
								
								public/share-image.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 710 KiB  | 
							
								
								
									
										19
									
								
								public/site.webmanifest
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,19 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "Hooks Builder",
 | 
			
		||||
    "short_name": "Hooks Builder",
 | 
			
		||||
    "icons": [
 | 
			
		||||
        {
 | 
			
		||||
            "src": "/android-chrome-192x192.png",
 | 
			
		||||
            "sizes": "192x192",
 | 
			
		||||
            "type": "image/png"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "src": "/android-chrome-512x512.png",
 | 
			
		||||
            "sizes": "512x512",
 | 
			
		||||
            "type": "image/png"
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "theme_color": "#161618",
 | 
			
		||||
    "background_color": "#161618",
 | 
			
		||||
    "display": "standalone"
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
<svg width="283" height="64" viewBox="0 0 283 64" fill="none" 
 | 
			
		||||
    xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
    <path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 1.1 KiB  | 
@@ -1,4 +1,4 @@
 | 
			
		||||
import Router from "next/router";
 | 
			
		||||
 | 
			
		||||
import toast from "react-hot-toast";
 | 
			
		||||
import state, { FaucetAccountRes } from '../index';
 | 
			
		||||
 | 
			
		||||
@@ -36,7 +36,6 @@ export const addFaucetAccount = async (showToast: boolean = false) => {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    const toastId = showToast ? toast.loading("Creating account") : "";
 | 
			
		||||
    console.log(Router)
 | 
			
		||||
    const res = await fetch(`${window.location.origin}/api/faucet`, {
 | 
			
		||||
      method: "POST",
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ export const compileCode = async (activeId: number) => {
 | 
			
		||||
  }
 | 
			
		||||
  // Set loading state to true
 | 
			
		||||
  state.compiling = true;
 | 
			
		||||
  state.logs = []
 | 
			
		||||
  try {
 | 
			
		||||
    const res = await fetch(process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT, {
 | 
			
		||||
      method: "POST",
 | 
			
		||||
@@ -38,7 +39,6 @@ export const compileCode = async (activeId: number) => {
 | 
			
		||||
          {
 | 
			
		||||
            type: "c",
 | 
			
		||||
            name: state.files[activeId].name,
 | 
			
		||||
            options: "-O0",
 | 
			
		||||
            src: state.files[activeId].content,
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
@@ -87,4 +87,4 @@ export const compileCode = async (activeId: number) => {
 | 
			
		||||
    });
 | 
			
		||||
    state.compiling = false;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
import { Octokit } from "@octokit/core";
 | 
			
		||||
import Router from "next/router";
 | 
			
		||||
import state from '../index';
 | 
			
		||||
import { templateFileIds } from '../constants';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const octokit = new Octokit();
 | 
			
		||||
 | 
			
		||||
@@ -17,6 +19,17 @@ export const fetchFiles = (gistId: string) => {
 | 
			
		||||
 | 
			
		||||
    octokit
 | 
			
		||||
      .request("GET /gists/{gist_id}", { gist_id: gistId })
 | 
			
		||||
      .then(res => {
 | 
			
		||||
        if (!Object.values(templateFileIds).includes(gistId)) {
 | 
			
		||||
          return res
 | 
			
		||||
        }
 | 
			
		||||
        // in case of templates, fetch header file(s) and append to res
 | 
			
		||||
        return octokit.request("GET /gists/{gist_id}", { gist_id: templateFileIds.headers }).then(({ data: { files: headerFiles } }) => {
 | 
			
		||||
          const files = { ...res.data.files, ...headerFiles }
 | 
			
		||||
          res.data.files = files
 | 
			
		||||
          return res
 | 
			
		||||
        })
 | 
			
		||||
      })
 | 
			
		||||
      .then((res) => {
 | 
			
		||||
        if (res.data.files && Object.keys(res.data.files).length > 0) {
 | 
			
		||||
          const files = Object.keys(res.data.files).map((filename) => ({
 | 
			
		||||
@@ -44,6 +57,7 @@ export const fetchFiles = (gistId: string) => {
 | 
			
		||||
        state.loading = false;
 | 
			
		||||
      })
 | 
			
		||||
      .catch((err) => {
 | 
			
		||||
        // console.error(err)
 | 
			
		||||
        state.loading = false;
 | 
			
		||||
        state.logs.push({
 | 
			
		||||
          type: "error",
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ import { saveFile } from "./saveFile";
 | 
			
		||||
import { syncToGist } from "./syncToGist";
 | 
			
		||||
import { updateEditorSettings } from "./updateEditorSettings";
 | 
			
		||||
import { downloadAsZip } from "./downloadAsZip";
 | 
			
		||||
import { sendTransaction } from "./sendTransaction";
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
  addFaucetAccount,
 | 
			
		||||
@@ -19,5 +20,6 @@ export {
 | 
			
		||||
  saveFile,
 | 
			
		||||
  syncToGist,
 | 
			
		||||
  updateEditorSettings,
 | 
			
		||||
  downloadAsZip
 | 
			
		||||
  downloadAsZip,
 | 
			
		||||
  sendTransaction
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										15
									
								
								state/actions/persistSplits.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,15 @@
 | 
			
		||||
import { snapshot } from "valtio"
 | 
			
		||||
import state from ".."
 | 
			
		||||
 | 
			
		||||
export type SplitSize = number[]
 | 
			
		||||
 | 
			
		||||
export const saveSplit = (splitId: string, event: SplitSize) => {
 | 
			
		||||
  state.splits[splitId] = event
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getSplit = (splitId: string): SplitSize | null => {
 | 
			
		||||
  const { splits } = snapshot(state)
 | 
			
		||||
  const split = splits[splitId]
 | 
			
		||||
  return split ? split : null
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -4,8 +4,9 @@ import state from '../index';
 | 
			
		||||
// Saves the current editor content to global state
 | 
			
		||||
export const saveFile = (showToast: boolean = true) => {
 | 
			
		||||
  const editorModels = state.editorCtx?.getModels();
 | 
			
		||||
  const sought = '/' + state.files[state.active].name;
 | 
			
		||||
  const currentModel = editorModels?.find((editorModel) => {
 | 
			
		||||
    return editorModel.uri.path === `/c/${state.files[state.active].name}`;
 | 
			
		||||
    return editorModel.uri.path.endsWith(sought);
 | 
			
		||||
  });
 | 
			
		||||
  if (state.files.length > 0) {
 | 
			
		||||
    state.files[state.active].content = currentModel?.getValue() || "";
 | 
			
		||||
@@ -13,4 +14,4 @@ export const saveFile = (showToast: boolean = true) => {
 | 
			
		||||
  if (showToast) {
 | 
			
		||||
    toast.success("Saved successfully", { position: "bottom-center" });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										53
									
								
								state/actions/sendTransaction.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,53 @@
 | 
			
		||||
import { derive, sign } from "xrpl-accountlib";
 | 
			
		||||
 | 
			
		||||
import state from '..'
 | 
			
		||||
import type { IAccount } from "..";
 | 
			
		||||
 | 
			
		||||
interface TransactionOptions {
 | 
			
		||||
    TransactionType: string,
 | 
			
		||||
    Account?: string,
 | 
			
		||||
    Fee?: string,
 | 
			
		||||
    Destination?: string
 | 
			
		||||
    [index: string]: any
 | 
			
		||||
}
 | 
			
		||||
interface OtherOptions {
 | 
			
		||||
    logPrefix?: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const sendTransaction = async (account: IAccount, txOptions: TransactionOptions, options?: OtherOptions) => {
 | 
			
		||||
    if (!state.client) throw Error('XRPL client not initalized')
 | 
			
		||||
 | 
			
		||||
    const { Fee = "1000", ...opts } = txOptions
 | 
			
		||||
    const tx: TransactionOptions = {
 | 
			
		||||
        Account: account.address,
 | 
			
		||||
        Sequence: account.sequence, // TODO auto-fillable
 | 
			
		||||
        Fee,  // TODO auto-fillable
 | 
			
		||||
        ...opts
 | 
			
		||||
    };
 | 
			
		||||
    const { logPrefix = '' } = options || {}
 | 
			
		||||
    try {
 | 
			
		||||
        const signedAccount = derive.familySeed(account.secret);
 | 
			
		||||
        const { signedTransaction } = sign(tx, signedAccount);
 | 
			
		||||
        const response = await state.client.send({
 | 
			
		||||
            command: "submit",
 | 
			
		||||
            tx_blob: signedTransaction,
 | 
			
		||||
        });
 | 
			
		||||
        if (response.engine_result === "tesSUCCESS") {
 | 
			
		||||
            state.transactionLogs.push({
 | 
			
		||||
                type: 'success',
 | 
			
		||||
                message: `${logPrefix}[${response.engine_result}] ${response.engine_result_message}`
 | 
			
		||||
            })
 | 
			
		||||
        } else {
 | 
			
		||||
            state.transactionLogs.push({
 | 
			
		||||
                type: "error",
 | 
			
		||||
                message: `${logPrefix}[${response.error || response.engine_result}] ${response.error_exception || response.engine_result_message}`,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
        console.error(err);
 | 
			
		||||
        state.transactionLogs.push({
 | 
			
		||||
            type: "error",
 | 
			
		||||
            message: err instanceof Error ? `${logPrefix}Error: ${err.message}` : `${logPrefix}Something went wrong, try again later`,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										1
									
								
								state/constants/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1 @@
 | 
			
		||||
export * from './templates'
 | 
			
		||||
							
								
								
									
										10
									
								
								state/constants/templates.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,10 @@
 | 
			
		||||
export const templateFileIds = {
 | 
			
		||||
    'starter': '1d14e51e2e02dc0a508cb0733767a914', // TODO currently same as accept
 | 
			
		||||
    'firewall': 'bcd6d0c0fcbe52545ddb802481ff9d26',
 | 
			
		||||
    'notary': 'a789c75f591eeab7932fd702ed8cf9ea',
 | 
			
		||||
    'carbon': '43925143fa19735d8c6505c34d3a6a47',
 | 
			
		||||
    'peggy': 'ceaf352e2a65741341033ab7ef05c448',
 | 
			
		||||
    'headers': '9b448e8a55fab11ef5d1274cb59f9cf3'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const apiHeaderFiles = ['hookapi.h', 'sfcodes.h', 'hookmacro.h']
 | 
			
		||||
@@ -1,7 +1,8 @@
 | 
			
		||||
import { proxy, ref, subscribe } from "valtio";
 | 
			
		||||
import { devtools } from 'valtio/utils'
 | 
			
		||||
import type monaco from "monaco-editor";
 | 
			
		||||
import { proxy, ref, subscribe } from "valtio";
 | 
			
		||||
import { devtools } from 'valtio/utils';
 | 
			
		||||
import { XrplClient } from "xrpl-client";
 | 
			
		||||
import { SplitSize } from "./actions/persistSplits";
 | 
			
		||||
 | 
			
		||||
export interface IFile {
 | 
			
		||||
  name: string;
 | 
			
		||||
@@ -49,10 +50,15 @@ export interface IState {
 | 
			
		||||
  compiling: boolean;
 | 
			
		||||
  logs: ILog[];
 | 
			
		||||
  deployLogs: ILog[];
 | 
			
		||||
  transactionLogs: ILog[];
 | 
			
		||||
  debugLogs: ILog[];
 | 
			
		||||
  editorCtx?: typeof monaco.editor;
 | 
			
		||||
  editorSettings: {
 | 
			
		||||
    tabSize: number;
 | 
			
		||||
  };
 | 
			
		||||
  splits: {
 | 
			
		||||
    [id: string]: SplitSize
 | 
			
		||||
  };
 | 
			
		||||
  client: XrplClient | null;
 | 
			
		||||
  clientStatus: "offline" | "online";
 | 
			
		||||
  mainModalOpen: boolean;
 | 
			
		||||
@@ -62,12 +68,16 @@ export interface IState {
 | 
			
		||||
// let localStorageState: null | string = null;
 | 
			
		||||
let initialState: IState = {
 | 
			
		||||
  files: [],
 | 
			
		||||
  // active file index on the Develop page editor
 | 
			
		||||
  active: 0,
 | 
			
		||||
  // Active file index on the Deploy page editor
 | 
			
		||||
  activeWat: 0,
 | 
			
		||||
  loading: false,
 | 
			
		||||
  compiling: false,
 | 
			
		||||
  logs: [],
 | 
			
		||||
  deployLogs: [],
 | 
			
		||||
  transactionLogs: [],
 | 
			
		||||
  debugLogs: [],
 | 
			
		||||
  editorCtx: undefined,
 | 
			
		||||
  gistId: undefined,
 | 
			
		||||
  gistOwner: undefined,
 | 
			
		||||
@@ -77,6 +87,7 @@ let initialState: IState = {
 | 
			
		||||
  editorSettings: {
 | 
			
		||||
    tabSize: 2,
 | 
			
		||||
  },
 | 
			
		||||
  splits: {},
 | 
			
		||||
  client: null,
 | 
			
		||||
  clientStatus: "offline" as "offline",
 | 
			
		||||
  mainModalOpen: false,
 | 
			
		||||
@@ -85,6 +96,9 @@ let initialState: IState = {
 | 
			
		||||
 | 
			
		||||
let localStorageAccounts: string | null = null;
 | 
			
		||||
let initialAccounts: IAccount[] = [];
 | 
			
		||||
 | 
			
		||||
// TODO: What exactly should we store in localStorage? editorSettings, splits, accounts?
 | 
			
		||||
 | 
			
		||||
// Check if there's a persited accounts in localStorage
 | 
			
		||||
if (typeof window !== "undefined") {
 | 
			
		||||
  try {
 | 
			
		||||
@@ -133,4 +147,4 @@ if (typeof window !== "undefined") {
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
export default state
 | 
			
		||||
export default state
 | 
			
		||||
 
 | 
			
		||||
@@ -1,28 +1,25 @@
 | 
			
		||||
// stitches.config.ts
 | 
			
		||||
import type Stitches from '@stitches/react';
 | 
			
		||||
import { createStitches } from '@stitches/react';
 | 
			
		||||
import type Stitches from "@stitches/react";
 | 
			
		||||
import { createStitches } from "@stitches/react";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  gray,
 | 
			
		||||
  blue,
 | 
			
		||||
  red,
 | 
			
		||||
  green,
 | 
			
		||||
  plum,
 | 
			
		||||
  crimson,
 | 
			
		||||
  grass,
 | 
			
		||||
  slate,
 | 
			
		||||
  mauve,
 | 
			
		||||
  pink,
 | 
			
		||||
  yellow,
 | 
			
		||||
  amber,
 | 
			
		||||
  purple,
 | 
			
		||||
  grayDark,
 | 
			
		||||
  blueDark,
 | 
			
		||||
  redDark,
 | 
			
		||||
  greenDark,
 | 
			
		||||
  plumDark,
 | 
			
		||||
  crimsonDark,
 | 
			
		||||
  grassDark,
 | 
			
		||||
  slateDark,
 | 
			
		||||
  mauveDark,
 | 
			
		||||
  pinkDark,
 | 
			
		||||
  yellowDark,
 | 
			
		||||
  amberDark,
 | 
			
		||||
  purpleDark,
 | 
			
		||||
} from '@radix-ui/colors';
 | 
			
		||||
} from "@radix-ui/colors";
 | 
			
		||||
 | 
			
		||||
export const {
 | 
			
		||||
  styled,
 | 
			
		||||
@@ -38,24 +35,25 @@ export const {
 | 
			
		||||
    colors: {
 | 
			
		||||
      ...gray,
 | 
			
		||||
      ...blue,
 | 
			
		||||
      ...red,
 | 
			
		||||
      ...green,
 | 
			
		||||
      ...plum,
 | 
			
		||||
      ...crimson,
 | 
			
		||||
      ...grass,
 | 
			
		||||
      ...slate,
 | 
			
		||||
      ...mauve,
 | 
			
		||||
      ...pink,
 | 
			
		||||
      ...yellow,
 | 
			
		||||
      ...amber,
 | 
			
		||||
      ...purple,
 | 
			
		||||
      accent: "#9D2DFF",
 | 
			
		||||
      background: "$gray1",
 | 
			
		||||
      backgroundAlt: "$gray4",
 | 
			
		||||
      text: "$gray12",
 | 
			
		||||
      primary: "$plum",
 | 
			
		||||
      white: "white",
 | 
			
		||||
      black: "black"
 | 
			
		||||
      black: "black",
 | 
			
		||||
      deep: "rgb(244, 244, 244)",
 | 
			
		||||
    },
 | 
			
		||||
    fonts: {
 | 
			
		||||
      body: 'Work Sans, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif',
 | 
			
		||||
      heading: 'Work Sans, sans-serif',
 | 
			
		||||
      monospace: 'Roboto Mono, monospace',
 | 
			
		||||
      heading: "Work Sans, sans-serif",
 | 
			
		||||
      monospace: "Roboto Mono, monospace",
 | 
			
		||||
    },
 | 
			
		||||
    fontSizes: {
 | 
			
		||||
      xs: "0.6875rem",
 | 
			
		||||
@@ -71,7 +69,7 @@ export const {
 | 
			
		||||
      "7xl": "4.5rem",
 | 
			
		||||
      "8xl": "6rem",
 | 
			
		||||
      "9xl": "8rem",
 | 
			
		||||
      default: '$md'
 | 
			
		||||
      default: "$md",
 | 
			
		||||
    },
 | 
			
		||||
    space: {
 | 
			
		||||
      px: "1px",
 | 
			
		||||
@@ -107,15 +105,15 @@ export const {
 | 
			
		||||
      72: "18rem",
 | 
			
		||||
      80: "20rem",
 | 
			
		||||
      96: "24rem",
 | 
			
		||||
      "widePlus": '2048px',
 | 
			
		||||
      "wide": '1536px',
 | 
			
		||||
      "layoutPlus": '1260px',
 | 
			
		||||
      "layout": '1024px',
 | 
			
		||||
      "copyUltra": '980px',
 | 
			
		||||
      "copyPlus": '768px',
 | 
			
		||||
      "copy": '680px',
 | 
			
		||||
      "narrowPlus": '600px',
 | 
			
		||||
      "narrow": '512px',
 | 
			
		||||
      widePlus: "2048px",
 | 
			
		||||
      wide: "1536px",
 | 
			
		||||
      layoutPlus: "1260px",
 | 
			
		||||
      layout: "1024px",
 | 
			
		||||
      copyUltra: "980px",
 | 
			
		||||
      copyPlus: "768px",
 | 
			
		||||
      copy: "680px",
 | 
			
		||||
      narrowPlus: "600px",
 | 
			
		||||
      narrow: "512px",
 | 
			
		||||
      xs: "20rem",
 | 
			
		||||
      sm: "24rem",
 | 
			
		||||
      md: "28rem",
 | 
			
		||||
@@ -215,62 +213,112 @@ export const {
 | 
			
		||||
    lg: "(min-width: 62em)",
 | 
			
		||||
    xl: "(min-width: 80em)",
 | 
			
		||||
    "2xl": "(min-width: 96em)",
 | 
			
		||||
    hover: '(any-hover: hover)',
 | 
			
		||||
    dark: '(prefers-color-scheme: dark)',
 | 
			
		||||
    light: '(prefers-color-scheme: light)',
 | 
			
		||||
    hover: "(any-hover: hover)",
 | 
			
		||||
    dark: "(prefers-color-scheme: dark)",
 | 
			
		||||
    light: "(prefers-color-scheme: light)",
 | 
			
		||||
  },
 | 
			
		||||
  utils: {
 | 
			
		||||
    // Abbreviated margin properties
 | 
			
		||||
    m: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'margin'>) => ({
 | 
			
		||||
    m: (
 | 
			
		||||
      value: Stitches.ScaleValue<"space"> | Stitches.PropertyValue<"margin">
 | 
			
		||||
    ) => ({
 | 
			
		||||
      margin: value,
 | 
			
		||||
    }),
 | 
			
		||||
    mt: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'marginTop'>) => ({
 | 
			
		||||
    mt: (
 | 
			
		||||
      value: Stitches.ScaleValue<"space"> | Stitches.PropertyValue<"marginTop">
 | 
			
		||||
    ) => ({
 | 
			
		||||
      marginTop: value,
 | 
			
		||||
    }),
 | 
			
		||||
    mr: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'marginRight'>) => ({
 | 
			
		||||
    mr: (
 | 
			
		||||
      value:
 | 
			
		||||
        | Stitches.ScaleValue<"space">
 | 
			
		||||
        | Stitches.PropertyValue<"marginRight">
 | 
			
		||||
    ) => ({
 | 
			
		||||
      marginRight: value,
 | 
			
		||||
    }),
 | 
			
		||||
    mb: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'marginBottom'>) => ({
 | 
			
		||||
    mb: (
 | 
			
		||||
      value:
 | 
			
		||||
        | Stitches.ScaleValue<"space">
 | 
			
		||||
        | Stitches.PropertyValue<"marginBottom">
 | 
			
		||||
    ) => ({
 | 
			
		||||
      marginBottom: value,
 | 
			
		||||
    }),
 | 
			
		||||
    ml: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'marginLeft'>) => ({
 | 
			
		||||
    ml: (
 | 
			
		||||
      value: Stitches.ScaleValue<"space"> | Stitches.PropertyValue<"marginLeft">
 | 
			
		||||
    ) => ({
 | 
			
		||||
      marginLeft: value,
 | 
			
		||||
    }),
 | 
			
		||||
    mx: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'marginLeft' | 'marginRight'>) => ({
 | 
			
		||||
    mx: (
 | 
			
		||||
      value:
 | 
			
		||||
        | Stitches.ScaleValue<"space">
 | 
			
		||||
        | Stitches.PropertyValue<"marginLeft" | "marginRight">
 | 
			
		||||
    ) => ({
 | 
			
		||||
      marginLeft: value,
 | 
			
		||||
      marginRight: value,
 | 
			
		||||
    }),
 | 
			
		||||
    my: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'marginTop' | 'marginBottom'>) => ({
 | 
			
		||||
    my: (
 | 
			
		||||
      value:
 | 
			
		||||
        | Stitches.ScaleValue<"space">
 | 
			
		||||
        | Stitches.PropertyValue<"marginTop" | "marginBottom">
 | 
			
		||||
    ) => ({
 | 
			
		||||
      marginTop: value,
 | 
			
		||||
      marginBottom: value,
 | 
			
		||||
    }),
 | 
			
		||||
    // Abbreviated margin properties
 | 
			
		||||
    p: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'padding'>) => ({
 | 
			
		||||
    p: (
 | 
			
		||||
      value: Stitches.ScaleValue<"space"> | Stitches.PropertyValue<"padding">
 | 
			
		||||
    ) => ({
 | 
			
		||||
      padding: value,
 | 
			
		||||
    }),
 | 
			
		||||
    pt: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'paddingTop'>) => ({
 | 
			
		||||
    pt: (
 | 
			
		||||
      value: Stitches.ScaleValue<"space"> | Stitches.PropertyValue<"paddingTop">
 | 
			
		||||
    ) => ({
 | 
			
		||||
      paddingTop: value,
 | 
			
		||||
    }),
 | 
			
		||||
    pr: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'paddingRight'>) => ({
 | 
			
		||||
    pr: (
 | 
			
		||||
      value:
 | 
			
		||||
        | Stitches.ScaleValue<"space">
 | 
			
		||||
        | Stitches.PropertyValue<"paddingRight">
 | 
			
		||||
    ) => ({
 | 
			
		||||
      paddingRight: value,
 | 
			
		||||
    }),
 | 
			
		||||
    pb: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'paddingBottom'>) => ({
 | 
			
		||||
    pb: (
 | 
			
		||||
      value:
 | 
			
		||||
        | Stitches.ScaleValue<"space">
 | 
			
		||||
        | Stitches.PropertyValue<"paddingBottom">
 | 
			
		||||
    ) => ({
 | 
			
		||||
      paddingBottom: value,
 | 
			
		||||
    }),
 | 
			
		||||
    pl: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'paddingLeft'>) => ({
 | 
			
		||||
    pl: (
 | 
			
		||||
      value:
 | 
			
		||||
        | Stitches.ScaleValue<"space">
 | 
			
		||||
        | Stitches.PropertyValue<"paddingLeft">
 | 
			
		||||
    ) => ({
 | 
			
		||||
      paddingLeft: value,
 | 
			
		||||
    }),
 | 
			
		||||
    px: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'paddingLeft' | 'paddingRight'>) => ({
 | 
			
		||||
    px: (
 | 
			
		||||
      value:
 | 
			
		||||
        | Stitches.ScaleValue<"space">
 | 
			
		||||
        | Stitches.PropertyValue<"paddingLeft" | "paddingRight">
 | 
			
		||||
    ) => ({
 | 
			
		||||
      paddingLeft: value,
 | 
			
		||||
      paddingRight: value,
 | 
			
		||||
    }),
 | 
			
		||||
    py: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'paddingTop' | 'paddingBottom'>) => ({
 | 
			
		||||
    py: (
 | 
			
		||||
      value:
 | 
			
		||||
        | Stitches.ScaleValue<"space">
 | 
			
		||||
        | Stitches.PropertyValue<"paddingTop" | "paddingBottom">
 | 
			
		||||
    ) => ({
 | 
			
		||||
      paddingTop: value,
 | 
			
		||||
      paddingBottom: value,
 | 
			
		||||
    }),
 | 
			
		||||
 | 
			
		||||
    // A property for applying width/height together
 | 
			
		||||
    size: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'width' | 'height'>) => ({
 | 
			
		||||
    size: (
 | 
			
		||||
      value:
 | 
			
		||||
        | Stitches.ScaleValue<"space">
 | 
			
		||||
        | Stitches.PropertyValue<"width" | "height">
 | 
			
		||||
    ) => ({
 | 
			
		||||
      width: value,
 | 
			
		||||
      height: value,
 | 
			
		||||
    }),
 | 
			
		||||
@@ -279,45 +327,44 @@ export const {
 | 
			
		||||
    // }),
 | 
			
		||||
 | 
			
		||||
    // A property to apply linear gradient
 | 
			
		||||
    linearGradient: (value: Stitches.ScaleValue<'space'>) => ({
 | 
			
		||||
    linearGradient: (value: Stitches.ScaleValue<"space">) => ({
 | 
			
		||||
      backgroundImage: `linear-gradient(${value})`,
 | 
			
		||||
    }),
 | 
			
		||||
 | 
			
		||||
    // An abbreviated property for border-radius
 | 
			
		||||
    br: (value: Stitches.ScaleValue<'space'>) => ({
 | 
			
		||||
    br: (value: Stitches.ScaleValue<"space">) => ({
 | 
			
		||||
      borderRadius: value,
 | 
			
		||||
    }),
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const darkTheme = createTheme('dark', {
 | 
			
		||||
export const darkTheme = createTheme("dark", {
 | 
			
		||||
  colors: {
 | 
			
		||||
    ...grayDark,
 | 
			
		||||
    ...blueDark,
 | 
			
		||||
    ...redDark,
 | 
			
		||||
    ...greenDark,
 | 
			
		||||
    ...plumDark,
 | 
			
		||||
    ...crimsonDark,
 | 
			
		||||
    ...grassDark,
 | 
			
		||||
    ...slateDark,
 | 
			
		||||
    ...mauveDark,
 | 
			
		||||
    ...pinkDark,
 | 
			
		||||
    ...yellowDark,
 | 
			
		||||
    ...amberDark,
 | 
			
		||||
    ...purpleDark,
 | 
			
		||||
    deep: "rgb(10, 10, 10)",
 | 
			
		||||
    // backgroundA: transparentize(0.1, grayDark.gray1),
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const globalStyles = globalCss({
 | 
			
		||||
  // body: { backgroundColor: '$background', color: '$text', fontFamily: 'Helvetica' },
 | 
			
		||||
  'html, body': {
 | 
			
		||||
    backgroundColor: '$gray1',
 | 
			
		||||
    color: '$gray12',
 | 
			
		||||
    fontFamily: '$body',
 | 
			
		||||
    fontSize: '$md',
 | 
			
		||||
    '-webkit-font-smoothing': 'antialiased',
 | 
			
		||||
    '-moz-osx-font-smoothing': 'grayscale'
 | 
			
		||||
  "html, body": {
 | 
			
		||||
    backgroundColor: "$mauve2",
 | 
			
		||||
    color: "$mauve12",
 | 
			
		||||
    fontFamily: "$body",
 | 
			
		||||
    fontSize: "$md",
 | 
			
		||||
    "-webkit-font-smoothing": "antialiased",
 | 
			
		||||
    "-moz-osx-font-smoothing": "grayscale",
 | 
			
		||||
  },
 | 
			
		||||
  a: {
 | 
			
		||||
    color: "inherit",
 | 
			
		||||
    textDecoration: "none",
 | 
			
		||||
  },
 | 
			
		||||
  'a': {
 | 
			
		||||
    color: 'inherit',
 | 
			
		||||
    textDecoration: 'none'
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -6,8 +6,47 @@ body,
 | 
			
		||||
  min-height: 100vh;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  overflow-y: hidden;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
* {
 | 
			
		||||
  box-sizing: border-box;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.gutter {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  transition: border-color 0.3s, background-color 0.3s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.gutter-vertical {
 | 
			
		||||
  margin-top: -4px;
 | 
			
		||||
}
 | 
			
		||||
.gutter-horizontal {
 | 
			
		||||
  margin-left: -4px;
 | 
			
		||||
}
 | 
			
		||||
.gutter-vertical:hover {
 | 
			
		||||
  cursor: row-resize;
 | 
			
		||||
  background-color: rgba(255, 255, 255, 0.25);
 | 
			
		||||
}
 | 
			
		||||
html.light .gutter-vertical:hover {
 | 
			
		||||
  background-color: rgba(0, 0, 0, 0.25);
 | 
			
		||||
}
 | 
			
		||||
.gutter-horizontal:hover {
 | 
			
		||||
  cursor: col-resize;
 | 
			
		||||
  background-color: rgba(255, 255, 255, 0.25);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
html.light .gutter-horizontal:hover {
 | 
			
		||||
  background-color: rgba(0, 0, 0, 0.25);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Adjust Monaco tooltip stylings */
 | 
			
		||||
.markdown-hover h3 {
 | 
			
		||||
  margin: 0;
 | 
			
		||||
}
 | 
			
		||||
.monaco-editor .monaco-hover hr {
 | 
			
		||||
  margin: 8px 0;
 | 
			
		||||
}
 | 
			
		||||
.monaco-editor .monaco-hover {
 | 
			
		||||
  z-index: 9999;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
  "inherit": true,
 | 
			
		||||
  "rules": [
 | 
			
		||||
    {
 | 
			
		||||
      "background": "1a1d1e",
 | 
			
		||||
      "background": "161618",
 | 
			
		||||
      "token": ""
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -182,10 +182,10 @@
 | 
			
		||||
  ],
 | 
			
		||||
  "colors": {
 | 
			
		||||
    "editor.foreground": "#D0D0FF",
 | 
			
		||||
    "editor.background": "#232326",
 | 
			
		||||
    "editor.background": "#1C1C1F",
 | 
			
		||||
    "editor.selectionBackground": "#ffffff30",
 | 
			
		||||
    "editor.lineHighlightBackground": "#ffffff20",
 | 
			
		||||
    "editorCursor.foreground": "#7070FF",
 | 
			
		||||
    "editorWhitespace.foreground": "#BFBFBF"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
  "inherit": true,
 | 
			
		||||
  "rules": [
 | 
			
		||||
    {
 | 
			
		||||
      "background": "FFFFFF",
 | 
			
		||||
      "background": "F4F2F4",
 | 
			
		||||
      "token": ""
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -89,10 +89,10 @@
 | 
			
		||||
  ],
 | 
			
		||||
  "colors": {
 | 
			
		||||
    "editor.foreground": "#000000",
 | 
			
		||||
    "editor.background": "#f4f2f4",
 | 
			
		||||
    "editor.background": "#F9F8F9",
 | 
			
		||||
    "editor.selectionBackground": "#B5D5FF",
 | 
			
		||||
    "editor.lineHighlightBackground": "#00000012",
 | 
			
		||||
    "editorCursor.foreground": "#000000",
 | 
			
		||||
    "editorWhitespace.foreground": "#BFBFBF"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										169
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						@@ -50,6 +50,13 @@
 | 
			
		||||
  dependencies:
 | 
			
		||||
    regenerator-runtime "^0.13.4"
 | 
			
		||||
 | 
			
		||||
"@babel/runtime@^7.12.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7":
 | 
			
		||||
  version "7.16.7"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa"
 | 
			
		||||
  integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    regenerator-runtime "^0.13.4"
 | 
			
		||||
 | 
			
		||||
"@babel/types@7.15.0":
 | 
			
		||||
  version "7.15.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.0.tgz#61af11f2286c4e9c69ca8deb5f4375a73c72dcbd"
 | 
			
		||||
@@ -76,6 +83,71 @@
 | 
			
		||||
    vscode-languageserver-textdocument "^1.0.1"
 | 
			
		||||
    vscode-uri "^3.0.2"
 | 
			
		||||
 | 
			
		||||
"@emotion/cache@^11.4.0", "@emotion/cache@^11.7.1":
 | 
			
		||||
  version "11.7.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.7.1.tgz#08d080e396a42e0037848214e8aa7bf879065539"
 | 
			
		||||
  integrity sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@emotion/memoize" "^0.7.4"
 | 
			
		||||
    "@emotion/sheet" "^1.1.0"
 | 
			
		||||
    "@emotion/utils" "^1.0.0"
 | 
			
		||||
    "@emotion/weak-memoize" "^0.2.5"
 | 
			
		||||
    stylis "4.0.13"
 | 
			
		||||
 | 
			
		||||
"@emotion/hash@^0.8.0":
 | 
			
		||||
  version "0.8.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
 | 
			
		||||
  integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
 | 
			
		||||
 | 
			
		||||
"@emotion/memoize@^0.7.4":
 | 
			
		||||
  version "0.7.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.5.tgz#2c40f81449a4e554e9fc6396910ed4843ec2be50"
 | 
			
		||||
  integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==
 | 
			
		||||
 | 
			
		||||
"@emotion/react@^11.1.1":
 | 
			
		||||
  version "11.7.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.7.1.tgz#3f800ce9b20317c13e77b8489ac4a0b922b2fe07"
 | 
			
		||||
  integrity sha512-DV2Xe3yhkF1yT4uAUoJcYL1AmrnO5SVsdfvu+fBuS7IbByDeTVx9+wFmvx9Idzv7/78+9Mgx2Hcmr7Fex3tIyw==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@babel/runtime" "^7.13.10"
 | 
			
		||||
    "@emotion/cache" "^11.7.1"
 | 
			
		||||
    "@emotion/serialize" "^1.0.2"
 | 
			
		||||
    "@emotion/sheet" "^1.1.0"
 | 
			
		||||
    "@emotion/utils" "^1.0.0"
 | 
			
		||||
    "@emotion/weak-memoize" "^0.2.5"
 | 
			
		||||
    hoist-non-react-statics "^3.3.1"
 | 
			
		||||
 | 
			
		||||
"@emotion/serialize@^1.0.2":
 | 
			
		||||
  version "1.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.2.tgz#77cb21a0571c9f68eb66087754a65fa97bfcd965"
 | 
			
		||||
  integrity sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@emotion/hash" "^0.8.0"
 | 
			
		||||
    "@emotion/memoize" "^0.7.4"
 | 
			
		||||
    "@emotion/unitless" "^0.7.5"
 | 
			
		||||
    "@emotion/utils" "^1.0.0"
 | 
			
		||||
    csstype "^3.0.2"
 | 
			
		||||
 | 
			
		||||
"@emotion/sheet@^1.1.0":
 | 
			
		||||
  version "1.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.1.0.tgz#56d99c41f0a1cda2726a05aa6a20afd4c63e58d2"
 | 
			
		||||
  integrity sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g==
 | 
			
		||||
 | 
			
		||||
"@emotion/unitless@^0.7.5":
 | 
			
		||||
  version "0.7.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
 | 
			
		||||
  integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
 | 
			
		||||
 | 
			
		||||
"@emotion/utils@^1.0.0":
 | 
			
		||||
  version "1.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.0.0.tgz#abe06a83160b10570816c913990245813a2fd6af"
 | 
			
		||||
  integrity sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA==
 | 
			
		||||
 | 
			
		||||
"@emotion/weak-memoize@^0.2.5":
 | 
			
		||||
  version "0.2.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
 | 
			
		||||
  integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
 | 
			
		||||
 | 
			
		||||
"@eslint/eslintrc@^0.4.3":
 | 
			
		||||
  version "0.4.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c"
 | 
			
		||||
@@ -861,6 +933,22 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11"
 | 
			
		||||
  integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
 | 
			
		||||
 | 
			
		||||
"@types/react-transition-group@^4.4.0":
 | 
			
		||||
  version "4.4.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.4.tgz#acd4cceaa2be6b757db61ed7b432e103242d163e"
 | 
			
		||||
  integrity sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@types/react" "*"
 | 
			
		||||
 | 
			
		||||
"@types/react@*":
 | 
			
		||||
  version "17.0.38"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.38.tgz#f24249fefd89357d5fa71f739a686b8d7c7202bd"
 | 
			
		||||
  integrity sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@types/prop-types" "*"
 | 
			
		||||
    "@types/scheduler" "*"
 | 
			
		||||
    csstype "^3.0.2"
 | 
			
		||||
 | 
			
		||||
"@types/react@17.0.31":
 | 
			
		||||
  version "17.0.31"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.31.tgz#fe05ebf91ff3ae35bb6b13f6c1b461db8089dff8"
 | 
			
		||||
@@ -1739,6 +1827,14 @@ doctrine@^3.0.0:
 | 
			
		||||
  dependencies:
 | 
			
		||||
    esutils "^2.0.2"
 | 
			
		||||
 | 
			
		||||
dom-helpers@^5.0.1:
 | 
			
		||||
  version "5.2.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
 | 
			
		||||
  integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@babel/runtime" "^7.8.7"
 | 
			
		||||
    csstype "^3.0.2"
 | 
			
		||||
 | 
			
		||||
domain-browser@4.19.0:
 | 
			
		||||
  version "4.19.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-4.19.0.tgz#1093e17c0a17dbd521182fe90d49ac1370054af1"
 | 
			
		||||
@@ -2431,6 +2527,13 @@ hmac-drbg@^1.0.1:
 | 
			
		||||
    minimalistic-assert "^1.0.0"
 | 
			
		||||
    minimalistic-crypto-utils "^1.0.1"
 | 
			
		||||
 | 
			
		||||
hoist-non-react-statics@^3.3.1:
 | 
			
		||||
  version "3.3.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
 | 
			
		||||
  integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    react-is "^16.7.0"
 | 
			
		||||
 | 
			
		||||
hotkeys-js@^3.8.1:
 | 
			
		||||
  version "3.8.7"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/hotkeys-js/-/hotkeys-js-3.8.7.tgz#c16cab978b53d7242f860ca3932e976b92399981"
 | 
			
		||||
@@ -2990,6 +3093,11 @@ md5.js@^1.3.4:
 | 
			
		||||
    inherits "^2.0.1"
 | 
			
		||||
    safe-buffer "^5.1.2"
 | 
			
		||||
 | 
			
		||||
memoize-one@^5.0.0:
 | 
			
		||||
  version "5.2.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
 | 
			
		||||
  integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
 | 
			
		||||
 | 
			
		||||
memoize-one@^6.0.0:
 | 
			
		||||
  version "6.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045"
 | 
			
		||||
@@ -3580,6 +3688,24 @@ progress@^2.0.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
 | 
			
		||||
  integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
 | 
			
		||||
 | 
			
		||||
prop-types@^15.5.7:
 | 
			
		||||
  version "15.8.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
 | 
			
		||||
  integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    loose-envify "^1.4.0"
 | 
			
		||||
    object-assign "^4.1.1"
 | 
			
		||||
    react-is "^16.13.1"
 | 
			
		||||
 | 
			
		||||
prop-types@^15.6.0, prop-types@^15.6.2:
 | 
			
		||||
  version "15.8.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.0.tgz#d237e624c45a9846e469f5f31117f970017ff588"
 | 
			
		||||
  integrity sha512-fDGekdaHh65eI3lMi5OnErU6a8Ighg2KjcjQxO7m8VHyWjcPyj5kiOgV1LQDOOOgVy3+5FgjXvdSSX7B8/5/4g==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    loose-envify "^1.4.0"
 | 
			
		||||
    object-assign "^4.1.1"
 | 
			
		||||
    react-is "^16.13.1"
 | 
			
		||||
 | 
			
		||||
prop-types@^15.7.2:
 | 
			
		||||
  version "15.7.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
 | 
			
		||||
@@ -3689,7 +3815,7 @@ react-is@17.0.2:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
 | 
			
		||||
  integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
 | 
			
		||||
 | 
			
		||||
react-is@^16.8.1:
 | 
			
		||||
react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1:
 | 
			
		||||
  version "16.13.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
 | 
			
		||||
  integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
 | 
			
		||||
@@ -3725,6 +3851,27 @@ react-remove-scroll@^2.4.0:
 | 
			
		||||
    use-callback-ref "^1.2.3"
 | 
			
		||||
    use-sidecar "^1.0.1"
 | 
			
		||||
 | 
			
		||||
react-select@^5.2.1:
 | 
			
		||||
  version "5.2.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/react-select/-/react-select-5.2.1.tgz#416c25c6b79b94687702374e019c4f2ed9d159d6"
 | 
			
		||||
  integrity sha512-OOyNzfKrhOcw/BlembyGWgdlJ2ObZRaqmQppPFut1RptJO423j+Y+JIsmxkvsZ4D/3CpOmwIlCvWbbAWEdh12A==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@babel/runtime" "^7.12.0"
 | 
			
		||||
    "@emotion/cache" "^11.4.0"
 | 
			
		||||
    "@emotion/react" "^11.1.1"
 | 
			
		||||
    "@types/react-transition-group" "^4.4.0"
 | 
			
		||||
    memoize-one "^5.0.0"
 | 
			
		||||
    prop-types "^15.6.0"
 | 
			
		||||
    react-transition-group "^4.3.0"
 | 
			
		||||
 | 
			
		||||
react-split@^2.0.14:
 | 
			
		||||
  version "2.0.14"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/react-split/-/react-split-2.0.14.tgz#ef198259bf43264d605f792fb3384f15f5b34432"
 | 
			
		||||
  integrity sha512-bKWydgMgaKTg/2JGQnaJPg51T6dmumTWZppFgEbbY0Fbme0F5TuatAScCLaqommbGQQf/ZT1zaejuPDriscISA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    prop-types "^15.5.7"
 | 
			
		||||
    split.js "^1.6.0"
 | 
			
		||||
 | 
			
		||||
react-stay-scrolled@^7.4.0:
 | 
			
		||||
  version "7.4.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/react-stay-scrolled/-/react-stay-scrolled-7.4.0.tgz#cb109b8dfd7834e5406d9c9322035ab40c398368"
 | 
			
		||||
@@ -3742,6 +3889,16 @@ react-style-singleton@^2.1.0:
 | 
			
		||||
    invariant "^2.2.4"
 | 
			
		||||
    tslib "^1.0.0"
 | 
			
		||||
 | 
			
		||||
react-transition-group@^4.3.0:
 | 
			
		||||
  version "4.4.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470"
 | 
			
		||||
  integrity sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@babel/runtime" "^7.5.5"
 | 
			
		||||
    dom-helpers "^5.0.1"
 | 
			
		||||
    loose-envify "^1.4.0"
 | 
			
		||||
    prop-types "^15.6.2"
 | 
			
		||||
 | 
			
		||||
react@17.0.2:
 | 
			
		||||
  version "17.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
 | 
			
		||||
@@ -4119,6 +4276,11 @@ source-map@^0.6.1:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
 | 
			
		||||
  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
 | 
			
		||||
 | 
			
		||||
split.js@^1.6.0:
 | 
			
		||||
  version "1.6.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/split.js/-/split.js-1.6.5.tgz#f7f61da1044c9984cb42947df4de4fadb5a3f300"
 | 
			
		||||
  integrity sha512-mPTnGCiS/RiuTNsVhCm9De9cCAUsrNFFviRbADdKiiV+Kk8HKp/0fWu7Kr8pi3/yBmsqLFHuXGT9UUZ+CNLwFw==
 | 
			
		||||
 | 
			
		||||
sprintf-js@~1.0.2:
 | 
			
		||||
  version "1.0.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
 | 
			
		||||
@@ -4265,6 +4427,11 @@ stylis@3.5.4:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.4.tgz#f665f25f5e299cf3d64654ab949a57c768b73fbe"
 | 
			
		||||
  integrity sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q==
 | 
			
		||||
 | 
			
		||||
stylis@4.0.13:
 | 
			
		||||
  version "4.0.13"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91"
 | 
			
		||||
  integrity sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==
 | 
			
		||||
 | 
			
		||||
supports-color@^5.3.0:
 | 
			
		||||
  version "5.5.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
 | 
			
		||||
 
 | 
			
		||||