Merge pull request #28 from eqlabs/feature/initial-structure
Feature/initial structure
This commit is contained in:
@@ -1,3 +1,9 @@
|
||||
# XRPL Hooks IDE
|
||||
|
||||
This is the repository for XRPL Hooks IDE. This project is built with Next.JS
|
||||
|
||||
## General
|
||||
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
8
components/Box.tsx
Normal file
8
components/Box.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import { styled } from "../stitches.config";
|
||||
|
||||
const Box = styled("div", {
|
||||
// all: "unset",
|
||||
boxSizing: "border-box",
|
||||
});
|
||||
|
||||
export default Box;
|
||||
171
components/Button.tsx
Normal file
171
components/Button.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
import { styled } from "../stitches.config";
|
||||
|
||||
const Button = styled("button", {
|
||||
// Reset
|
||||
all: "unset",
|
||||
appereance: "none",
|
||||
fontFamily: "$body",
|
||||
alignItems: "center",
|
||||
boxSizing: "border-box",
|
||||
userSelect: "none",
|
||||
"&::before": {
|
||||
boxSizing: "border-box",
|
||||
},
|
||||
"&::after": {
|
||||
boxSizing: "border-box",
|
||||
},
|
||||
// Custom reset?
|
||||
display: "inline-flex",
|
||||
flexShrink: 0,
|
||||
justifyContent: "center",
|
||||
lineHeight: "1",
|
||||
gap: "5px",
|
||||
WebkitTapHighlightColor: "rgba(0,0,0,0)",
|
||||
// Custom
|
||||
height: "$6",
|
||||
px: "$2",
|
||||
fontSize: "$2",
|
||||
fontWeight: 500,
|
||||
fontVariantNumeric: "tabular-nums",
|
||||
backgroundColor: "red",
|
||||
cursor: "pointer",
|
||||
"&:disabled": {
|
||||
opacity: 0.8,
|
||||
pointerEvents: "none",
|
||||
},
|
||||
variants: {
|
||||
size: {
|
||||
sm: {
|
||||
borderRadius: "$sm",
|
||||
height: "$7",
|
||||
px: "$3",
|
||||
fontSize: "$xs",
|
||||
},
|
||||
md: {
|
||||
borderRadius: "$sm",
|
||||
height: "$8",
|
||||
px: "$3",
|
||||
fontSize: "$xs",
|
||||
},
|
||||
lg: {
|
||||
borderRadius: "$sm",
|
||||
height: "$10",
|
||||
px: "$4",
|
||||
fontSize: "$xs",
|
||||
},
|
||||
},
|
||||
variant: {
|
||||
default: {
|
||||
backgroundColor: "$slate12",
|
||||
boxShadow: "inset 0 0 0 1px $colors$slate12",
|
||||
color: "$slate1",
|
||||
"@hover": {
|
||||
"&:hover": {
|
||||
backgroundColor: "$slate12",
|
||||
boxShadow: "inset 0 0 0 1px $colors$slate12",
|
||||
},
|
||||
},
|
||||
"&:active": {
|
||||
backgroundColor: "$slate10",
|
||||
boxShadow: "inset 0 0 0 1px $colors$slate11",
|
||||
},
|
||||
"&:focus": {
|
||||
boxShadow:
|
||||
"inset 0 0 0 1px $colors$slate12, 0 0 0 1px $colors$slate12",
|
||||
},
|
||||
'&[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",
|
||||
},
|
||||
},
|
||||
primary: {
|
||||
backgroundColor: `$pink9`,
|
||||
boxShadow: "inset 0 0 0 1px $colors$pink9",
|
||||
color: "$white",
|
||||
"@hover": {
|
||||
"&:hover": {
|
||||
backgroundColor: "$pink10",
|
||||
boxShadow: "inset 0 0 0 1px $colors$pink11",
|
||||
},
|
||||
},
|
||||
"&:active": {
|
||||
backgroundColor: "$pink8",
|
||||
boxShadow: "inset 0 0 0 1px $colors$pink8",
|
||||
},
|
||||
"&:focus": {
|
||||
boxShadow: "inset 0 0 0 1px $colors$pink8",
|
||||
},
|
||||
'&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
|
||||
{
|
||||
backgroundColor: "$slate4",
|
||||
boxShadow: "inset 0 0 0 1px $colors$pink8",
|
||||
},
|
||||
},
|
||||
},
|
||||
outline: {
|
||||
true: {
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
},
|
||||
uppercase: {
|
||||
true: {
|
||||
textTransform: "uppercase",
|
||||
},
|
||||
},
|
||||
ghost: {
|
||||
true: {
|
||||
boxShadow: "none",
|
||||
background: "transparent",
|
||||
color: "$slate12",
|
||||
"@hover": {
|
||||
"&:hover": {
|
||||
backgroundColor: "$slate6",
|
||||
boxShadow: "none",
|
||||
},
|
||||
},
|
||||
"&:active": {
|
||||
backgroundColor: "$slate8",
|
||||
boxShadow: "none",
|
||||
},
|
||||
"&:focus": {
|
||||
boxShadow: "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
compoundVariants: [
|
||||
{
|
||||
outline: true,
|
||||
variant: "default",
|
||||
css: {
|
||||
background: "transparent",
|
||||
color: "$slate12",
|
||||
boxShadow: "inset 0 0 0 1px $colors$slate10",
|
||||
"&:hover": {
|
||||
color: "$slate12",
|
||||
background: "$slate5",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
outline: true,
|
||||
variant: "primary",
|
||||
css: {
|
||||
background: "transparent",
|
||||
color: "$slate12",
|
||||
"&:hover": {
|
||||
color: "$slate12",
|
||||
background: "$slate5",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
defaultVariants: {
|
||||
size: "md",
|
||||
variant: "default",
|
||||
},
|
||||
});
|
||||
|
||||
export default Button;
|
||||
12
components/Container.tsx
Normal file
12
components/Container.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import { styled } from "../stitches.config";
|
||||
import Box from "./Box";
|
||||
|
||||
const Container = styled(Box, {
|
||||
width: "100%",
|
||||
marginLeft: "auto",
|
||||
marginRight: "auto",
|
||||
px: "$4",
|
||||
maxWidth: "100%",
|
||||
});
|
||||
|
||||
export default Container;
|
||||
103
components/Dialog.tsx
Normal file
103
components/Dialog.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
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 * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||
import { styled } 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(DialogPrimitive.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.blackA9,
|
||||
},
|
||||
});
|
||||
|
||||
const StyledContent = styled(DialogPrimitive.Content, {
|
||||
zIndex: 1000,
|
||||
backgroundColor: "$slate2",
|
||||
color: "$slate12",
|
||||
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: "$slate5",
|
||||
boxShadow:
|
||||
"0px 10px 38px 0px rgba(22, 23, 24, 0.85), 0px 10px 20px 0px rgba(22, 23, 24, 0.6)",
|
||||
},
|
||||
});
|
||||
|
||||
const Content: React.FC<{ css?: Stiches.CSS }> = ({ css, children }) => {
|
||||
return (
|
||||
<div>
|
||||
<StyledOverlay />
|
||||
<StyledContent css={css}>{children}</StyledContent>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const StyledTitle = styled(DialogPrimitive.Title, {
|
||||
margin: 0,
|
||||
fontWeight: 500,
|
||||
color: "$slate12",
|
||||
fontSize: 17,
|
||||
});
|
||||
|
||||
const StyledDescription = styled(DialogPrimitive.Description, {
|
||||
margin: "10px 0 20px",
|
||||
color: "$slate11",
|
||||
fontSize: 15,
|
||||
lineHeight: 1.5,
|
||||
});
|
||||
|
||||
// Exports
|
||||
export const Dialog = 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}` },
|
||||
});
|
||||
@@ -1,17 +1,8 @@
|
||||
/** @jsxImportSource theme-ui */
|
||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||
import { ForwardRefExoticComponent, RefAttributes } from "react";
|
||||
import { keyframes } from "@emotion/react";
|
||||
import { ThemeUIStyleObject, jsx } from "theme-ui";
|
||||
import { theme } from "../theme";
|
||||
import { keyframes } from "@stitches/react";
|
||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
||||
|
||||
interface StyledProps {
|
||||
sx?: ThemeUIStyleObject;
|
||||
}
|
||||
|
||||
const Root = DropdownMenu.Root;
|
||||
|
||||
const Trigger = DropdownMenu.Trigger;
|
||||
import { styled } from "../stitches.config";
|
||||
import { blackA, slateDark } from "@radix-ui/colors";
|
||||
|
||||
const slideUpAndFade = keyframes({
|
||||
"0%": { opacity: 0, transform: "translateY(2px)" },
|
||||
@@ -33,123 +24,108 @@ const slideLeftAndFade = keyframes({
|
||||
"100%": { opacity: 1, transform: "translateX(0)" },
|
||||
});
|
||||
|
||||
const bounce = keyframes`
|
||||
from, 20%, 53%, 80%, to {
|
||||
transform: translate3d(0,0,0);
|
||||
}
|
||||
|
||||
40%, 43% {
|
||||
transform: translate3d(0, -30px, 0);
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: translate3d(0, -15px, 0);
|
||||
}
|
||||
|
||||
90% {
|
||||
transform: translate3d(0,-4px,0);
|
||||
}
|
||||
`;
|
||||
|
||||
const animationTypeOne = keyframes({
|
||||
"0%": {
|
||||
opacity: 1,
|
||||
const StyledContent = styled(DropdownMenuPrimitive.Content, {
|
||||
minWidth: 220,
|
||||
backgroundColor: "$slate2",
|
||||
borderRadius: 6,
|
||||
padding: 5,
|
||||
boxShadow:
|
||||
"0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2)",
|
||||
"@media (prefers-reduced-motion: no-preference)": {
|
||||
animationDuration: "400ms",
|
||||
animationTimingFunction: "cubic-bezier(0.16, 1, 0.3, 1)",
|
||||
willChange: "transform, opacity",
|
||||
'&[data-state="open"]': {
|
||||
'&[data-side="top"]': { animationName: slideDownAndFade },
|
||||
'&[data-side="right"]': { animationName: slideLeftAndFade },
|
||||
'&[data-side="bottom"]': { animationName: slideUpAndFade },
|
||||
'&[data-side="left"]': { animationName: slideRightAndFade },
|
||||
},
|
||||
},
|
||||
"20%": {
|
||||
opacity: 0,
|
||||
},
|
||||
"100%": {
|
||||
opacity: 1,
|
||||
".dark &": {
|
||||
backgroundColor: "$slate5",
|
||||
boxShadow:
|
||||
"0px 10px 38px -10px rgba(22, 23, 24, 0.85), 0px 10px 20px -15px rgba(22, 23, 24, 0.6)",
|
||||
},
|
||||
});
|
||||
|
||||
const fadeIn = keyframes({ from: { opacity: 0 }, to: { opacity: 1 } });
|
||||
|
||||
const itemStyles: ThemeUIStyleObject = {
|
||||
const itemStyles = {
|
||||
all: "unset",
|
||||
fontSize: 1,
|
||||
fontSize: 13,
|
||||
lineHeight: 1,
|
||||
color: (theme) => theme.rawColors?.modes?.light?.text,
|
||||
color: "$slate12",
|
||||
borderRadius: 3,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
height: "auto",
|
||||
padding: "10px 5px",
|
||||
height: 32,
|
||||
padding: "0 5px",
|
||||
position: "relative",
|
||||
paddingLeft: 2,
|
||||
paddingLeft: "5px",
|
||||
userSelect: "none",
|
||||
gap: "$2",
|
||||
|
||||
"&[data-disabled]": {
|
||||
color: (theme) => theme.rawColors?.modes?.light.muted,
|
||||
color: "$slate9",
|
||||
pointerEvents: "none",
|
||||
},
|
||||
|
||||
"&:focus": {
|
||||
backgroundColor: (theme) => theme.rawColors?.modes?.light?.text,
|
||||
color: (theme) => theme.rawColors?.modes?.light?.background,
|
||||
backgroundColor: "$pink9",
|
||||
color: "$white",
|
||||
},
|
||||
};
|
||||
|
||||
const Content = (props: DropdownMenu.DropdownMenuContentProps) => (
|
||||
<DropdownMenu.Content
|
||||
{...props}
|
||||
sx={{
|
||||
minWidth: 220,
|
||||
backgroundColor: (theme) => theme.rawColors?.modes?.light?.background,
|
||||
borderRadius: 6,
|
||||
padding: 1,
|
||||
boxShadow:
|
||||
"0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2)",
|
||||
"@media (prefers-reduced-motion: no-preference)": {
|
||||
animationDuration: "400ms",
|
||||
animationTimingFunction: "cubic-bezier(0.16, 1, 0.3, 1)",
|
||||
willChange: "transform, opacity",
|
||||
'&[data-state="open"]': {
|
||||
'&[data-side="top"]': {
|
||||
animationName: slideDownAndFade.toString(),
|
||||
},
|
||||
'&[data-side="right"]': {
|
||||
animationName: slideLeftAndFade.toString(),
|
||||
},
|
||||
'&[data-side="bottom"]': {
|
||||
animationName: slideUpAndFade.toString(),
|
||||
},
|
||||
'&[data-side="left"]': {
|
||||
animationName: slideRightAndFade.toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
const StyledItem = styled(DropdownMenuPrimitive.Item, { ...itemStyles });
|
||||
const StyledCheckboxItem = styled(DropdownMenuPrimitive.CheckboxItem, {
|
||||
...itemStyles,
|
||||
});
|
||||
const StyledRadioItem = styled(DropdownMenuPrimitive.RadioItem, {
|
||||
...itemStyles,
|
||||
});
|
||||
const StyledTriggerItem = styled(DropdownMenuPrimitive.TriggerItem, {
|
||||
'&[data-state="open"]': {
|
||||
backgroundColor: "$pink9",
|
||||
color: "$pink9",
|
||||
},
|
||||
...itemStyles,
|
||||
});
|
||||
|
||||
const Item = (props: DropdownMenu.MenuItemProps) => (
|
||||
<DropdownMenu.Item {...props} sx={{ ...itemStyles }} />
|
||||
);
|
||||
const StyledLabel = styled(DropdownMenuPrimitive.Label, {
|
||||
paddingLeft: 25,
|
||||
fontSize: 12,
|
||||
lineHeight: "25px",
|
||||
color: "$slate11",
|
||||
});
|
||||
|
||||
const Label = (props: DropdownMenu.MenuLabelProps) => (
|
||||
<DropdownMenu.Label {...props} sx={{ ...itemStyles }} />
|
||||
);
|
||||
const StyledSeparator = styled(DropdownMenuPrimitive.Separator, {
|
||||
height: 1,
|
||||
backgroundColor: "$slate7",
|
||||
margin: 5,
|
||||
});
|
||||
|
||||
const Group = DropdownMenu.Group;
|
||||
const Separator = DropdownMenu.Separator;
|
||||
const StyledItemIndicator = styled(DropdownMenuPrimitive.ItemIndicator, {
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
width: 25,
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
});
|
||||
|
||||
const Arrow = (props: DropdownMenu.MenuArrowProps) => (
|
||||
<DropdownMenu.Arrow
|
||||
{...props}
|
||||
sx={{
|
||||
fill: (theme) => theme.rawColors?.modes?.light.background,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
const StyledArrow = styled(DropdownMenuPrimitive.Arrow, {
|
||||
fill: "$slate2",
|
||||
});
|
||||
|
||||
const Dropdown = {
|
||||
Root,
|
||||
Label,
|
||||
Trigger,
|
||||
Content,
|
||||
Item,
|
||||
Arrow,
|
||||
Group,
|
||||
Separator,
|
||||
};
|
||||
|
||||
export default Dropdown;
|
||||
// Exports
|
||||
export const DropdownMenu = DropdownMenuPrimitive.Root;
|
||||
export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
||||
export const DropdownMenuContent = StyledContent;
|
||||
export const DropdownMenuItem = StyledItem;
|
||||
export const DropdownMenuCheckboxItem = StyledCheckboxItem;
|
||||
export const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
||||
export const DropdownMenuRadioItem = StyledRadioItem;
|
||||
export const DropdownMenuItemIndicator = StyledItemIndicator;
|
||||
export const DropdownMenuTriggerItem = StyledTriggerItem;
|
||||
export const DropdownMenuLabel = StyledLabel;
|
||||
export const DropdownMenuSeparator = StyledSeparator;
|
||||
export const DropdownMenuArrow = StyledArrow;
|
||||
|
||||
234
components/EditorNavigation.tsx
Normal file
234
components/EditorNavigation.tsx
Normal file
@@ -0,0 +1,234 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import { Plus, Share, DownloadSimple, Gear, X } from "phosphor-react";
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
import { createNewFile, state, updateEditorSettings } from "../state";
|
||||
import Box from "./Box";
|
||||
import Button from "./Button";
|
||||
import Container from "./Container";
|
||||
import {
|
||||
Dialog,
|
||||
DialogTrigger,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
DialogClose,
|
||||
} 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";
|
||||
|
||||
const EditorNavigation = () => {
|
||||
const snap = useSnapshot(state);
|
||||
const [filename, setFilename] = useState("");
|
||||
const { theme } = useTheme();
|
||||
const { data: session, status } = useSession();
|
||||
const router = useRouter();
|
||||
const [editorSettings, setEditorSettings] = useState(snap.editorSettings);
|
||||
return (
|
||||
<Flex css={{ flexShrink: 0, gap: "$0" }}>
|
||||
<Flex css={{ overflowX: "scroll", py: "$3", flex: 1 }}>
|
||||
<Container css={{ flex: 1 }}>
|
||||
<Stack css={{ gap: "$3", flex: 1, flexWrap: "nowrap" }}>
|
||||
{state.loading && "loading"}
|
||||
{snap.files &&
|
||||
snap.files.length > 0 &&
|
||||
snap.files?.map((file, index) => (
|
||||
<Button
|
||||
size="sm"
|
||||
outline={snap.active !== index}
|
||||
onClick={() => (state.active = index)}
|
||||
key={file.name}
|
||||
css={{
|
||||
"&:hover": {
|
||||
span: {
|
||||
visibility: "visible",
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
{file.name}
|
||||
<Box
|
||||
as="span"
|
||||
css={{
|
||||
display: "flex",
|
||||
p: "1px",
|
||||
borderRadius: "$full",
|
||||
mr: "-4px",
|
||||
}}
|
||||
onClick={() => state.files.splice(index, 1)}
|
||||
>
|
||||
<X size="13px" />
|
||||
</Box>
|
||||
</Button>
|
||||
))}
|
||||
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
ghost
|
||||
size="sm"
|
||||
css={{ alignItems: "center", px: "$2", mr: "$3" }}
|
||||
>
|
||||
<Plus size="16px" />{" "}
|
||||
{snap.files.length === 0 && "Add new file"}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogTitle>Create new file</DialogTitle>
|
||||
<DialogDescription>
|
||||
<span>
|
||||
Create empty C file or select one of the existing ones
|
||||
</span>
|
||||
<input
|
||||
value={filename}
|
||||
onChange={(e) => setFilename(e.target.value)}
|
||||
/>
|
||||
</DialogDescription>
|
||||
|
||||
<Flex
|
||||
css={{ marginTop: 25, justifyContent: "flex-end", gap: "$3" }}
|
||||
>
|
||||
<DialogClose asChild>
|
||||
<Button outline>Cancel</Button>
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
createNewFile(filename);
|
||||
// reset
|
||||
setFilename("");
|
||||
}}
|
||||
>
|
||||
Create file
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</Flex>
|
||||
<DialogClose asChild>
|
||||
<Box css={{ position: "absolute", top: "$3", right: "$3" }}>
|
||||
<X size="20px" />
|
||||
</Box>
|
||||
</DialogClose>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</Stack>
|
||||
</Container>
|
||||
</Flex>
|
||||
<Flex
|
||||
css={{
|
||||
py: "$3",
|
||||
backgroundColor: "$slate3",
|
||||
zIndex: 1,
|
||||
}}
|
||||
>
|
||||
<Container css={{ width: "unset" }}>
|
||||
<Stack
|
||||
css={{
|
||||
display: "inline-flex",
|
||||
marginLeft: "auto",
|
||||
flexShrink: 0,
|
||||
gap: "$0",
|
||||
border: "1px solid $slate10",
|
||||
borderRadius: "$sm",
|
||||
zIndex: 9,
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
button: {
|
||||
borderRadius: "$0",
|
||||
px: "$2",
|
||||
alignSelf: "flex-start",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Button ghost size="sm" css={{ alignItems: "center" }}>
|
||||
<DownloadSimple size="16px" />
|
||||
</Button>
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button ghost size="sm" css={{ alignItems: "center" }}>
|
||||
<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" }}
|
||||
>
|
||||
<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" }}
|
||||
>
|
||||
<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>
|
||||
</Stack>
|
||||
</Container>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditorNavigation;
|
||||
8
components/Flex.tsx
Normal file
8
components/Flex.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import { styled } from "../stitches.config";
|
||||
import Box from "./Box";
|
||||
|
||||
const Flex = styled(Box, {
|
||||
display: "flex",
|
||||
});
|
||||
|
||||
export default Flex;
|
||||
@@ -1,29 +1,49 @@
|
||||
import { Box } from "theme-ui";
|
||||
import { useSnapshot } from "valtio";
|
||||
import Container from "./Container";
|
||||
import Box from "./Box";
|
||||
|
||||
import LogText from "./LogText";
|
||||
import { state } from "../state";
|
||||
|
||||
const Footer = () => {
|
||||
const snap = useSnapshot(state);
|
||||
return (
|
||||
<Box as="footer" sx={{ display: "flex" }}>
|
||||
<Box
|
||||
as="pre"
|
||||
sx={{
|
||||
borderRadius: "6px",
|
||||
backgroundColor: "#242426",
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
height: "160px",
|
||||
fontFamily: "monospace",
|
||||
fontSize: 0,
|
||||
overflowY: "scroll",
|
||||
py: 3,
|
||||
px: 3,
|
||||
m: 3,
|
||||
}}
|
||||
>
|
||||
{snap.logs.map((log, index) => index + 1 + ": " + log + "\n")}
|
||||
</Box>
|
||||
<Box
|
||||
as="footer"
|
||||
css={{
|
||||
display: "flex",
|
||||
borderTop: "1px solid $slate6",
|
||||
background: "$slate1",
|
||||
}}
|
||||
>
|
||||
<Container css={{ py: "$4", flexShrink: 1 }}>
|
||||
<Box
|
||||
as="pre"
|
||||
css={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
height: "160px",
|
||||
fontSize: "13px",
|
||||
fontWeight: "$body",
|
||||
fontFamily: "$monospace",
|
||||
overflowY: "scroll",
|
||||
wordWrap: "break-word",
|
||||
py: 3,
|
||||
px: 3,
|
||||
m: 3,
|
||||
}}
|
||||
>
|
||||
{snap.logs.map((log, index) => (
|
||||
<Box key={log.type + index}>
|
||||
<LogText capitalize variant={log.type}>
|
||||
{log.type}:{" "}
|
||||
</LogText>
|
||||
<LogText>{log.message}</LogText>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
10
components/Heading.tsx
Normal file
10
components/Heading.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { styled } from "../stitches.config";
|
||||
|
||||
const Heading = styled("span", {
|
||||
fontFamily: "$heading",
|
||||
lineHeight: "$heading",
|
||||
fontWeight: "$heading",
|
||||
textTransform: "uppercase",
|
||||
});
|
||||
|
||||
export default Heading;
|
||||
@@ -1,70 +1,95 @@
|
||||
/** @jsxImportSource theme-ui */
|
||||
import { useRef } from "react";
|
||||
import { useSnapshot } from "valtio";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { useSnapshot, ref } from "valtio";
|
||||
import Editor from "@monaco-editor/react";
|
||||
import type monaco from "monaco-editor";
|
||||
import { useColorMode } from "@theme-ui/color-modes";
|
||||
import { Button, Box } from "theme-ui";
|
||||
import { ArrowRight } from "phosphor-react";
|
||||
import { Play } from "phosphor-react";
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
import { state } from "../state";
|
||||
import Box from "./Box";
|
||||
import Button from "./Button";
|
||||
import dark from "../theme/editor/amy.json";
|
||||
import light from "../theme/editor/xcode_default.json";
|
||||
import { compileCode, saveFile, state } from "../state";
|
||||
|
||||
import EditorNavigation from "./EditorNavigation";
|
||||
import Spinner from "./Spinner";
|
||||
|
||||
const HooksEditor = () => {
|
||||
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>();
|
||||
const [mode, setColorMode] = useColorMode();
|
||||
const snap = useSnapshot(state);
|
||||
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 sx={{ flex: 1, display: "flex", position: "relative" }}>
|
||||
<Box
|
||||
css={{
|
||||
flex: 1,
|
||||
flexShrink: 1,
|
||||
display: "flex",
|
||||
position: "relative",
|
||||
flexDirection: "column",
|
||||
backgroundColor: "$slate3",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<EditorNavigation />
|
||||
<Editor
|
||||
defaultLanguage={snap.files?.[snap.active]?.language}
|
||||
keepCurrentModel
|
||||
// defaultLanguage={snap.files?.[snap.active]?.language}
|
||||
path={snap.files?.[snap.active]?.name}
|
||||
defaultValue={snap.files?.[snap.active]?.content}
|
||||
// defaultValue={snap.files?.[snap.active]?.content}
|
||||
beforeMount={(monaco) => {
|
||||
// @ts-expect-error
|
||||
monaco.editor.defineTheme("dark", dark);
|
||||
// @ts-expect-error
|
||||
monaco.editor.defineTheme("light", light);
|
||||
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,
|
||||
() => {
|
||||
if (
|
||||
state.files &&
|
||||
state.files.length > 0 &&
|
||||
state.files[snap.active]
|
||||
) {
|
||||
console.log(
|
||||
`File ${snap.files[snap.active].name} saved successfully ✅`
|
||||
);
|
||||
state.files[snap.active].content = editor.getValue();
|
||||
}
|
||||
saveFile(editor.getValue());
|
||||
}
|
||||
);
|
||||
}}
|
||||
theme={mode === "dark" ? "dark" : "light"}
|
||||
wrapperProps={{ style: { display: "flex", flex: 1 } }}
|
||||
theme={theme === "dark" ? "dark" : "light"}
|
||||
/>
|
||||
<Button
|
||||
sx={{
|
||||
variant="primary"
|
||||
uppercase
|
||||
onClick={() => compileCode(snap.active)}
|
||||
disabled={snap.compiling}
|
||||
css={{
|
||||
position: "absolute",
|
||||
bottom: 1,
|
||||
left: 3,
|
||||
bottom: "$4",
|
||||
left: "$4",
|
||||
alignItems: "center",
|
||||
display: "flex",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
Compile{" "}
|
||||
<ArrowRight sx={{ mb: "0px", ml: 2 }} weight="bold" size="20px" />
|
||||
<Play weight="bold" size="16px" />
|
||||
Compile to Wasm
|
||||
{snap.compiling && <Spinner />}
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
|
||||
27
components/LogText.tsx
Normal file
27
components/LogText.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { styled } from "../stitches.config";
|
||||
|
||||
const Text = styled("span", {
|
||||
fontFamily: "$monospace",
|
||||
lineHeight: "$body",
|
||||
color: "$text",
|
||||
variants: {
|
||||
variant: {
|
||||
log: {
|
||||
color: "$text",
|
||||
},
|
||||
warning: {
|
||||
color: "$yellow11",
|
||||
},
|
||||
error: {
|
||||
color: "$red11",
|
||||
},
|
||||
},
|
||||
capitalize: {
|
||||
true: {
|
||||
textTransform: "capitalize",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default Text;
|
||||
@@ -1,28 +1,38 @@
|
||||
/** @jsxImportSource theme-ui */
|
||||
import { useColorMode } from "theme-ui";
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
function Logo(props: React.SVGProps<SVGSVGElement>) {
|
||||
const [mode] = useColorMode();
|
||||
import { styled } from "../stitches.config";
|
||||
|
||||
const SVG = styled("svg", {
|
||||
"& #path-one, & #path-two": {
|
||||
fill: "$text",
|
||||
},
|
||||
});
|
||||
function Logo({
|
||||
width,
|
||||
height,
|
||||
}: {
|
||||
width?: string | number;
|
||||
height?: string | number;
|
||||
}) {
|
||||
return (
|
||||
<svg
|
||||
width="1em"
|
||||
height="1em"
|
||||
<SVG
|
||||
width={width || "1em"}
|
||||
height={height || "1em"}
|
||||
viewBox="0 0 28 22"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<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"
|
||||
sx={{ fill: "text" }}
|
||||
/>
|
||||
<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"
|
||||
sx={{ fill: "text" }}
|
||||
/>
|
||||
</svg>
|
||||
</SVG>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,89 +1,105 @@
|
||||
/** @jsxImportSource theme-ui */
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
import { DownloadSimple, Plus, Share, BookOpen } from "phosphor-react";
|
||||
import {
|
||||
Container,
|
||||
Box,
|
||||
Heading,
|
||||
Button,
|
||||
Spinner,
|
||||
useColorMode,
|
||||
} from "theme-ui";
|
||||
Gear,
|
||||
GithubLogo,
|
||||
SignOut,
|
||||
User,
|
||||
ArrowSquareOut,
|
||||
} from "phosphor-react";
|
||||
import { useSnapshot } from "valtio";
|
||||
import { Sun, MoonStars } from "phosphor-react";
|
||||
import Image from "next/image";
|
||||
import { useSession, signIn, signOut } from "next-auth/react";
|
||||
import Dropdown from "./DropdownMenu";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuArrow,
|
||||
DropdownMenuSeparator,
|
||||
} from "./DropdownMenu";
|
||||
|
||||
import Stack from "./Stack";
|
||||
import Logo from "./Logo";
|
||||
// import Button from "./Button";
|
||||
import { state } from "../state";
|
||||
import Button from "./Button";
|
||||
import Container from "./Container";
|
||||
import Box from "./Box";
|
||||
import Flex from "./Flex";
|
||||
import ThemeChanger from "./ThemeChanger";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
const Navigation = () => {
|
||||
const snap = useSnapshot(state);
|
||||
const [mode, setColorMode] = useColorMode();
|
||||
const { data: session, status } = useSession();
|
||||
console.log(session);
|
||||
const router = useRouter();
|
||||
return (
|
||||
<Box
|
||||
as="nav"
|
||||
sx={{
|
||||
css={{
|
||||
display: "flex",
|
||||
height: "60px",
|
||||
bg: "background",
|
||||
// borderBottom: "1px solid",
|
||||
borderColor: "text",
|
||||
borderBottom: "1px solid $slate6",
|
||||
position: "relative",
|
||||
zIndex: 2003,
|
||||
}}
|
||||
>
|
||||
<Container sx={{ display: "flex", alignItems: "center", py: 2 }}>
|
||||
<Container
|
||||
css={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
py: "$2",
|
||||
}}
|
||||
>
|
||||
<Link href="/" passHref>
|
||||
<Box as="a" sx={{ display: "flex", alignItems: "center" }}>
|
||||
<Box
|
||||
as="a"
|
||||
css={{ display: "flex", alignItems: "center", color: "$textColor" }}
|
||||
>
|
||||
<Logo width="30px" height="30px" />
|
||||
<Heading as="h3" sx={{ fontWeight: "bold", ml: 2 }}>
|
||||
XRPL Hooks
|
||||
</Heading>
|
||||
</Box>
|
||||
</Link>
|
||||
<Box sx={{ ml: 3 }}></Box>
|
||||
<Stack sx={{ ml: 3 }} spacing={2}>
|
||||
{state.loading && "loading"}
|
||||
{snap.files &&
|
||||
snap.files.length > 0 &&
|
||||
snap.files?.map((file, index) => (
|
||||
<Button
|
||||
onClick={() => (state.active = index)}
|
||||
key={file.name}
|
||||
variant={snap.active === index ? "secondary" : "muted"}
|
||||
>
|
||||
{file.name}
|
||||
</Button>
|
||||
))}
|
||||
<Stack css={{ ml: "$4", gap: "$3" }}>
|
||||
<Link href="/develop" passHref shallow>
|
||||
<Button
|
||||
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 sx={{ color: "text", ml: "auto" }}>
|
||||
<Plus sx={{ display: "flex" }} size="20px" />
|
||||
<Share sx={{ display: "flex" }} size="20px" />
|
||||
<DownloadSimple sx={{ display: "flex" }} size="20px" />
|
||||
<Box
|
||||
color="text"
|
||||
onClick={(e) => {
|
||||
setColorMode(mode === "light" ? "dark" : "light");
|
||||
}}
|
||||
sx={{
|
||||
display: "flex",
|
||||
marginLeft: "auto",
|
||||
cursor: "pointer",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
mb: 1,
|
||||
}}
|
||||
>
|
||||
{mode === "dark" ? <Sun size="20px" /> : <MoonStars size="20px" />}
|
||||
</Box>
|
||||
<Stack css={{ color: "text", ml: "auto" }}>
|
||||
<ThemeChanger />
|
||||
{status === "authenticated" && (
|
||||
<Dropdown.Root>
|
||||
<Dropdown.Trigger asChild>
|
||||
<Box sx={{ borderRadius: "100%", overflow: "hidden" }}>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Box
|
||||
css={{
|
||||
borderRadius: "$full",
|
||||
overflow: "hidden",
|
||||
width: "30px",
|
||||
height: "30px",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src={session?.user?.image || ""}
|
||||
width="30px"
|
||||
@@ -92,31 +108,48 @@ const Navigation = () => {
|
||||
alt="User avatar"
|
||||
/>
|
||||
</Box>
|
||||
</Dropdown.Trigger>
|
||||
<Dropdown.Content>
|
||||
<Dropdown.Item onClick={() => signOut()}>Log out</Dropdown.Item>
|
||||
<Dropdown.Arrow offset={10} />
|
||||
</Dropdown.Content>
|
||||
</Dropdown.Root>
|
||||
</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
|
||||
sx={{ size: "sm", cursor: "pointer" }}
|
||||
onClick={() => signIn("github")}
|
||||
>
|
||||
Github Login
|
||||
<Button outline onClick={() => signIn("github")}>
|
||||
<GithubLogo size="16px" /> Github Login
|
||||
</Button>
|
||||
)}
|
||||
{status === "loading" && <Spinner size="20px" />}
|
||||
{status === "loading" && "loading"}
|
||||
{/* <Box
|
||||
sx={{
|
||||
css={{
|
||||
border: "1px solid",
|
||||
borderRadius: "3px",
|
||||
borderColor: "text",
|
||||
p: 1,
|
||||
}}
|
||||
>
|
||||
<BookOpen sx={{ display: "flex" }} size="20px" />
|
||||
<BookOpen size="20px" />
|
||||
</Box> */}
|
||||
</Stack>
|
||||
</Container>
|
||||
|
||||
13
components/Spinner.tsx
Normal file
13
components/Spinner.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Spinner as SpinnerIcon } from "phosphor-react";
|
||||
import { styled, keyframes } from "../stitches.config";
|
||||
|
||||
const rotate = keyframes({
|
||||
"0%": { transform: "rotate(0deg)" },
|
||||
"100%": { transform: "rotate(360deg)" },
|
||||
});
|
||||
|
||||
const Spinner = styled(SpinnerIcon, {
|
||||
animation: `${rotate} 150ms cubic-bezier(0.16, 1, 0.3, 1) infinite`,
|
||||
});
|
||||
|
||||
export default Spinner;
|
||||
@@ -1,38 +1,14 @@
|
||||
import { Children } from "react";
|
||||
import { Flex, Box, ThemeUIStyleObject, BoxProps } from "theme-ui";
|
||||
import { useBreakpointIndex } from "@theme-ui/match-media";
|
||||
|
||||
const Stack: React.FC<
|
||||
BoxProps & {
|
||||
spacing?: (number | string | null)[] | string | number;
|
||||
direction?: ("column" | "row") | ("column" | "row" | null)[];
|
||||
sx?: ThemeUIStyleObject;
|
||||
}
|
||||
> = ({ spacing = 3, direction = "row", sx, children, ...rest }) => {
|
||||
const breakpointIndex = useBreakpointIndex();
|
||||
const currentDirection = Array.isArray(direction)
|
||||
? direction[breakpointIndex]
|
||||
: direction;
|
||||
const childrenLength = Array.isArray(children) ? children.length : null;
|
||||
return (
|
||||
<Box
|
||||
{...rest}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: direction,
|
||||
...sx,
|
||||
}}
|
||||
>
|
||||
{Children.map(children, (child, index) => (
|
||||
<Box
|
||||
mt={currentDirection === "column" && index !== 0 ? spacing : 0}
|
||||
ml={currentDirection === "row" && index !== 0 ? spacing : 0}
|
||||
>
|
||||
{child}
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
import Box from "./Box";
|
||||
import { styled } from "../stitches.config";
|
||||
import type * as Stitches from "@stitches/react";
|
||||
|
||||
export default Stack;
|
||||
const StackComponent = styled(Box, {
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "row",
|
||||
gap: "$4",
|
||||
});
|
||||
|
||||
export default StackComponent;
|
||||
|
||||
9
components/Text.tsx
Normal file
9
components/Text.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { styled } from "../stitches.config";
|
||||
|
||||
const Text = styled("span", {
|
||||
fontFamily: "$body",
|
||||
lineHeight: "$body",
|
||||
color: "$text",
|
||||
});
|
||||
|
||||
export default Text;
|
||||
37
components/ThemeChanger.tsx
Normal file
37
components/ThemeChanger.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { Sun, Moon } from "phosphor-react";
|
||||
|
||||
import Box from "./Box";
|
||||
|
||||
const ThemeChanger = () => {
|
||||
const { theme, setTheme } = useTheme();
|
||||
const [mounted, setMounted] = useState(false);
|
||||
useEffect(() => setMounted(true), []);
|
||||
|
||||
if (!mounted) return null;
|
||||
|
||||
return (
|
||||
<Box
|
||||
onClick={() => {
|
||||
setTheme(theme && theme === "light" ? "dark" : "light");
|
||||
}}
|
||||
css={{
|
||||
display: "flex",
|
||||
marginLeft: "auto",
|
||||
cursor: "pointer",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
color: "$muted",
|
||||
}}
|
||||
>
|
||||
{theme === "dark" ? (
|
||||
<Sun weight="bold" size="16px" />
|
||||
) : (
|
||||
<Moon weight="bold" size="16px" />
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeChanger;
|
||||
36
hooks/useWindowSize.tsx
Normal file
36
hooks/useWindowSize.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
// Define general type for useWindowSize hook, which includes width and height
|
||||
interface Size {
|
||||
width: number | undefined;
|
||||
height: number | undefined;
|
||||
}
|
||||
|
||||
// Hook
|
||||
function useWindowSize(): Size {
|
||||
// Initialize state with undefined width/height so server and client renders match
|
||||
// Learn more here: https://joshwcomeau.com/react/the-perils-of-rehydration/
|
||||
const [windowSize, setWindowSize] = useState<Size>({
|
||||
width: undefined,
|
||||
height: undefined,
|
||||
});
|
||||
useEffect(() => {
|
||||
// Handler to call on window resize
|
||||
function handleResize() {
|
||||
// Set window width/height to state
|
||||
setWindowSize({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
});
|
||||
}
|
||||
// Add event listener
|
||||
window.addEventListener("resize", handleResize);
|
||||
// Call handler right away so state gets updated with initial window size
|
||||
handleResize();
|
||||
// Remove event listener on cleanup
|
||||
return () => window.removeEventListener("resize", handleResize);
|
||||
}, []); // Empty array ensures that effect is only run on mount
|
||||
return windowSize;
|
||||
}
|
||||
|
||||
export default useWindowSize;
|
||||
10097
package-lock.json
generated
10097
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@@ -12,20 +12,21 @@
|
||||
"@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-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",
|
||||
"@wasmer/wasi": "^0.12.0",
|
||||
"@wasmer/wasm-transformer": "^0.12.0",
|
||||
"@wasmer/wasmfs": "^0.12.0",
|
||||
"monaco-editor": "^0.29.1",
|
||||
"next": "^12.0.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",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"theme-ui": "^0.11.3",
|
||||
"react-hot-toast": "^2.1.1",
|
||||
"valtio": "^1.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/** @jsxImportSource theme-ui */
|
||||
import type { GetStaticPaths, GetStaticProps, NextPage } from "next";
|
||||
import Head from "next/head";
|
||||
import { Box } from "theme-ui";
|
||||
|
||||
import { useRouter } from "next/router";
|
||||
import HooksEditor from "../components/HooksEditor";
|
||||
import { useEffect } from "react";
|
||||
import { fetchFiles } from "../state";
|
||||
import Footer from "../components/Footer";
|
||||
|
||||
const Home: NextPage = () => {
|
||||
const router = useRouter();
|
||||
const index = router.query.index;
|
||||
const gistId = index && Array.isArray(index) ? index[0] : "";
|
||||
useEffect(() => {
|
||||
fetchFiles(gistId);
|
||||
}, [gistId]);
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>XRPL Hooks Playground</title>
|
||||
</Head>
|
||||
|
||||
<main sx={{ 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,
|
||||
};
|
||||
};
|
||||
@@ -1,19 +1,28 @@
|
||||
/** @jsxImportSource theme-ui */
|
||||
import "../styles/globals.css";
|
||||
import type { AppProps } from "next/app";
|
||||
import { ThemeProvider } from "theme-ui";
|
||||
import { SessionProvider } from "next-auth/react";
|
||||
import { ThemeProvider } from "next-themes";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
import { theme } from "../theme";
|
||||
import { darkTheme } from "../stitches.config";
|
||||
import Navigation from "../components/Navigation";
|
||||
|
||||
function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
|
||||
return (
|
||||
<>
|
||||
<SessionProvider session={session}>
|
||||
<ThemeProvider theme={theme}>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="dark"
|
||||
enableSystem={false}
|
||||
value={{
|
||||
light: "light",
|
||||
dark: darkTheme.className,
|
||||
}}
|
||||
>
|
||||
<Navigation />
|
||||
<Component {...pageProps} />
|
||||
<Toaster />
|
||||
</ThemeProvider>
|
||||
</SessionProvider>
|
||||
</>
|
||||
|
||||
@@ -6,6 +6,8 @@ import Document, {
|
||||
DocumentContext,
|
||||
} from "next/document";
|
||||
|
||||
import { globalStyles, getCssText } from "../stitches.config";
|
||||
|
||||
class MyDocument extends Document {
|
||||
static async getInitialProps(ctx: DocumentContext) {
|
||||
const initialProps = await Document.getInitialProps(ctx);
|
||||
@@ -13,6 +15,7 @@ class MyDocument extends Document {
|
||||
return initialProps;
|
||||
}
|
||||
render() {
|
||||
globalStyles();
|
||||
return (
|
||||
<Html>
|
||||
<Head>
|
||||
@@ -28,6 +31,10 @@ class MyDocument extends Document {
|
||||
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() }}
|
||||
/>
|
||||
</Head>
|
||||
<body>
|
||||
<Main />
|
||||
|
||||
12
pages/_middleware.ts
Normal file
12
pages/_middleware.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
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");
|
||||
|
||||
}
|
||||
}
|
||||
@@ -20,10 +20,10 @@ export default NextAuth({
|
||||
token: "https://github.com/login/oauth/access_token",
|
||||
userinfo: "https://api.github.com/user",
|
||||
profile(profile) {
|
||||
console.log(profile)
|
||||
return {
|
||||
id: profile.id.toString(),
|
||||
name: profile.name || profile.login,
|
||||
username: profile.login,
|
||||
email: profile.email,
|
||||
image: profile.avatar_url,
|
||||
}
|
||||
@@ -34,15 +34,16 @@ export default NextAuth({
|
||||
],
|
||||
callbacks: {
|
||||
async jwt({ token, user, account, profile, isNewUser }) {
|
||||
console.log('jwt', { token, account })
|
||||
if (account && account.access_token) {
|
||||
token.accessToken = account.access_token;
|
||||
token.username = user?.username || '';
|
||||
}
|
||||
return token
|
||||
},
|
||||
async session({ session, token }) {
|
||||
console.log('session', { token, session })
|
||||
session.accessToken = token.accessToken;
|
||||
session.accessToken = token.accessToken as string;
|
||||
const user = { ...session.user, username: token.username };
|
||||
session['user']['username'] = token.username as string;
|
||||
return session
|
||||
}
|
||||
},
|
||||
|
||||
12
pages/deploy/index.tsx
Normal file
12
pages/deploy/index.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
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;
|
||||
39
pages/develop/index.tsx
Normal file
39
pages/develop/index.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
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,
|
||||
// };
|
||||
// };
|
||||
7
pages/test/index.tsx
Normal file
7
pages/test/index.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import Container from "../../components/Container";
|
||||
|
||||
const Test = () => {
|
||||
return <Container>This will be the test page</Container>;
|
||||
};
|
||||
|
||||
export default Test;
|
||||
163
state.ts
163
state.ts
@@ -1,105 +1,150 @@
|
||||
import { proxy } from 'valtio';
|
||||
import { proxy, subscribe } from 'valtio';
|
||||
import { devtools } from 'valtio/utils';
|
||||
import { Octokit } from '@octokit/core';
|
||||
import type monaco from 'monaco-editor';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
const octokit = new Octokit();
|
||||
|
||||
interface Files {
|
||||
interface File {
|
||||
name: string,
|
||||
language: string,
|
||||
content: string
|
||||
}
|
||||
|
||||
interface IState {
|
||||
files: {
|
||||
name: string;
|
||||
language: string;
|
||||
content: string;
|
||||
}[] | [],
|
||||
files: File[],
|
||||
active: number;
|
||||
loading: boolean;
|
||||
logs: string[];
|
||||
}
|
||||
|
||||
const initFiles = [
|
||||
{
|
||||
name: 'hello.c',
|
||||
language: 'c',
|
||||
content: `
|
||||
#include <stdio.h>
|
||||
int main() {
|
||||
// printf() displays the string inside quotation
|
||||
printf("Hello, World!");
|
||||
return 0;
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'another.c',
|
||||
language: 'c',
|
||||
content: `
|
||||
#include <stdio.h>
|
||||
int main()
|
||||
{
|
||||
/* printf function displays the content that is
|
||||
* passed between the double quotes.
|
||||
*/
|
||||
printf("Hello World");
|
||||
return 0;
|
||||
}
|
||||
`,
|
||||
compiling: boolean;
|
||||
logs: {
|
||||
type: 'error' | 'warning' | 'log',
|
||||
message: string;
|
||||
}[];
|
||||
editorCtx?: typeof monaco.editor;
|
||||
editorSettings: {
|
||||
tabSize: number;
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
const state = proxy<IState>({
|
||||
let localStorageState: null | string = null;
|
||||
let initialState = {
|
||||
files: [],
|
||||
active: 0,
|
||||
loading: false,
|
||||
logs: []
|
||||
});
|
||||
compiling: false,
|
||||
logs: [],
|
||||
editorCtx: undefined,
|
||||
editorSettings: {
|
||||
tabSize: 2
|
||||
}
|
||||
}
|
||||
|
||||
// state.files = initFiles;
|
||||
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);
|
||||
}
|
||||
|
||||
// const initState = ({ gistId }: { gistId?: string }) => {
|
||||
// if (!gistId) {
|
||||
// return initialState;
|
||||
// }
|
||||
// }
|
||||
|
||||
// const state = initialState;
|
||||
// Initialize state
|
||||
export const state = proxy<IState>(initialState);
|
||||
|
||||
// Fetch content from Githug Gists
|
||||
export const fetchFiles = (gistId: string) => {
|
||||
if (gistId) {
|
||||
console.log('callling')
|
||||
state.logs.push({ type: 'log', message: `Fetching Gist with id: ${gistId}` });
|
||||
octokit.request("GET /gists/{gist_id}", { gist_id: gistId }).then(res => {
|
||||
state.logs.push('Fetching files from Github Gists...');
|
||||
|
||||
if (res.data.files && Object.keys(res.data.files).length > 0) {
|
||||
const files = Object.keys(res.data.files).map(filename => ({
|
||||
name: res.data.files?.[filename]?.filename || 'noname.c',
|
||||
language: res.data.files?.[filename]?.language?.toLowerCase() || '',
|
||||
content: res.data.files?.[filename]?.content || ''
|
||||
}))
|
||||
state.files = initFiles
|
||||
state.loading = false;
|
||||
if (files.length > 0) {
|
||||
state.logs.push('Fetched successfully ✅')
|
||||
state.logs.push({ type: 'log', message: 'Fetched successfully ✅' })
|
||||
state.files = files;
|
||||
return
|
||||
}
|
||||
return state.files = initFiles
|
||||
return
|
||||
}
|
||||
|
||||
}).catch(err => {
|
||||
state.loading = false;
|
||||
return state.files = initFiles
|
||||
state.logs.push({ type: 'error', message: `Couldn't find Gist with id: ${gistId}` })
|
||||
return
|
||||
})
|
||||
return
|
||||
}
|
||||
state.loading = false;
|
||||
return state.files = initFiles
|
||||
// return state.files = initFiles
|
||||
}
|
||||
|
||||
const unsub = devtools(state, 'Files State')
|
||||
export const updateEditorSettings = (editorSettings: IState['editorSettings']) => {
|
||||
state.editorCtx?.getModels().forEach(model => {
|
||||
console.log(model.uri)
|
||||
model.updateOptions({
|
||||
...editorSettings
|
||||
})
|
||||
});
|
||||
return state.editorSettings = editorSettings;
|
||||
}
|
||||
|
||||
export { state };
|
||||
export const saveFile = (value: string) => {
|
||||
toast.success('Saved successfully', { position: 'bottom-center' })
|
||||
}
|
||||
|
||||
export const createNewFile = (name: string) => {
|
||||
state.files.push({ name, language: 'c', content: "" })
|
||||
state.active = state.files.length - 1;
|
||||
}
|
||||
|
||||
export const compileCode = async (activeId: number) => {
|
||||
if (!process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT) {
|
||||
throw Error('Missing env!')
|
||||
};
|
||||
if (state.compiling) {
|
||||
return;
|
||||
}
|
||||
state.compiling = true;
|
||||
try {
|
||||
const res = await fetch(process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"output": "wasm",
|
||||
"compress": true,
|
||||
"files": [
|
||||
{
|
||||
"type": "c",
|
||||
"name": state.files[activeId].name,
|
||||
"options": "-g -O3",
|
||||
"src": state.files[activeId].content
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
||||
const json = await res.json();
|
||||
state.compiling = false;
|
||||
toast.success('Compiled successfully!');
|
||||
console.log(json)
|
||||
} catch {
|
||||
state.logs.push({ type: 'error', message: 'Error occured while compiling!' })
|
||||
state.compiling = false;
|
||||
}
|
||||
}
|
||||
|
||||
const unsub = devtools(state, 'Files State');
|
||||
|
||||
subscribe(state, () => {
|
||||
const { editorCtx, ...storedState } = state;
|
||||
localStorage.setItem('hooksIdeState', JSON.stringify(storedState))
|
||||
});
|
||||
304
stitches.config.ts
Normal file
304
stitches.config.ts
Normal file
@@ -0,0 +1,304 @@
|
||||
// stitches.config.ts
|
||||
import type Stitches from '@stitches/react';
|
||||
import { createStitches } from '@stitches/react';
|
||||
import {
|
||||
gray,
|
||||
blue,
|
||||
red,
|
||||
green,
|
||||
plum,
|
||||
slate,
|
||||
pink,
|
||||
yellow,
|
||||
grayDark,
|
||||
blueDark,
|
||||
redDark,
|
||||
greenDark,
|
||||
plumDark,
|
||||
slateDark,
|
||||
pinkDark,
|
||||
yellowDark
|
||||
} from '@radix-ui/colors';
|
||||
|
||||
export const {
|
||||
styled,
|
||||
css,
|
||||
globalCss,
|
||||
keyframes,
|
||||
getCssText,
|
||||
theme,
|
||||
createTheme,
|
||||
config,
|
||||
} = createStitches({
|
||||
theme: {
|
||||
colors: {
|
||||
...gray,
|
||||
...blue,
|
||||
...red,
|
||||
...green,
|
||||
...plum,
|
||||
...slate,
|
||||
...pink,
|
||||
...yellow,
|
||||
background: "$gray1",
|
||||
text: "$gray12",
|
||||
primary: "$plum",
|
||||
white: "white",
|
||||
black: "black"
|
||||
},
|
||||
fonts: {
|
||||
body: 'Work Sans, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif',
|
||||
heading: 'Work Sans, sans-serif',
|
||||
monospace: 'Roboto, monospace',
|
||||
},
|
||||
fontSizes: {
|
||||
xs: "0.75rem",
|
||||
sm: "0.875rem",
|
||||
md: "1rem",
|
||||
lg: "1.125rem",
|
||||
xl: "1.25rem",
|
||||
"2xl": "1.5rem",
|
||||
"3xl": "1.875rem",
|
||||
"4xl": "2.25rem",
|
||||
"5xl": "3rem",
|
||||
"6xl": "3.75rem",
|
||||
"7xl": "4.5rem",
|
||||
"8xl": "6rem",
|
||||
"9xl": "8rem",
|
||||
default: '$md'
|
||||
},
|
||||
space: {
|
||||
px: "1px",
|
||||
0.5: "0.125rem",
|
||||
1: "0.25rem",
|
||||
1.5: "0.375rem",
|
||||
2: "0.5rem",
|
||||
2.5: "0.625rem",
|
||||
3: "0.75rem",
|
||||
3.5: "0.875rem",
|
||||
4: "1rem",
|
||||
5: "1.25rem",
|
||||
6: "1.5rem",
|
||||
7: "1.75rem",
|
||||
8: "2rem",
|
||||
9: "2.25rem",
|
||||
10: "2.5rem",
|
||||
12: "3rem",
|
||||
14: "3.5rem",
|
||||
16: "4rem",
|
||||
20: "5rem",
|
||||
24: "6rem",
|
||||
28: "7rem",
|
||||
32: "8rem",
|
||||
36: "9rem",
|
||||
40: "10rem",
|
||||
44: "11rem",
|
||||
48: "12rem",
|
||||
52: "13rem",
|
||||
56: "14rem",
|
||||
60: "15rem",
|
||||
64: "16rem",
|
||||
72: "18rem",
|
||||
80: "20rem",
|
||||
96: "24rem",
|
||||
"widePlus": '2048px',
|
||||
"wide": '1536px',
|
||||
"layoutPlus": '1260px',
|
||||
"layout": '1024px',
|
||||
"copyUltra": '980px',
|
||||
"copyPlus": '768px',
|
||||
"copy": '680px',
|
||||
"narrowPlus": '600px',
|
||||
"narrow": '512px',
|
||||
xs: "20rem",
|
||||
sm: "24rem",
|
||||
md: "28rem",
|
||||
lg: "32rem",
|
||||
xl: "36rem",
|
||||
"2xl": "42rem",
|
||||
"3xl": "48rem",
|
||||
"4xl": "56rem",
|
||||
"5xl": "64rem",
|
||||
"6xl": "72rem",
|
||||
"7xl": "80rem",
|
||||
"8xl": "90rem",
|
||||
},
|
||||
sizes: {
|
||||
px: "1px",
|
||||
0.5: "0.125rem",
|
||||
1: "0.25rem",
|
||||
1.5: "0.375rem",
|
||||
2: "0.5rem",
|
||||
2.5: "0.625rem",
|
||||
3: "0.75rem",
|
||||
3.5: "0.875rem",
|
||||
4: "1rem",
|
||||
5: "1.25rem",
|
||||
6: "1.5rem",
|
||||
7: "1.75rem",
|
||||
8: "2rem",
|
||||
9: "2.25rem",
|
||||
10: "2.5rem",
|
||||
12: "3rem",
|
||||
14: "3.5rem",
|
||||
16: "4rem",
|
||||
20: "5rem",
|
||||
24: "6rem",
|
||||
28: "7rem",
|
||||
32: "8rem",
|
||||
36: "9rem",
|
||||
40: "10rem",
|
||||
44: "11rem",
|
||||
48: "12rem",
|
||||
52: "13rem",
|
||||
56: "14rem",
|
||||
60: "15rem",
|
||||
64: "16rem",
|
||||
72: "18rem",
|
||||
80: "20rem",
|
||||
96: "24rem",
|
||||
max: "max-content",
|
||||
min: "min-content",
|
||||
full: "100%",
|
||||
"3xs": "14rem",
|
||||
"2xs": "16rem",
|
||||
xs: "20rem",
|
||||
sm: "24rem",
|
||||
md: "28rem",
|
||||
lg: "32rem",
|
||||
xl: "36rem",
|
||||
"2xl": "42rem",
|
||||
"3xl": "48rem",
|
||||
"4xl": "56rem",
|
||||
"5xl": "64rem",
|
||||
"6xl": "72rem",
|
||||
"7xl": "80rem",
|
||||
"8xl": "90rem",
|
||||
},
|
||||
radii: {
|
||||
none: "0",
|
||||
sm: "0.2rem",
|
||||
base: "0.25rem",
|
||||
md: "0.375rem",
|
||||
lg: "0.5rem",
|
||||
xl: "0.75rem",
|
||||
"2xl": "1rem",
|
||||
"3xl": "1.5rem",
|
||||
full: "9999px",
|
||||
},
|
||||
fontWeights: {
|
||||
body: 400,
|
||||
heading: 400,
|
||||
bold: 700,
|
||||
},
|
||||
lineHeights: {
|
||||
one: 1,
|
||||
body: 1.5,
|
||||
heading: 0.85,
|
||||
},
|
||||
letterSpacings: {},
|
||||
borderWidths: {},
|
||||
borderStyles: {},
|
||||
shadows: {},
|
||||
zIndices: {},
|
||||
transitions: {},
|
||||
},
|
||||
media: {
|
||||
sm: "(min-width: 30em)",
|
||||
md: "(min-width: 48em)",
|
||||
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)',
|
||||
},
|
||||
utils: {
|
||||
// Abbreviated margin properties
|
||||
m: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'margin'>) => ({
|
||||
margin: value,
|
||||
}),
|
||||
mt: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'marginTop'>) => ({
|
||||
marginTop: value,
|
||||
}),
|
||||
mr: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'marginRight'>) => ({
|
||||
marginRight: value,
|
||||
}),
|
||||
mb: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'marginBottom'>) => ({
|
||||
marginBottom: value,
|
||||
}),
|
||||
ml: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'marginLeft'>) => ({
|
||||
marginLeft: value,
|
||||
}),
|
||||
mx: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'marginLeft' | 'marginRight'>) => ({
|
||||
marginLeft: value,
|
||||
marginRight: value,
|
||||
}),
|
||||
my: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'marginTop' | 'marginBottom'>) => ({
|
||||
marginTop: value,
|
||||
marginBottom: value,
|
||||
}),
|
||||
// Abbreviated margin properties
|
||||
p: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'padding'>) => ({
|
||||
padding: value,
|
||||
}),
|
||||
pt: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'paddingTop'>) => ({
|
||||
paddingTop: value,
|
||||
}),
|
||||
pr: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'paddingRight'>) => ({
|
||||
paddingRight: value,
|
||||
}),
|
||||
pb: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'paddingBottom'>) => ({
|
||||
paddingBottom: value,
|
||||
}),
|
||||
pl: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'paddingLeft'>) => ({
|
||||
paddingLeft: value,
|
||||
}),
|
||||
px: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'paddingLeft' | 'paddingRight'>) => ({
|
||||
paddingLeft: value,
|
||||
paddingRight: value,
|
||||
}),
|
||||
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'>) => ({
|
||||
width: value,
|
||||
height: value,
|
||||
}),
|
||||
// color: (value: Stitches.PropertyValue<'color'> | Stitches.PropertyValue<'width' | 'height'> => ({
|
||||
// color: value
|
||||
// }),
|
||||
|
||||
// A property to apply linear gradient
|
||||
linearGradient: (value: Stitches.ScaleValue<'space'>) => ({
|
||||
backgroundImage: `linear-gradient(${value})`,
|
||||
}),
|
||||
|
||||
// An abbreviated property for border-radius
|
||||
br: (value: Stitches.ScaleValue<'space'>) => ({
|
||||
borderRadius: value,
|
||||
}),
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
export const darkTheme = createTheme('dark', {
|
||||
colors: {
|
||||
...grayDark,
|
||||
...blueDark,
|
||||
...redDark,
|
||||
...greenDark,
|
||||
...plumDark,
|
||||
...slateDark,
|
||||
...pinkDark,
|
||||
...yellowDark
|
||||
},
|
||||
});
|
||||
|
||||
export const globalStyles = globalCss({
|
||||
// body: { backgroundColor: '$background', color: '$text', fontFamily: 'Helvetica' },
|
||||
'html, body': { backgroundColor: '$gray1', color: '$gray12', fontFamily: '$body', fontSize: '$md' },
|
||||
});
|
||||
@@ -3,7 +3,7 @@
|
||||
"inherit": true,
|
||||
"rules": [
|
||||
{
|
||||
"background": "200020",
|
||||
"background": "1a1d1e",
|
||||
"token": ""
|
||||
},
|
||||
{
|
||||
@@ -182,7 +182,7 @@
|
||||
],
|
||||
"colors": {
|
||||
"editor.foreground": "#D0D0FF",
|
||||
"editor.background": "#000000",
|
||||
"editor.background": "#202425",
|
||||
"editor.selectionBackground": "#ffffff30",
|
||||
"editor.lineHighlightBackground": "#ffffff20",
|
||||
"editorCursor.foreground": "#7070FF",
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
],
|
||||
"colors": {
|
||||
"editor.foreground": "#000000",
|
||||
"editor.background": "#FFFFFF",
|
||||
"editor.background": "#f1f3f5",
|
||||
"editor.selectionBackground": "#B5D5FF",
|
||||
"editor.lineHighlightBackground": "#00000012",
|
||||
"editorCursor.foreground": "#000000",
|
||||
|
||||
180
theme/index.ts
180
theme/index.ts
@@ -1,180 +0,0 @@
|
||||
import type { Theme } from "theme-ui";
|
||||
import { darken, lighten } from '@theme-ui/color'
|
||||
|
||||
const makeTheme = <T extends Theme>(t: T) => t
|
||||
|
||||
export const theme = makeTheme({
|
||||
config: {
|
||||
initialColorModeName: 'light',
|
||||
},
|
||||
breakpoints: ['40em', '52em', '64em', '78em'],
|
||||
space: [0, 4, 8, 16, 32, 64, 128, 256, 512],
|
||||
fonts: {
|
||||
body: 'Work Sans, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif',
|
||||
heading: 'Work Sans, sans-serif',
|
||||
monospace: 'Roboto, monospace',
|
||||
},
|
||||
fontSizes: [12, 14, 16, 18, 20, 24, 32, 48, 64, 96],
|
||||
fontWeights: {
|
||||
body: 400,
|
||||
heading: 400,
|
||||
bold: 700,
|
||||
},
|
||||
layout: {
|
||||
container: {
|
||||
maxWidth: "100%",
|
||||
width: "100%",
|
||||
mx: "auto",
|
||||
px: 3
|
||||
}
|
||||
},
|
||||
lineHeights: {
|
||||
body: 1.5,
|
||||
heading: 0.85,
|
||||
},
|
||||
colors: {
|
||||
text: '#000000',
|
||||
background: '#FFFFFF',
|
||||
primary: '#9A52FF',
|
||||
secondary: '#30c',
|
||||
muted: '#C6C6D3',
|
||||
modes: {
|
||||
dark: {
|
||||
text: '#FFFFFF',
|
||||
background: '#000000',
|
||||
primary: '#9A52FF',
|
||||
secondary: '#30c',
|
||||
}
|
||||
}
|
||||
},
|
||||
text: {
|
||||
heading: {
|
||||
fontFamily: 'heading',
|
||||
lineHeight: 'heading',
|
||||
fontWeight: 'heading',
|
||||
},
|
||||
monospace: {
|
||||
fontWeight: 300
|
||||
}
|
||||
},
|
||||
sizes: {
|
||||
"widePlus": 2048,
|
||||
"wide": 1536,
|
||||
"layoutPlus": 1260,
|
||||
"layout": 1024,
|
||||
"copyUltra": 980,
|
||||
"copyPlus": 768,
|
||||
"copy": 680,
|
||||
"narrowPlus": 600,
|
||||
"narrow": 512,
|
||||
sm: {
|
||||
paddingX: 3,
|
||||
paddingY: 1
|
||||
},
|
||||
md: {
|
||||
px: 4,
|
||||
py: 2
|
||||
},
|
||||
lg: {
|
||||
px: 6,
|
||||
py: 4
|
||||
},
|
||||
},
|
||||
buttons: {
|
||||
|
||||
primary: {
|
||||
size: 'md',
|
||||
color: 'white',
|
||||
bg: 'primary',
|
||||
'&:hover': {
|
||||
bg: darken('primary', 0.1),
|
||||
borderColor: darken('primary', 0.1)
|
||||
},
|
||||
fontWeight: 600,
|
||||
borderRadius: '3px',
|
||||
fontSize: 1,
|
||||
border: '1px solid',
|
||||
borderColor: 'primary',
|
||||
},
|
||||
secondary: {
|
||||
color: 'black',
|
||||
bg: 'muted',
|
||||
fontSize: 1,
|
||||
borderRadius: '3px',
|
||||
border: '1px solid',
|
||||
borderColor: 'muted',
|
||||
'&:hover': {
|
||||
bg: darken('muted', 0.1),
|
||||
borderColor: darken('muted', 0.1),
|
||||
},
|
||||
cursor: 'pointer'
|
||||
},
|
||||
muted: {
|
||||
color: 'text',
|
||||
bg: 'background',
|
||||
fontSize: 1,
|
||||
border: '1px solid',
|
||||
borderColor: 'text',
|
||||
borderRadius: '3px',
|
||||
'&:hover': {
|
||||
bg: darken('background', 0.1),
|
||||
},
|
||||
cursor: 'pointer'
|
||||
}
|
||||
},
|
||||
styles: {
|
||||
root: {
|
||||
fontFamily: 'body',
|
||||
lineHeight: 'body',
|
||||
fontWeight: 'body',
|
||||
},
|
||||
h1: {
|
||||
variant: 'text.heading',
|
||||
fontSize: 5,
|
||||
},
|
||||
h2: {
|
||||
variant: 'text.heading',
|
||||
fontSize: 4,
|
||||
},
|
||||
h3: {
|
||||
variant: 'text.heading',
|
||||
fontSize: 3,
|
||||
},
|
||||
h4: {
|
||||
variant: 'text.heading',
|
||||
fontSize: 2,
|
||||
},
|
||||
h5: {
|
||||
variant: 'text.heading',
|
||||
fontSize: 1,
|
||||
},
|
||||
h6: {
|
||||
variant: 'text.heading',
|
||||
fontSize: 0,
|
||||
},
|
||||
pre: {
|
||||
fontFamily: 'monospace',
|
||||
overflowX: 'auto',
|
||||
code: {
|
||||
color: 'inherit',
|
||||
},
|
||||
},
|
||||
code: {
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 'inherit',
|
||||
},
|
||||
table: {
|
||||
width: '100%',
|
||||
borderCollapse: 'separate',
|
||||
borderSpacing: 0,
|
||||
},
|
||||
th: {
|
||||
textAlign: 'left',
|
||||
borderBottomStyle: 'solid',
|
||||
},
|
||||
td: {
|
||||
textAlign: 'left',
|
||||
borderBottomStyle: 'solid',
|
||||
},
|
||||
},
|
||||
});
|
||||
13
types/next-auth.d.ts
vendored
Normal file
13
types/next-auth.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import NextAuth, { User } from "next-auth"
|
||||
|
||||
declare module "next-auth" {
|
||||
/**
|
||||
* Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
|
||||
*/
|
||||
interface Session {
|
||||
user: User & {
|
||||
username: string;
|
||||
}
|
||||
accessToken?: string;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user