Compare commits
	
		
			50 Commits
		
	
	
		
			test-page
			...
			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 | ||
| 
						 | 
					739918647d | ||
| 
						 | 
					1f334d6253 | ||
| 
						 | 
					0f15a85c45 | ||
| 
						 | 
					0c4330e329 | ||
| 
						 | 
					a9676288ea | ||
| 
						 | 
					7354474c70 | ||
| 
						 | 
					ce5b307a8b | 
@@ -181,7 +181,11 @@ const AccountDialog = ({
 | 
			
		||||
                  target="_blank"
 | 
			
		||||
                  rel="noreferrer noopener"
 | 
			
		||||
                >
 | 
			
		||||
                  <Button size="sm" ghost css={{ color: "$green11 !important", mt: "$3" }}>
 | 
			
		||||
                  <Button
 | 
			
		||||
                    size="sm"
 | 
			
		||||
                    ghost
 | 
			
		||||
                    css={{ color: "$grass11 !important", mt: "$3" }}
 | 
			
		||||
                  >
 | 
			
		||||
                    <ArrowSquareOut size="15px" />
 | 
			
		||||
                  </Button>
 | 
			
		||||
                </a>
 | 
			
		||||
@@ -195,13 +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>
 | 
			
		||||
@@ -289,18 +287,19 @@ const Accounts: FC<AccountProps> = props => {
 | 
			
		||||
        display: "flex",
 | 
			
		||||
        backgroundColor: props.card ? "$deep" : "$mauve1",
 | 
			
		||||
        position: "relative",
 | 
			
		||||
        width: "100%",
 | 
			
		||||
        flex: "1",
 | 
			
		||||
        height: "100%",
 | 
			
		||||
        flexShrink: 0,
 | 
			
		||||
        borderTop: "1px solid $mauve6",
 | 
			
		||||
        borderRight: "1px solid $mauve6",
 | 
			
		||||
        borderLeft: "1px solid $mauve6",
 | 
			
		||||
        borderBottom: "1px solid $mauve6",
 | 
			
		||||
        border: "1px solid $mauve6",
 | 
			
		||||
        borderRadius: props.card ? "$md" : undefined,
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      <Container css={{ p: 0, flexShrink: 1, height: "100%" }}>
 | 
			
		||||
        <Flex css={{ py: "$3", borderBottom: props.card ? "1px solid $mauve6" : undefined }}>
 | 
			
		||||
        <Flex
 | 
			
		||||
          css={{
 | 
			
		||||
            py: "$3",
 | 
			
		||||
            borderBottom: props.card ? "1px solid $mauve6" : undefined,
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          <Heading
 | 
			
		||||
            as="h3"
 | 
			
		||||
            css={{
 | 
			
		||||
@@ -350,7 +349,7 @@ const Accounts: FC<AccountProps> = props => {
 | 
			
		||||
                borderBottom: props.card ? "1px solid $mauve6" : undefined,
 | 
			
		||||
                "@hover": {
 | 
			
		||||
                  "&:hover": {
 | 
			
		||||
                    background: "$mauve3",
 | 
			
		||||
                    background: "$backgroundAlt",
 | 
			
		||||
                  },
 | 
			
		||||
                },
 | 
			
		||||
              }}
 | 
			
		||||
@@ -363,7 +362,12 @@ const Accounts: FC<AccountProps> = props => {
 | 
			
		||||
              >
 | 
			
		||||
                <Box>
 | 
			
		||||
                  <Text>{account.name} </Text>
 | 
			
		||||
                  <Text css={{ color: "$mauve9" }}>
 | 
			
		||||
                  <Text
 | 
			
		||||
                    css={{
 | 
			
		||||
                      color: "$mauve9",
 | 
			
		||||
                      wordBreak: "break-word",
 | 
			
		||||
                    }}
 | 
			
		||||
                  >
 | 
			
		||||
                    {account.address} (
 | 
			
		||||
                    {Dinero({
 | 
			
		||||
                      amount: Number(account?.xrp || "0"),
 | 
			
		||||
@@ -400,7 +404,7 @@ const Accounts: FC<AccountProps> = props => {
 | 
			
		||||
              </Flex>
 | 
			
		||||
              {props.showHookStats && (
 | 
			
		||||
                <Text muted small css={{ mt: "$2" }}>
 | 
			
		||||
                  X hooks installed
 | 
			
		||||
                  {account.hooks.length} hook{account.hooks.length === 1 ? "" : "s"} installed
 | 
			
		||||
                </Text>
 | 
			
		||||
              )}
 | 
			
		||||
            </Flex>
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ loader.config({
 | 
			
		||||
 | 
			
		||||
const validateWritability = (editor: monaco.editor.IStandaloneCodeEditor) => {
 | 
			
		||||
  const currPath = editor.getModel()?.uri.path;
 | 
			
		||||
  if (apiHeaderFiles.find(h => currPath?.endsWith(h))) {
 | 
			
		||||
  if (apiHeaderFiles.find((h) => currPath?.endsWith(h))) {
 | 
			
		||||
    editor.updateOptions({ readOnly: true });
 | 
			
		||||
  } else {
 | 
			
		||||
    editor.updateOptions({ readOnly: false });
 | 
			
		||||
@@ -60,7 +60,7 @@ const HooksEditor = () => {
 | 
			
		||||
        display: "flex",
 | 
			
		||||
        position: "relative",
 | 
			
		||||
        flexDirection: "column",
 | 
			
		||||
        backgroundColor: "$mauve3",
 | 
			
		||||
        backgroundColor: "$mauve2",
 | 
			
		||||
        width: "100%",
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
@@ -71,15 +71,15 @@ const HooksEditor = () => {
 | 
			
		||||
          keepCurrentModel
 | 
			
		||||
          defaultLanguage={snap.files?.[snap.active]?.language}
 | 
			
		||||
          language={snap.files?.[snap.active]?.language}
 | 
			
		||||
          path={`file://work/c/${snap.files?.[snap.active]?.name}`}
 | 
			
		||||
          path={`file:///work/c/${snap.files?.[snap.active]?.name}`}
 | 
			
		||||
          defaultValue={snap.files?.[snap.active]?.content}
 | 
			
		||||
          beforeMount={monaco => {
 | 
			
		||||
          beforeMount={(monaco) => {
 | 
			
		||||
            if (!snap.editorCtx) {
 | 
			
		||||
              snap.files.forEach(file =>
 | 
			
		||||
              snap.files.forEach((file) =>
 | 
			
		||||
                monaco.editor.createModel(
 | 
			
		||||
                  file.content,
 | 
			
		||||
                  file.language,
 | 
			
		||||
                  monaco.Uri.parse(`file://work/c/${file.name}`)
 | 
			
		||||
                  monaco.Uri.parse(`file:///work/c/${file.name}`)
 | 
			
		||||
                )
 | 
			
		||||
              );
 | 
			
		||||
            }
 | 
			
		||||
@@ -100,7 +100,7 @@ const HooksEditor = () => {
 | 
			
		||||
              // listen when the web socket is opened
 | 
			
		||||
              listen({
 | 
			
		||||
                webSocket: webSocket as WebSocket,
 | 
			
		||||
                onConnection: connection => {
 | 
			
		||||
                onConnection: (connection) => {
 | 
			
		||||
                  // create and start the language client
 | 
			
		||||
                  const languageClient = createLanguageClient(connection);
 | 
			
		||||
                  const disposable = languageClient.start();
 | 
			
		||||
@@ -139,10 +139,13 @@ const HooksEditor = () => {
 | 
			
		||||
                enabled: true,
 | 
			
		||||
              },
 | 
			
		||||
            });
 | 
			
		||||
            editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {
 | 
			
		||||
              saveFile();
 | 
			
		||||
            });
 | 
			
		||||
            validateWritability(editor)
 | 
			
		||||
            editor.addCommand(
 | 
			
		||||
              monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS,
 | 
			
		||||
              () => {
 | 
			
		||||
                saveFile();
 | 
			
		||||
              }
 | 
			
		||||
            );
 | 
			
		||||
            validateWritability(editor);
 | 
			
		||||
          }}
 | 
			
		||||
          theme={theme === "dark" ? "dark" : "light"}
 | 
			
		||||
        />
 | 
			
		||||
@@ -160,7 +163,9 @@ const HooksEditor = () => {
 | 
			
		||||
              <Box css={{ display: "inline-flex", pl: "35px" }}>
 | 
			
		||||
                <ArrowBendLeftUp size={30} />
 | 
			
		||||
              </Box>
 | 
			
		||||
              <Box css={{ pl: "0px", pt: "15px", flex: 1, display: "inline-flex" }}>
 | 
			
		||||
              <Box
 | 
			
		||||
                css={{ pl: "0px", pt: "15px", flex: 1, display: "inline-flex" }}
 | 
			
		||||
              >
 | 
			
		||||
                <Text
 | 
			
		||||
                  css={{
 | 
			
		||||
                    fontSize: "14px",
 | 
			
		||||
@@ -168,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>
 | 
			
		||||
 
 | 
			
		||||
@@ -117,15 +117,17 @@ export const Input = styled("input", {
 | 
			
		||||
    },
 | 
			
		||||
    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",
 | 
			
		||||
          boxShadow:
 | 
			
		||||
            "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",
 | 
			
		||||
          boxShadow:
 | 
			
		||||
            "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,7 +27,7 @@ import {
 | 
			
		||||
  DialogTrigger,
 | 
			
		||||
} from "./Dialog";
 | 
			
		||||
import PanelBox from "./PanelBox";
 | 
			
		||||
import { templateFileIds } from '../state/constants';
 | 
			
		||||
import { templateFileIds } from "../state/constants";
 | 
			
		||||
 | 
			
		||||
const Navigation = () => {
 | 
			
		||||
  const router = useRouter();
 | 
			
		||||
@@ -40,9 +40,11 @@ const Navigation = () => {
 | 
			
		||||
      as="nav"
 | 
			
		||||
      css={{
 | 
			
		||||
        display: "flex",
 | 
			
		||||
        backgroundColor: "$mauve1",
 | 
			
		||||
        borderBottom: "1px solid $mauve6",
 | 
			
		||||
        position: "relative",
 | 
			
		||||
        zIndex: 2003,
 | 
			
		||||
        height: "60px",
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      <Container
 | 
			
		||||
@@ -60,7 +62,7 @@ const Navigation = () => {
 | 
			
		||||
            pr: "$4",
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          <Link href="/" passHref>
 | 
			
		||||
          <Link href={gistId ? `/develop/${gistId}` : "/develop"} passHref>
 | 
			
		||||
            <Box
 | 
			
		||||
              as="a"
 | 
			
		||||
              css={{
 | 
			
		||||
@@ -69,7 +71,7 @@ const Navigation = () => {
 | 
			
		||||
                color: "$textColor",
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              <Logo width="30px" height="30px" />
 | 
			
		||||
              <Logo width="32px" height="32px" />
 | 
			
		||||
            </Box>
 | 
			
		||||
          </Link>
 | 
			
		||||
          <Flex
 | 
			
		||||
@@ -83,20 +85,42 @@ const Navigation = () => {
 | 
			
		||||
              <Spinner />
 | 
			
		||||
            ) : (
 | 
			
		||||
              <>
 | 
			
		||||
                <Heading css={{ lineHeight: 1 }}>{snap.files?.[0]?.name || "XRPL Hooks"}</Heading>
 | 
			
		||||
                <Text css={{ fontSize: "$xs", color: "$mauve10", lineHeight: 1 }}>
 | 
			
		||||
                <Heading css={{ lineHeight: 1 }}>
 | 
			
		||||
                  {snap.files?.[0]?.name || "XRPL Hooks"}
 | 
			
		||||
                </Heading>
 | 
			
		||||
                <Text
 | 
			
		||||
                  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>
 | 
			
		||||
              </>
 | 
			
		||||
            )}
 | 
			
		||||
          </Flex>
 | 
			
		||||
          {router.isReady && (
 | 
			
		||||
            <ButtonGroup css={{ marginLeft: "auto" }}>
 | 
			
		||||
              <Dialog open={snap.mainModalOpen} onOpenChange={open => (state.mainModalOpen = open)}>
 | 
			
		||||
              <Dialog
 | 
			
		||||
                open={snap.mainModalOpen}
 | 
			
		||||
                onOpenChange={(open) => (state.mainModalOpen = open)}
 | 
			
		||||
              >
 | 
			
		||||
                <DialogTrigger asChild>
 | 
			
		||||
                  <Button outline>
 | 
			
		||||
                    <FolderOpen size="15px" />
 | 
			
		||||
@@ -130,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",
 | 
			
		||||
                        },
 | 
			
		||||
                      }}
 | 
			
		||||
                    >
 | 
			
		||||
@@ -144,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
 | 
			
		||||
@@ -157,17 +185,20 @@ const Navigation = () => {
 | 
			
		||||
                            mb: "$7",
 | 
			
		||||
                          }}
 | 
			
		||||
                        >
 | 
			
		||||
                          Hooks add smart contract functionality to the XRP Ledger.
 | 
			
		||||
                          Hooks add smart contract functionality to the XRP
 | 
			
		||||
                          Ledger.
 | 
			
		||||
                        </Text>
 | 
			
		||||
                        <Flex css={{ flexDirection: "column", gap: "$2", mt: "$2" }}>
 | 
			
		||||
                        <Flex
 | 
			
		||||
                          css={{ flexDirection: "column", gap: "$2", mt: "$2" }}
 | 
			
		||||
                        >
 | 
			
		||||
                          <Text
 | 
			
		||||
                            css={{
 | 
			
		||||
                              display: "inline-flex",
 | 
			
		||||
                              alignItems: "center",
 | 
			
		||||
                              gap: "$3",
 | 
			
		||||
                              color: "$green9",
 | 
			
		||||
                              color: "$purple10",
 | 
			
		||||
                              "&:hover": {
 | 
			
		||||
                                color: "$green11 !important",
 | 
			
		||||
                                color: "$purple11",
 | 
			
		||||
                              },
 | 
			
		||||
                              "&:focus": {
 | 
			
		||||
                                outline: 0,
 | 
			
		||||
@@ -178,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
 | 
			
		||||
@@ -186,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,
 | 
			
		||||
@@ -206,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,
 | 
			
		||||
@@ -235,38 +266,53 @@ 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",
 | 
			
		||||
                        },
 | 
			
		||||
                      }}
 | 
			
		||||
                    >
 | 
			
		||||
                      <PanelBox as="a" href={`/develop/${templateFileIds.starter}`}>
 | 
			
		||||
                      <PanelBox
 | 
			
		||||
                        as="a"
 | 
			
		||||
                        href={`/develop/${templateFileIds.starter}`}
 | 
			
		||||
                      >
 | 
			
		||||
                        <Heading>Starter</Heading>
 | 
			
		||||
                        <Text>Just an empty starter with essential imports</Text>
 | 
			
		||||
                        <Text>
 | 
			
		||||
                          Just a basic starter with essential imports
 | 
			
		||||
                        </Text>
 | 
			
		||||
                      </PanelBox>
 | 
			
		||||
                      <PanelBox as="a" href={`/develop/${templateFileIds.starter}`}>
 | 
			
		||||
                      <PanelBox
 | 
			
		||||
                        as="a"
 | 
			
		||||
                        href={`/develop/${templateFileIds.firewall}`}
 | 
			
		||||
                      >
 | 
			
		||||
                        <Heading>Firewall</Heading>
 | 
			
		||||
                        <Text>This Hook essentially checks a blacklist of accounts</Text>
 | 
			
		||||
                        <Text>
 | 
			
		||||
                          This Hook essentially checks a blacklist of accounts
 | 
			
		||||
                        </Text>
 | 
			
		||||
                      </PanelBox>
 | 
			
		||||
                      <PanelBox as="a" href={`/develop/${templateFileIds.accept}`}>
 | 
			
		||||
                        <Heading>Accept</Heading>
 | 
			
		||||
                        <Text>This hook just accepts any transaction coming through it</Text>
 | 
			
		||||
                      </PanelBox>
 | 
			
		||||
                      <PanelBox as="a" href={`/develop/${templateFileIds.notary}`}>
 | 
			
		||||
                      <PanelBox
 | 
			
		||||
                        as="a"
 | 
			
		||||
                        href={`/develop/${templateFileIds.notary}`}
 | 
			
		||||
                      >
 | 
			
		||||
                        <Heading>Notary</Heading>
 | 
			
		||||
                        <Text>Collecting signatures for multi-sign transactions</Text>
 | 
			
		||||
                        <Text>
 | 
			
		||||
                          Collecting signatures for multi-sign transactions
 | 
			
		||||
                        </Text>
 | 
			
		||||
                      </PanelBox>
 | 
			
		||||
                      <PanelBox as="a" href={`/develop/${templateFileIds.carbon}`}>
 | 
			
		||||
                      <PanelBox
 | 
			
		||||
                        as="a"
 | 
			
		||||
                        href={`/develop/${templateFileIds.carbon}`}
 | 
			
		||||
                      >
 | 
			
		||||
                        <Heading>Carbon</Heading>
 | 
			
		||||
                        <Text>Send a percentage of sum to an address</Text>
 | 
			
		||||
                      </PanelBox>
 | 
			
		||||
                      <PanelBox as="a" href={`/develop/${templateFileIds.peggy}`}>
 | 
			
		||||
                      <PanelBox
 | 
			
		||||
                        as="a"
 | 
			
		||||
                        href={`/develop/${templateFileIds.peggy}`}
 | 
			
		||||
                      >
 | 
			
		||||
                        <Heading>Peggy</Heading>
 | 
			
		||||
                        <Text>An oracle based stabe coin hook</Text>
 | 
			
		||||
                        <Text>An oracle based stable coin hook</Text>
 | 
			
		||||
                      </PanelBox>
 | 
			
		||||
                    </Flex>
 | 
			
		||||
                  </Flex>
 | 
			
		||||
@@ -313,25 +359,53 @@ const Navigation = () => {
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            <ButtonGroup>
 | 
			
		||||
              <Link href={gistId ? `/develop/${gistId}` : "/develop"} passHref shallow>
 | 
			
		||||
                <Button as="a" outline={!router.pathname.includes("/develop")} uppercase>
 | 
			
		||||
              <Link
 | 
			
		||||
                href={gistId ? `/develop/${gistId}` : "/develop"}
 | 
			
		||||
                passHref
 | 
			
		||||
                shallow
 | 
			
		||||
              >
 | 
			
		||||
                <Button
 | 
			
		||||
                  as="a"
 | 
			
		||||
                  outline={!router.pathname.includes("/develop")}
 | 
			
		||||
                  uppercase
 | 
			
		||||
                >
 | 
			
		||||
                  Develop
 | 
			
		||||
                </Button>
 | 
			
		||||
              </Link>
 | 
			
		||||
              <Link href={gistId ? `/deploy/${gistId}` : "/deploy"} passHref shallow>
 | 
			
		||||
                <Button as="a" outline={!router.pathname.includes("/deploy")} uppercase>
 | 
			
		||||
              <Link
 | 
			
		||||
                href={gistId ? `/deploy/${gistId}` : "/deploy"}
 | 
			
		||||
                passHref
 | 
			
		||||
                shallow
 | 
			
		||||
              >
 | 
			
		||||
                <Button
 | 
			
		||||
                  as="a"
 | 
			
		||||
                  outline={!router.pathname.includes("/deploy")}
 | 
			
		||||
                  uppercase
 | 
			
		||||
                >
 | 
			
		||||
                  Deploy
 | 
			
		||||
                </Button>
 | 
			
		||||
              </Link>
 | 
			
		||||
              <Link href={gistId ? `/test/${gistId}` : "/test"} passHref shallow>
 | 
			
		||||
                <Button as="a" outline={!router.pathname.includes("/test")} uppercase>
 | 
			
		||||
              <Link
 | 
			
		||||
                href={gistId ? `/test/${gistId}` : "/test"}
 | 
			
		||||
                passHref
 | 
			
		||||
                shallow
 | 
			
		||||
              >
 | 
			
		||||
                <Button
 | 
			
		||||
                  as="a"
 | 
			
		||||
                  outline={!router.pathname.includes("/test")}
 | 
			
		||||
                  uppercase
 | 
			
		||||
                >
 | 
			
		||||
                  Test
 | 
			
		||||
                </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",
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ const Select: FC<Props> = props => {
 | 
			
		||||
  colors.selected = colors.secondary;
 | 
			
		||||
  return (
 | 
			
		||||
    <SelectInput
 | 
			
		||||
      menuPosition="fixed"
 | 
			
		||||
      theme={theme => ({
 | 
			
		||||
        ...theme,
 | 
			
		||||
        spacing: {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,24 +1,66 @@
 | 
			
		||||
import { useEffect, useState } from "react";
 | 
			
		||||
import React, {
 | 
			
		||||
  useEffect,
 | 
			
		||||
  useState,
 | 
			
		||||
  Fragment,
 | 
			
		||||
  isValidElement,
 | 
			
		||||
  useCallback,
 | 
			
		||||
} from "react";
 | 
			
		||||
import type { ReactNode, ReactElement } from "react";
 | 
			
		||||
import { Button, Stack } from ".";
 | 
			
		||||
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 }: Props) => {
 | 
			
		||||
export const Tabs = ({
 | 
			
		||||
  children,
 | 
			
		||||
  activeIndex,
 | 
			
		||||
  activeHeader,
 | 
			
		||||
  headless,
 | 
			
		||||
  keepAllAlive = false,
 | 
			
		||||
  onCreateNewTab,
 | 
			
		||||
  onCloseTab,
 | 
			
		||||
  defaultExtension = "",
 | 
			
		||||
  forceDefaultExtension,
 | 
			
		||||
}: Props) => {
 | 
			
		||||
  const [active, setActive] = useState(activeIndex || 0);
 | 
			
		||||
  const tabProps: TabProps[] = children.map(elem => elem.props);
 | 
			
		||||
  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);
 | 
			
		||||
@@ -26,10 +68,57 @@ export const Tabs = ({ children, activeIndex, activeHeader, headless }: Props) =
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (activeHeader) {
 | 
			
		||||
      const idx = tabProps.findIndex(tab => tab.header === activeHeader);
 | 
			
		||||
      const idx = tabs.findIndex((tab) => tab.header === activeHeader);
 | 
			
		||||
      setActive(idx);
 | 
			
		||||
    }
 | 
			
		||||
  }, [activeHeader, tabProps]);
 | 
			
		||||
  }, [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 (
 | 
			
		||||
    <>
 | 
			
		||||
@@ -40,11 +129,13 @@ export const Tabs = ({ children, activeIndex, activeHeader, headless }: Props) =
 | 
			
		||||
            flex: 1,
 | 
			
		||||
            flexWrap: "nowrap",
 | 
			
		||||
            marginBottom: "-1px",
 | 
			
		||||
            width: "100%",
 | 
			
		||||
            overflow: "auto",
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          {tabProps.map((prop, idx) => (
 | 
			
		||||
          {tabs.map((tab, idx) => (
 | 
			
		||||
            <Button
 | 
			
		||||
              key={prop.header}
 | 
			
		||||
              key={tab.header}
 | 
			
		||||
              role="tab"
 | 
			
		||||
              tabIndex={idx}
 | 
			
		||||
              onClick={() => setActive(idx)}
 | 
			
		||||
@@ -59,12 +150,108 @@ export const Tabs = ({ children, activeIndex, activeHeader, headless }: Props) =
 | 
			
		||||
                },
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              {prop.header || idx}
 | 
			
		||||
              {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>
 | 
			
		||||
      )}
 | 
			
		||||
      {tabProps[active].children}
 | 
			
		||||
      {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>
 | 
			
		||||
      )}
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
  }
 | 
			
		||||
]
 | 
			
		||||
@@ -41,6 +41,7 @@
 | 
			
		||||
    "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",
 | 
			
		||||
 
 | 
			
		||||
@@ -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, Box } from "../../components";
 | 
			
		||||
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,25 +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, height: 'calc(100vh - 30vh - 60px)' }}>
 | 
			
		||||
    <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%", minHeight: '225px', height: '30vh' }}>
 | 
			
		||||
        <Box css={{ width: "100%" }}>
 | 
			
		||||
      <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 />
 | 
			
		||||
        </Box>
 | 
			
		||||
        <Box css={{ width: "100%" }}>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>
 | 
			
		||||
          <LogBox
 | 
			
		||||
            title="Deploy Log"
 | 
			
		||||
            logs={snap.deployLogs}
 | 
			
		||||
            logs={deployLogs}
 | 
			
		||||
            clearLog={() => (state.deployLogs = [])}
 | 
			
		||||
          />
 | 
			
		||||
        </Box>
 | 
			
		||||
      </Flex>
 | 
			
		||||
    </>
 | 
			
		||||
        </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,6 +1,21 @@
 | 
			
		||||
import { Container, Flex, Box, Tabs, Tab, Input, Select, Text, Button } from "../../components";
 | 
			
		||||
import { Play } from "phosphor-react";
 | 
			
		||||
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,
 | 
			
		||||
@@ -9,105 +24,417 @@ const Accounts = dynamic(() => import("../../components/Accounts"), {
 | 
			
		||||
  ssr: false,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const Transaction = () => {
 | 
			
		||||
  const options = [
 | 
			
		||||
    { value: "chocolate", label: "Chocolate" },
 | 
			
		||||
    { value: "strawberry", label: "Strawberry" },
 | 
			
		||||
    { value: "vanilla", label: "Vanilla" },
 | 
			
		||||
    {
 | 
			
		||||
      value: "long",
 | 
			
		||||
      label: "lorem woy uiwyf wyfw8 fwfw98f w98fy wf8fw89f 9w8fy w9fyw9f wf7tdw9f ",
 | 
			
		||||
    },
 | 
			
		||||
  ];
 | 
			
		||||
// 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 (
 | 
			
		||||
    <>
 | 
			
		||||
      <Container css={{ p: "$3 0", fontSize: "$sm" }}>
 | 
			
		||||
        <Flex column fluid>
 | 
			
		||||
          <Flex row fluid css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}>
 | 
			
		||||
    <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="transaction"
 | 
			
		||||
              instanceId="transactionsType"
 | 
			
		||||
              placeholder="Select transaction type"
 | 
			
		||||
              options={transactionsOptions}
 | 
			
		||||
              hideSelectedOptions
 | 
			
		||||
              css={{ width: "70%" }}
 | 
			
		||||
              options={options}
 | 
			
		||||
              value={selectedTransaction}
 | 
			
		||||
              onChange={(tt) => setSelectedTransaction(tt as any)}
 | 
			
		||||
            />
 | 
			
		||||
          </Flex>
 | 
			
		||||
          <Flex row fluid css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}>
 | 
			
		||||
          <Flex
 | 
			
		||||
            row
 | 
			
		||||
            fluid
 | 
			
		||||
            css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}
 | 
			
		||||
          >
 | 
			
		||||
            <Text muted css={{ mr: "$3" }}>
 | 
			
		||||
              From account:{" "}
 | 
			
		||||
              Account:{" "}
 | 
			
		||||
            </Text>
 | 
			
		||||
            <Select
 | 
			
		||||
              instanceId="from-account"
 | 
			
		||||
              placeholder="Select account from which to send from"
 | 
			
		||||
              placeholder="Select your account"
 | 
			
		||||
              css={{ width: "70%" }}
 | 
			
		||||
              options={options}
 | 
			
		||||
            />
 | 
			
		||||
          </Flex>
 | 
			
		||||
          <Flex row fluid css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}>
 | 
			
		||||
            <Text muted css={{ mr: "$3" }}>
 | 
			
		||||
              Amount (XRP):{" "}
 | 
			
		||||
            </Text>
 | 
			
		||||
            <Input defaultValue="0" variant="deep" css={{ width: "70%", flex: "inherit", height: "$9" }} />
 | 
			
		||||
          </Flex>
 | 
			
		||||
          <Flex row fluid css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}>
 | 
			
		||||
            <Text muted css={{ mr: "$3" }}>
 | 
			
		||||
              To account:{" "}
 | 
			
		||||
            </Text>
 | 
			
		||||
            <Select
 | 
			
		||||
              instanceId="to-account"
 | 
			
		||||
              placeholder="Select account from which to send from"
 | 
			
		||||
              css={{ width: "70%" }}
 | 
			
		||||
              options={options}
 | 
			
		||||
              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" }}>
 | 
			
		||||
      <Flex
 | 
			
		||||
        row
 | 
			
		||||
        css={{
 | 
			
		||||
          justifyContent: "space-between",
 | 
			
		||||
          position: "absolute",
 | 
			
		||||
          left: 0,
 | 
			
		||||
          bottom: 0,
 | 
			
		||||
          width: "100%",
 | 
			
		||||
        }}
 | 
			
		||||
      >
 | 
			
		||||
        <Button outline>VIEW AS JSON</Button>
 | 
			
		||||
        <Flex row>
 | 
			
		||||
          <Button outline css={{ mr: "$3" }}>
 | 
			
		||||
          <Button onClick={resetState} outline css={{ mr: "$3" }}>
 | 
			
		||||
            RESET
 | 
			
		||||
          </Button>
 | 
			
		||||
          <Button variant="primary">
 | 
			
		||||
          <Button
 | 
			
		||||
            variant="primary"
 | 
			
		||||
            onClick={submitTest}
 | 
			
		||||
            isLoading={txIsLoading}
 | 
			
		||||
            disabled={txIsDisabled}
 | 
			
		||||
          >
 | 
			
		||||
            <Play weight="bold" size="16px" />
 | 
			
		||||
            RUN TEST
 | 
			
		||||
          </Button>
 | 
			
		||||
        </Flex>
 | 
			
		||||
      </Flex>
 | 
			
		||||
    </>
 | 
			
		||||
    </Box>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Test = () => {
 | 
			
		||||
  const { transactionLogs } = useSnapshot(state);
 | 
			
		||||
  const [tabHeaders, setTabHeaders] = useState<string[]>(["test1.json"]);
 | 
			
		||||
  return (
 | 
			
		||||
    <Container css={{ py: "$3", px: 0 }}>
 | 
			
		||||
      <Flex row fluid css={{ justifyContent: 'center', mb: "$2", height: '40vh', minHeight: '300px', p: '$3 $2' }}>
 | 
			
		||||
        <Box css={{ width: "55%", px: "$2" }}>
 | 
			
		||||
          <Tabs>
 | 
			
		||||
            {/* TODO Dynamic tabs */}
 | 
			
		||||
            <Tab header="test1.json">
 | 
			
		||||
              <Transaction />
 | 
			
		||||
            </Tab>
 | 
			
		||||
            <Tab header="test2.json">
 | 
			
		||||
              <Transaction />
 | 
			
		||||
            </Tab>
 | 
			
		||||
          </Tabs>
 | 
			
		||||
        </Box>
 | 
			
		||||
        <Box css={{ width: "45%", mx: "$2", height: '100%' }}>
 | 
			
		||||
          <Accounts card hideDeployBtn showHookStats />
 | 
			
		||||
        </Box>
 | 
			
		||||
      </Flex>
 | 
			
		||||
    <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 css={{ borderBottom: "1px solid $mauve8" }}>
 | 
			
		||||
        <Box css={{ width: "50%", borderRight: "1px solid $mauve8" }}>
 | 
			
		||||
          <LogBox title="From Log" logs={[]} clearLog={() => {}} />
 | 
			
		||||
        </Box>
 | 
			
		||||
        <Box css={{ width: "50%" }}>
 | 
			
		||||
          <LogBox title="To Log" logs={[]} clearLog={() => {}} />
 | 
			
		||||
        </Box>
 | 
			
		||||
      </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>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											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  | 
@@ -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;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -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,6 +1,5 @@
 | 
			
		||||
export const templateFileIds = {
 | 
			
		||||
    'starter': '1d14e51e2e02dc0a508cb0733767a914', // TODO currently same as accept
 | 
			
		||||
    'accept': '1d14e51e2e02dc0a508cb0733767a914',
 | 
			
		||||
    'firewall': 'bcd6d0c0fcbe52545ddb802481ff9d26',
 | 
			
		||||
    'notary': 'a789c75f591eeab7932fd702ed8cf9ea',
 | 
			
		||||
    'carbon': '43925143fa19735d8c6505c34d3a6a47',
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -70,6 +76,8 @@ let initialState: IState = {
 | 
			
		||||
  compiling: false,
 | 
			
		||||
  logs: [],
 | 
			
		||||
  deployLogs: [],
 | 
			
		||||
  transactionLogs: [],
 | 
			
		||||
  debugLogs: [],
 | 
			
		||||
  editorCtx: undefined,
 | 
			
		||||
  gistId: undefined,
 | 
			
		||||
  gistOwner: undefined,
 | 
			
		||||
@@ -79,6 +87,7 @@ let initialState: IState = {
 | 
			
		||||
  editorSettings: {
 | 
			
		||||
    tabSize: 2,
 | 
			
		||||
  },
 | 
			
		||||
  splits: {},
 | 
			
		||||
  client: null,
 | 
			
		||||
  clientStatus: "offline" as "offline",
 | 
			
		||||
  mainModalOpen: false,
 | 
			
		||||
@@ -87,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 {
 | 
			
		||||
@@ -135,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,25 +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",
 | 
			
		||||
      'deep': 'rgb(248, 248, 248)'
 | 
			
		||||
      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",
 | 
			
		||||
@@ -72,7 +69,7 @@ export const {
 | 
			
		||||
      "7xl": "4.5rem",
 | 
			
		||||
      "8xl": "6rem",
 | 
			
		||||
      "9xl": "8rem",
 | 
			
		||||
      default: '$md'
 | 
			
		||||
      default: "$md",
 | 
			
		||||
    },
 | 
			
		||||
    space: {
 | 
			
		||||
      px: "1px",
 | 
			
		||||
@@ -108,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",
 | 
			
		||||
@@ -216,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,
 | 
			
		||||
    }),
 | 
			
		||||
@@ -280,46 +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)'
 | 
			
		||||
    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"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						@@ -3688,6 +3688,15 @@ 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"
 | 
			
		||||
@@ -3855,6 +3864,14 @@ react-select@^5.2.1:
 | 
			
		||||
    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"
 | 
			
		||||
@@ -4259,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"
 | 
			
		||||
 
 | 
			
		||||