Compare commits

..

40 Commits

Author SHA1 Message Date
Valtteri Karesto
a0303ecfa4 Merge pull request #32 from eqlabs/feat/fix-scrollbars
Fix scrollbars
2021-11-26 15:23:13 +02:00
Valtteri Karesto
5a79e07c2d Fix scrollbars 2021-11-26 15:20:30 +02:00
Valtteri Karesto
1107bb8196 Merge pull request #31 from eqlabs/feat/add-gist-integration
Feat/add gist integration
2021-11-26 00:11:24 +02:00
Valtteri Karesto
405aafed7e Removed unused code 2021-11-26 00:02:39 +02:00
Valtteri Karesto
03156474f3 Move build button and add loading states 2021-11-25 23:55:45 +02:00
Valtteri Karesto
7982209732 Move deploy button to footer 2021-11-25 23:55:21 +02:00
Valtteri Karesto
0f9963b972 Refactor global navigation and editor navigation 2021-11-25 23:55:12 +02:00
Valtteri Karesto
650243279a Add sync to gist issue #19 feature and other fixes to global state 2021-11-25 23:54:47 +02:00
Valtteri Karesto
6f183049d5 Add some safeguards to data and new button size 2021-11-25 23:54:03 +02:00
Valtteri Karesto
48706effc1 Style tweaks to button component styles 2021-11-25 23:32:09 +02:00
Valtteri Karesto
c9740b1e8a Minor tweaks to dropdown menu 2021-11-25 23:31:49 +02:00
Valtteri Karesto
166300b8d5 Custom alert dialog page 2021-11-25 23:31:33 +02:00
Valtteri Karesto
99968855e0 Add alert dialog from radix 2021-11-25 23:31:24 +02:00
Valtteri Karesto
a62a9c3700 Separate login page so we maintain the app state, related to issue #17 2021-11-25 23:31:10 +02:00
Valtteri Karesto
4e971ce119 Conditional chaining to map 2021-11-25 22:02:02 +02:00
Valtteri Karesto
72ba2072ec Uppercase variant for Heading 2021-11-25 22:01:27 +02:00
Valtteri Karesto
bd8d3c39c2 New size for input component 2021-11-25 22:01:13 +02:00
Valtteri Karesto
7142f5b5e2 Added buttongroup component 2021-11-25 22:00:56 +02:00
Valtteri Karesto
37516c602d Add some dependencies and theme style change 2021-11-25 17:30:11 +02:00
Valtteri Karesto
cfa7a3bd30 update dialog styles 2021-11-25 17:29:52 +02:00
Valtteri Karesto
266e4c3e6c Create panel box component 2021-11-25 17:29:40 +02:00
Valtteri Karesto
4cf6d376f0 Update themechanger styles 2021-11-25 17:29:31 +02:00
Valtteri Karesto
b2f49625db Add truncate string util 2021-11-25 17:29:20 +02:00
Valtteri Karesto
0b9fd172ce Add patterns to modal 2021-11-25 17:29:08 +02:00
Valtteri Karesto
2582981d85 Open modal if no gist id or files 2021-11-25 17:28:41 +02:00
Valtteri Karesto
a0eda59982 Added some tsconfigs 2021-11-25 17:28:26 +02:00
Valtteri Karesto
9ae9db984d Renamed pages 2021-11-25 17:28:15 +02:00
Valtteri Karesto
8f2c78b08b Merge pull request #30 from eqlabs/feat/button-loading-state
Feat/button loading state
2021-11-20 01:37:26 +02:00
Valtteri Karesto
460412d3d7 Button and theme colors changed 2021-11-20 01:34:40 +02:00
Valtteri Karesto
1a182858b4 Added new shade of gray 2021-11-20 01:34:20 +02:00
Valtteri Karesto
baac750e43 Some state management fixes 2021-11-20 01:34:10 +02:00
Valtteri Karesto
74979decbe Merge pull request #29 from eqlabs/feature/empty-pages
Add styles to empty pages
2021-11-19 16:44:24 +02:00
Valtteri Karesto
db50d86921 Add styles to empty pages 2021-11-19 16:42:26 +02:00
Valtteri Karesto
19741add40 Add back persisted state 2021-11-19 16:27:48 +02:00
Valtteri Karesto
d79d013238 Fixing build error 2021-11-19 16:24:37 +02:00
Valtteri Karesto
6d8ce9158d Added pages/index.tsx 2021-11-19 16:21:11 +02:00
Valtteri Karesto
c6102c2e6a Removed more unused deps and commented code 2021-11-19 16:12:01 +02:00
Valtteri Karesto
25cd64f8f6 Removed unused deps 2021-11-19 16:07:19 +02:00
Valtteri Karesto
33ef84c73e hotfix:fix state temporarily 2021-11-19 16:02:15 +02:00
Valtteri Karesto
1c7e2998f5 Merge pull request #28 from eqlabs/feature/initial-structure
Feature/initial structure
2021-11-19 15:52:34 +02:00
36 changed files with 1554 additions and 627 deletions

View File

@@ -0,0 +1,88 @@
import React from "react";
import { blackA } from "@radix-ui/colors";
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
import { styled, keyframes } from "../stitches.config";
const overlayShow = keyframes({
"0%": { opacity: 0 },
"100%": { opacity: 1 },
});
const contentShow = keyframes({
"0%": { opacity: 0, transform: "translate(-50%, -48%) scale(.96)" },
"100%": { opacity: 1, transform: "translate(-50%, -50%) scale(1)" },
});
const StyledOverlay = styled(AlertDialogPrimitive.Overlay, {
zIndex: 1000,
backgroundColor: blackA.blackA9,
position: "fixed",
inset: 0,
"@media (prefers-reduced-motion: no-preference)": {
animation: `${overlayShow} 150ms cubic-bezier(0.16, 1, 0.3, 1)`,
},
".dark &": {
backgroundColor: blackA.blackA11,
},
});
const Root: React.FC<AlertDialogPrimitive.AlertDialogProps> = ({
children,
...rest
}) => {
return (
<AlertDialogPrimitive.Root {...rest}>
<StyledOverlay />
{children}
</AlertDialogPrimitive.Root>
);
};
const StyledContent = styled(AlertDialogPrimitive.Content, {
zIndex: 1000,
backgroundColor: "$mauve2",
color: "$mauve12",
borderRadius: "$md",
boxShadow:
"0px 10px 38px -5px rgba(22, 23, 24, 0.25), 0px 10px 20px -5px rgba(22, 23, 24, 0.2)",
position: "fixed",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: "90vw",
maxWidth: "450px",
maxHeight: "85vh",
padding: 25,
"@media (prefers-reduced-motion: no-preference)": {
animation: `${contentShow} 150ms cubic-bezier(0.16, 1, 0.3, 1)`,
},
"&:focus": { outline: "none" },
".dark &": {
backgroundColor: "$mauve5",
boxShadow:
"0px 10px 38px 0px rgba(0, 0, 0, 0.85), 0px 10px 20px 0px rgba(0, 0, 0, 0.6)",
},
});
const StyledTitle = styled(AlertDialogPrimitive.Title, {
margin: 0,
color: "$mauve12",
fontWeight: 500,
fontSize: "$lg",
});
const StyledDescription = styled(AlertDialogPrimitive.Description, {
marginBottom: 20,
color: "$mauve11",
lineHeight: 1.5,
fontSize: "$sm",
});
// Exports
export const AlertDialog = Root;
export const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
export const AlertDialogContent = StyledContent;
export const AlertDialogTitle = StyledTitle;
export const AlertDialogDescription = StyledDescription;
export const AlertDialogAction = AlertDialogPrimitive.Action;
export const AlertDialogCancel = AlertDialogPrimitive.Cancel;

View File

