Merge pull request #28 from eqlabs/feature/initial-structure

Feature/initial structure
This commit is contained in:
Valtteri Karesto
2021-11-19 15:52:34 +02:00
committed by GitHub
36 changed files with 1668 additions and 11273 deletions

View File

@@ -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
View 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
View 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
View 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
View 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}` },
});

View File

@@ -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;

View 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
View File

@@ -0,0 +1,8 @@
import { styled } from "../stitches.config";
import Box from "./Box";
const Flex = styled(Box, {
display: "flex",
});
export default Flex;

View File

@@ -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
View 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;

View File

@@ -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
View 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;

View File

@@ -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>
);
}

View File

@@ -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
View 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;

View File

@@ -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
View File

@@ -0,0 +1,9 @@
import { styled } from "../stitches.config";
const Text = styled("span", {
fontFamily: "$body",
lineHeight: "$body",
color: "$text",
});
export default Text;

View 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
View 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

File diff suppressed because it is too large Load Diff

View File

@@ -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": {

View File

@@ -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,
};
};

View File

@@ -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>
</>

View File

@@ -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
View 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");
}
}

View File

@@ -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
View 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
View 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
View 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
View File

@@ -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
View 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' },
});

View File

@@ -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",

View File

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

View File

@@ -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
View 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;
}
}

735
yarn.lock

File diff suppressed because it is too large Load Diff