@@ -1,6 +1,9 @@
import React from "react";
import { styled } from "../stitches.config";
import Flex from "./Flex";
import Spinner from "./Spinner";
const Button = styled("button", {
export const StyledButton = styled("button", {
// Reset
all: "unset",
appereance: "none",
@@ -27,14 +30,21 @@ const Button = styled("button", {
fontSize: "$2",
fontWeight: 500,
fontVariantNumeric: "tabular-nums",
backgroundColor: "red",
cursor: "pointer",
width: "max-content",
"&:disabled": {
opacity: 0.8,
opacity: 0.6,
pointerEvents: "none",
cursor: "not-allowed",
},
variants: {
size: {
xs: {
borderRadius: "$sm",
height: "$5",
px: "$2",
fontSize: "$xs",
},
sm: {
borderRadius: "$sm",
height: "$7",
@@ -56,27 +66,27 @@ const Button = styled("button", {
},
variant: {
default: {
backgroundColor: "$slate12",
boxShadow: "inset 0 0 0 1px $colors$slate12",
color: "$slate1",
backgroundColor: "$mauve12",
boxShadow: "inset 0 0 0 1px $colors$mauve12",
color: "$mauve1",
"@hover": {
"&:hover": {
backgroundColor: "$slate12",
boxShadow: "inset 0 0 0 1px $colors$slate12",
backgroundColor: "$mauve12",
boxShadow: "inset 0 0 0 1px $colors$mauve12",
},
},
"&:active": {
backgroundColor: "$slate10",
boxShadow: "inset 0 0 0 1px $colors$slate11",
backgroundColor: "$mauve10",
boxShadow: "inset 0 0 0 1px $colors$mauve11",
},
"&:focus": {
boxShadow:
"inset 0 0 0 1px $colors$slate12, 0 0 0 1px $colors$slate12",
"inset 0 0 0 1px $colors$mauve12, inset 0 0 0 2px $colors$mauve12",
},
'&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
{
backgroundColor: "$slate4",
boxShadow: "inset 0 0 0 1px $colors$slate8",
backgroundColor: "$mauve4",
boxShadow: "inset 0 0 0 1px $colors$mauve8",
},
},
primary: {
@@ -94,11 +104,11 @@ const Button = styled("button", {
boxShadow: "inset 0 0 0 1px $colors$pink8",
},
"&:focus": {
boxShadow: "inset 0 0 0 1px $colors$pink8",
boxShadow: "inset 0 0 0 2px $colors$pink12",
},
'&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
{
backgroundColor: "$slate4",
backgroundColor: "$mauve4",
boxShadow: "inset 0 0 0 1px $colors$pink8",
},
},
@@ -113,19 +123,24 @@ const Button = styled("button", {
textTransform: "uppercase",
},
},
fullWidth: {
true: {
width: "100%",
},
},
ghost: {
true: {
boxShadow: "none",
background: "transparent",
color: "$slate12",
color: "$mauve12",
"@hover": {
"&:hover": {
backgroundColor: "$slate6",
backgroundColor: "$mauve6",
boxShadow: "none",
},
},
"&:active": {
backgroundColor: "$slate8",
backgroundColor: "$mauve8",
boxShadow: "none",
},
"&:focus": {
@@ -133,19 +148,26 @@ const Button = styled("button", {
},
},
},
isLoading: {
true: {
"& .button-content": {
visibility: "hidden",
},
pointerEvents: "none",
},
},
},
compoundVariants: [
{
outline: true,
variant: "default",
css: {
background: "transparent",
color: "$slate12",
boxShadow: "inset 0 0 0 1px $colors$slate10",
color: "$mauve12",
boxShadow: "inset 0 0 0 1px $colors$mauve10",
"&:hover": {
color: "$slate12",
background: "$slate5",
color: "$mauve12",
background: "$mauve5",
},
},
},
@@ -154,10 +176,10 @@ const Button = styled("button", {
variant: "primary",
css: {
background: "transparent",
color: "$slate12",
color: "$mauve12",
"&:hover": {
color: "$slate12",
background: "$slate5",
color: "$mauve12",
background: "$mauve5",
},
},
},
@@ -168,4 +190,22 @@ const Button = styled("button", {
},
});
export default Button;
const CustomButton: React.FC<
React.ComponentProps<typeof StyledButton> & { as?: string }
> = React.forwardRef(({ children, as = "button", ...rest }, ref) => (
// @ts-expect-error
<StyledButton {...rest} ref={ref} as={as}>
<Flex
as="span"
css={{ gap: "$2", alignItems: "center" }}
className="button-content"
>
{children}
</Flex>
{rest.isLoading && <Spinner css={{ position: "absolute" }} />}
</StyledButton>
));
CustomButton.displayName = "CustomButton";
export default CustomButton;

View File

@@ -0,0 +1,29 @@
import { styled } from "../stitches.config";
import { StyledButton } from "./Button";
const ButtonGroup = styled("div", {
display: "flex",
marginLeft: "1px",
[`& ${StyledButton}`]: {
marginLeft: "-1px",
px: "$4",
zIndex: 2,
position: "relative",
"&:hover, &:focus": {
zIndex: 200,
},
},
[`& ${StyledButton}:not(:only-of-type):not(:first-child):not(:last-child)`]: {
borderRadius: 0,
},
[`& ${StyledButton}:first-child:not(:only-of-type)`]: {
borderBottomRightRadius: 0,
borderTopRightRadius: 0,
},
[`& ${StyledButton}:last-child:not(:only-of-type)`]: {
borderBottomLeftRadius: 0,
borderTopLeftRadius: 0,
},
});
export default ButtonGroup;

View File

@@ -1,7 +1,7 @@
import React from "react";
import * as Stiches from "@stitches/react";
import { keyframes } from "@stitches/react";
import { violet, blackA, mauve, whiteA } from "@radix-ui/colors";
import { blackA } from "@radix-ui/colors";
import * as DialogPrimitive from "@radix-ui/react-dialog";
import { styled } from "../stitches.config";
@@ -23,14 +23,14 @@ const StyledOverlay = styled(DialogPrimitive.Overlay, {
animation: `${overlayShow} 150ms cubic-bezier(0.16, 1, 0.3, 1)`,
},
".dark &": {
backgroundColor: blackA.blackA9,
backgroundColor: blackA.blackA11,
},
});
const StyledContent = styled(DialogPrimitive.Content, {
zIndex: 1000,
backgroundColor: "$slate2",
color: "$slate12",
backgroundColor: "$mauve2",
color: "$mauve12",
borderRadius: "$md",
boxShadow:
"0px 10px 38px -5px rgba(22, 23, 24, 0.25), 0px 10px 20px -5px rgba(22, 23, 24, 0.2)",
@@ -47,9 +47,9 @@ const StyledContent = styled(DialogPrimitive.Content, {
},
"&:focus": { outline: "none" },
".dark &": {
backgroundColor: "$slate5",
backgroundColor: "$mauve5",
boxShadow:
"0px 10px 38px 0px rgba(22, 23, 24, 0.85), 0px 10px 20px 0px rgba(22, 23, 24, 0.6)",
"0px 10px 38px 0px rgba(0, 0, 0, 0.85), 0px 10px 20px 0px rgba(0, 0, 0, 0.6)",
},
});
@@ -65,39 +65,21 @@ const Content: React.FC<{ css?: Stiches.CSS }> = ({ css, children }) => {
const StyledTitle = styled(DialogPrimitive.Title, {
margin: 0,
fontWeight: 500,
color: "$slate12",
color: "$mauve12",
fontSize: 17,
});
const StyledDescription = styled(DialogPrimitive.Description, {
margin: "10px 0 20px",
color: "$slate11",
color: "$mauve11",
fontSize: 15,
lineHeight: 1.5,
});
// Exports
export const Dialog = DialogPrimitive.Root;
export const Dialog = styled(DialogPrimitive.Root);
export const DialogTrigger = DialogPrimitive.Trigger;
export const DialogContent = Content;
export const DialogTitle = StyledTitle;
export const DialogDescription = StyledDescription;
export const DialogClose = DialogPrimitive.Close;
const Input = styled("input", {
all: "unset",
width: "100%",
flex: "1",
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
borderRadius: 4,
padding: "0 10px",
fontSize: 15,
lineHeight: 1,
color: violet.violet11,
boxShadow: `0 0 0 1px ${violet.violet7}`,
height: 35,
"&:focus": { boxShadow: `0 0 0 2px ${violet.violet8}` },
});

View File

@@ -2,7 +2,6 @@ import { keyframes } from "@stitches/react";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import { styled } from "../stitches.config";
import { blackA, slateDark } from "@radix-ui/colors";
const slideUpAndFade = keyframes({
"0%": { opacity: 0, transform: "translateY(2px)" },
@@ -26,7 +25,7 @@ const slideLeftAndFade = keyframes({
const StyledContent = styled(DropdownMenuPrimitive.Content, {
minWidth: 220,
backgroundColor: "$slate2",
backgroundColor: "$mauve2",
borderRadius: 6,
padding: 5,
boxShadow:
@@ -43,7 +42,7 @@ const StyledContent = styled(DropdownMenuPrimitive.Content, {
},
},
".dark &": {
backgroundColor: "$slate5",
backgroundColor: "$mauve5",
boxShadow:
"0px 10px 38px -10px rgba(22, 23, 24, 0.85), 0px 10px 20px -15px rgba(22, 23, 24, 0.6)",
},
@@ -53,7 +52,7 @@ const itemStyles = {
all: "unset",
fontSize: 13,
lineHeight: 1,
color: "$slate12",
color: "$mauve12",
borderRadius: 3,
display: "flex",
alignItems: "center",
@@ -62,10 +61,12 @@ const itemStyles = {
position: "relative",
paddingLeft: "5px",
userSelect: "none",
py: "$0.5",
pr: "$2",
gap: "$2",
"&[data-disabled]": {
color: "$slate9",
color: "$mauve9",
pointerEvents: "none",
},
@@ -94,12 +95,12 @@ const StyledLabel = styled(DropdownMenuPrimitive.Label, {
paddingLeft: 25,
fontSize: 12,
lineHeight: "25px",
color: "$slate11",
color: "$mauve11",
});
const StyledSeparator = styled(DropdownMenuPrimitive.Separator, {
height: 1,
backgroundColor: "$slate7",
backgroundColor: "$mauve7",
margin: 5,
});
@@ -113,7 +114,10 @@ const StyledItemIndicator = styled(DropdownMenuPrimitive.ItemIndicator, {
});
const StyledArrow = styled(DropdownMenuPrimitive.Arrow, {
fill: "$slate2",
fill: "$mauve2",
".dark &": {
fill: "$mauve5",
},
});
// Exports

View File

@@ -1,8 +1,37 @@
import React, { useRef, useState } from "react";
import { Plus, Share, DownloadSimple, Gear, X } from "phosphor-react";
import { useTheme } from "next-themes";
import React, { useState, useEffect } from "react";
import {
Plus,
Share,
DownloadSimple,
Gear,
X,
GithubLogo,
SignOut,
ArrowSquareOut,
CloudArrowUp,
CaretDown,
User,
FilePlus,
} from "phosphor-react";
import Image from "next/image";
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuArrow,
DropdownMenuSeparator,
} from "./DropdownMenu";
import NewWindow from "react-new-window";
import { signOut, useSession } from "next-auth/react";
import { useSnapshot } from "valtio";
import { createNewFile, state, updateEditorSettings } from "../state";
import {
createNewFile,
state,
syncToGist,
updateEditorSettings,
} from "../state";
import Box from "./Box";
import Button from "./Button";
import Container from "./Container";
@@ -16,23 +45,52 @@ import {
} from "./Dialog";
import Flex from "./Flex";
import Stack from "./Stack";
import { useSnapshot } from "valtio";
import { useSession } from "next-auth/react";
import { useRouter } from "next/router";
import Input from "./Input";
import toast from "react-hot-toast";
import {
AlertDialog,
AlertDialogContent,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogCancel,
AlertDialogAction,
} from "./AlertDialog";
const EditorNavigation = () => {
const snap = useSnapshot(state);
const [createNewAlertOpen, setCreateNewAlertOpen] = useState(false);
const [editorSettingsOpen, setEditorSettingsOpen] = useState(false);
const [filename, setFilename] = useState("");
const { theme } = useTheme();
const { data: session, status } = useSession();
const router = useRouter();
const [popup, setPopUp] = useState(false);
const [editorSettings, setEditorSettings] = useState(snap.editorSettings);
useEffect(() => {
if (session && session.user && popup) {
setPopUp(false);
}
}, [session, popup]);
return (
<Flex css={{ flexShrink: 0, gap: "$0" }}>
<Flex css={{ overflowX: "scroll", py: "$3", flex: 1 }}>
<Flex
css={{
overflowX: "scroll",
py: "$3",
flex: 1,
"&::-webkit-scrollbar": {
height: 0,
background: "transparent",
},
}}
>
<Container css={{ flex: 1 }}>
<Stack css={{ gap: "$3", flex: 1, flexWrap: "nowrap" }}>
{state.loading && "loading"}
<Stack
css={{
gap: "$3",
flex: 1,
flexWrap: "nowrap",
marginBottom: "-1px",
}}
>
{snap.files &&
snap.files.length > 0 &&
snap.files?.map((file, index) => (
@@ -40,7 +98,7 @@ const EditorNavigation = () => {
size="sm"
outline={snap.active !== index}
onClick={() => (state.active = index)}
key={file.name}
key={file.name + index}
css={{
"&:hover": {
span: {
@@ -54,13 +112,28 @@ const EditorNavigation = () => {
as="span"
css={{
display: "flex",
p: "1px",
p: "2px",
borderRadius: "$full",
mr: "-4px",
"&:hover": {
// boxSizing: "0px 0px 1px",
backgroundColor: "$mauve2",
color: "$mauve12",
},
}}
onClick={(ev: React.MouseEvent<HTMLElement>) => {
ev.stopPropagation();
// Remove file from state
state.files.splice(index, 1);
// Change active file state
// 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;
}}
onClick={() => state.files.splice(index, 1)}
>
<X size="13px" />
<X size="9px" weight="bold" />
</Box>
</Button>
))}
@@ -79,10 +152,8 @@ const EditorNavigation = () => {
<DialogContent>
<DialogTitle>Create new file</DialogTitle>
<DialogDescription>
<span>
Create empty C file or select one of the existing ones
</span>
<input
<label>Filename</label>
<Input
value={filename}
onChange={(e) => setFilename(e.target.value)}
/>
@@ -120,113 +191,276 @@ const EditorNavigation = () => {
<Flex
css={{
py: "$3",
backgroundColor: "$slate3",
backgroundColor: "$mauve3",
zIndex: 1,
}}
>
<Container css={{ width: "unset" }}>
<Container
css={{ width: "unset", display: "flex", alignItems: "center" }}
>
{status === "authenticated" ? (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Box
css={{
display: "flex",
borderRadius: "$full",
overflow: "hidden",
width: "$6",
height: "$6",
boxShadow: "0px 0px 0px 1px $colors$mauve11",
position: "relative",
mr: "$3",
"@hover": {
"&:hover": {
cursor: "pointer",
boxShadow: "0px 0px 0px 1px $colors$mauve12",
},
},
}}
>
<Image
src={session?.user?.image || ""}
width="30px"
height="30px"
objectFit="cover"
alt="User avatar"
/>
</Box>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem disabled onClick={() => signOut()}>
<User size="16px" /> {session?.user?.username} (
{session?.user.name})
</DropdownMenuItem>
<DropdownMenuItem
onClick={() =>
window.open(
`http://gist.github.com/${session?.user.username}`
)
}
>
<ArrowSquareOut size="16px" />
Go to your Gist
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => signOut({ callbackUrl: "/" })}>
<SignOut size="16px" /> Log out
</DropdownMenuItem>
<DropdownMenuArrow offset={10} />
</DropdownMenuContent>
</DropdownMenu>
) : (
<Button
outline
size="sm"
css={{ mr: "$3" }}
onClick={() => setPopUp(true)}
>
<GithubLogo size="16px" /> Login
</Button>
)}
<Stack
css={{
display: "inline-flex",
marginLeft: "auto",
flexShrink: 0,
gap: "$0",
border: "1px solid $slate10",
borderRadius: "$sm",
boxShadow: "inset 0px 0px 0px 1px $colors$mauve10",
zIndex: 9,
position: "relative",
overflow: "hidden",
button: {
borderRadius: "$0",
borderRadius: 0,
px: "$2",
alignSelf: "flex-start",
boxShadow: "none",
},
"button:not(:first-child):not(:last-child)": {
borderRight: 0,
borderLeft: 0,
},
"button:first-child": {
borderTopLeftRadius: "$sm",
borderBottomLeftRadius: "$sm",
},
"button:last-child": {
borderTopRightRadius: "$sm",
borderBottomRightRadius: "$sm",
boxShadow: "inset 0px 0px 0px 1px $colors$mauve10",
"&:hover": {
boxShadow: "inset 0px 0px 0px 1px $colors$mauve12",
},
},
}}
>
<Button ghost size="sm" css={{ alignItems: "center" }}>
<Button outline size="sm" css={{ alignItems: "center" }}>
<DownloadSimple size="16px" />
</Button>
<Dialog>
<DialogTrigger asChild>
<Button ghost size="sm" css={{ alignItems: "center" }}>
<Button
outline
size="sm"
css={{ alignItems: "center" }}
onClick={() => {
navigator.clipboard.writeText(
`${window.location.origin}/develop/${snap.gistId}`
);
toast.success("Copied share link to clipboard!");
}}
>
<Share size="16px" />
</Button>
<Button
outline
size="sm"
disabled={!session || !session.user}
isLoading={snap.gistLoading}
css={{ alignItems: "center" }}
onClick={() => {
if (snap.gistOwner === session?.user.username) {
syncToGist(session);
} else {
setCreateNewAlertOpen(true);
}
}}
>
<CloudArrowUp size="16px" />
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button outline size="sm">
<CaretDown size="16px" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<DownloadSimple size="16px" /> Download as ZIP
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
navigator.clipboard.writeText(
`${window.location.origin}/develop/${snap.gistId}`
);
toast.success("Copied share link to clipboard!");
}}
>
<Share size="16px" />
</Button>
</DialogTrigger>
<DialogContent>
<DialogTitle>Share hook</DialogTitle>
<DialogDescription>
<span>
We will store your hook code in public GitHub Gist and
generate link to that
</span>
</DialogDescription>
<Flex
css={{ marginTop: 25, justifyContent: "flex-end", gap: "$3" }}
Copy share link to clipboard
</DropdownMenuItem>
<DropdownMenuItem
disabled={
session?.user.username !== snap.gistOwner || !snap.gistId
}
onClick={() => {
syncToGist(session);
}}
>
<DialogClose asChild>
<Button outline>Cancel</Button>
</DialogClose>
</Flex>
<DialogClose asChild>
<Box css={{ position: "absolute", top: "$3", right: "$3" }}>
<X size="20px" />
</Box>
</DialogClose>
</DialogContent>
</Dialog>
<Dialog>
<DialogTrigger asChild>
<Button ghost size="sm" css={{ alignItems: "center" }}>
<Gear size="16px" />
</Button>
</DialogTrigger>
<DialogContent>
<DialogTitle>Editor settings</DialogTitle>
<DialogDescription>
<span>You can edit your editor settings here</span>
<input
value={editorSettings.tabSize}
onChange={(e) =>
setEditorSettings((curr) => ({
...curr,
tabSize: Number(e.target.value),
}))
}
/>
</DialogDescription>
<Flex
css={{ marginTop: 25, justifyContent: "flex-end", gap: "$3" }}
<CloudArrowUp size="16px" /> Push to Gist
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
disabled={status !== "authenticated"}
onClick={() => {
setCreateNewAlertOpen(true);
}}
>
<DialogClose asChild>
<Button
outline
onClick={() => updateEditorSettings(editorSettings)}
>
Cancel
</Button>
</DialogClose>
<DialogClose asChild>
<Button
variant="primary"
onClick={() => updateEditorSettings(editorSettings)}
>
Save changes
</Button>
</DialogClose>
</Flex>
<DialogClose asChild>
<Box css={{ position: "absolute", top: "$3", right: "$3" }}>
<X size="20px" />
</Box>
</DialogClose>
</DialogContent>
</Dialog>
<FilePlus size="16px" /> Create as a new Gist
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setEditorSettingsOpen(true)}>
<Gear size="16px" /> Editor Settings
</DropdownMenuItem>
<DropdownMenuArrow offset={10} />
</DropdownMenuContent>
</DropdownMenu>
</Stack>
{popup && !session ? (
<NewWindow center="parent" url="/sign-in" />
) : null}
</Container>
</Flex>
<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.
</AlertDialogDescription>
<Flex css={{ justifyContent: "flex-end", gap: "$3" }}>
<AlertDialogCancel asChild>
<Button outline>Cancel</Button>
</AlertDialogCancel>
<AlertDialogAction asChild>
<Button
variant="primary"
onClick={() => {
syncToGist(session, true);
}}
>
<FilePlus size="15px" /> Create new Gist
</Button>
</AlertDialogAction>
</Flex>
</AlertDialogContent>
</AlertDialog>
<Dialog open={editorSettingsOpen} onOpenChange={setEditorSettingsOpen}>
<DialogTrigger asChild>
{/* <Button outline size="sm" css={{ alignItems: "center" }}>
<Gear size="16px" />
</Button> */}
</DialogTrigger>
<DialogContent>
<DialogTitle>Editor settings</DialogTitle>
<DialogDescription>
<label>Tab size</label>
<Input
type="number"
min="1"
value={editorSettings.tabSize}
onChange={(e) =>
setEditorSettings((curr) => ({
...curr,
tabSize: Number(e.target.value),
}))
}
/>
</DialogDescription>
<Flex css={{ marginTop: 25, justifyContent: "flex-end", gap: "$3" }}>
<DialogClose asChild>
<Button
outline
onClick={() => updateEditorSettings(editorSettings)}
>
Cancel
</Button>
</DialogClose>
<DialogClose asChild>
<Button
variant="primary"
onClick={() => updateEditorSettings(editorSettings)}
>
Save changes
</Button>
</DialogClose>
</Flex>
<DialogClose asChild>
<Box css={{ position: "absolute", top: "$3", right: "$3" }}>
<X size="20px" />
</Box>
</DialogClose>
</DialogContent>
</Dialog>
</Flex>
);
};

View File

@@ -1,9 +1,13 @@
import React from "react";
import { useSnapshot } from "valtio";
import Container from "./Container";
import Box from "./Box";
import LogText from "./LogText";
import { state } from "../state";
import { compileCode, state } from "../state";
import { Play, Prohibit } from "phosphor-react";
import Button from "./Button";
import Heading from "./Heading";
const Footer = () => {
const snap = useSnapshot(state);
@@ -12,11 +16,33 @@ const Footer = () => {
as="footer"
css={{
display: "flex",
borderTop: "1px solid $slate6",
background: "$slate1",
borderTop: "1px solid $mauve6",
background: "$mauve1",
position: "relative",
}}
>
<Container css={{ py: "$4", flexShrink: 1 }}>
<Container css={{ py: "$3", flexShrink: 1 }}>
<Heading
as="h3"
css={{ fontWeight: 300, m: 0, fontSize: "11px", color: "$mauve9" }}
>
DEVELOPMENT LOG
</Heading>
<Button
ghost
size="xs"
css={{
position: "absolute",
right: "$3",
top: "$2",
color: "$mauve10",
}}
onClick={() => {
state.logs = [];
}}
>
<Prohibit size="14px" />
</Button>
<Box
as="pre"
css={{
@@ -27,15 +53,13 @@ const Footer = () => {
fontSize: "13px",
fontWeight: "$body",
fontFamily: "$monospace",
overflowY: "scroll",
overflowY: "auto",
wordWrap: "break-word",
py: 3,
px: 3,
m: 3,
}}
>
{snap.logs.map((log, index) => (
<Box key={log.type + index}>
{snap.logs?.map((log, index) => (
<Box as="span" key={log.type + index}>
<LogText capitalize variant={log.type}>
{log.type}:{" "}
</LogText>
@@ -43,6 +67,24 @@ const Footer = () => {
</Box>
))}
</Box>
<Button
variant="primary"
uppercase
disabled={!snap.files.length}
isLoading={snap.compiling}
onClick={() => compileCode(snap.active)}
css={{
position: "absolute",
bottom: "$4",
left: "$4",
alignItems: "center",
display: "flex",
cursor: "pointer",
}}
>
<Play weight="bold" size="16px" />
Compile to Wasm
</Button>
</Container>
</Box>
);

View File

@@ -4,7 +4,13 @@ const Heading = styled("span", {
fontFamily: "$heading",
lineHeight: "$heading",
fontWeight: "$heading",
textTransform: "uppercase",
variants: {
uppercase: {
true: {
textTransform: "uppercase",
},
},
},
});
export default Heading;

View File

@@ -1,33 +1,25 @@
import React, { useEffect, useRef } from "react";
import React, { useRef } from "react";
import { useSnapshot, ref } from "valtio";
import Editor from "@monaco-editor/react";
import type monaco from "monaco-editor";
import { Play } from "phosphor-react";
import { ArrowBendLeftUp } from "phosphor-react";
import { useTheme } from "next-themes";
import { useRouter } from "next/router";
import Box from "./Box";
import Button from "./Button";
import Container from "./Container";
import dark from "../theme/editor/amy.json";
import light from "../theme/editor/xcode_default.json";
import { compileCode, saveFile, state } from "../state";
import { saveFile, state } from "../state";
import EditorNavigation from "./EditorNavigation";
import Spinner from "./Spinner";
import Text from "./Text";
const HooksEditor = () => {
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>();
const snap = useSnapshot(state);
const router = useRouter();
const { theme } = useTheme();
// useEffect(() => {
// if (snap.editorCtx) {
// snap.editorCtx.getModels().forEach((model) => {
// // console.log(model.id,);
// snap.editorCtx?.createModel(model.getValue(), "c", model.uri);
// });
// }
// // eslint-disable-next-line react-hooks/exhaustive-deps
// }, []);
console.log("reinit");
return (
<Box
css={{
@@ -36,61 +28,76 @@ const HooksEditor = () => {
display: "flex",
position: "relative",
flexDirection: "column",
backgroundColor: "$slate3",
backgroundColor: "$mauve3",
width: "100%",
}}
>
<EditorNavigation />
<Editor
keepCurrentModel
// defaultLanguage={snap.files?.[snap.active]?.language}
path={snap.files?.[snap.active]?.name}
// defaultValue={snap.files?.[snap.active]?.content}
beforeMount={(monaco) => {
if (!state.editorCtx) {
state.editorCtx = ref(monaco.editor);
// @ts-expect-error
monaco.editor.defineTheme("dark", dark);
// @ts-expect-error
monaco.editor.defineTheme("light", light);
}
}}
onMount={(editor, monaco) => {
editorRef.current = editor;
// hook editor to global state
editor.updateOptions({
minimap: {
enabled: false,
},
...snap.editorSettings,
});
editor.addCommand(
monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S,
() => {
saveFile(editor.getValue());
{snap.files.length > 0 && router.isReady ? (
<Editor
keepCurrentModel
defaultLanguage={snap.files?.[snap.active]?.language}
language={snap.files?.[snap.active]?.language}
path={snap.files?.[snap.active]?.name}
defaultValue={snap.files?.[snap.active]?.content}
beforeMount={(monaco) => {
if (!state.editorCtx) {
state.editorCtx = ref(monaco.editor);
// @ts-expect-error
monaco.editor.defineTheme("dark", dark);
// @ts-expect-error
monaco.editor.defineTheme("light", light);
}
);
}}
theme={theme === "dark" ? "dark" : "light"}
/>
<Button
variant="primary"
uppercase
onClick={() => compileCode(snap.active)}
disabled={snap.compiling}
css={{
position: "absolute",
bottom: "$4",
left: "$4",
alignItems: "center",
display: "flex",
cursor: "pointer",
}}
>
<Play weight="bold" size="16px" />
Compile to Wasm
{snap.compiling && <Spinner />}
</Button>
}}
onMount={(editor, monaco) => {
editorRef.current = editor;
// hook editor to global state
editor.updateOptions({
minimap: {
enabled: false,
},
...snap.editorSettings,
});
editor.addCommand(
monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S,
() => {
saveFile(editor.getValue());
}
);
}}
theme={theme === "dark" ? "dark" : "light"}
/>
) : (
<Container>
{!snap.loading && router.isReady && (
<Box
css={{
flexDirection: "row",
width: "$spaces$wide",
gap: "$3",
display: "inline-flex",
}}
>
<Box css={{ display: "inline-flex", pl: "35px" }}>
<ArrowBendLeftUp size={30} />
</Box>
<Box
css={{ pl: "0px", pt: "15px", flex: 1, display: "inline-flex" }}
>
<Text
css={{
fontSize: "14px",
maxWidth: "220px",
fontFamily: "$monospace",
}}
>
Click the link above to create a your file
</Text>
</Box>
</Box>
)}
</Container>
)}
</Box>
);
};

147
components/Input.tsx Normal file
View File

@@ -0,0 +1,147 @@
import { styled } from "../stitches.config";
export const Input = styled("input", {
// Reset
appearance: "none",
borderWidth: "0",
boxSizing: "border-box",
fontFamily: "inherit",
outline: "none",
width: "100%",
flex: "1",
backgroundColor: "$mauve4",
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
borderRadius: "$sm",
px: "$2",
fontSize: "$md",
lineHeight: 1,
color: "$mauve12",
boxShadow: `0 0 0 1px $colors$mauve8`,
height: 35,
WebkitTapHighlightColor: "rgba(0,0,0,0)",
"&::before": {
boxSizing: "border-box",
},
"&::after": {
boxSizing: "border-box",
},
fontVariantNumeric: "tabular-nums",
"&:-webkit-autofill": {
boxShadow: "inset 0 0 0 1px $colors$blue6, inset 0 0 0 100px $colors$blue3",
},
"&:-webkit-autofill::first-line": {
fontFamily: "$untitled",
color: "$mauve12",
},
"&:focus": {
boxShadow: `0 0 0 1px $colors$mauve10`,
"&:-webkit-autofill": {
boxShadow: `0 0 0 1px $colors$mauve10`,
},
},
"&::placeholder": {
color: "$mauve9",
},
"&:disabled": {
pointerEvents: "none",
backgroundColor: "$mauve2",
color: "$mauve8",
cursor: "not-allowed",
"&::placeholder": {
color: "$mauve7",
},
},
"&:read-only": {
backgroundColor: "$mauve2",
"&:focus": {
boxShadow: "inset 0px 0px 0px 1px $colors$mauve7",
},
},
variants: {
size: {
sm: {
height: "$5",
fontSize: "$1",
lineHeight: "$sizes$4",
"&:-webkit-autofill::first-line": {
fontSize: "$1",
},
},
md: {
height: "$8",
fontSize: "$1",
lineHeight: "$sizes$5",
"&:-webkit-autofill::first-line": {
fontSize: "$1",
},
},
lg: {
height: "$12",
fontSize: "$2",
lineHeight: "$sizes$6",
"&:-webkit-autofill::first-line": {
fontSize: "$3",
},
},
},
variant: {
ghost: {
boxShadow: "none",
backgroundColor: "transparent",
"@hover": {
"&:hover": {
boxShadow: "inset 0 0 0 1px $colors$mauve7",
},
},
"&:focus": {
backgroundColor: "$loContrast",
boxShadow: `0 0 0 1px $colors$mauve10`,
},
"&:disabled": {
backgroundColor: "transparent",
},
"&:read-only": {
backgroundColor: "transparent",
},
},
},
state: {
invalid: {
boxShadow: "inset 0 0 0 1px $colors$red7",
"&:focus": {
boxShadow:
"inset 0px 0px 0px 1px $colors$red8, 0px 0px 0px 1px $colors$red8",
},
},
valid: {
boxShadow: "inset 0 0 0 1px $colors$green7",
"&:focus": {
boxShadow:
"inset 0px 0px 0px 1px $colors$green8, 0px 0px 0px 1px $colors$green8",
},
},
},
cursor: {
default: {
cursor: "default",
"&:focus": {
cursor: "text",
},
},
text: {
cursor: "text",
},
},
},
defaultVariants: {
size: "md",
},
});
export default Input;

View File

@@ -1,5 +1,3 @@
import { useTheme } from "next-themes";
import { styled } from "../stitches.config";
const SVG = styled("svg", {

View File

@@ -1,43 +1,45 @@
import React from "react";
import Link from "next/link";
import {
Gear,
GithubLogo,
SignOut,
User,
ArrowSquareOut,
} from "phosphor-react";
import { useSnapshot } from "valtio";
import Image from "next/image";
import { useSession, signIn, signOut } from "next-auth/react";
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuArrow,
DropdownMenuSeparator,
} from "./DropdownMenu";
import { useRouter } from "next/router";
import { FolderOpen, X, ArrowUpRight, BookOpen } from "phosphor-react";
import Stack from "./Stack";
import Logo from "./Logo";
import Button from "./Button";
import Flex from "./Flex";
import Container from "./Container";
import Box from "./Box";
import Flex from "./Flex";
import ThemeChanger from "./ThemeChanger";
import { useRouter } from "next/router";
import { state } from "../state";
import Heading from "./Heading";
import Text from "./Text";
import Spinner from "./Spinner";
import truncate from "../utils/truncate";
import ButtonGroup from "./ButtonGroup";
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogTitle,
DialogTrigger,
} from "./Dialog";
import PanelBox from "./PanelBox";
const Navigation = () => {
const { data: session, status } = useSession();
const router = useRouter();
const snap = useSnapshot(state);
const slug = router.query?.slug;
const gistId = Array.isArray(slug) ? slug[0] : null;
return (
<Box
as="nav"
css={{
display: "flex",
height: "60px",
borderBottom: "1px solid $slate6",
borderBottom: "1px solid $mauve6",
position: "relative",
zIndex: 2003,
}}
@@ -46,112 +48,333 @@ const Navigation = () => {
css={{
display: "flex",
alignItems: "center",
py: "$2",
}}
>
<Link href="/" passHref>
<Box
as="a"
css={{ display: "flex", alignItems: "center", color: "$textColor" }}
>
<Logo width="30px" height="30px" />
</Box>
</Link>
<Stack css={{ ml: "$4", gap: "$3" }}>
<Link href="/develop" passHref shallow>
<Button
<Flex
css={{
flex: 1,
alignItems: "center",
borderRight: "1px solid $colors$mauve6",
py: "$3",
pr: "$4",
}}
>
<Link href="/" passHref>
<Box
as="a"
outline={!router.pathname.includes("/develop")}
uppercase
>
Develop
</Button>
</Link>
<Link href="/deploy" passHref shallow>
<Button
as="a"
outline={!router.pathname.includes("/deploy")}
uppercase
>
Deploy
</Button>
</Link>
<Link href="/test" passHref shallow>
<Button
as="a"
outline={!router.pathname.includes("/test")}
uppercase
>
Test
</Button>
</Link>
</Stack>
<Stack css={{ color: "text", ml: "auto" }}>
<ThemeChanger />
{status === "authenticated" && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Box
css={{
borderRadius: "$full",
overflow: "hidden",
width: "30px",
height: "30px",
position: "relative",
}}
>
<Image
src={session?.user?.image || ""}
width="30px"
height="30px"
objectFit="cover"
alt="User avatar"
/>
</Box>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem disabled onClick={() => signOut()}>
<User size="16px" /> {session?.user?.username} (
{session?.user.name})
</DropdownMenuItem>
<DropdownMenuItem
onClick={() =>
window.open(
`http://gist.github.com/${session?.user.username}`
)
}
>
<ArrowSquareOut size="16px" />
Go to your Gist
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<Gear size="16px" /> Settings
</DropdownMenuItem>
<DropdownMenuItem onClick={() => signOut()}>
<SignOut size="16px" /> Log out
</DropdownMenuItem>
<DropdownMenuArrow offset={10} />
</DropdownMenuContent>
</DropdownMenu>
)}
{status === "unauthenticated" && (
<Button outline onClick={() => signIn("github")}>
<GithubLogo size="16px" /> Github Login
</Button>
)}
{status === "loading" && "loading"}
{/* <Box
css={{
border: "1px solid",
borderRadius: "3px",
borderColor: "text",
p: 1,
display: "flex",
alignItems: "center",
color: "$textColor",
}}
>
<BookOpen size="20px" />
</Box> */}
</Stack>
<Logo width="30px" height="30px" />
</Box>
</Link>
<Flex
css={{
ml: "$5",
flexDirection: "column",
gap: "1px",
}}
>
{snap.loading ? (
<Spinner />
) : (
<>
<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>
</Text>
</>
)}
</Flex>
<ButtonGroup css={{ marginLeft: "auto" }}>
<Dialog
open={snap.mainModalOpen}
onOpenChange={(open) => (state.mainModalOpen = open)}
>
<DialogTrigger asChild>
<Button outline>
<FolderOpen size="15px" />
</Button>
</DialogTrigger>
<DialogContent
css={{
maxWidth: "100%",
width: "80vw",
height: "80%",
backgroundColor: "$mauve1 !important",
overflowY: "auto",
p: 0,
}}
>
<Flex
css={{
flexDirection: "column",
flex: 1,
height: "auto",
"@md": {
flexDirection: "row",
height: "100%",
},
}}
>
<Flex
css={{
borderBottom: "1px solid $colors$mauve5",
width: "100%",
flexDirection: "column",
p: "$7",
height: "100%",
"@md": {
width: "30%",
borderBottom: "0px",
borderRight: "1px solid $colors$mauve5",
},
}}
>
<DialogTitle
css={{
textTransform: "uppercase",
display: "inline-flex",
alignItems: "center",
gap: "$3",
fontSize: "$xl",
}}
>
<Logo width="30px" height="30px" /> XRPL Hooks Editor
</DialogTitle>
<DialogDescription as="div">
<Text
css={{
display: "inline-flex",
color: "inherit",
my: "$5",
mb: "$7",
}}
>
Hooks add smart contract functionality to the XRP
Ledger.
</Text>
<Flex
css={{ flexDirection: "column", gap: "$2", mt: "$2" }}
>
<Text
css={{
display: "inline-flex",
alignItems: "center",
gap: "$3",
color: "$green9",
"&:hover": {
color: "$green11 !important",
},
"&:focus": {
outline: 0,
},
}}
as="a"
rel="noreferrer noopener"
target="_blank"
href="https://github.com/XRPL-Labs/xrpld-hooks"
>
<ArrowUpRight size="15px" /> Developing Hooks
</Text>
<Text
css={{
display: "inline-flex",
alignItems: "center",
gap: "$3",
color: "$green9",
"&:hover": {
color: "$green11 !important",
},
"&:focus": {
outline: 0,
},
}}
as="a"
rel="noreferrer noopener"
target="_blank"
href="https://xrpl-hooks.readme.io/docs"
>
<ArrowUpRight size="15px" /> Hooks documentation
</Text>
<Text
css={{
display: "inline-flex",
alignItems: "center",
gap: "$3",
color: "$green9",
"&:hover": {
color: "$green11 !important",
},
"&:focus": {
outline: 0,
},
}}
as="a"
rel="noreferrer noopener"
target="_blank"
href="https://xrpl.org/docs.html"
>
<ArrowUpRight size="15px" /> XRPL documentation
</Text>
</Flex>
</DialogDescription>
</Flex>
<Flex
css={{
display: "grid",
gridTemplateColumns: "1fr",
gridTemplateRows: "max-content",
flex: 1,
p: "$7",
gap: "$3",
alignItems: "flex-start",
flexWrap: "wrap",
backgroundImage: `url('/pattern.svg'), url('/pattern-2.svg')`,
backgroundRepeat: "no-repeat",
backgroundPosition: "bottom left, top right",
"@md": {
gridTemplateColumns: "1fr 1fr 1fr",
gridTemplateRows: "max-content",
},
}}
>
<PanelBox
as="a"
href="/develop/be088224fb37c0075e84491da0e602c1"
>
<Heading>Starter</Heading>
<Text>Just an empty starter with essential imports</Text>
</PanelBox>
<PanelBox
as="a"
href="/develop/be088224fb37c0075e84491da0e602c1"
>
<Heading>Firewall</Heading>
<Text>
This Hook essentially checks a blacklist of accounts
</Text>
</PanelBox>
<PanelBox
as="a"
href="/develop/be088224fb37c0075e84491da0e602c1"
>
<Heading>Accept</Heading>
<Text>
This hook just accepts any transaction coming through it
</Text>
</PanelBox>
<PanelBox
as="a"
href="/develop/be088224fb37c0075e84491da0e602c1"
>
<Heading>Accept</Heading>
<Text>
This hook just accepts any transaction coming through it
</Text>
</PanelBox>
</Flex>
</Flex>
<DialogClose asChild>
<Box
css={{
position: "absolute",
top: "$1",
right: "$1",
cursor: "pointer",
background: "$mauve1",
display: "flex",
borderRadius: "$full",
p: "$1",
}}
>
<X size="20px" />
</Box>
</DialogClose>
</DialogContent>
</Dialog>
<ThemeChanger />
</ButtonGroup>
</Flex>
<Flex
css={{
flexWrap: "nowrap",
marginLeft: "$4",
overflowX: "scroll",
"&::-webkit-scrollbar": {
height: 0,
background: "transparent",
},
}}
>
<Stack
css={{
ml: "$4",
gap: "$3",
flexWrap: "nowrap",
alignItems: "center",
marginLeft: "auto",
}}
>
<ButtonGroup>
<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
>
Deploy
</Button>
</Link>
<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>
</Stack>
</Flex>
</Container>
</Box>
);

30
components/PanelBox.tsx Normal file
View File

@@ -0,0 +1,30 @@
import { styled } from "../stitches.config";
import Heading from "./Heading";
import Text from "./Text";
const PanelBox = styled("div", {
display: "flex",
flexDirection: "column",
border: "1px solid $colors$mauve5",
backgroundColor: "$mauve1",
padding: "$3",
borderRadius: "$sm",
fontWeight: "lighter",
height: "auto",
cursor: "pointer",
flex: "1 1 0px",
"&:hover": {
border: "1px solid $colors$mauve9",
},
[`& ${Heading}`]: {
fontWeight: "lighter",
mb: "$2",
},
[`& ${Text}`]: {
fontWeight: "lighter",
color: "$mauve10",
fontSize: "$sm",
},
});
export default PanelBox;

View File

@@ -3,11 +3,12 @@ import { styled, keyframes } from "../stitches.config";
const rotate = keyframes({
"0%": { transform: "rotate(0deg)" },
"100%": { transform: "rotate(360deg)" },
"100%": { transform: "rotate(-360deg)" },
});
const Spinner = styled(SpinnerIcon, {
animation: `${rotate} 150ms cubic-bezier(0.16, 1, 0.3, 1) infinite`,
animation: `${rotate} 150ms linear infinite`,
fontSize: "16px",
});
export default Spinner;

View File

@@ -1,8 +1,5 @@
import { Children } from "react";
import Box from "./Box";
import { styled } from "../stitches.config";
import type * as Stitches from "@stitches/react";
const StackComponent = styled(Box, {
display: "flex",

View File

@@ -2,7 +2,7 @@ import { useState, useEffect } from "react";
import { useTheme } from "next-themes";
import { Sun, Moon } from "phosphor-react";
import Box from "./Box";
import Button from "./Button";
const ThemeChanger = () => {
const { theme, setTheme } = useTheme();
@@ -12,7 +12,8 @@ const ThemeChanger = () => {
if (!mounted) return null;
return (
<Box
<Button
outline
onClick={() => {
setTheme(theme && theme === "light" ? "dark" : "light");
}}
@@ -25,12 +26,8 @@ const ThemeChanger = () => {
color: "$muted",
}}
>
{theme === "dark" ? (
<Sun weight="bold" size="16px" />
) : (
<Moon weight="bold" size="16px" />
)}
</Box>
{theme === "dark" ? <Sun size="15px" /> : <Moon size="15px" />}
</Button>
);
};

View File

@@ -9,24 +9,25 @@
"lint": "next lint"
},
"dependencies": {
"@mattjennings/react-modal": "^1.0.3",
"@monaco-editor/react": "^4.3.1",
"@octokit/core": "^3.5.1",
"@radix-ui/colors": "^0.1.7",
"@radix-ui/react-alert-dialog": "^0.1.1",
"@radix-ui/react-dialog": "^0.1.1",
"@radix-ui/react-dropdown-menu": "^0.1.1",
"@stitches/react": "^1.2.5",
"@theme-ui/color": "^0.11.3",
"@theme-ui/match-media": "^0.11.3",
"@radix-ui/react-id": "^0.1.1",
"@stitches/react": "^1.2.6-0",
"monaco-editor": "^0.29.1",
"next": "^12.0.4",
"next-auth": "^4.0.0-beta.5",
"next-themes": "^0.0.15",
"octokit": "^1.7.0",
"phosphor-react": "^1.3.1",
"re-resizable": "^6.9.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-hot-toast": "^2.1.1",
"react-new-window": "^0.2.1",
"valtio": "^1.2.5"
},
"devDependencies": {

View File

@@ -1,30 +1,67 @@
import { useEffect } from "react";
import "../styles/globals.css";
import type { AppProps } from "next/app";
import Head from "next/head";
import { SessionProvider } from "next-auth/react";
import { ThemeProvider } from "next-themes";
import { Toaster } from "react-hot-toast";
import { useRouter } from "next/router";
import { IdProvider } from "@radix-ui/react-id";
import { darkTheme } from "../stitches.config";
import { darkTheme, css } from "../stitches.config";
import Navigation from "../components/Navigation";
import { fetchFiles, state } from "../state";
function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
const router = useRouter();
const slug = router.query?.slug;
const gistId = (Array.isArray(slug) && slug[0]) ?? null;
useEffect(() => {
if (router.pathname.includes("/develop")) {
if (gistId && router.isReady) {
fetchFiles(gistId);
} else {
if (!gistId && router.isReady) {
state.mainModalOpen = true;
}
}
}
}, [gistId, router.isReady, router.pathname]);
return (
<>
<SessionProvider session={session}>
<ThemeProvider
attribute="class"
defaultTheme="dark"
enableSystem={false}
value={{
light: "light",
dark: darkTheme.className,
}}
>
<Navigation />
<Component {...pageProps} />
<Toaster />
</ThemeProvider>
</SessionProvider>
<Head>
<title>XRPL Hooks Playground</title>
</Head>
<IdProvider>
<SessionProvider session={session}>
<ThemeProvider
attribute="class"
defaultTheme="dark"
enableSystem={false}
value={{
light: "light",
dark: darkTheme.className,
}}
>
<Navigation />
<Component {...pageProps} />
<Toaster
toastOptions={{
className: css({
backgroundColor: "$mauve1",
color: "$mauve10",
fontSize: "$sm",
".dark &": {
backgroundColor: "$mauve4",
color: "$mauve12",
},
})(),
}}
/>
</ThemeProvider>
</SessionProvider>
</IdProvider>
</>
);
}

View File

@@ -1,11 +1,9 @@
import type { NextRequest, NextFetchEvent } from 'next/server';
import { NextResponse as Response } from 'next/server';
import { getToken } from "next-auth/jwt"
export default function middleware(req: NextRequest, ev: NextFetchEvent) {
if (req.nextUrl.pathname === "/") {
console.log('kissa', ev);
return Response.redirect("/develop");
}

View File

@@ -1,5 +1,4 @@
import NextAuth from "next-auth"
import GithubProvider from "next-auth/providers/github"
export default NextAuth({
// Configure one or more authentication providers
@@ -42,7 +41,6 @@ export default NextAuth({
},
async session({ session, token }) {
session.accessToken = token.accessToken as string;
const user = { ...session.user, username: token.username };
session['user']['username'] = token.username as string;
return session
}

View File

@@ -0,0 +1,9 @@
import Container from "../../components/Container";
const Deploy = () => {
return (
<Container css={{ py: "$10" }}>This will be the deploy page</Container>
);
};
export default Deploy;

View File

@@ -1,12 +0,0 @@
import { useSnapshot } from "valtio";
import Container from "../../components/Container";
import { state } from "../../state";
const Deploy = () => {
const snap = useSnapshot(state);
return (
<Container>This will be the deploy page {JSON.stringify(snap)}</Container>
);
};
export default Deploy;

View File

@@ -0,0 +1,24 @@
import dynamic from "next/dynamic";
import type { NextPage } from "next";
const HooksEditor = dynamic(() => import("../../components/HooksEditor"), {
ssr: false,
});
const Footer = dynamic(() => import("../../components/Footer"), {
ssr: false,
});
const Home: NextPage = () => {
return (
<>
<main style={{ display: "flex", flex: 1 }}>
<HooksEditor />
</main>
<Footer />
</>
);
};
export default Home;

View File

@@ -1,39 +0,0 @@
import type { NextPage } from "next";
import Head from "next/head";
import dynamic from "next/dynamic";
import Footer from "../../components/Footer";
const HooksEditor = dynamic(() => import("../../components/HooksEditor"), {
ssr: false,
});
const Home: NextPage = () => {
return (
<>
<Head>
<title>XRPL Hooks Playground</title>
</Head>
<main style={{ display: "flex", flex: 1 }}>
<HooksEditor />
</main>
<Footer />
</>
);
};
export default Home;
// export const getStaticPaths: GetStaticPaths = async () => {
// // ...
// return { paths: [], fallback: "blocking" };
// };
// export const getStaticProps: GetStaticProps = async (context) => {
// // ...
// return {
// props: {},
// revalidate: 60,
// };
// };

5
pages/index.tsx Normal file
View File

@@ -0,0 +1,5 @@
const Home = () => {
return <p>homepage</p>;
};
export default Home;

38
pages/sign-in.tsx Normal file
View File

@@ -0,0 +1,38 @@
import { useEffect } from "react";
import { signIn, useSession } from "next-auth/react";
import Box from "../components/Box";
import Spinner from "../components/Spinner";
const SignInPage = () => {
const { data: session, status } = useSession();
useEffect(() => {
if (status !== "loading" && !session)
void signIn("github", { redirect: false });
if (status !== "loading" && session) window.close();
}, [session, status]);
return (
<Box
css={{
display: "flex",
backgroundColor: "$mauve1",
position: "absolute",
top: 0,
right: 0,
bottom: 0,
left: 0,
zIndex: 9999,
textAlign: "center",
justifyContent: "center",
alignItems: "center",
gap: "$2",
}}
>
Logging in <Spinner />
</Box>
);
};
export default SignInPage;

View File

@@ -1,7 +1,7 @@
import Container from "../../components/Container";
const Test = () => {
return <Container>This will be the test page</Container>;
return <Container css={{ py: "$10" }}>This will be the test page</Container>;
};
export default Test;

9
public/pattern-2.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 138 KiB

9
public/pattern.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 134 KiB

166
state.ts
View File

@@ -1,21 +1,27 @@
import { proxy, subscribe } from 'valtio';
import { proxy } from 'valtio';
import { devtools } from 'valtio/utils';
import { Octokit } from '@octokit/core';
import type monaco from 'monaco-editor';
import toast from 'react-hot-toast';
import Router from 'next/router';
import type { Session } from 'next-auth';
const octokit = new Octokit();
interface File {
name: string,
language: string,
content: string
name: string;
language: string;
content: string;
}
interface IState {
files: File[],
gistId?: string | null,
gistOwner?: string | null,
gistName?: string | null,
active: number;
loading: boolean;
gistLoading: boolean;
compiling: boolean;
logs: {
type: 'error' | 'warning' | 'log',
@@ -24,10 +30,11 @@ interface IState {
editorCtx?: typeof monaco.editor;
editorSettings: {
tabSize: number;
}
},
mainModalOpen: boolean;
}
let localStorageState: null | string = null;
// let localStorageState: null | string = null;
let initialState = {
files: [],
active: 0,
@@ -35,30 +42,38 @@ let initialState = {
compiling: false,
logs: [],
editorCtx: undefined,
gistId: undefined,
gistOwner: undefined,
gistName: undefined,
gistLoading: false,
editorSettings: {
tabSize: 2
}
},
mainModalOpen: false
}
if (typeof window !== 'undefined') {
try {
localStorageState = localStorage.getItem('hooksIdeState');
} catch (err) {
console.log(`localStorage state broken`);
localStorage.removeItem('hooksIdeState');
}
}
if (localStorageState) {
initialState = JSON.parse(localStorageState);
}
// Check if there's a persited state in localStorage
// if (typeof window !== 'undefined') {
// try {
// localStorageState = localStorage.getItem('hooksIdeState');
// } catch (err) {
// console.log(`localStorage state broken`);
// localStorage.removeItem('hooksIdeState');
// }
// }
// if (localStorageState) {
// initialState = JSON.parse(localStorageState);
// }
// Initialize state
export const state = proxy<IState>(initialState);
export const state = proxy<IState>({ ...initialState, logs: [] });
// Fetch content from Githug Gists
export const fetchFiles = (gistId: string) => {
state.loading = true;
if (gistId) {
state.logs.push({ type: 'log', message: `Fetching Gist with id: ${gistId}` });
octokit.request("GET /gists/{gist_id}", { gist_id: gistId }).then(res => {
if (res.data.files && Object.keys(res.data.files).length > 0) {
const files = Object.keys(res.data.files).map(filename => ({
@@ -70,11 +85,17 @@ export const fetchFiles = (gistId: string) => {
if (files.length > 0) {
state.logs.push({ type: 'log', message: 'Fetched successfully ✅' })
state.files = files;
state.gistId = gistId;
state.gistName = Object.keys(res.data.files)?.[0] || 'untitled';
state.gistOwner = res.data.owner?.login;
return
} else {
// Open main modal if now files
state.mainModalOpen = true;
}
return
return Router.push({ pathname: '/develop' })
}
state.loading = false;
}).catch(err => {
state.loading = false;
state.logs.push({ type: 'error', message: `Couldn't find Gist with id: ${gistId}` })
@@ -86,9 +107,72 @@ export const fetchFiles = (gistId: string) => {
// return state.files = initFiles
}
export const syncToGist = async (session?: Session | null, createNewGist?: boolean) => {
let files: Record<string, { filename: string, content: string }> = {};
state.gistLoading = true;
if (!session || !session.user) {
state.gistLoading = false;
return toast.error('You need to be logged in!')
}
const toastId = toast.loading('Pushing to Gist');
if (!state.files || !state.files.length) {
state.gistLoading = false;
return toast.error(`You need to create some files we can push to gist`, { id: toastId })
}
if (state.gistId && session?.user.username === state.gistOwner && !createNewGist) {
const currentFilesRes = await octokit.request("GET /gists/{gist_id}", { gist_id: state.gistId });
if (currentFilesRes.data.files) {
Object.keys(currentFilesRes?.data?.files).forEach(filename => {
files[`${filename}`] = { filename, content: "" }
})
}
state.files.forEach(file => {
files[`${file.name}`] = { filename: file.name, content: file.content }
})
// Update existing Gist
octokit.request("PATCH /gists/{gist_id}", {
gist_id: state.gistId, files, headers: {
authorization: `token ${session?.accessToken || ''}`
}
}).then(res => {
state.gistLoading = false;
return toast.success('Updated to gist successfully!', { id: toastId })
}).catch(err => {
console.log(err);
state.gistLoading = false;
return toast.error(`Could not update Gist, try again later!`, { id: toastId })
})
} else {
// Not Gist of the current user or it isn't Gist yet
state.files.forEach(file => {
files[`${file.name}`] = { filename: file.name, content: file.content }
})
octokit.request("POST /gists", {
files,
public: true,
headers: {
authorization: `token ${session?.accessToken || ''}`
}
}).then(res => {
state.gistLoading = false;
state.gistOwner = res.data.owner?.login;
state.gistId = res.data.id;
state.gistName = Array.isArray(res.data.files) ? Object.keys(res.data?.files)?.[0] : 'Untitled';
Router.push({ pathname: `/develop/${res.data.id}` })
return toast.success('Created new gist successfully!', { id: toastId })
}).catch(err => {
console.log(err);
state.gistLoading = false;
return toast.error(`Could not create Gist, try again later!`, { id: toastId })
})
}
}
export const updateEditorSettings = (editorSettings: IState['editorSettings']) => {
state.editorCtx?.getModels().forEach(model => {
console.log(model.uri)
model.updateOptions({
...editorSettings
})
@@ -97,11 +181,17 @@ export const updateEditorSettings = (editorSettings: IState['editorSettings']) =
}
export const saveFile = (value: string) => {
const editorModels = state.editorCtx?.getModels();
const currentModel = editorModels?.find(editorModel => editorModel.uri.path === `/${state.files[state.active].name}`);
if (state.files.length > 0) {
state.files[state.active].content = currentModel?.getValue() || '';
}
toast.success('Saved successfully', { position: 'bottom-center' })
}
export const createNewFile = (name: string) => {
state.files.push({ name, language: 'c', content: "" })
const emptyFile: File = { name, language: 'c', content: "" };
state.files.push(emptyFile)
state.active = state.files.length - 1;
}
@@ -134,17 +224,31 @@ export const compileCode = async (activeId: number) => {
});
const json = await res.json();
state.compiling = false;
toast.success('Compiled successfully!');
console.log(json)
} catch {
if (!json.success) {
state.logs.push({ type: 'error', message: json.message })
if (json.tasks && json.tasks.length > 0) {
json.tasks.forEach((task: any) => {
if (!task.success) {
state.logs.push({ type: 'error', message: task?.console })
}
})
}
return toast.error(`Couldn't compile!`, { position: 'bottom-center' });
}
state.logs.push({ type: 'log', message: 'Compiled successfully ✅' })
toast.success('Compiled successfully!', { position: 'bottom-center' });
} catch (err) {
console.log(err)
state.logs.push({ type: 'error', message: 'Error occured while compiling!' })
state.compiling = false;
}
}
const unsub = devtools(state, 'Files State');
if (process.env.NODE_ENV !== 'production') {
devtools(state, 'Files State');
}
subscribe(state, () => {
const { editorCtx, ...storedState } = state;
localStorage.setItem('hooksIdeState', JSON.stringify(storedState))
});
// subscribe(state, () => {
// const { editorCtx, ...storedState } = state;
// localStorage.setItem('hooksIdeState', JSON.stringify(storedState))
// });

View File

@@ -8,6 +8,7 @@ import {
green,
plum,
slate,
mauve,
pink,
yellow,
grayDark,
@@ -16,8 +17,9 @@ import {
greenDark,
plumDark,
slateDark,
mauveDark,
pinkDark,
yellowDark
yellowDark,
} from '@radix-ui/colors';
export const {
@@ -38,6 +40,7 @@ export const {
...green,
...plum,
...slate,
...mauve,
...pink,
...yellow,
background: "$gray1",
@@ -188,7 +191,7 @@ export const {
},
fontWeights: {
body: 400,
heading: 400,
heading: 700,
bold: 700,
},
lineHeights: {
@@ -293,6 +296,7 @@ export const darkTheme = createTheme('dark', {
...greenDark,
...plumDark,
...slateDark,
...mauveDark,
...pinkDark,
...yellowDark
},
@@ -300,5 +304,12 @@ export const darkTheme = createTheme('dark', {
export const globalStyles = globalCss({
// body: { backgroundColor: '$background', color: '$text', fontFamily: 'Helvetica' },
'html, body': { backgroundColor: '$gray1', color: '$gray12', fontFamily: '$body', fontSize: '$md' },
'html, body': {
backgroundColor: '$gray1',
color: '$gray12',
fontFamily: '$body',
fontSize: '$md',
'-webkit-font-smoothing': 'antialiased',
'-moz-osx-font-smoothing': 'grayscale'
},
});

View File

@@ -182,7 +182,7 @@
],
"colors": {
"editor.foreground": "#D0D0FF",
"editor.background": "#202425",
"editor.background": "#232326",
"editor.selectionBackground": "#ffffff30",
"editor.lineHighlightBackground": "#ffffff20",
"editorCursor.foreground": "#7070FF",

View File

@@ -89,7 +89,7 @@
],
"colors": {
"editor.foreground": "#000000",
"editor.background": "#f1f3f5",
"editor.background": "#f4f2f4",
"editor.selectionBackground": "#B5D5FF",
"editor.lineHighlightBackground": "#00000012",
"editorCursor.foreground": "#000000",

View File

@@ -17,7 +17,8 @@
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true
"incremental": true,
"noUnusedLocals": true
},
"include": [
"next-env.d.ts",

8
utils/truncate.ts Normal file
View File

@@ -0,0 +1,8 @@
const truncate = (str: string, max: number = 8) => {
const array = str.trim().split('');
const ellipsis = array.length > max ? '...' : '';
return array.slice(0, max).join('') + ellipsis;
};
export default truncate

179
yarn.lock
View File

@@ -43,7 +43,7 @@
core-js-pure "^3.16.0"
regenerator-runtime "^0.13.4"
"@babel/runtime@7.15.4", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.0", "@babel/runtime@^7.15.4":
"@babel/runtime@7.15.4", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.13.10", "@babel/runtime@^7.15.4":
version "7.15.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a"
integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==
@@ -58,71 +58,6 @@
"@babel/helper-validator-identifier" "^7.14.9"
to-fast-properties "^2.0.0"
"@emotion/cache@^11.5.0":
version "11.5.0"
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.5.0.tgz#a5eb78cbef8163939ee345e3ddf0af217b845e62"
integrity sha512-mAZ5QRpLriBtaj/k2qyrXwck6yeoz1V5lMt/jfj6igWU35yYlNKs2LziXVgvH81gnJZ+9QQNGelSsnuoAy6uIw==
dependencies:
"@emotion/memoize" "^0.7.4"
"@emotion/sheet" "^1.0.3"
"@emotion/utils" "^1.0.0"
"@emotion/weak-memoize" "^0.2.5"
stylis "^4.0.10"
"@emotion/hash@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
"@emotion/memoize@^0.7.4":
version "0.7.5"
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.5.tgz#2c40f81449a4e554e9fc6396910ed4843ec2be50"
integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==
"@emotion/react@^11.1.1":
version "11.5.0"
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.5.0.tgz#19b5771bbfbda5e8517e948a2d9064810f0022bd"
integrity sha512-MYq/bzp3rYbee4EMBORCn4duPQfgpiEB5XzrZEBnUZAL80Qdfr7CEv/T80jwaTl/dnZmt9SnTa8NkTrwFNpLlw==
dependencies:
"@babel/runtime" "^7.13.10"
"@emotion/cache" "^11.5.0"
"@emotion/serialize" "^1.0.2"
"@emotion/sheet" "^1.0.3"
"@emotion/utils" "^1.0.0"
"@emotion/weak-memoize" "^0.2.5"
hoist-non-react-statics "^3.3.1"
"@emotion/serialize@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.2.tgz#77cb21a0571c9f68eb66087754a65fa97bfcd965"
integrity sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==
dependencies:
"@emotion/hash" "^0.8.0"
"@emotion/memoize" "^0.7.4"
"@emotion/unitless" "^0.7.5"
"@emotion/utils" "^1.0.0"
csstype "^3.0.2"
"@emotion/sheet@^1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.0.3.tgz#00c326cd7985c5ccb8fe2c1b592886579dcfab8f"
integrity sha512-YoX5GyQ4db7LpbmXHMuc8kebtBGP6nZfRC5Z13OKJMixBEwdZrJ914D6yJv/P+ZH/YY3F5s89NYX2hlZAf3SRQ==
"@emotion/unitless@^0.7.5":
version "0.7.5"
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
"@emotion/utils@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.0.0.tgz#abe06a83160b10570816c913990245813a2fd6af"
integrity sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA==
"@emotion/weak-memoize@^0.2.5":
version "0.2.5"
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
"@eslint/eslintrc@^0.4.3":
version "0.4.3"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c"
@@ -172,14 +107,6 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf"
integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==
"@mattjennings/react-modal@^1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@mattjennings/react-modal/-/react-modal-1.0.3.tgz#f796c5238c6d799290edee2884f377aa2bc2902e"
integrity sha512-k3Kyhrt35fshWXa7jqAWCivf/aM5T5oM1kxc9zCsb3W87arRtt0s1pY43hQeV149OV3Wh9Tn+2xlgAq+Tsj0HQ==
dependencies:
"@theme-ui/match-media" "^0.3.1"
react-scrolllock "^5.0.1"
"@monaco-editor/loader@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.2.0.tgz#373fad69973384624e3d9b60eefd786461a76acd"
@@ -560,6 +487,18 @@
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-alert-dialog@^0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-alert-dialog/-/react-alert-dialog-0.1.1.tgz#979dc343bc2766b0049e4940a49867a3c4b002f8"
integrity sha512-2oiBy38KK/hZZFpAsjUibxjbKv+HXwsGQUcKKXZH5/D+9BPScKcF192r7aAbQer3NNMHuPEOGuGeYKWegUEzTA==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "0.1.0"
"@radix-ui/react-compose-refs" "0.1.0"
"@radix-ui/react-context" "0.1.1"
"@radix-ui/react-dialog" "0.1.1"
"@radix-ui/react-slot" "0.1.1"
"@radix-ui/react-arrow@0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-0.1.1.tgz#e8e05444b37b9f71bf712a8cd4dd07dbd419e749"
@@ -593,7 +532,7 @@
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-dialog@^0.1.1":
"@radix-ui/react-dialog@0.1.1", "@radix-ui/react-dialog@^0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-0.1.1.tgz#515bcda3ab6a3a9b1cc20237eedc38b6c5921a7d"
integrity sha512-+M/tY9n2/5yhpZrWankiVPJPFHkmgn4q+lXOeVRkMOFsyXQKhwhmPihYFUBk3BszsdKeV5BrZvdDpbWve3ZKKA==
@@ -657,7 +596,7 @@
"@radix-ui/react-primitive" "0.1.1"
"@radix-ui/react-use-callback-ref" "0.1.0"
"@radix-ui/react-id@0.1.1":
"@radix-ui/react-id@0.1.1", "@radix-ui/react-id@^0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-0.1.1.tgz#42c8f3967875e6824b2ac9d49c66317047c8d6ff"
integrity sha512-Vlg5me65+NUgxPBuA0Lk6FerNe+Mq4EuJ8xzpskGxS2t8p1puI3IkyLZ2wWtDSb1KXazoaHn8adBypagt+1P0g==
@@ -825,36 +764,10 @@
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.0.8.tgz#be3e914e84eacf16dbebd311c0d0b44aa1174c64"
integrity sha512-ZK5v4bJwgXldAUA8r3q9YKfCwOqoHTK/ZqRjSeRXQrBXWouoPnS4MQtgC4AXGiiBuUu5wxrRgTlv0ktmM4P1Aw==
"@stitches/react@^1.2.5":
version "1.2.5"
resolved "https://registry.yarnpkg.com/@stitches/react/-/react-1.2.5.tgz#2353343c220f0c59ba388a26fdd9ff7962cb6031"
integrity sha512-95Wwjp5cvoYQjg616OBiHZM1PnIF51pnFQIgSPxPzS/xXBrer9sNO1tfpVfLYfOifvuotse2IFNbypJ92BXzvg==
"@theme-ui/color@^0.11.3":
version "0.11.3"
resolved "https://registry.yarnpkg.com/@theme-ui/color/-/color-0.11.3.tgz#5f85aae99178e51e4d63b1d3cb3e31cd209e7c49"
integrity sha512-Z8c0RXVsphzhGWZDgPowwFTOLwpOhHQ4Oz+EbZFphRmXe+t6LXBhJ8vzRFEC4H5i+tV3xmunVVX7hwKlfdjO7Q==
dependencies:
"@theme-ui/css" "0.11.3"
polished "^4.0.5"
"@theme-ui/css@0.11.3":
version "0.11.3"
resolved "https://registry.yarnpkg.com/@theme-ui/css/-/css-0.11.3.tgz#9962db78b9d60a96b2f6497f858e6baa7aa0f5c5"
integrity sha512-b2vRZEc6ZBMYCVFqvSSrWdVbKgSJHOOTMow1czApJjrmZBygkG9DN0/1hgO9spDkmfp/tmRmBzrTlgFlE6pybA==
dependencies:
"@emotion/react" "^11.1.1"
csstype "^3.0.5"
"@theme-ui/match-media@^0.11.3":
version "0.11.3"
resolved "https://registry.yarnpkg.com/@theme-ui/match-media/-/match-media-0.11.3.tgz#86fa82b47eae00e0f9dd5ae995e1fe5c28b4e48f"
integrity sha512-UZXJ5DI/Q+3h9JtK0FFxEesfLUEP6JNXukng3SqprYcKcDBQRqVa1+Qc/pPkIklEK9li3/c0MBGGNHqKFMRnig==
"@theme-ui/match-media@^0.3.1":
version "0.3.5"
resolved "https://registry.yarnpkg.com/@theme-ui/match-media/-/match-media-0.3.5.tgz#e676b9f74e1f58608cfdb48ae8be69c4b7517109"
integrity sha512-twhqy3H++jP7TqCZSRvSSc1/hxqjVokX+G/gf5S6lYQj4+zaksTO/AyzBzb/6iJLLkN2WeL9P2yjCeSTMxdJGw==
"@stitches/react@^1.2.6-0":
version "1.2.6-0"
resolved "https://registry.yarnpkg.com/@stitches/react/-/react-1.2.6-0.tgz#ea7a1d35940f96337d18f5afd41aee60cbdcede7"
integrity sha512-hws7FTFTatR5RhLkg6lFWzvGXpZYNA9OqAA8HC/jCJDDvfEpN/8+9DjTX7k7/4oEWyaZBOfALAyHj/YrgScjxQ==
"@types/aws-lambda@^8.10.83":
version "8.10.84"
@@ -1505,7 +1418,7 @@ cssnano-simple@3.0.0:
dependencies:
cssnano-preset-simple "^3.0.0"
csstype@^3.0.2, csstype@^3.0.4, csstype@^3.0.5:
csstype@^3.0.2, csstype@^3.0.4:
version "3.0.9"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.9.tgz#6410af31b26bd0520933d02cbc64fce9ce3fbf0b"
integrity sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw==
@@ -1956,11 +1869,6 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
md5.js "^1.3.4"
safe-buffer "^5.1.1"
exenv@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
integrity sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -1987,6 +1895,11 @@ fast-levenshtein@^2.0.6:
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
fast-memoize@^2.5.1:
version "2.5.2"
resolved "https://registry.yarnpkg.com/fast-memoize/-/fast-memoize-2.5.2.tgz#79e3bb6a4ec867ea40ba0e7146816f6cdce9b57e"
integrity sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==
fastq@^1.6.0:
version "1.13.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c"
@@ -2239,13 +2152,6 @@ hmac-drbg@^1.0.1:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
hoist-non-react-statics@^3.3.1:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
dependencies:
react-is "^16.7.0"
http-errors@1.7.3:
version "1.7.3"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
@@ -3184,13 +3090,6 @@ platform@1.3.6:
resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7"
integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==
polished@^4.0.5:
version "4.1.3"
resolved "https://registry.yarnpkg.com/polished/-/polished-4.1.3.tgz#7a3abf2972364e7d97770b827eec9a9e64002cfc"
integrity sha512-ocPAcVBUOryJEKe0z2KLd1l9EBa1r5mSwlKpExmrLzsnIzJo4axsoU9O2BjOTkDGDT4mZ0WFE5XKTlR3nLnZOA==
dependencies:
"@babel/runtime" "^7.14.0"
postcss@8.2.15:
version "8.2.15"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.15.tgz#9e66ccf07292817d226fc315cbbf9bc148fbca65"
@@ -3305,6 +3204,13 @@ raw-body@2.4.1:
iconv-lite "0.4.24"
unpipe "1.0.0"
re-resizable@^6.9.1:
version "6.9.1"
resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.9.1.tgz#6be082b55d02364ca4bfee139e04feebdf52441c"
integrity sha512-KRYAgr9/j1PJ3K+t+MBhlQ+qkkoLDJ1rs0z1heIWvYbCW/9Vq4djDU+QumJ3hQbwwtzXF6OInla6rOx6hhgRhQ==
dependencies:
fast-memoize "^2.5.1"
react-dom@17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
@@ -3326,11 +3232,18 @@ react-is@17.0.2:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
react-is@^16.7.0, react-is@^16.8.1:
react-is@^16.8.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-new-window@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/react-new-window/-/react-new-window-0.2.1.tgz#510aa91ba0334f0d42ba499c617a37f46c4d84ba"
integrity sha512-3s6W8eipRbjoLGvWpFRPSt2/z5h9FTS01C+ZjaEZX7/cF6ejGc6JVuL4oEo0s73IluaqoLIDB49AK4fSMW8pJw==
dependencies:
prop-types "^15.7.2"
react-refresh@0.8.3:
version "0.8.3"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
@@ -3355,13 +3268,6 @@ react-remove-scroll@^2.4.0:
use-callback-ref "^1.2.3"
use-sidecar "^1.0.1"
react-scrolllock@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/react-scrolllock/-/react-scrolllock-5.0.1.tgz#da1cfb7b6d55c86ae41dbad5274b778c307752b7"
integrity sha512-poeEsjnZAlpA6fJlaNo4rZtcip2j6l5mUGU/SJe1FFlicEudS943++u7ZSdA7lk10hoyYK3grOD02/qqt5Lxhw==
dependencies:
exenv "^1.2.2"
react-style-singleton@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.1.1.tgz#ce7f90b67618be2b6b94902a30aaea152ce52e66"
@@ -3725,11 +3631,6 @@ stylis@3.5.4:
resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.4.tgz#f665f25f5e299cf3d64654ab949a57c768b73fbe"
integrity sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q==
stylis@^4.0.10:
version "4.0.10"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.10.tgz#446512d1097197ab3f02fb3c258358c3f7a14240"
integrity sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg==
supports-color@^5.3.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"