Compare commits

...

51 Commits

Author SHA1 Message Date
Valtteri Karesto
746112e637 fixes issue #115 2022-03-09 11:57:06 +02:00
Valtteri Karesto
13bfd42093 Decorations should now work correctly 2022-03-09 11:14:43 +02:00
Valtteri Karesto
c1f7d7d51c Make sure not all files are defined as C files 2022-03-09 11:14:14 +02:00
Valtteri Karesto
e11ddaffb0 Remove html from md now that the paragraph styling works 2022-03-08 11:36:43 +02:00
Valtteri Karesto
2e88f568b8 Add imports 2022-03-08 11:24:14 +02:00
Valtteri Karesto
237d504f17 Add better styling for markdown 2022-03-08 11:24:02 +02:00
Valtteri Karesto
197fc09e1d delete unused files 2022-03-08 11:23:51 +02:00
Valtteri Karesto
2c74a93aee Add better logic for markdown files 2022-03-08 11:22:58 +02:00
Valtteri Karesto
5209644780 Add raw-loader 2022-03-08 11:22:39 +02:00
Valtteri Karesto
ed37427da8 Add new markdown files 2022-03-08 09:56:07 +02:00
Valtteri Karesto
daee9de96c Update md script and readme 2022-03-08 09:55:40 +02:00
Valtteri Karesto
e4b10d12c2 Remove unused script 2022-03-08 09:54:47 +02:00
Valtteri Karesto
64eabb4502 Remove unused dependencies 2022-03-08 09:54:21 +02:00
Valtteri Karesto
12a24d3d86 Remove rst files 2022-03-08 09:54:11 +02:00
Valtteri Karesto
395e02343b Add logic to enrich the hover messages with rst file contents 2022-03-04 12:48:03 +02:00
Valtteri Karesto
3682dd4946 Add readme how to use the script 2022-03-04 12:38:39 +02:00
Valtteri Karesto
3070ed706e Add script that converts rst to json which contains md 2022-03-04 12:38:24 +02:00
Valtteri Karesto
4b73687779 Add lodash.uniqby 2022-03-04 12:37:51 +02:00
muzamil
0a44b5b5d1 Merge pull request #94 from eqlabs/feat/wasm-stats
Compiled wasm file stats
2022-03-01 15:14:14 +05:30
Vaclav Barta
cc83924c27 Merge pull request #98 from eqlabs/bugfix/typos
fix for #97
2022-02-15 08:49:38 +01:00
Vaclav Barta
e3e964f72a fix for #97 2022-02-11 13:54:52 +01:00
muzam
0def1d30a6 compiled wasm file stats 2022-02-09 19:06:29 +05:30
Joni Juup
bdb2c0cf8f Merge pull request #93 from eqlabs/bugfix/monaco-popups
Fix monaco popup issues
2022-02-09 15:10:28 +02:00
muzamil
3e8dbc9793 Merge pull request #91 from eqlabs/fixes
Clearing some issues
2022-02-09 18:32:47 +05:30
muzam
d735cd3833 Merge branch 'main' into fixes 2022-02-09 18:31:35 +05:30
Joni Juup
eddf228283 more specific rule for the box itself 2022-02-09 13:14:17 +02:00
Joni Juup
f9d617efdc more specific css rule for hr 2022-02-09 13:09:36 +02:00
Joni Juup
43796021da fix monaco editor issues with popups 2022-02-09 13:04:41 +02:00
Joni Juup
241c21782d Merge pull request #89 from eqlabs/enhancement/small-ui-fixes
new XRPL Hooks logo
clicking on the logo won't open the template dialog (retain gist path)
clicking gist under the Hook name opens the gist in a new window
documentation button open documentation in a new window
added meta tags & favicons
adjusted color schemes, removed pink (now matches design)
2022-02-09 12:42:18 +02:00
muzam
1a3f5d144c change accept template to starter 2022-02-09 14:26:19 +05:30
Joni Juup
66fb68d52e update share image 2022-02-08 18:58:59 +02:00
Joni Juup
aaeb32a576 update favicons 2022-02-08 18:29:12 +02:00
Joni Juup
78b5dcceb6 adjusting colors and themes 2022-02-08 18:07:37 +02:00
muzam
ce81c11c29 Update hooks installed info in card 2022-02-08 19:56:30 +05:30
muzam
3a98b95e3d fix firewall template link 2022-02-08 18:59:40 +05:30
muzam
8bc36655e2 disallow illegal characters in filename 2022-02-08 15:26:22 +05:30
muzam
b6ab536a60 Clear log on compile 2022-02-08 15:08:38 +05:30
Joni Juup
37a3d2b207 add share image 2022-02-07 17:10:00 +02:00
Joni Juup
cd0c5f8a0d meta tag changes 2022-02-07 16:54:31 +02:00
Vaclav Barta
8dde89fa9a Merge pull request #88 from eqlabs/feature/optimization
remove hardcoded file compilation options
2022-02-07 15:35:33 +01:00
Joni Juup
ca3d60cfb8 small ui fixes 2022-02-07 16:23:39 +02:00
Vaclav Barta
1a4d53cfbc removed hardcoded file compilation options 2022-02-07 15:03:47 +01:00
Joni Juup
94e126782b Merge pull request #65 from eqlabs/feature/panel-resize
Added panel resizing to all views.
2022-02-02 15:41:07 +02:00
Joni Juup
cc03c64f0a added a fixed height for logbox header to logbox content box height can be calculated easily 2022-02-02 15:03:31 +02:00
Joni Juup
3647aa6274 adjusted gutter sizes and highlight style 2022-02-02 12:39:08 +02:00
Joni Juup
a2a58f0ba9 light mode support 2022-02-02 12:15:58 +02:00
Joni Juup
c544a03be4 fixed log overflow, resize sizing 2022-02-02 12:12:07 +02:00
Joni Juup
9a09da88ec add panel resizing to views 2022-02-01 16:44:51 +02:00
Joni Juup
5850551906 fixed merge conflicts 2022-02-01 15:47:24 +02:00
muzam
e35e520d24 minor fix 2022-02-01 19:07:29 +05:30
muzamil
8077fc5865 Merge pull request #66 from eqlabs/feat/tabs
Transaction tabs
2022-02-01 19:02:26 +05:30
88 changed files with 2357 additions and 420 deletions

1
.prettierignore Normal file
View File

@@ -0,0 +1 @@
*.md

View File

@@ -181,7 +181,11 @@ const AccountDialog = ({
target="_blank"
rel="noreferrer noopener"
>
<Button size="sm" ghost css={{ color: "$green11 !important", mt: "$3" }}>
<Button
size="sm"
ghost
css={{ color: "$grass11 !important", mt: "$3" }}
>
<ArrowSquareOut size="15px" />
</Button>
</a>
@@ -195,13 +199,7 @@ const AccountDialog = ({
fontFamily: "$monospace",
}}
>
{activeAccount && activeAccount?.hooks?.length > 0
? activeAccount?.hooks
.map(i => {
return `${i?.substring(0, 6)}...${i?.substring(i.length - 4)}`;
})
.join(", ")
: ""}
{activeAccount && activeAccount.hooks.length}
</Text>
</Flex>
</Flex>
@@ -289,18 +287,19 @@ const Accounts: FC<AccountProps> = props => {
display: "flex",
backgroundColor: props.card ? "$deep" : "$mauve1",
position: "relative",
width: "100%",
flex: "1",
height: "100%",
flexShrink: 0,
borderTop: "1px solid $mauve6",
borderRight: "1px solid $mauve6",
borderLeft: "1px solid $mauve6",
borderBottom: "1px solid $mauve6",
border: "1px solid $mauve6",
borderRadius: props.card ? "$md" : undefined,
}}
>
<Container css={{ p: 0, flexShrink: 1, height: "100%" }}>
<Flex css={{ py: "$3", borderBottom: props.card ? "1px solid $mauve6" : undefined }}>
<Flex
css={{
py: "$3",
borderBottom: props.card ? "1px solid $mauve6" : undefined,
}}
>
<Heading
as="h3"
css={{
@@ -363,7 +362,12 @@ const Accounts: FC<AccountProps> = props => {
>
<Box>
<Text>{account.name} </Text>
<Text css={{ color: "$mauve9" }}>
<Text
css={{
color: "$textMuted",
wordBreak: "break-word",
}}
>
{account.address} (
{Dinero({
amount: Number(account?.xrp || "0"),
@@ -400,7 +404,7 @@ const Accounts: FC<AccountProps> = props => {
</Flex>
{props.showHookStats && (
<Text muted small css={{ mt: "$2" }}>
X hooks installed
{account.hooks.length} hook{account.hooks.length === 1 ? "" : "s"} installed
</Text>
)}
</Flex>

View File

@@ -66,6 +66,12 @@ export const StyledButton = styled("button", {
},
},
variant: {
link: {
textDecoration: "underline",
fontSize: "inherit",
color: "$textMuted",
textUnderlineOffset: "2px",
},
default: {
backgroundColor: "$mauve12",
boxShadow: "inset 0 0 0 1px $colors$mauve12",
@@ -81,8 +87,7 @@ export const StyledButton = styled("button", {
boxShadow: "inset 0 0 0 1px $colors$mauve11",
},
"&:focus": {
boxShadow:
"inset 0 0 0 1px $colors$mauve12, inset 0 0 0 2px $colors$mauve12",
boxShadow: "inset 0 0 0 1px $colors$mauve12, inset 0 0 0 2px $colors$mauve12",
},
'&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
{
@@ -91,26 +96,26 @@ export const StyledButton = styled("button", {
},
},
primary: {
backgroundColor: `$pink9`,
boxShadow: "inset 0 0 0 1px $colors$pink9",
backgroundColor: `$accent`,
boxShadow: "inset 0 0 0 1px $colors$purple9",
color: "$white",
"@hover": {
"&:hover": {
backgroundColor: "$pink10",
boxShadow: "inset 0 0 0 1px $colors$pink11",
backgroundColor: "$purple10",
boxShadow: "inset 0 0 0 1px $colors$purple11",
},
},
"&:active": {
backgroundColor: "$pink8",
boxShadow: "inset 0 0 0 1px $colors$pink8",
backgroundColor: "$purple8",
boxShadow: "inset 0 0 0 1px $colors$purple8",
},
"&:focus": {
boxShadow: "inset 0 0 0 2px $colors$pink12",
boxShadow: "inset 0 0 0 2px $colors$purple12",
},
'&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
{
backgroundColor: "$mauve4",
boxShadow: "inset 0 0 0 1px $colors$pink8",
boxShadow: "inset 0 0 0 1px $colors$purple8",
},
},
secondary: {
@@ -137,7 +142,11 @@ export const StyledButton = styled("button", {
},
},
},
muted: {
true: {
color: "$textMuted",
},
},
outline: {
true: {
backgroundColor: "transparent",
@@ -227,21 +236,16 @@ export const StyledButton = styled("button", {
},
});
const CustomButton: React.FC<
React.ComponentProps<typeof StyledButton> & { as?: string }
> = React.forwardRef(({ children, as = "button", ...rest }, ref) => (
// @ts-expect-error
<StyledButton {...rest} ref={ref} as={as}>
<Flex
as="span"
css={{ gap: "$2", alignItems: "center" }}
className="button-content"
>
{children}
</Flex>
{rest.isLoading && <Spinner css={{ position: "absolute" }} />}
</StyledButton>
));
const CustomButton: React.FC<React.ComponentProps<typeof StyledButton> & { as?: string }> =
React.forwardRef(({ children, as = "button", ...rest }, ref) => (
// @ts-expect-error
<StyledButton {...rest} ref={ref} as={as}>
<Flex as="span" css={{ gap: "$2", alignItems: "center" }} className="button-content">
{children}
</Flex>
{rest.isLoading && <Spinner css={{ position: "absolute" }} />}
</StyledButton>
));
CustomButton.displayName = "CustomButton";

View File

@@ -1,20 +1,22 @@
import React, { useRef } from "react";
import React, { useRef, useState } from "react";
import { useSnapshot, ref } from "valtio";
import Editor, { loader } from "@monaco-editor/react";
import type monaco from "monaco-editor";
import { useTheme } from "next-themes";
import { useRouter } from "next/router";
import NextLink from "next/link";
import ReactTimeAgo from "react-time-ago";
import filesize from "filesize";
import Box from "./Box";
import Container from "./Container";
import dark from "../theme/editor/amy.json";
import light from "../theme/editor/xcode_default.json";
import state from "../state";
import wat from "../utils/wat-highlight";
import EditorNavigation from "./EditorNavigation";
import Text from "./Text";
import Link from "./Link";
import { Button, Text, Link, Flex } from ".";
loader.config({
paths: {
@@ -22,11 +24,72 @@ loader.config({
},
});
const FILESIZE_BREAKPOINTS: [number, number] = [2 * 1024, 5 * 1024];
const DeployEditor = () => {
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>();
const snap = useSnapshot(state);
const router = useRouter();
const { theme } = useTheme();
const [showContent, setShowContent] = useState(false);
const activeFile = snap.files[snap.active];
const compiledSize = activeFile?.compiledContent?.byteLength || 0;
const color =
compiledSize > FILESIZE_BREAKPOINTS[1]
? "$error"
: compiledSize > FILESIZE_BREAKPOINTS[0]
? "$warning"
: "$success";
const CompiledStatView = activeFile && (
<Flex
column
align="center"
css={{
fontSize: "$sm",
fontFamily: "$monospace",
textAlign: "center",
}}
>
<Flex row align="center">
<Text css={{ mr: "$1" }}>
Compiled {activeFile.name.split(".")[0] + ".wasm"}
</Text>
{activeFile?.lastCompiled && (
<ReactTimeAgo date={activeFile.lastCompiled} locale="en-US" />
)}
{activeFile.compiledContent?.byteLength && (
<Text css={{ ml: "$2", color }}>
({filesize(activeFile.compiledContent.byteLength)})
</Text>
)}
</Flex>
<Button variant="link" onClick={() => setShowContent(true)}>
View as WAT-file
</Button>
</Flex>
);
const NoContentView = !snap.loading && router.isReady && (
<Text
css={{
mt: "-60px",
fontSize: "$sm",
fontFamily: "$monospace",
maxWidth: "300px",
textAlign: "center",
}}
>
{`You haven't compiled any files yet, compile files on `}
<NextLink shallow href={`/develop/${router.query.slug}`} passHref>
<Link as="a">develop view</Link>
</NextLink>
</Text>
);
const isContent =
snap.files?.filter((file) => file.compiledWatContent).length > 0 &&
router.isReady;
return (
<Box
css={{
@@ -34,65 +97,53 @@ const DeployEditor = () => {
display: "flex",
position: "relative",
flexDirection: "column",
backgroundColor: "$mauve3",
backgroundColor: "$mauve2",
width: "100%",
}}
>
<EditorNavigation showWat />
{snap.files?.filter((file) => file.compiledWatContent).length > 0 &&
router.isReady ? (
<Editor
className="hooks-editor"
// keepCurrentModel
defaultLanguage={snap.files?.[snap.active]?.language}
language={snap.files?.[snap.active]?.language}
path={`file://tmp/c/${snap.files?.[snap.active]?.name}.wat`}
value={snap.files?.[snap.active]?.compiledWatContent || ""}
beforeMount={(monaco) => {
if (!state.editorCtx) {
state.editorCtx = ref(monaco.editor);
// @ts-expect-error
monaco.editor.defineTheme("dark", dark);
// @ts-expect-error
monaco.editor.defineTheme("light", light);
}
}}
onMount={(editor, monaco) => {
editorRef.current = editor;
editor.updateOptions({
glyphMargin: true,
readOnly: true,
});
}}
theme={theme === "dark" ? "dark" : "light"}
/>
) : (
<Container
css={{
display: "flex",
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
{!snap.loading && router.isReady && (
<Text
css={{
mt: "-60px",
fontSize: "14px",
fontFamily: "$monospace",
maxWidth: "300px",
textAlign: "center",
}}
>
{`You haven't compiled any files yet, compile files on `}
<NextLink shallow href={`/develop/${router.query.slug}`} passHref>
<Link as="a">develop view</Link>
</NextLink>
</Text>
)}
</Container>
)}
<Container
css={{
display: "flex",
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
{!isContent ? (
NoContentView
) : !showContent ? (
CompiledStatView
) : (
<Editor
className="hooks-editor"
defaultLanguage={"wat"}
language={"wat"}
path={`file://tmp/c/${snap.files?.[snap.active]?.name}.wat`}
value={snap.files?.[snap.active]?.compiledWatContent || ""}
beforeMount={(monaco) => {
monaco.languages.register({ id: "wat" });
monaco.languages.setLanguageConfiguration("wat", wat.config);
monaco.languages.setMonarchTokensProvider("wat", wat.tokens);
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;
editor.updateOptions({
glyphMargin: true,
readOnly: true,
});
}}
theme={theme === "dark" ? "dark" : "light"}
/>
)}
</Container>
</Box>
);
};

View File

@@ -71,7 +71,7 @@ const itemStyles = {
},
"&:focus": {
backgroundColor: "$pink9",
backgroundColor: "$purple9",
color: "$white",
},
};
@@ -85,8 +85,8 @@ const StyledRadioItem = styled(DropdownMenuPrimitive.RadioItem, {
});
const StyledTriggerItem = styled(DropdownMenuPrimitive.TriggerItem, {
'&[data-state="open"]': {
backgroundColor: "$pink9",
color: "$pink9",
backgroundColor: "$purple9",
color: "$purple9",
},
...itemStyles,
});

View File

@@ -26,7 +26,12 @@ import NewWindow from "react-new-window";
import { signOut, useSession } from "next-auth/react";
import { useSnapshot } from "valtio";
import { createNewFile, syncToGist, updateEditorSettings, downloadAsZip } from "../state/actions";
import {
createNewFile,
syncToGist,
updateEditorSettings,
downloadAsZip,
} from "../state/actions";
import state from "../state";
import Box from "./Box";
import Button from "./Button";
@@ -57,7 +62,7 @@ import { styled } from "../stitches.config";
const DEFAULT_EXTENSION = ".c";
const ErrorText = styled(Text, {
color: "$red9",
color: "$error",
mt: "$1",
display: "block",
});
@@ -85,9 +90,16 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
const validateFilename = useCallback(
(filename: string): { error: string | null } => {
// check if filename already exists
if (snap.files.find(file => file.name === filename)) {
return { error: "Filename already exists." };
}
// check for illegal characters
const ILLEGAL_REGEX = /[/]/gi;
if (filename.match(ILLEGAL_REGEX)) {
return { error: "Filename contains illegal characters" };
}
// More checks in future
return { error: null };
},
@@ -95,7 +107,9 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
);
const handleConfirm = useCallback(() => {
// add default extension in case omitted
let _filename = filename.includes(".") ? filename : filename + DEFAULT_EXTENSION;
let _filename = filename.includes(".")
? filename
: filename + DEFAULT_EXTENSION;
const chk = validateFilename(_filename);
if (chk.error) {
setNewfileError(`Error: ${chk.error}`);
@@ -139,7 +153,9 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
return (
<Button
size="sm"
outline={showWat ? snap.activeWat !== index : snap.active !== index}
outline={
showWat ? snap.activeWat !== index : snap.active !== index
}
onClick={() => (state.active = index)}
key={file.name + index}
css={{
@@ -174,7 +190,8 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
// If deleted file is behind active tab
// we keep the current state otherwise
// select previous file on the list
state.active = index > snap.active ? snap.active : snap.active - 1;
state.active =
index > snap.active ? snap.active : snap.active - 1;
}}
>
<X size="9px" weight="bold" />
@@ -184,10 +201,18 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
);
})}
{!showWat && (
<Dialog open={isNewfileDialogOpen} onOpenChange={setIsNewfileDialogOpen}>
<Dialog
open={isNewfileDialogOpen}
onOpenChange={setIsNewfileDialogOpen}
>
<DialogTrigger asChild>
<Button ghost size="sm" css={{ alignItems: "center", px: "$2", mr: "$3" }}>
<Plus size="16px" /> {snap.files.length === 0 && "Add new file"}
<Button
ghost
size="sm"
css={{ alignItems: "center", px: "$2", mr: "$3" }}
>
<Plus size="16px" />{" "}
{snap.files.length === 0 && "Add new file"}
</Button>
</DialogTrigger>
<DialogContent>
@@ -196,8 +221,8 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
<label>Filename</label>
<Input
value={filename}
onChange={e => setFilename(e.target.value)}
onKeyPress={e => {
onChange={(e) => setFilename(e.target.value)}
onKeyPress={(e) => {
if (e.key === "Enter") {
handleConfirm();
}
@@ -216,10 +241,7 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
<DialogClose asChild>
<Button outline>Cancel</Button>
</DialogClose>
<Button
variant="primary"
onClick={handleConfirm}
>
<Button variant="primary" onClick={handleConfirm}>
Create file
</Button>
</Flex>
@@ -237,11 +259,13 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
<Flex
css={{
py: "$3",
backgroundColor: "$mauve3",
backgroundColor: "$mauve2",
zIndex: 1,
}}
>
<Container css={{ width: "unset", display: "flex", alignItems: "center" }}>
<Container
css={{ width: "unset", display: "flex", alignItems: "center" }}
>
{status === "authenticated" ? (
<DropdownMenu>
<DropdownMenuTrigger asChild>
@@ -274,10 +298,15 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem disabled onClick={() => signOut()}>
<User size="16px" /> {session?.user?.username} ({session?.user.name})
<User size="16px" /> {session?.user?.username} (
{session?.user.name})
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => window.open(`http://gist.github.com/${session?.user.username}`)}
onClick={() =>
window.open(
`http://gist.github.com/${session?.user.username}`
)
}
>
<ArrowSquareOut size="16px" />
Go to your Gist
@@ -291,7 +320,12 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
</DropdownMenuContent>
</DropdownMenu>
) : (
<Button outline size="sm" css={{ mr: "$3" }} onClick={() => setPopUp(true)}>
<Button
outline
size="sm"
css={{ mr: "$3" }}
onClick={() => setPopUp(true)}
>
<GithubLogo size="16px" /> Login
</Button>
)}
@@ -330,7 +364,13 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
},
}}
>
<Button isLoading={snap.zipLoading} onClick={downloadAsZip} outline size="sm" css={{ alignItems: "center" }}>
<Button
isLoading={snap.zipLoading}
onClick={downloadAsZip}
outline
size="sm"
css={{ alignItems: "center" }}
>
<DownloadSimple size="16px" />
</Button>
<Button
@@ -338,7 +378,9 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
size="sm"
css={{ alignItems: "center" }}
onClick={() => {
navigator.clipboard.writeText(`${window.location.origin}/develop/${snap.gistId}`);
navigator.clipboard.writeText(
`${window.location.origin}/develop/${snap.gistId}`
);
toast.success("Copied share link to clipboard!");
}}
>
@@ -368,7 +410,10 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem disabled={snap.zipLoading} onClick={downloadAsZip}>
<DropdownMenuItem
disabled={snap.zipLoading}
onClick={downloadAsZip}
>
<DownloadSimple size="16px" /> Download as ZIP
</DropdownMenuItem>
<DropdownMenuItem
@@ -383,7 +428,9 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
Copy share link to clipboard
</DropdownMenuItem>
<DropdownMenuItem
disabled={session?.user.username !== snap.gistOwner || !snap.gistId}
disabled={
session?.user.username !== snap.gistOwner || !snap.gistId
}
onClick={() => {
syncToGist(session);
}}
@@ -409,15 +456,21 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
</DropdownMenu>
</Stack>
{popup && !session ? <NewWindow center="parent" url="/sign-in" /> : null}
{popup && !session ? (
<NewWindow center="parent" url="/sign-in" />
) : null}
</Container>
</Flex>
<AlertDialog open={createNewAlertOpen} onOpenChange={value => setCreateNewAlertOpen(value)}>
<AlertDialog
open={createNewAlertOpen}
onOpenChange={(value) => setCreateNewAlertOpen(value)}
>
<AlertDialogContent>
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
<AlertDialogDescription>
This action will create new <strong>public</strong> Github Gist from your current saved
files. You can delete gist anytime from your GitHub Gists page.
This action will create new <strong>public</strong> Github Gist from
your current saved files. You can delete gist anytime from your
GitHub Gists page.
</AlertDialogDescription>
<Flex css={{ justifyContent: "flex-end", gap: "$3" }}>
<AlertDialogCancel asChild>
@@ -451,8 +504,8 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
type="number"
min="1"
value={editorSettings.tabSize}
onChange={e =>
setEditorSettings(curr => ({
onChange={(e) =>
setEditorSettings((curr) => ({
...curr,
tabSize: Number(e.target.value),
}))
@@ -462,12 +515,18 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
<Flex css={{ marginTop: 25, justifyContent: "flex-end", gap: "$3" }}>
<DialogClose asChild>
<Button outline onClick={() => updateEditorSettings(editorSettings)}>
<Button
outline
onClick={() => updateEditorSettings(editorSettings)}
>
Cancel
</Button>
</DialogClose>
<DialogClose asChild>
<Button variant="primary" onClick={() => updateEditorSettings(editorSettings)}>
<Button
variant="primary"
onClick={() => updateEditorSettings(editorSettings)}
>
Save changes
</Button>
</DialogClose>

View File

@@ -16,9 +16,37 @@ const Flex = styled(Box, {
},
fluid: {
true: {
width: '100%'
}
}
width: "100%",
},
},
align: {
start: {
alignItems: "start",
},
center: {
alignItems: "center",
},
end: {
alignItems: "end",
},
},
justify: {
start: {
justifyContent: "start",
},
center: {
justifyContent: "center",
},
end: {
justifyContent: "end",
},
"space-between": {
justifyContent: "space-between",
},
"space-around": {
justifyContent: "space-around",
},
},
},
});

View File

@@ -5,6 +5,7 @@ import type monaco from "monaco-editor";
import { ArrowBendLeftUp } from "phosphor-react";
import { useTheme } from "next-themes";
import { useRouter } from "next/router";
import uniqBy from "lodash.uniqby";
import Box from "./Box";
import Container from "./Container";
@@ -21,6 +22,8 @@ import { createLanguageClient, createWebSocket } from "../utils/languageClient";
import { listen } from "@codingame/monaco-jsonrpc";
import ReconnectingWebSocket from "reconnecting-websocket";
import docs from "../xrpl-hooks-docs/docs";
loader.config({
paths: {
vs: "https://cdn.jsdelivr.net/npm/monaco-editor@0.30.1/min/vs",
@@ -29,15 +32,80 @@ loader.config({
const validateWritability = (editor: monaco.editor.IStandaloneCodeEditor) => {
const currPath = editor.getModel()?.uri.path;
if (apiHeaderFiles.find(h => currPath?.endsWith(h))) {
if (apiHeaderFiles.find((h) => currPath?.endsWith(h))) {
editor.updateOptions({ readOnly: true });
} else {
editor.updateOptions({ readOnly: false });
}
};
let decorations: { [key: string]: string[] } = {};
const setMarkers = (monacoE: typeof monaco) => {
// Get all the markers that are active at the moment,
// Also if same error is there twice, we can show the content
// only once (that's why we're using uniqBy)
const markers = uniqBy(
monacoE.editor
.getModelMarkers({})
// Filter out the markers that are hooks specific
.filter(
(marker) =>
typeof marker?.code === "string" &&
// Take only markers that starts with "hooks-"
marker?.code?.includes("hooks-")
),
"code"
);
// Get the active model (aka active file you're editing)
// const model = monacoE.editor?.getModel(
// monacoE.Uri.parse(`file:///work/c/${state.files?.[state.active]?.name}`)
// );
// console.log(state.active);
// Add decoration (aka extra hoverMessages) to markers in the
// exact same range (location) where the markers are
const models = monacoE.editor.getModels();
models.forEach((model) => {
console.log(decorations);
decorations[model.id] = model?.deltaDecorations(
decorations?.[model.id] || [],
markers
.filter((marker) =>
marker?.resource.path
.split("/")
.includes(`${state.files?.[state.active]?.name}`)
)
.map((marker) => ({
range: new monacoE.Range(
marker.startLineNumber,
marker.startColumn,
marker.endLineNumber,
marker.endColumn
),
options: {
hoverMessage: {
value:
// Find the related hover message markdown from the
// /xrpl-hooks-docs/xrpl-hooks-docs-files.json file
// which was generated from rst files
(typeof marker.code === "string" &&
docs[marker?.code]?.toString()) ||
"",
supportHtml: true,
isTrusted: true,
},
},
}))
);
});
console.log("decorat", decorations);
};
const HooksEditor = () => {
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>();
const monacoRef = useRef<typeof monaco>();
const subscriptionRef = useRef<ReconnectingWebSocket | null>(null);
const snap = useSnapshot(state);
const router = useRouter();
@@ -52,6 +120,11 @@ const HooksEditor = () => {
subscriptionRef?.current?.close();
};
}, []);
useEffect(() => {
if (monacoRef.current) {
setMarkers(monacoRef.current);
}
}, [snap.active]);
return (
<Box
css={{
@@ -60,11 +133,12 @@ const HooksEditor = () => {
display: "flex",
position: "relative",
flexDirection: "column",
backgroundColor: "$mauve3",
backgroundColor: "$mauve2",
width: "100%",
}}
>
<EditorNavigation />
{console.log(snap)}
{snap.files.length > 0 && router.isReady ? (
<Editor
className="hooks-editor"
@@ -73,9 +147,10 @@ const HooksEditor = () => {
language={snap.files?.[snap.active]?.language}
path={`file:///work/c/${snap.files?.[snap.active]?.name}`}
defaultValue={snap.files?.[snap.active]?.content}
beforeMount={monaco => {
beforeMount={(monaco) => {
console.log(monaco.languages.getLanguages());
if (!snap.editorCtx) {
snap.files.forEach(file =>
snap.files.forEach((file) =>
monaco.editor.createModel(
file.content,
file.language,
@@ -100,7 +175,7 @@ const HooksEditor = () => {
// listen when the web socket is opened
listen({
webSocket: webSocket as WebSocket,
onConnection: connection => {
onConnection: (connection) => {
// create and start the language client
const languageClient = createLanguageClient(connection);
const disposable = languageClient.start();
@@ -133,16 +208,29 @@ const HooksEditor = () => {
}}
onMount={(editor, monaco) => {
editorRef.current = editor;
monacoRef.current = monaco;
editor.updateOptions({
glyphMargin: true,
lightbulb: {
enabled: true,
},
});
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {
saveFile();
editor.addCommand(
monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS,
() => {
saveFile();
}
);
// When the markers (errors/warnings from clangd language server) change
// Lets improve the markers by adding extra content to them from related
// md files
monaco.editor.onDidChangeMarkers(() => {
if (monacoRef.current) {
setMarkers(monacoRef.current);
}
});
validateWritability(editor)
validateWritability(editor);
}}
theme={theme === "dark" ? "dark" : "light"}
/>
@@ -160,7 +248,9 @@ const HooksEditor = () => {
<Box css={{ display: "inline-flex", pl: "35px" }}>
<ArrowBendLeftUp size={30} />
</Box>
<Box css={{ pl: "0px", pt: "15px", flex: 1, display: "inline-flex" }}>
<Box
css={{ pl: "0px", pt: "15px", flex: 1, display: "inline-flex" }}
>
<Text
css={{
fontSize: "14px",
@@ -168,7 +258,7 @@ const HooksEditor = () => {
fontFamily: "$monospace",
}}
>
Click the link above to create a your file
Click the link above to create your file
</Text>
</Box>
</Box>

View File

@@ -117,15 +117,17 @@ export const Input = styled("input", {
},
state: {
invalid: {
boxShadow: "inset 0 0 0 1px $colors$red7",
boxShadow: "inset 0 0 0 1px $colors$crimson7",
"&:focus": {
boxShadow: "inset 0px 0px 0px 1px $colors$red8, 0px 0px 0px 1px $colors$red8",
boxShadow:
"inset 0px 0px 0px 1px $colors$crimson8, 0px 0px 0px 1px $colors$crimson8",
},
},
valid: {
boxShadow: "inset 0 0 0 1px $colors$green7",
boxShadow: "inset 0 0 0 1px $colors$grass7",
"&:focus": {
boxShadow: "inset 0px 0px 0px 1px $colors$green8, 0px 0px 0px 1px $colors$green8",
boxShadow:
"inset 0px 0px 0px 1px $colors$grass8, 0px 0px 0px 1px $colors$grass8",
},
},
},

View File

@@ -21,7 +21,14 @@ interface ILogBox {
enhanced?: boolean;
}
const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children, renderNav, enhanced }) => {
const LogBox: React.FC<ILogBox> = ({
title,
clearLog,
logs,
children,
renderNav,
enhanced,
}) => {
const logRef = useRef<HTMLPreElement>(null);
const { stayScrolled /*, scrollBottom*/ } = useStayScrolled(logRef);
@@ -38,10 +45,23 @@ const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children, renderNav,
background: "$mauve1",
position: "relative",
flex: 1,
height: "100%",
}}
>
<Container css={{ px: 0, flexShrink: 1 }}>
<Flex css={{ py: "$3", alignItems: "center", fontSize: "$sm", fontWeight: 300 }}>
<Container
css={{
px: 0,
height: "100%",
}}
>
<Flex
css={{
height: "48px",
alignItems: "center",
fontSize: "$sm",
fontWeight: 300,
}}
>
<Heading
as="h3"
css={{
@@ -67,6 +87,7 @@ const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children, renderNav,
)}
</Flex>
</Flex>
<Box
as="pre"
ref={logRef}
@@ -76,14 +97,14 @@ const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children, renderNav,
display: "flex",
flexDirection: "column",
width: "100%",
height: "160px",
height: "calc(100% - 48px)", // 100% minus the logbox header height
overflowY: "auto",
fontSize: "13px",
fontWeight: "$body",
fontFamily: "$monospace",
px: "$3",
pb: "$2",
whiteSpace: "normal",
overflowY: "auto",
}}
>
{logs?.map((log, index) => (
@@ -96,7 +117,7 @@ const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children, renderNav,
backgroundColor: enhanced ? "$backgroundAlt" : undefined,
},
},
p: "$2 $1",
p: enhanced ? "$2 $1" : undefined,
}}
>
<LogText variant={log.type}>

View File

@@ -4,20 +4,20 @@ const Text = styled("span", {
fontFamily: "$monospace",
lineHeight: "$body",
color: "$text",
wordWrap: 'break-word',
wordWrap: "break-word",
variants: {
variant: {
log: {
color: "$text",
},
warning: {
color: "$yellow11",
color: "$warning",
},
error: {
color: "$red11",
color: "$error",
},
success: {
color: "$green11",
color: "$success",
},
},
capitalize: {

View File

@@ -1,8 +1,8 @@
import { styled } from "../stitches.config";
const SVG = styled("svg", {
"& #path-one, & #path-two": {
fill: "$text",
"& #path": {
fill: "$accent",
},
});
function Logo({
@@ -14,21 +14,18 @@ function Logo({
}) {
return (
<SVG
width={width || "1em"}
height={height || "1em"}
viewBox="0 0 28 22"
width={width || "1.1em"}
height={height || "1.1em"}
viewBox="0 0 294 283"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
id="path-one"
d="M19.603 3.87h2.3l-4.786 4.747a4.466 4.466 0 01-6.276 0L6.054 3.871h2.3l3.636 3.605a2.828 2.828 0 003.975 0l3.638-3.605zM8.325 17.069h-2.3l4.816-4.776a4.466 4.466 0 016.276 0l4.816 4.776h-2.3l-3.665-3.635a2.828 2.828 0 00-3.975 0l-3.668 3.635z"
/>
<path
id="path-two"
fillRule="evenodd"
clipRule="evenodd"
d="M1.556 9.769h4.751v1.555H1.556v10.072H0V0h1.556v9.769zM26.444 9.769h-4.751v1.555h4.751v10.072H28V0h-1.556v9.769z"
id="path"
d="M265.827 235L172.416 141.589L265.005 49H226.822L147.732 128.089H53.5514L27.4824 155.089H147.732L227.643 235H265.827Z"
fill="#9D2DFF"
/>
</SVG>
);

View File

@@ -27,7 +27,7 @@ import {
DialogTrigger,
} from "./Dialog";
import PanelBox from "./PanelBox";
import { templateFileIds } from '../state/constants';
import { templateFileIds } from "../state/constants";
const Navigation = () => {
const router = useRouter();
@@ -40,9 +40,11 @@ const Navigation = () => {
as="nav"
css={{
display: "flex",
backgroundColor: "$mauve1",
borderBottom: "1px solid $mauve6",
position: "relative",
zIndex: 2003,
height: "60px",
}}
>
<Container
@@ -60,7 +62,7 @@ const Navigation = () => {
pr: "$4",
}}
>
<Link href="/" passHref>
<Link href={gistId ? `/develop/${gistId}` : "/develop"} passHref>
<Box
as="a"
css={{
@@ -69,7 +71,7 @@ const Navigation = () => {
color: "$textColor",
}}
>
<Logo width="30px" height="30px" />
<Logo width="32px" height="32px" />
</Box>
</Link>
<Flex
@@ -83,20 +85,42 @@ const Navigation = () => {
<Spinner />
) : (
<>
<Heading css={{ lineHeight: 1 }}>{snap.files?.[0]?.name || "XRPL Hooks"}</Heading>
<Text css={{ fontSize: "$xs", color: "$mauve10", lineHeight: 1 }}>
<Heading css={{ lineHeight: 1 }}>
{snap.files?.[0]?.name || "XRPL Hooks"}
</Heading>
<Text
css={{ fontSize: "$xs", color: "$mauve10", lineHeight: 1 }}
>
{snap.files.length > 0 ? "Gist: " : "Playground"}
<Text css={{ color: "$mauve12" }}>
{snap.files.length > 0 &&
`${snap.gistOwner || "-"}/${truncate(snap.gistId || "")}`}
</Text>
{snap.files.length > 0 && (
<Link
href={`https://gist.github.com/${snap.gistOwner || ""}/${
snap.gistId || ""
}`}
passHref
>
<Text
as="a"
target="_blank"
rel="noreferrer noopener"
css={{ color: "$mauve12" }}
>
{`${snap.gistOwner || "-"}/${truncate(
snap.gistId || ""
)}`}
</Text>
</Link>
)}
</Text>
</>
)}
</Flex>
{router.isReady && (
<ButtonGroup css={{ marginLeft: "auto" }}>
<Dialog open={snap.mainModalOpen} onOpenChange={open => (state.mainModalOpen = open)}>
<Dialog
open={snap.mainModalOpen}
onOpenChange={(open) => (state.mainModalOpen = open)}
>
<DialogTrigger asChild>
<Button outline>
<FolderOpen size="15px" />
@@ -130,10 +154,12 @@ const Navigation = () => {
flexDirection: "column",
p: "$7",
height: "100%",
backgroundColor: "$mauve2",
"@md": {
width: "30%",
maxWidth: "300px",
borderBottom: "0px",
borderRight: "1px solid $colors$mauve5",
borderRight: "1px solid $colors$mauve6",
},
}}
>
@@ -144,9 +170,11 @@ const Navigation = () => {
alignItems: "center",
gap: "$3",
fontSize: "$xl",
lineHeight: "$one",
fontWeight: "$bold",
}}
>
<Logo width="30px" height="30px" /> XRPL Hooks Editor
<Logo width="48px" height="48px" /> XRPL Hooks Builder
</DialogTitle>
<DialogDescription as="div">
<Text
@@ -157,17 +185,20 @@ const Navigation = () => {
mb: "$7",
}}
>
Hooks add smart contract functionality to the XRP Ledger.
Hooks add smart contract functionality to the XRP
Ledger.
</Text>
<Flex css={{ flexDirection: "column", gap: "$2", mt: "$2" }}>
<Flex
css={{ flexDirection: "column", gap: "$2", mt: "$2" }}
>
<Text
css={{
display: "inline-flex",
alignItems: "center",
gap: "$3",
color: "$green9",
color: "$purple10",
"&:hover": {
color: "$green11 !important",
color: "$purple11",
},
"&:focus": {
outline: 0,
@@ -178,7 +209,7 @@ const Navigation = () => {
target="_blank"
href="https://github.com/XRPL-Labs/xrpld-hooks"
>
<ArrowUpRight size="15px" /> Developing Hooks
<ArrowUpRight size="15px" /> Hooks Github
</Text>
<Text
@@ -186,9 +217,9 @@ const Navigation = () => {
display: "inline-flex",
alignItems: "center",
gap: "$3",
color: "$green9",
color: "$purple10",
"&:hover": {
color: "$green11 !important",
color: "$purple11",
},
"&:focus": {
outline: 0,
@@ -206,9 +237,9 @@ const Navigation = () => {
display: "inline-flex",
alignItems: "center",
gap: "$3",
color: "$green9",
color: "$purple10",
"&:hover": {
color: "$green11 !important",
color: "$purple11",
},
"&:focus": {
outline: 0,
@@ -235,38 +266,53 @@ const Navigation = () => {
gap: "$3",
alignItems: "flex-start",
flexWrap: "wrap",
backgroundImage: `url('/pattern.svg'), url('/pattern-2.svg')`,
backgroundRepeat: "no-repeat",
backgroundPosition: "bottom left, top right",
backgroundColor: "$mauve1",
"@md": {
gridTemplateColumns: "1fr 1fr 1fr",
gridTemplateRows: "max-content",
},
}}
>
<PanelBox as="a" href={`/develop/${templateFileIds.starter}`}>
<PanelBox
as="a"
href={`/develop/${templateFileIds.starter}`}
>
<Heading>Starter</Heading>
<Text>Just an empty starter with essential imports</Text>
<Text>
Just a basic starter with essential imports
</Text>
</PanelBox>
<PanelBox as="a" href={`/develop/${templateFileIds.starter}`}>
<PanelBox
as="a"
href={`/develop/${templateFileIds.firewall}`}
>
<Heading>Firewall</Heading>
<Text>This Hook essentially checks a blacklist of accounts</Text>
<Text>
This Hook essentially checks a blacklist of accounts
</Text>
</PanelBox>
<PanelBox as="a" href={`/develop/${templateFileIds.accept}`}>
<Heading>Accept</Heading>
<Text>This hook just accepts any transaction coming through it</Text>
</PanelBox>
<PanelBox as="a" href={`/develop/${templateFileIds.notary}`}>
<PanelBox
as="a"
href={`/develop/${templateFileIds.notary}`}
>
<Heading>Notary</Heading>
<Text>Collecting signatures for multi-sign transactions</Text>
<Text>
Collecting signatures for multi-sign transactions
</Text>
</PanelBox>
<PanelBox as="a" href={`/develop/${templateFileIds.carbon}`}>
<PanelBox
as="a"
href={`/develop/${templateFileIds.carbon}`}
>
<Heading>Carbon</Heading>
<Text>Send a percentage of sum to an address</Text>
</PanelBox>
<PanelBox as="a" href={`/develop/${templateFileIds.peggy}`}>
<PanelBox
as="a"
href={`/develop/${templateFileIds.peggy}`}
>
<Heading>Peggy</Heading>
<Text>An oracle based stabe coin hook</Text>
<Text>An oracle based stable coin hook</Text>
</PanelBox>
</Flex>
</Flex>
@@ -313,25 +359,53 @@ const Navigation = () => {
}}
>
<ButtonGroup>
<Link href={gistId ? `/develop/${gistId}` : "/develop"} passHref shallow>
<Button as="a" outline={!router.pathname.includes("/develop")} uppercase>
<Link
href={gistId ? `/develop/${gistId}` : "/develop"}
passHref
shallow
>
<Button
as="a"
outline={!router.pathname.includes("/develop")}
uppercase
>
Develop
</Button>
</Link>
<Link href={gistId ? `/deploy/${gistId}` : "/deploy"} passHref shallow>
<Button as="a" outline={!router.pathname.includes("/deploy")} uppercase>
<Link
href={gistId ? `/deploy/${gistId}` : "/deploy"}
passHref
shallow
>
<Button
as="a"
outline={!router.pathname.includes("/deploy")}
uppercase
>
Deploy
</Button>
</Link>
<Link href={gistId ? `/test/${gistId}` : "/test"} passHref shallow>
<Button as="a" outline={!router.pathname.includes("/test")} uppercase>
<Link
href={gistId ? `/test/${gistId}` : "/test"}
passHref
shallow
>
<Button
as="a"
outline={!router.pathname.includes("/test")}
uppercase
>
Test
</Button>
</Link>
</ButtonGroup>
<Button outline disabled>
<BookOpen size="15px" />
</Button>
<Link href="https://xrpl-hooks.readme.io/" passHref>
<a target="_blank" rel="noreferrer noopener">
<Button outline>
<BookOpen size="15px" />
</Button>
</a>
</Link>
</Stack>
</Flex>
</Container>

View File

@@ -5,8 +5,8 @@ import Text from "./Text";
const PanelBox = styled("div", {
display: "flex",
flexDirection: "column",
border: "1px solid $colors$mauve5",
backgroundColor: "$mauve1",
border: "1px solid $colors$mauve6",
backgroundColor: "$mauve2",
padding: "$3",
borderRadius: "$sm",
fontWeight: "lighter",

View File

@@ -1,4 +1,10 @@
import React, { useEffect, useState, Fragment, isValidElement, useCallback } from "react";
import React, {
useEffect,
useState,
Fragment,
isValidElement,
useCallback,
} from "react";
import type { ReactNode, ReactElement } from "react";
import { Box, Button, Flex, Input, Stack, Text } from ".";
import {
@@ -13,7 +19,7 @@ import { Plus, X } from "phosphor-react";
import { styled } from "../stitches.config";
const ErrorText = styled(Text, {
color: "$red9",
color: "$error",
mt: "$1",
display: "block",
});
@@ -50,7 +56,7 @@ export const Tabs = ({
forceDefaultExtension,
}: Props) => {
const [active, setActive] = useState(activeIndex || 0);
const tabs: TabProps[] = children.map(elem => elem.props);
const tabs: TabProps[] = children.map((elem) => elem.props);
const [isNewtabDialogOpen, setIsNewtabDialogOpen] = useState(false);
const [tabname, setTabname] = useState("");
@@ -62,7 +68,7 @@ export const Tabs = ({
useEffect(() => {
if (activeHeader) {
const idx = tabs.findIndex(tab => tab.header === activeHeader);
const idx = tabs.findIndex((tab) => tab.header === activeHeader);
setActive(idx);
}
}, [activeHeader, tabs]);
@@ -74,7 +80,7 @@ export const Tabs = ({
const validateTabname = useCallback(
(tabname: string): { error: string | null } => {
if (tabs.find(tab => tab.header === tabname)) {
if (tabs.find((tab) => tab.header === tabname)) {
return { error: "Name already exists." };
}
return { error: null };
@@ -170,9 +176,16 @@ export const Tabs = ({
</Button>
))}
{onCreateNewTab && (
<Dialog open={isNewtabDialogOpen} onOpenChange={setIsNewtabDialogOpen}>
<Dialog
open={isNewtabDialogOpen}
onOpenChange={setIsNewtabDialogOpen}
>
<DialogTrigger asChild>
<Button ghost size="sm" css={{ alignItems: "center", px: "$2", mr: "$3" }}>
<Button
ghost
size="sm"
css={{ alignItems: "center", px: "$2", mr: "$3" }}
>
<Plus size="16px" /> {tabs.length === 0 && "Add new tab"}
</Button>
</DialogTrigger>
@@ -182,8 +195,8 @@ export const Tabs = ({
<label>Tabname</label>
<Input
value={tabname}
onChange={e => setTabname(e.target.value)}
onKeyPress={e => {
onChange={(e) => setTabname(e.target.value)}
onKeyPress={(e) => {
if (e.key === "Enter") {
handleCreateTab();
}
@@ -235,7 +248,9 @@ export const Tabs = ({
);
})
) : (
<Fragment key={tabs[active].header || active}>{tabs[active].children}</Fragment>
<Fragment key={tabs[active].header || active}>
{tabs[active].children}
</Fragment>
)}
</>
);

View File

@@ -1,15 +1,16 @@
export { default as Flex } from './Flex'
export { default as Container } from './Container'
export { default as Heading } from './Heading'
export { default as Stack } from './Stack'
export { default as Text } from './Text'
export { default as Input } from './Input'
export { default as Select } from './Select'
export * from './Tabs'
export * from './AlertDialog'
export { default as Box } from './Box'
export { default as Button } from './Button'
export { default as ButtonGroup } from './ButtonGroup'
export { default as DeployFooter } from './DeployFooter'
export * from './Dialog'
export * from './DropdownMenu'
export { default as Flex } from "./Flex";
export { default as Link } from "./Link";
export { default as Container } from "./Container";
export { default as Heading } from "./Heading";
export { default as Stack } from "./Stack";
export { default as Text } from "./Text";
export { default as Input } from "./Input";
export { default as Select } from "./Select";
export * from "./Tabs";
export * from "./AlertDialog";
export { default as Box } from "./Box";
export { default as Button } from "./Button";
export { default as ButtonGroup } from "./ButtonGroup";
export { default as DeployFooter } from "./DeployFooter";
export * from "./Dialog";
export * from "./DropdownMenu";

View File

@@ -11,6 +11,10 @@ module.exports = {
if (!isServer) {
config.resolve.fallback.fs = false;
}
config.module.rules.push({
test: /\.md$/,
use: "raw-loader",
});
return config;
},
};

View File

@@ -23,7 +23,10 @@
"base64-js": "^1.5.1",
"dinero.js": "^1.9.1",
"file-saver": "^2.0.5",
"filesize": "^8.0.7",
"javascript-time-ago": "^2.3.11",
"jszip": "^3.7.1",
"lodash.uniqby": "^4.7.0",
"monaco-editor": "^0.30.1",
"next": "^12.0.4",
"next-auth": "^4.0.0-beta.5",
@@ -41,7 +44,9 @@
"react-hot-toast": "^2.1.1",
"react-new-window": "^0.2.1",
"react-select": "^5.2.1",
"react-split": "^2.0.14",
"react-stay-scrolled": "^7.4.0",
"react-time-ago": "^7.1.9",
"reconnecting-websocket": "^4.4.0",
"valtio": "^1.2.5",
"vscode-languageserver": "^7.0.0",
@@ -53,10 +58,12 @@
"devDependencies": {
"@types/dinero.js": "^1.9.0",
"@types/file-saver": "^2.0.4",
"@types/lodash.uniqby": "^4.7.6",
"@types/pako": "^1.0.2",
"@types/react": "17.0.31",
"eslint": "7.32.0",
"eslint-config-next": "11.1.2",
"raw-loader": "^4.0.2",
"typescript": "4.4.4"
}
}

View File

@@ -13,11 +13,18 @@ import Navigation from "../components/Navigation";
import { fetchFiles } from "../state/actions";
import state from "../state";
import TimeAgo from "javascript-time-ago";
import en from "javascript-time-ago/locale/en.json";
TimeAgo.addDefaultLocale(en);
function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
const router = useRouter();
const slug = router.query?.slug;
const gistId = (Array.isArray(slug) && slug[0]) ?? null;
const origin = "https://xrpl-hooks-ide.vercel.app"; // TODO: Change when site is deployed
const shareImg = "/share-image.png";
useEffect(() => {
if (gistId && router.isReady) {
fetchFiles(gistId);
@@ -31,7 +38,73 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
return (
<>
<Head>
<title>XRPL Hooks Playground</title>
<meta charSet="utf-8" />
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
<meta name="format-detection" content="telephone=no" />
<meta property="og:url" content={`${origin}${router.asPath}`} />
<title>XRPL Hooks Editor</title>
<meta property="og:title" content="XRPL Hooks Editor" />
<meta name="twitter:title" content="XRPL Hooks Editor" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@xrpllabs" />
<meta
name="description"
content="Playground for buildings Hooks, that add smart contract functionality to the XRP Ledger."
/>
<meta
property="og:description"
content="Playground for buildings Hooks, that add smart contract functionality to the XRP Ledger."
/>
<meta
name="twitter:description"
content="Playground for buildings Hooks, that add smart contract functionality to the XRP Ledger.."
/>
<meta property="og:image" content={`${origin}${shareImg}`} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:image" content={`${origin}${shareImg}`} />
<link
rel="apple-touch-icon"
sizes="180x180"
href="/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/favicon-16x16.png"
/>
<link rel="manifest" href="/site.webmanifest" />
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#161618" />
<meta name="application-name" content="XRPL Hooks Editor" />
<meta name="msapplication-TileColor" content="#c10ad0" />
<meta
name="theme-color"
content="#161618"
media="(prefers-color-scheme: dark)"
/>
<meta
name="theme-color"
content="#FDFCFD"
media="(prefers-color-scheme: light)"
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossOrigin=""
/>
<link
href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital@0;1&family=Work+Sans:wght@400;600;700&display=swap"
rel="stylesheet"
/>
</Head>
<IdProvider>
<SessionProvider session={session}>

View File

@@ -16,21 +16,10 @@ class MyDocument extends Document {
}
render() {
globalStyles();
return (
<Html>
<Head>
<meta name="description" content="Playground for XRPL Hooks" />
<link rel="icon" href="/favicon.ico" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossOrigin=""
/>
<link
href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital@0;1&family=Work+Sans:wght@400;600;700&display=swap"
rel="stylesheet"
/>
<style
id="stitches"
dangerouslySetInnerHTML={{ __html: getCssText() }}

View File

@@ -1,8 +1,8 @@
import React from "react";
import dynamic from "next/dynamic";
import { Flex, Box } from "../../components";
import { useSnapshot } from "valtio";
import state from "../../state";
import Split from "react-split";
const DeployEditor = dynamic(() => import("../../components/DeployEditor"), {
ssr: false,
@@ -19,23 +19,41 @@ const LogBox = dynamic(() => import("../../components/LogBox"), {
const Deploy = () => {
const snap = useSnapshot(state);
return (
<>
<main style={{ display: "flex", flex: 1, height: 'calc(100vh - 30vh - 60px)' }}>
<Split
direction="vertical"
gutterSize={4}
gutterAlign="center"
sizes={[40, 60]}
style={{ height: "calc(100vh - 60px)" }}
>
<main style={{ display: "flex", flex: 1, position: "relative" }}>
<DeployEditor />
</main>
<Flex css={{ flexDirection: "row", width: "100%", minHeight: '225px', height: '30vh' }}>
<Box css={{ width: "100%" }}>
<Split
direction="horizontal"
sizes={[50, 50]}
minSize={[320, 160]}
gutterSize={4}
gutterAlign="center"
style={{
display: "flex",
flexDirection: "row",
width: "100%",
height: "100%",
}}
>
<div style={{ alignItems: "stretch", display: "flex" }}>
<Accounts />
</Box>
<Box css={{ width: "100%" }}>
</div>
<div>
<LogBox
title="Deploy Log"
logs={snap.deployLogs}
clearLog={() => (state.deployLogs = [])}
/>
</Box>
</Flex>
</>
</div>
</Split>
</Split>
);
};

View File

@@ -2,6 +2,7 @@ import dynamic from "next/dynamic";
import { useSnapshot } from "valtio";
import Hotkeys from "react-hot-keys";
import { Play } from "phosphor-react";
import Split from "react-split";
import type { NextPage } from "next";
import { compileCode } from "../../state/actions";
@@ -19,8 +20,16 @@ const LogBox = dynamic(() => import("../../components/LogBox"), {
const Home: NextPage = () => {
const snap = useSnapshot(state);
return (
<>
<Split
direction="vertical"
sizes={[70, 30]}
minSize={[100, 100]}
gutterAlign="center"
gutterSize={4}
style={{ height: "calc(100vh - 60px)" }}
>
<main style={{ display: "flex", flex: 1, position: "relative" }}>
<HooksEditor />
{snap.files[snap.active]?.name?.split(".")?.[1].toLowerCase() ===
@@ -65,7 +74,7 @@ const Home: NextPage = () => {
logs={snap.logs}
/>
</Box>
</>
</Split>
);
};

View File

@@ -1,7 +1,18 @@
import { Container, Flex, Box, Tabs, Tab, Input, Select, Text, Button } from "../../components";
import {
Container,
Flex,
Box,
Tabs,
Tab,
Input,
Select,
Text,
Button,
} from "../../components";
import { Play } from "phosphor-react";
import dynamic from "next/dynamic";
import { useSnapshot } from "valtio";
import Split from "react-split";
import state from "../../state";
import { sendTransaction } from "../../state/actions";
import { useCallback, useEffect, useState, FC } from "react";
@@ -19,7 +30,10 @@ const Accounts = dynamic(() => import("../../components/Accounts"), {
});
// type SelectOption<T> = { value: T, label: string };
type TxFields = Omit<typeof transactionsData[0], "Account" | "Sequence" | "TransactionType">;
type TxFields = Omit<
typeof transactionsData[0],
"Account" | "Sequence" | "TransactionType"
>;
type OtherFields = (keyof Omit<TxFields, "Destination">)[];
interface Props {
@@ -29,7 +43,7 @@ interface Props {
const Transaction: FC<Props> = ({ header, ...props }) => {
const snap = useSnapshot(state);
const transactionsOptions = transactionsData.map(tx => ({
const transactionsOptions = transactionsData.map((tx) => ({
value: tx.TransactionType,
label: tx.TransactionType,
}));
@@ -37,18 +51,20 @@ const Transaction: FC<Props> = ({ header, ...props }) => {
typeof transactionsOptions[0] | null
>(null);
const accountOptions = snap.accounts.map(acc => ({
const accountOptions = snap.accounts.map((acc) => ({
label: acc.name,
value: acc.address,
}));
const [selectedAccount, setSelectedAccount] = useState<typeof accountOptions[0] | null>(null);
const [selectedAccount, setSelectedAccount] = useState<
typeof accountOptions[0] | null
>(null);
const destAccountOptions = snap.accounts
.map(acc => ({
.map((acc) => ({
label: acc.name,
value: acc.address,
}))
.filter(acc => acc.value !== selectedAccount?.value);
.filter((acc) => acc.value !== selectedAccount?.value);
const [selectedDestAccount, setSelectedDestAccount] = useState<
typeof destAccountOptions[0] | null
>(null);
@@ -59,7 +75,9 @@ const Transaction: FC<Props> = ({ header, ...props }) => {
useEffect(() => {
const transactionType = selectedTransaction?.value;
const account = snap.accounts.find(acc => acc.address === selectedAccount?.value);
const account = snap.accounts.find(
(acc) => acc.address === selectedAccount?.value
);
if (!account || !transactionType || txIsLoading) {
setTxIsDisabled(true);
} else {
@@ -69,7 +87,7 @@ const Transaction: FC<Props> = ({ header, ...props }) => {
useEffect(() => {
let _txFields: TxFields | undefined = transactionsData.find(
tx => tx.TransactionType === selectedTransaction?.value
(tx) => tx.TransactionType === selectedTransaction?.value
);
if (!_txFields) return setTxFields({});
_txFields = { ..._txFields } as TxFields;
@@ -85,7 +103,9 @@ const Transaction: FC<Props> = ({ header, ...props }) => {
}, [selectedTransaction, setSelectedDestAccount]);
const submitTest = useCallback(async () => {
const account = snap.accounts.find(acc => acc.address === selectedAccount?.value);
const account = snap.accounts.find(
(acc) => acc.address === selectedAccount?.value
);
const TransactionType = selectedTransaction?.value;
if (!account || !TransactionType || txIsDisabled) return;
@@ -95,7 +115,7 @@ const Transaction: FC<Props> = ({ header, ...props }) => {
let options = { ...txFields };
options.Destination = selectedDestAccount?.value;
(Object.keys(options) as (keyof TxFields)[]).forEach(field => {
(Object.keys(options) as (keyof TxFields)[]).forEach((field) => {
let _value = options[field];
// convert currency
if (typeof _value === "object" && _value.type === "currency") {
@@ -162,12 +182,20 @@ const Transaction: FC<Props> = ({ header, ...props }) => {
}, []);
const usualFields = ["TransactionType", "Amount", "Account", "Destination"];
const otherFields = Object.keys(txFields).filter(k => !usualFields.includes(k)) as OtherFields;
const otherFields = Object.keys(txFields).filter(
(k) => !usualFields.includes(k)
) as OtherFields;
return (
<Box css={{ position: "relative", height: "calc(100% - 28px)" }} {...props}>
<Container css={{ p: "$3 0", fontSize: "$sm", height: "calc(100% - 28px)" }}>
<Container
css={{ p: "$3 0", fontSize: "$sm", height: "calc(100% - 28px)" }}
>
<Flex column fluid css={{ height: "100%", overflowY: "auto" }}>
<Flex row fluid css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}>
<Flex
row
fluid
css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}
>
<Text muted css={{ mr: "$3" }}>
Transaction type:{" "}
</Text>
@@ -178,10 +206,14 @@ const Transaction: FC<Props> = ({ header, ...props }) => {
hideSelectedOptions
css={{ width: "70%" }}
value={selectedTransaction}
onChange={tt => setSelectedTransaction(tt as any)}
onChange={(tt) => setSelectedTransaction(tt as any)}
/>
</Flex>
<Flex row fluid css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}>
<Flex
row
fluid
css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}
>
<Text muted css={{ mr: "$3" }}>
Account:{" "}
</Text>
@@ -191,17 +223,25 @@ const Transaction: FC<Props> = ({ header, ...props }) => {
css={{ width: "70%" }}
options={accountOptions}
value={selectedAccount}
onChange={acc => setSelectedAccount(acc as any)}
onChange={(acc) => setSelectedAccount(acc as any)}
/>
</Flex>
{txFields.Amount !== undefined && (
<Flex row fluid css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}>
<Flex
row
fluid
css={{
justifyContent: "flex-end",
alignItems: "center",
mb: "$3",
}}
>
<Text muted css={{ mr: "$3" }}>
Amount (XRP):{" "}
</Text>
<Input
value={txFields.Amount.value}
onChange={e =>
onChange={(e) =>
setTxFields({
...txFields,
Amount: { type: "currency", value: e.target.value },
@@ -213,7 +253,15 @@ const Transaction: FC<Props> = ({ header, ...props }) => {
</Flex>
)}
{txFields.Destination !== undefined && (
<Flex row fluid css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}>
<Flex
row
fluid
css={{
justifyContent: "flex-end",
alignItems: "center",
mb: "$3",
}}
>
<Text muted css={{ mr: "$3" }}>
Destination account:{" "}
</Text>
@@ -224,28 +272,36 @@ const Transaction: FC<Props> = ({ header, ...props }) => {
options={destAccountOptions}
value={selectedDestAccount}
isClearable
onChange={acc => setSelectedDestAccount(acc as any)}
onChange={(acc) => setSelectedDestAccount(acc as any)}
/>
</Flex>
)}
{otherFields.map(field => {
{otherFields.map((field) => {
let _value = txFields[field];
let value = typeof _value === "object" ? _value.value : _value;
value = typeof value === "object" ? JSON.stringify(value) : value?.toLocaleString();
let isCurrency = typeof _value === "object" && _value.type === "currency";
value =
typeof value === "object"
? JSON.stringify(value)
: value?.toLocaleString();
let isCurrency =
typeof _value === "object" && _value.type === "currency";
return (
<Flex
key={field}
row
fluid
css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}
css={{
justifyContent: "flex-end",
alignItems: "center",
mb: "$3",
}}
>
<Text muted css={{ mr: "$3" }}>
{field + (isCurrency ? " (XRP)" : "")}:{" "}
</Text>
<Input
value={value}
onChange={e =>
onChange={(e) =>
setTxFields({
...txFields,
[field]:
@@ -296,44 +352,92 @@ const Test = () => {
const snap = useSnapshot(state);
const [tabHeaders, setTabHeaders] = useState<string[]>(["test1.json"]);
return (
<Container css={{ py: "$3", px: 0 }}>
<Flex
row
fluid
css={{ justifyContent: "center", mb: "$2", height: "40vh", minHeight: "300px", p: "$3 $2" }}
<Container css={{ px: 0 }}>
<Split
direction="vertical"
sizes={[50, 50]}
gutterSize={4}
gutterAlign="center"
style={{ height: "calc(100vh - 60px)" }}
>
<Box css={{ width: "55%", px: "$2" }}>
<Tabs
keepAllAlive
forceDefaultExtension
defaultExtension=".json"
onCreateNewTab={name => setTabHeaders(tabHeaders.concat(name))}
onCloseTab={index => setTabHeaders(tabHeaders.filter((_, idx) => idx !== index))}
<Flex
row
fluid
css={{
justifyContent: "center",
p: "$3 $2",
}}
>
<Split
direction="horizontal"
sizes={[50, 50]}
minSize={[180, 320]}
gutterSize={4}
gutterAlign="center"
style={{
display: "flex",
flexDirection: "row",
width: "100%",
height: "100%",
}}
>
{tabHeaders.map(header => (
<Tab key={header} header={header}>
<Transaction header={header} />
</Tab>
))}
</Tabs>
</Box>
<Box css={{ width: "45%", mx: "$2", height: "100%" }}>
<Accounts card hideDeployBtn showHookStats />
</Box>
</Flex>
<Box css={{ width: "55%", px: "$2" }}>
<Tabs
keepAllAlive
forceDefaultExtension
defaultExtension=".json"
onCreateNewTab={(name) =>
setTabHeaders(tabHeaders.concat(name))
}
onCloseTab={(index) =>
setTabHeaders(tabHeaders.filter((_, idx) => idx !== index))
}
>
{tabHeaders.map((header) => (
<Tab key={header} header={header}>
<Transaction header={header} />
</Tab>
))}
</Tabs>
</Box>
<Box css={{ width: "45%", mx: "$2", height: "100%" }}>
<Accounts card hideDeployBtn showHookStats />
</Box>
</Split>
</Flex>
<Flex row fluid css={{ borderBottom: "1px solid $mauve8" }}>
<Box css={{ width: "50%", borderRight: "1px solid $mauve8" }}>
<LogBox
title="Development Log"
logs={snap.transactionLogs}
clearLog={() => (state.transactionLogs = [])}
/>
</Box>
<Box css={{ width: "50%" }}>
<DebugStream />
</Box>
</Flex>
<Flex row fluid>
<Split
direction="horizontal"
sizes={[50, 50]}
minSize={[320, 160]}
gutterSize={4}
gutterAlign="center"
style={{
display: "flex",
flexDirection: "row",
width: "100%",
height: "100%",
}}
>
<Box
css={{
borderRight: "1px solid $mauve8",
height: "100%",
}}
>
<LogBox
title="Development Log"
logs={snap.transactionLogs}
clearLog={() => (state.transactionLogs = [])}
/>
</Box>
<Box css={{ height: "100%" }}>
<DebugStream />
</Box>
</Split>
</Flex>
</Split>
</Container>
);
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
public/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

9
public/browserconfig.xml Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#161618</TileColor>
</tile>
</msapplication>
</browserconfig>

BIN
public/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 737 B

BIN
public/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 15 KiB

BIN
public/mstile-150x150.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="588.000000pt" height="588.000000pt" viewBox="0 0 588.000000 588.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,588.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M3843 4097 c-381 -380 -737 -736 -791 -790 l-99 -99 -940 1 -940 0
-204 -211 c-112 -116 -228 -235 -257 -265 -29 -30 -51 -57 -48 -60 2 -3 542
-5 1198 -5 l1193 0 799 -799 799 -799 380 0 c209 0 378 2 376 5 -2 2 -422 423
-933 934 l-928 929 921 920 c507 507 921 923 921 927 0 3 -170 5 -377 5 l-378
0 -692 -693z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 837 B

BIN
public/share-image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 KiB

19
public/site.webmanifest Normal file
View File

@@ -0,0 +1,19 @@
{
"name": "Hooks Builder",
"short_name": "Hooks Builder",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#161618",
"background_color": "#161618",
"display": "standalone"
}

View File

@@ -1,4 +0,0 @@
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

4
raw-loader.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
declare module "*.md" {
const content: string;
export default content;
};

View File

@@ -25,6 +25,7 @@ export const compileCode = async (activeId: number) => {
}
// Set loading state to true
state.compiling = true;
state.logs = []
try {
const res = await fetch(process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT, {
method: "POST",
@@ -38,7 +39,6 @@ export const compileCode = async (activeId: number) => {
{
type: "c",
name: state.files[activeId].name,
options: "-O0",
src: state.files[activeId].content,
},
],
@@ -66,6 +66,7 @@ export const compileCode = async (activeId: number) => {
// Decode base64 encoded wasm that is coming back from the endpoint
const bufferData = await decodeBinary(json.output);
state.files[state.active].compiledContent = ref(bufferData);
state.files[state.active].lastCompiled = new Date();
// Import wabt from and create human readable version of wasm file and
// put it into state
import("wabt").then((wabt) => {
@@ -87,4 +88,4 @@ export const compileCode = async (activeId: number) => {
});
state.compiling = false;
}
};
};

View File

@@ -1,8 +1,17 @@
import state, { IFile } from '../index';
/* Initializes empty file to global state */
const languageMapping = {
'ts': 'typescript',
'js': 'javascript',
'md': 'markdown',
'c': 'c',
'h': 'c',
'other': ''
} /* Initializes empty file to global state */
export const createNewFile = (name: string) => {
const emptyFile: IFile = { name, language: "c", content: "" };
const tempName = name.split('.');
const fileExt = tempName[tempName.length - 1] || 'other';
const emptyFile: IFile = { name, language: languageMapping[fileExt as 'ts' | 'js' | 'md' | 'c' | 'h' | 'other'], content: "" };
state.files.push(emptyFile);
state.active = state.files.length - 1;
};

View File

@@ -1,6 +1,5 @@
export const templateFileIds = {
'starter': '1d14e51e2e02dc0a508cb0733767a914', // TODO currently same as accept
'accept': '1d14e51e2e02dc0a508cb0733767a914',
'firewall': 'bcd6d0c0fcbe52545ddb802481ff9d26',
'notary': 'a789c75f591eeab7932fd702ed8cf9ea',
'carbon': '43925143fa19735d8c6505c34d3a6a47',

View File

@@ -9,6 +9,7 @@ export interface IFile {
content: string;
compiledContent?: ArrayBuffer | null;
compiledWatContent?: string | null;
lastCompiled?: Date
}
export interface FaucetAccountRes {

View File

@@ -1,29 +1,27 @@
// stitches.config.ts
import type Stitches from '@stitches/react';
import { createStitches } from '@stitches/react';
import type Stitches from "@stitches/react";
import { createStitches } from "@stitches/react";
import {
gray,
blue,
red,
green,
plum,
crimson,
grass,
slate,
mauve,
pink,
yellow,
amber,
purple,
grayDark,
blueDark,
redDark,
greenDark,
plumDark,
crimsonDark,
grassDark,
slateDark,
mauveDark,
pinkDark,
yellowDark,
amberDark,
purpleDark,
} from '@radix-ui/colors';
red,
redDark,
} from "@radix-ui/colors";
export const {
styled,
@@ -39,26 +37,30 @@ export const {
colors: {
...gray,
...blue,
...red,
...green,
...plum,
...crimson,
...grass,
...slate,
...mauve,
...pink,
...yellow,
...amber,
...purple,
...red,
accent: "#9D2DFF",
background: "$gray1",
backgroundAlt: "$gray4",
text: "$gray12",
textMuted: "$gray10",
primary: "$plum",
error: '$red9',
warning: '$amber11',
success: "$grass11",
white: "white",
black: "black",
'deep': 'rgb(244, 244, 244)'
deep: "rgb(244, 244, 244)",
},
fonts: {
body: 'Work Sans, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif',
heading: 'Work Sans, sans-serif',
monospace: 'Roboto Mono, monospace',
heading: "Work Sans, sans-serif",
monospace: "Roboto Mono, monospace",
},
fontSizes: {
xs: "0.6875rem",
@@ -74,7 +76,7 @@ export const {
"7xl": "4.5rem",
"8xl": "6rem",
"9xl": "8rem",
default: '$md'
default: "$md",
},
space: {
px: "1px",
@@ -110,15 +112,15 @@ export const {
72: "18rem",
80: "20rem",
96: "24rem",
"widePlus": '2048px',
"wide": '1536px',
"layoutPlus": '1260px',
"layout": '1024px',
"copyUltra": '980px',
"copyPlus": '768px',
"copy": '680px',
"narrowPlus": '600px',
"narrow": '512px',
widePlus: "2048px",
wide: "1536px",
layoutPlus: "1260px",
layout: "1024px",
copyUltra: "980px",
copyPlus: "768px",
copy: "680px",
narrowPlus: "600px",
narrow: "512px",
xs: "20rem",
sm: "24rem",
md: "28rem",
@@ -218,62 +220,112 @@ export const {
lg: "(min-width: 62em)",
xl: "(min-width: 80em)",
"2xl": "(min-width: 96em)",
hover: '(any-hover: hover)',
dark: '(prefers-color-scheme: dark)',
light: '(prefers-color-scheme: light)',
hover: "(any-hover: hover)",
dark: "(prefers-color-scheme: dark)",
light: "(prefers-color-scheme: light)",
},
utils: {
// Abbreviated margin properties
m: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'margin'>) => ({
m: (
value: Stitches.ScaleValue<"space"> | Stitches.PropertyValue<"margin">
) => ({
margin: value,
}),
mt: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'marginTop'>) => ({
mt: (
value: Stitches.ScaleValue<"space"> | Stitches.PropertyValue<"marginTop">
) => ({
marginTop: value,
}),
mr: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'marginRight'>) => ({
mr: (
value:
| Stitches.ScaleValue<"space">
| Stitches.PropertyValue<"marginRight">
) => ({
marginRight: value,
}),
mb: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'marginBottom'>) => ({
mb: (
value:
| Stitches.ScaleValue<"space">
| Stitches.PropertyValue<"marginBottom">
) => ({
marginBottom: value,
}),
ml: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'marginLeft'>) => ({
ml: (
value: Stitches.ScaleValue<"space"> | Stitches.PropertyValue<"marginLeft">
) => ({
marginLeft: value,
}),
mx: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'marginLeft' | 'marginRight'>) => ({
mx: (
value:
| Stitches.ScaleValue<"space">
| Stitches.PropertyValue<"marginLeft" | "marginRight">
) => ({
marginLeft: value,
marginRight: value,
}),
my: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'marginTop' | 'marginBottom'>) => ({
my: (
value:
| Stitches.ScaleValue<"space">
| Stitches.PropertyValue<"marginTop" | "marginBottom">
) => ({
marginTop: value,
marginBottom: value,
}),
// Abbreviated margin properties
p: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'padding'>) => ({
p: (
value: Stitches.ScaleValue<"space"> | Stitches.PropertyValue<"padding">
) => ({
padding: value,
}),
pt: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'paddingTop'>) => ({
pt: (
value: Stitches.ScaleValue<"space"> | Stitches.PropertyValue<"paddingTop">
) => ({
paddingTop: value,
}),
pr: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'paddingRight'>) => ({
pr: (
value:
| Stitches.ScaleValue<"space">
| Stitches.PropertyValue<"paddingRight">
) => ({
paddingRight: value,
}),
pb: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'paddingBottom'>) => ({
pb: (
value:
| Stitches.ScaleValue<"space">
| Stitches.PropertyValue<"paddingBottom">
) => ({
paddingBottom: value,
}),
pl: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'paddingLeft'>) => ({
pl: (
value:
| Stitches.ScaleValue<"space">
| Stitches.PropertyValue<"paddingLeft">
) => ({
paddingLeft: value,
}),
px: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'paddingLeft' | 'paddingRight'>) => ({
px: (
value:
| Stitches.ScaleValue<"space">
| Stitches.PropertyValue<"paddingLeft" | "paddingRight">
) => ({
paddingLeft: value,
paddingRight: value,
}),
py: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'paddingTop' | 'paddingBottom'>) => ({
py: (
value:
| Stitches.ScaleValue<"space">
| Stitches.PropertyValue<"paddingTop" | "paddingBottom">
) => ({
paddingTop: value,
paddingBottom: value,
}),
// A property for applying width/height together
size: (value: Stitches.ScaleValue<'space'> | Stitches.PropertyValue<'width' | 'height'>) => ({
size: (
value:
| Stitches.ScaleValue<"space">
| Stitches.PropertyValue<"width" | "height">
) => ({
width: value,
height: value,
}),
@@ -282,47 +334,45 @@ export const {
// }),
// A property to apply linear gradient
linearGradient: (value: Stitches.ScaleValue<'space'>) => ({
linearGradient: (value: Stitches.ScaleValue<"space">) => ({
backgroundImage: `linear-gradient(${value})`,
}),
// An abbreviated property for border-radius
br: (value: Stitches.ScaleValue<'space'>) => ({
br: (value: Stitches.ScaleValue<"space">) => ({
borderRadius: value,
}),
},
});
export const darkTheme = createTheme('dark', {
export const darkTheme = createTheme("dark", {
colors: {
...grayDark,
...blueDark,
...redDark,
...greenDark,
...plumDark,
...crimsonDark,
...grassDark,
...slateDark,
...mauveDark,
...pinkDark,
...yellowDark,
...amberDark,
...purpleDark,
deep: 'rgb(10, 10, 10)',
...redDark,
deep: "rgb(10, 10, 10)",
// backgroundA: transparentize(0.1, grayDark.gray1),
},
});
export const globalStyles = globalCss({
// body: { backgroundColor: '$background', color: '$text', fontFamily: 'Helvetica' },
'html, body': {
backgroundColor: '$gray1',
color: '$gray12',
fontFamily: '$body',
fontSize: '$md',
'-webkit-font-smoothing': 'antialiased',
'-moz-osx-font-smoothing': 'grayscale'
"html, body": {
backgroundColor: "$mauve2",
color: "$mauve12",
fontFamily: "$body",
fontSize: "$md",
"-webkit-font-smoothing": "antialiased",
"-moz-osx-font-smoothing": "grayscale",
},
a: {
color: "inherit",
textDecoration: "none",
},
'a': {
color: 'inherit',
textDecoration: 'none'
}
});

View File

@@ -6,8 +6,51 @@ body,
min-height: 100vh;
display: flex;
flex-direction: column;
overflow-y: hidden;
}
* {
box-sizing: border-box;
}
.monaco-hover p {
margin-bottom: 10px !important;
}
.gutter {
position: relative;
transition: border-color 0.3s, background-color 0.3s;
}
.gutter-vertical {
margin-top: -4px;
}
.gutter-horizontal {
margin-left: -4px;
}
.gutter-vertical:hover {
cursor: row-resize;
background-color: rgba(255, 255, 255, 0.25);
}
html.light .gutter-vertical:hover {
background-color: rgba(0, 0, 0, 0.25);
}
.gutter-horizontal:hover {
cursor: col-resize;
background-color: rgba(255, 255, 255, 0.25);
}
html.light .gutter-horizontal:hover {
background-color: rgba(0, 0, 0, 0.25);
}
/* Adjust Monaco tooltip stylings */
.markdown-hover h3 {
margin: 0;
}
.monaco-editor .monaco-hover hr {
margin: 8px 0;
}
.monaco-editor .monaco-hover {
z-index: 9999;
}

View File

@@ -3,7 +3,7 @@
"inherit": true,
"rules": [
{
"background": "1a1d1e",
"background": "161618",
"token": ""
},
{
@@ -182,10 +182,10 @@
],
"colors": {
"editor.foreground": "#D0D0FF",
"editor.background": "#232326",
"editor.background": "#1C1C1F",
"editor.selectionBackground": "#ffffff30",
"editor.lineHighlightBackground": "#ffffff20",
"editorCursor.foreground": "#7070FF",
"editorWhitespace.foreground": "#BFBFBF"
}
}
}

View File

@@ -3,7 +3,7 @@
"inherit": true,
"rules": [
{
"background": "FFFFFF",
"background": "F4F2F4",
"token": ""
},
{
@@ -89,10 +89,10 @@
],
"colors": {
"editor.foreground": "#000000",
"editor.background": "#f4f2f4",
"editor.background": "#F9F8F9",
"editor.selectionBackground": "#B5D5FF",
"editor.lineHighlightBackground": "#00000012",
"editorCursor.foreground": "#000000",
"editorWhitespace.foreground": "#BFBFBF"
}
}
}

View File

@@ -26,6 +26,7 @@
"**/*.tsx"
],
"exclude": [
"node_modules"
"node_modules",
"*.md"
]
}

714
utils/wat-highlight.ts Normal file
View File

@@ -0,0 +1,714 @@
// 'WebAssembly Text Format' Monarch language
import type monaco from 'monaco-editor';
const WebAssemblyTextLanguage: { config: monaco.languages.LanguageConfiguration, tokens: monaco.languages.IMonarchLanguage } = {
config: {
brackets: [
["(", ")"],
["if", "end"],
["loop", "end"],
["block", "end"],
],
autoClosingPairs: [
{ open: "(", close: ")" },
{ open: "if", close: "end" },
{ open: "loop", close: "end" },
{ open: "block", close: "end" },
],
surroundingPairs: [
{ open: "(", close: ")" },
{ open: "if", close: "end" },
{ open: "loop", close: "end" },
{ open: "block", close: "end" },
],
},
tokens: {
keywords: [
"module",
"import",
"export",
"memory",
"data",
"table",
"elem",
"start",
"func",
"tag",
"type",
"param",
"result",
"global",
"local",
"mut",
"struct",
"array",
"field",
],
types: [
"i8",
"i16",
"i32",
"i64",
"f32",
"f64",
"v128",
"i31ref",
"eqref",
"anyref",
"dataref",
"externref",
"funcref",
"exnref",
"extern",
"null",
"any",
"eq",
],
instructions: [
"pop",
"nop",
"drop",
"data.drop",
"elem.drop",
"local.get",
"local.set",
"local.tee",
"global.get",
"global.set",
"tuple.make",
"tuple.extract",
"select",
"v128.const",
"v128.and",
"v128.or",
"v128.xor",
"v128.not",
"v128.andnot",
"v128.bitselect",
"v128.load",
"v128.load8x8_s",
"v128.load8x8_u",
"v128.load16x4_s",
"v128.load16x4_u",
"v128.load32x2_s",
"v128.load32x2_u",
"v128.load8_lane",
"v128.load16_lane",
"v128.load32_lane",
"v128.load64_lane",
"v128.load8_splat",
"v128.load16_splat",
"v128.load32_splat",
"v128.load64_splat",
"v128.load32_zero",
"v128.load64_zero",
"v128.store",
"v128.store8_lane",
"v128.store16_lane",
"v128.store32_lane",
"v128.store64_lane",
"v128.any_true",
"i8x16.shuffle",
"i8x16.swizzle",
"i8x16.bitmask",
"i8x16.splat",
"i8x16.popcnt",
"i8x16.replace_lane",
"i8x16.extract_lane_s",
"i8x16.extract_lane_u",
"i8x16.all_true",
"i8x16.abs",
"i8x16.add",
"i8x16.add_sat_s",
"i8x16.add_sat_u",
"i8x16.sub",
"i8x16.sub_sat_s",
"i8x16.sub_sat_u",
"i8x16.mul",
"i8x16.neg",
"i8x16.shl",
"i8x16.shr_s",
"i8x16.shr_u",
"i8x16.eq",
"i8x16.ne",
"i8x16.lt_s",
"i8x16.lt_u",
"i8x16.le_s",
"i8x16.le_u",
"i8x16.gt_s",
"i8x16.gt_u",
"i8x16.ge_s",
"i8x16.ge_u",
"i8x16.min_s",
"i8x16.min_u",
"i8x16.max_s",
"i8x16.max_u",
"i8x16.avgr_u",
"i8x16.narrow_i16x8_s",
"i8x16.narrow_i16x8_u",
"i16x8.bitmask",
"i16x8.splat",
"i16x8.load_8x8_s",
"i16x8.load_8x8_u",
"i16x8.replace_lane",
"i16x8.extract_lane_s",
"i16x8.extract_lane_u",
"i16x8.extend_low_i8x16_s",
"i16x8.extend_high_i8x16_s",
"i16x8.extend_low_i8x16_u",
"i16x8.extend_high_i8x16_u",
"i16x8.all_true",
"i16x8.abs",
"i16x8.add",
"i16x8.add_sat_s",
"i16x8.add_sat_u",
"i16x8.extadd_pairwise_i8x16_s",
"i16x8.extadd_pairwise_i8x16_u",
"i16x8.sub",
"i16x8.sub_sat_s",
"i16x8.sub_sat_u",
"i16x8.q15mulr_sat_s",
"i16x8.mul",
"i16x8.extmul_low_i8x16_s",
"i16x8.extmul_high_i8x16_s",
"i16x8.extmul_low_i8x16_u",
"i16x8.extmul_high_i8x16_u",
"i16x8.neg",
"i16x8.shl",
"i16x8.shr_s",
"i16x8.shr_u",
"i16x8.eq",
"i16x8.ne",
"i16x8.lt_s",
"i16x8.lt_u",
"i16x8.le_s",
"i16x8.le_u",
"i16x8.gt_s",
"i16x8.gt_u",
"i16x8.ge_s",
"i16x8.ge_u",
"i16x8.min_s",
"i16x8.min_u",
"i16x8.max_s",
"i16x8.max_u",
"i16x8.avgr_u",
"i16x8.narrow_i32x4_s",
"i16x8.narrow_i32x4_u",
"i32x4.bitmask",
"i32x4.splat",
"i32x4.load_16x4_s",
"i32x4.load_16x4_u",
"i32x4.replace_lane",
"i32x4.extract_lane",
"i32x4.extend_low_i16x8_s",
"i32x4.extend_high_i16x8_s",
"i32x4.extend_low_i16x8_u",
"i32x4.extend_high_i16x8_u",
"i32x4.all_true",
"i32x4.abs",
"i32x4.add",
"i32x4.extadd_pairwise_i16x8_s",
"i32x4.extadd_pairwise_i16x8_u",
"i32x4.sub",
"i32x4.mul",
"i32x4.extmul_low_i16x8_s",
"i32x4.extmul_high_i16x8_s",
"i32x4.extmul_low_i16x8_u",
"i32x4.extmul_high_i16x8_u",
"i32x4.neg",
"i32x4.shl",
"i32x4.shr_s",
"i32x4.shr_u",
"i32x4.eq",
"i32x4.ne",
"i32x4.lt_s",
"i32x4.lt_u",
"i32x4.le_s",
"i32x4.le_u",
"i32x4.gt_s",
"i32x4.gt_u",
"i32x4.ge_s",
"i32x4.ge_u",
"i32x4.min_s",
"i32x4.min_u",
"i32x4.max_s",
"i32x4.max_u",
"i32x4.trunc_sat_f32x4_s",
"i32x4.trunc_sat_f32x4_u",
"i32x4.trunc_sat_f64x2_s_zero",
"i32x4.trunc_sat_f64x2_u_zero",
"i32x4.dot_i16x8_s",
"i64x2.bitmask",
"i64x2.splat",
"i64x2.load32x2_s",
"i64x2.load32x2_u",
"i64x2.replace_lane",
"i64x2.extract_lane",
"i64x2.extend_low_i32x4_s",
"i64x2.extend_high_i32x4_s",
"i64x2.extend_low_i32x4_u",
"i64x2.extend_high_i32x4_u",
"i64x2.all_true",
"i64x2.abs",
"i64x2.add",
"i64x2.sub",
"i64x2.mul",
"i64x2.neg",
"i64x2.shl",
"i64x2.shr_s",
"i64x2.shr_u",
"f32x4.splat",
"f32x4.replace_lane",
"f32x4.extract_lane",
"f32x4.add",
"f32x4.sub",
"f32x4.mul",
"i64x2.extmul_low_i32x4_s",
"i64x2.extmul_high_i32x4_s",
"i64x2.extmul_low_i32x4_u",
"i64x2.extmul_high_i32x4_u",
"i64x2.eq",
"i64x2.ne",
"i64x2.lt_s",
"i64x2.le_s",
"i64x2.gt_s",
"i64x2.ge_s",
"f32x4.neg",
"f32x4.eq",
"f32x4.ne",
"f32x4.lt",
"f32x4.le",
"f32x4.gt",
"f32x4.ge",
"f32x4.abs",
"f32x4.min",
"f32x4.pmin",
"f32x4.max",
"f32x4.pmax",
"f32x4.div",
"f32x4.sqrt",
"f32x4.ceil",
"f32x4.floor",
"f32x4.trunc",
"f32x4.nearest",
"f32x4.demote_f64x2_zero",
"f32x4.convert_i32x4_s",
"f32x4.convert_i32x4_u",
"f64x2.splat",
"f64x2.replace_lane",
"f64x2.extract_lane",
"f64x2.add",
"f64x2.sub",
"f64x2.mul",
"f64x2.neg",
"f64x2.eq",
"f64x2.ne",
"f64x2.lt",
"f64x2.le",
"f64x2.gt",
"f64x2.ge",
"f64x2.abs",
"f64x2.min",
"f64x2.max",
"f64x2.pmin",
"f64x2.pmax",
"f64x2.div",
"f64x2.sqrt",
"f64x2.ceil",
"f64x2.floor",
"f64x2.trunc",
"f64x2.nearest",
"f64x2.promote_low_f32x4",
"f64x2.convert_low_i32x4_s",
"f64x2.convert_low_i32x4_u",
"i32.atomic.load",
"i32.atomic.load8_u",
"i32.atomic.load16_u",
"i32.atomic.store",
"i32.atomic.store8",
"i32.atomic.store16",
"i32.atomic.rmw.add",
"i32.atomic.rmw.sub",
"i32.atomic.rmw.and",
"i32.atomic.rmw.or",
"i32.atomic.rmw.xor",
"i32.atomic.rmw.xchg",
"i32.atomic.rmw.cmpxchg",
"i32.atomic.rmw8.add_u",
"i32.atomic.rmw8.sub_u",
"i32.atomic.rmw8.and_u",
"i32.atomic.rmw8.or_u",
"i32.atomic.rmw8.xor_u",
"i32.atomic.rmw8.xchg_u",
"i32.atomic.rmw8.cmpxchg_u",
"i32.atomic.rmw16.add_u",
"i32.atomic.rmw16.sub_u",
"i32.atomic.rmw16.and_u",
"i32.atomic.rmw16.or_u",
"i32.atomic.rmw16.xor_u",
"i32.atomic.rmw16.xchg_u",
"i32.atomic.rmw16.cmpxchg_u",
"i64.atomic.load",
"i64.atomic.load8_u",
"i64.atomic.load16_u",
"i64.atomic.load32_u",
"i64.atomic.store",
"i64.atomic.store8",
"i64.atomic.store16",
"i64.atomic.store32",
"i64.atomic.rmw.add",
"i64.atomic.rmw.sub",
"i64.atomic.rmw.and",
"i64.atomic.rmw.or",
"i64.atomic.rmw.xor",
"i64.atomic.rmw.xchg",
"i64.atomic.rmw.cmpxchg",
"i64.atomic.rmw8.add_u",
"i64.atomic.rmw8.sub_u",
"i64.atomic.rmw8.and_u",
"i64.atomic.rmw8.or_u",
"i64.atomic.rmw8.xor_u",
"i64.atomic.rmw8.xchg_u",
"i64.atomic.rmw8.cmpxchg_u",
"i64.atomic.rmw16.add_u",
"i64.atomic.rmw16.sub_u",
"i64.atomic.rmw16.and_u",
"i64.atomic.rmw16.or_u",
"i64.atomic.rmw16.xor_u",
"i64.atomic.rmw16.xchg_u",
"i64.atomic.rmw16.cmpxchg_u",
"i64.atomic.rmw32.add_u",
"i64.atomic.rmw32.sub_u",
"i64.atomic.rmw32.and_u",
"i64.atomic.rmw32.or_u",
"i64.atomic.rmw32.xor_u",
"i64.atomic.rmw32.xchg_u",
"i64.atomic.rmw32.cmpxchg_u",
"atomic.fence",
"func.bind",
"ref",
"ref.eq",
"ref.null",
"ref.is_null",
"ref.is_func",
"ref.is_data",
"ref.is_i31",
"ref.as_func",
"ref.as_non_null",
"ref.as_data",
"ref.as_i31",
"ref.func",
"ref.cast",
"ref.cast_static",
"ref.test",
"ref.test_static",
"table.get",
"table.set",
"table.size",
"table.grow",
"table.fill",
"table.init",
"table.copy",
"throw",
"rethrow",
"i32.load",
"i32.load8_s",
"i32.load8_u",
"i32.load16_s",
"i32.load16_u",
"i32.store",
"i32.store8",
"i32.store16",
"i32.const",
"i32.eqz",
"i32.eq",
"i32.ne",
"i32.lt_s",
"i32.lt_u",
"i32.le_s",
"i32.le_u",
"i32.gt_s",
"i32.gt_u",
"i32.ge_s",
"i32.ge_u",
"i32.clz",
"i32.ctz",
"i32.popcnt",
"i32.add",
"i32.sub",
"i32.mul",
"i32.div_s",
"i32.div_u",
"i32.rem_s",
"i32.rem_u",
"i32.and",
"i32.or",
"i32.xor",
"i32.shl",
"i32.shr_s",
"i32.shr_u",
"i32.rotl",
"i32.rotr",
"i32.wrap_i64",
"i32.trunc_f32_s",
"i32.trunc_f32_u",
"i32.trunc_f64_s",
"i32.trunc_f64_u",
"i32.reinterpret_f32",
"i64.load",
"i64.load8_s",
"i64.load8_u",
"i64.load16_s",
"i64.load16_u",
"i64.load32_s",
"i64.load32_u",
"i64.store",
"i64.store8",
"i64.store16",
"i64.store32",
"i64.const",
"i64.eqz",
"i64.eq",
"i64.ne",
"i64.lt_s",
"i64.lt_u",
"i64.le_s",
"i64.le_u",
"i64.gt_s",
"i64.gt_u",
"i64.ge_s",
"i64.ge_u",
"i64.clz",
"i64.ctz",
"i64.popcnt",
"i64.add",
"i64.sub",
"i64.mul",
"i64.div_s",
"i64.div_u",
"i64.rem_s",
"i64.rem_u",
"i64.and",
"i64.or",
"i64.xor",
"i64.shl",
"i64.shr_s",
"i64.shr_u",
"i64.rotl",
"i64.rotr",
"i64.extend_i32_s",
"i64.extend_i32_u",
"i64.trunc_f32_s",
"i64.trunc_f32_u",
"i64.trunc_f64_s",
"i64.trunc_f64_u",
"i64.reinterpret_f64",
"f32.load",
"f32.store",
"f32.const",
"f32.eq",
"f32.ne",
"f32.lt",
"f32.le",
"f32.gt",
"f32.ge",
"f32.abs",
"f32.neg",
"f32.ceil",
"f32.floor",
"f32.trunc",
"f32.nearest",
"f32.sqrt",
"f32.add",
"f32.sub",
"f32.mul",
"f32.div",
"f32.min",
"f32.max",
"f32.copysign",
"f32.convert_i32_s",
"f32.convert_i32_u",
"f32.convert_i64_s",
"f32.convert_i64_u",
"f32.demote_f64",
"f32.reinterpret_i32",
"f64.load",
"f64.store",
"f64.const",
"f64.eq",
"f64.ne",
"f64.lt",
"f64.le",
"f64.gt",
"f64.ge",
"f64.abs",
"f64.neg",
"f64.ceil",
"f64.floor",
"f64.trunc",
"f64.nearest",
"f64.sqrt",
"f64.add",
"f64.sub",
"f64.mul",
"f64.div",
"f64.min",
"f64.max",
"f64.copysign",
"f64.convert_i32_s",
"f64.convert_i32_u",
"f64.convert_i64_s",
"f64.convert_i64_u",
"f64.promote_f32",
"f64.reinterpret_i64",
"i32.extend8_s",
"i32.extend16_s",
"i64.extend8_s",
"i64.extend16_s",
"i64.extend32_s",
"i32.trunc_sat_f32_s",
"i32.trunc_sat_f32_u",
"i32.trunc_sat_f64_s",
"i32.trunc_sat_f64_u",
"i64.trunc_sat_f32_s",
"i64.trunc_sat_f32_u",
"i64.trunc_sat_f64_s",
"i64.trunc_sat_f64_u",
"memory.size",
"memory.grow",
"memory.copy",
"memory.fill",
"memory.init",
"memory.atomic.notify",
"memory.atomic.wait32",
"memory.atomic.wait64",
"i31.new",
"i31.get_u",
"i31.get_s",
"array.new",
"array.new_default",
"array.init",
"array.init_static",
"array.get",
"array.get_s",
"array.get_u",
"array.set",
"array.len",
"array.copy",
"struct.new",
"struct.new_default",
"struct.new_with_rtt",
"struct.new_default_with_rtt",
"struct.get",
"struct.get_s",
"struct.get_u",
"struct.set",
"rtt.canon",
"rtt.sub",
"rtt.fresh_sub",
],
controlInstructions: [
"block",
"loop",
"if",
"else",
"then",
"end",
"do",
"let",
"br",
"br_if",
"br_table",
"br_on_exn",
"br_on_null",
"br_on_non_null",
"br_on_cast",
"br_on_cast_static",
"br_on_cast_fail",
"br_on_cast_static_fail",
"br_on_func",
"br_on_non_func",
"br_on_data",
"br_on_non_data",
"br_on_i31",
"br_on_non_i31",
"call",
"call_indirect",
"call_ref",
"return",
"return_call",
"return_call_indirect",
"return_call_ref",
"try",
"catch",
"catch_all",
"delegate",
"unreachable",
],
escapes:
/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
digits: /\d+(_+\d+)*/,
octaldigits: /[0-7]+(_+[0-7]+)*/,
binarydigits: /[0-1]+(_+[0-1]+)*/,
hexdigits: /[[0-9a-fA-F]+(_+[0-9a-fA-F]+)*/,
tokenizer: {
root: [
// whitespace
{ include: "@whitespace" },
// strings
[/"([^"\\]|\\.)*$/, "string.invalid"], // non-teminated string
[/"/, "string", "@string"],
// numbers (not all of these are generated, but here to be sure)
[/(@digits)[eE]([\-+]?(@digits))?[fFdD]?/, "number.float"],
[/(@digits)\.(@digits)([eE][\-+]?(@digits))?[fFdD]?/, "number.float"],
[/0[xX](@hexdigits)[Ll]?/, "number.hex"],
[/0(@octaldigits)[Ll]?/, "number.octal"],
[/0[bB](@binarydigits)[Ll]?/, "number.binary"],
[/(@digits)[fFdD]/, "number.float"],
[/(@digits)[lL]?/, "number"],
// variable names
[/\$[^\s\)]*/, { token: "identifier" }],
// instructions and types
[
/[a-z0-9_]+(?:\.[a-z0-9_]+)*/,
{
cases: {
"@types": { token: "type.$0" },
"@keywords": { token: "keyword.$0" },
"@controlInstructions": { token: "controlInstruction.$0" },
"@instructions": { token: "instruction.$0" },
"@default": "identifier",
},
},
],
],
string: [
[/[^\\"]+/, "string"],
[/@escapes/, "string.escape"],
[/\\./, "string.escape.invalid"],
[/"/, "string", "@pop"],
],
whitespace: [
[/[ \t\r\n]+/, ""],
[/(;; )(ERROR |FAILURE )([^\n]*)/, ["comment", "error", ""]],
[/(;; )(WARNING )([^\n]*)/, ["comment", "warning", ""]],
[/(;; )(INFO )([^\n]*)/, ["comment", "info", ""]],
[/(;; )(PEDANTIC )([^\n]*)/, ["comment", "pedantic", ""]],
[/(;; +)(~+|\^)$/, ["comment", "underline"]],
[/(;; )([^\n]*)/, ["comment", ""]],
[/;;[^\n]*/, "comment"],
],
},
},
};
export default WebAssemblyTextLanguage;

87
xrpl-hooks-docs/docs.ts Normal file
View File

@@ -0,0 +1,87 @@
import hooksAccountBufLen from "./md/hooks-account-buf-len.md";
import hooksAccountConvBufLen from "./md/hooks-account-conv-buf-len.md";
import hooksAccountConvPure from "./md/hooks-account-conv-pure.md";
import hooksArrayBufLen from "./md/hooks-array-buf-len.md";
import hooksBurdenPrereq from "./md/hooks-burden-prereq.md";
import hooksDetailBufLen from "./md/hooks-detail-buf-len.md";
import hooksDetailPrereq from "./md/hooks-detail-prereq.md";
import hooksEmitBufLen from "./md/hooks-emit-buf-len.md";
import hooksEmitPrereq from "./md/hooks-emit-prereq.md";
import hooksEntryPointRecursion from "./md/hooks-entry-point-recursion.md";
import hooksEntryPointsNeg from "./md/hooks-entry-points-neg.md";
import hooksEntryPoints from "./md/hooks-entry-points.md";
import hooksFeePrereq from "./md/hooks-fee-prereq.md";
import hooksFieldAddBufLen from "./md/hooks-field-add-buf-len.md";
import hooksFieldBufLen from "./md/hooks-field-buf-len.md";
import hooksFieldDelBufLen from "./md/hooks-field-del-buf-len.md";
import hooksFloatArithPure from "./md/hooks-float-arith-pure.md";
import hooksFloatComparePure from "./md/hooks-float-compare-pure.md";
import hooksFloatIntPure from "./md/hooks-float-int-pure.md";
import hooksFloatManipPure from "./md/hooks-float-manip-pure.md";
import hooksFloatOnePure from "./md/hooks-float-one-pure.md";
import hooksFloatPure from "./md/hooks-float-pure.md";
import hooksGuardCalled from "./md/hooks-guard-called.md";
import hooksGuardInFor from "./md/hooks-guard-in-for.md";
import hooksGuardInWhile from "./md/hooks-guard-in-while.md";
import hooksHashBufLen from "./md/hooks-hash-buf-len.md";
import hooksKeyletBufLen from "./md/hooks-keylet-buf-len.md";
import hooksRaddrConvBufLen from "./md/hooks-raddr-conv-buf-len.md";
import hooksRaddrConvPure from "./md/hooks-raddr-conv-pure.md";
import hooksReserveLimit from "./md/hooks-reserve-limit.md";
import hooksSlotHashBufLen from "./md/hooks-slot-hash-buf-len.md";
import hooksSlotKeyletBufLen from "./md/hooks-slot-keylet-buf-len.md";
import hooksSlotLimit from "./md/hooks-slot-limit.md";
import hooksSlotSubLimit from "./md/hooks-slot-sub-limit.md";
import hooksSlotTypeLimit from "./md/hooks-slot-type-limit.md";
import hooksStateBufLen from "./md/hooks-state-buf-len.md";
import hooksTransactionHashBufLen from "./md/hooks-transaction-hash-buf-len.md";
import hooksTransactionSlotLimit from "./md/hooks-transaction-slot-limit.md";
import hooksValidateBufLen from "./md/hooks-validate-buf-len.md";
import hooksVerifyBufLen from "./md/hooks-verify-buf-len.md";
const docs: { [key: string]: string; } = {
"hooks-account-buf-len": hooksAccountBufLen,
"hooks-account-conv-buf-len": hooksAccountConvBufLen,
"hooks-account-conv-pure": hooksAccountConvPure,
"hooks-array-buf-len": hooksArrayBufLen,
"hooks-burden-prereq": hooksBurdenPrereq,
"hooks-detail-buf-len": hooksDetailBufLen,
"hooks-detail-prereq": hooksDetailPrereq,
"hooks-emit-buf-len": hooksEmitBufLen,
"hooks-emit-prereq": hooksEmitPrereq,
"hooks-entry-point-recursion": hooksEntryPointRecursion,
"hooks-entry-points-neg": hooksEntryPointsNeg,
"hooks-entry-points": hooksEntryPoints,
"hooks-fee-prereq": hooksFeePrereq,
"hooks-field-add-buf-len": hooksFieldAddBufLen,
"hooks-field-buf-len": hooksFieldBufLen,
"hooks-field-del-buf-len": hooksFieldDelBufLen,
"hooks-float-arith-pure": hooksFloatArithPure,
"hooks-float-compare-pure": hooksFloatComparePure,
"hooks-float-int-pure": hooksFloatIntPure,
"hooks-float-manip-pure": hooksFloatManipPure,
"hooks-float-one-pure": hooksFloatOnePure,
"hooks-float-pure": hooksFloatPure,
"hooks-guard-called": hooksGuardCalled,
"hooks-guard-in-for": hooksGuardInFor,
"hooks-guard-in-while": hooksGuardInWhile,
"hooks-hash-buf-len": hooksHashBufLen,
"hooks-keylet-buf-len": hooksKeyletBufLen,
"hooks-raddr-conv-buf-len": hooksRaddrConvBufLen,
"hooks-raddr-conv-pure": hooksRaddrConvPure,
"hooks-reserve-limit": hooksReserveLimit,
"hooks-slot-hash-buf-len": hooksSlotHashBufLen,
"hooks-slot-keylet-buf-len": hooksSlotKeyletBufLen,
"hooks-slot-limit": hooksSlotLimit,
"hooks-slot-sub-limit": hooksSlotSubLimit,
"hooks-slot-type-limit": hooksSlotTypeLimit,
"hooks-state-buf-len": hooksStateBufLen,
"hooks-transaction-hash-buf-len": hooksTransactionHashBufLen,
"hooks-transaction-slot-limit": hooksTransactionSlotLimit,
"hooks-validate-buf-len": hooksValidateBufLen,
"hooks-verify-buf-len": hooksVerifyBufLen,
};
export default docs;

View File

@@ -0,0 +1,6 @@
# hooks-account-buf-len
Function `hook_account` has fixed-size account ID output.
This check warns about too-small size of its output buffer (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,7 @@
# hooks-account-conv-buf-len
Function `util_raddr` has fixed-size account ID input.
This check warns unless the correct size is passed in the input size
parameter (if it's specified by a constant - variable parameter is
ignored).

View File

@@ -0,0 +1,10 @@
# hooks-account-conv-pure
Hooks identify accounts by the 20 byte account ID, which can be
converted to an raddr using the `util_raddr` function. If the account
ID never changes, a more efficient way to do this is precompute the
raddr from the account ID.
This check warns about calls of `util_raddr` with constant input and
proposes to add a tracing statement showing the computed value (so
that the user can use it to replace the call).

View File

@@ -0,0 +1,9 @@
# hooks-array-buf-len
Hook API `sto_subarray` requires non-empty input buffer and takes a
parameter specifying the array index, whose value is limited - the
sought object cannot be found if the limit is exceeded.
This check warns about empty input as well as too-large values of the
index specified in calls to `sto_subarray` (if they're specified by
constants - variable parameters are ignored).

View File

@@ -0,0 +1,5 @@
# hooks-burden-prereq
Hook API `etxn_burden` computes transaction burden, based on (i.a.)
the number of reserved transactions, so a call to it must be preceded
by a call to `etxn_reserve`.

View File

@@ -0,0 +1,6 @@
# hooks-detail-buf-len
Function `etxn_details` has fixed-size sfEmitDetails output.
This check warns about too-small size of its output buffer (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,5 @@
# hooks-detail-prereq
Hook API `etxn_details` serializes emit details, based on (i.a.) the
number of reserved transactions, so a call to it must be preceded by a
call to `etxn_reserve`.

View File

@@ -0,0 +1,6 @@
# hooks-emit-buf-len
Function `emit` has fixed-size transaction hash output.
This check warns about too-small size of its output buffer (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,5 @@
# hooks-emit-prereq
Before emitting a transaction using `emit` Hook API, a hook must set a
maximal count of transactions it plans to emit, by calling
`etxn_reserve`.

View File

@@ -0,0 +1,4 @@
# hooks-entry-point-recursion
Recursive calls are disallowed in the implementation of hook entry
points.

View File

@@ -0,0 +1,4 @@
# hooks-entry-points-neg
Shows error on function definitions with unexpected (that is, neither
`hook` nor `cbak`) names.

View File

@@ -0,0 +1,6 @@
# hooks-entry-points
A Hook always implements and exports exactly two functions: `cbak` and
`hook`.
This check shows error on translation units that do not have them.

View File

@@ -0,0 +1,5 @@
# hooks-fee-prereq
Hook API `etxn_fee_base` estimates a transaction fee, based on (i.a.)
the number of reserved transactions, so a call to it must be preceded
by a call to `etxn_reserve`.

View File

@@ -0,0 +1,9 @@
# hooks-field-add-buf-len
Emplacing a new field into STObject by calling `sto_emplace` requires
enough space to serialize the new STObject into; the API also limits
sizes of the old object and field.
This check warns about insufficient output buffer space as well as
too-large values of the inputs in calls to `sto_emplace` (if they're
specified by constants - variable parameters are ignored).

View File

@@ -0,0 +1,6 @@
# hooks-field-buf-len
Hook API `sto_subfield` requires non-empty input buffer.
This check warns about empty input in calls to `sto_subfield` (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,9 @@
# hooks-field-del-buf-len
Erasing a field from STObject by calling `sto_erase` requires enough
space to serialize the new STObject into; the API also limits size of
the old object.
This check warns about insufficient output buffer space as well as
too-large value of the input STObject in calls to `sto_erase` (if
they're specified by constants - variable parameters are ignored).

View File

@@ -0,0 +1,15 @@
# hooks-float-arith-pure
Hooks can compute floating-point values in XFL format by calling
functions `float_multiply`, `float_mulratio`, `float_negate`,
`float_sum`, `float_invert` and `float_divide` and access their
constituent parts by calling `float_exponent`, `float_mantissa` and
`float_sign`. If the inputs of the computation never change, a more
efficient way to do this is to precompute it.
This check warns about calls of the aforementioned functions with
constant inputs and in simple cases proposes to add a tracing
statement showing the computed value (so that the user can use it to
replace the call). It also checks that the divisor passed to
`float_divide`, `float_mulratio` and `float_invert` is not 0 (if it's
specified by a constant - variable parameters are ignored).

View File

@@ -0,0 +1,9 @@
# hooks-float-compare-pure
Hooks can compare floating-point values in XFL format by calling the
`float_compare` function. If the inputs of the comparison never
change, its result is fixed and the function need not be called.
This check warns about calls of `float_compare` with constant inputs
as well as invalid values of the comparison mode parameter (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,10 @@
# hooks-float-int-pure
Hooks can convert floating-point values in XFL format to integers by
calling the `float_int` function. If the inputs of this function never
change, a more efficient way to do this is to precompute the integer
value.
This check warns about calls of `float_int` with constant inputs as
well as invalid values of the decimal places parameter (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,13 @@
# hooks-float-manip-pure
Hooks can directly manipulate floating-point values in XFL format by
calling functions `float_exponent_set`, `float_mantissa_set` and
`float_sign_set`. If the inputs of the update never change, a more
efficient way to do this is to precompute it.
This check warns about calls of the aforementioned functions with
constant inputs and in simple cases proposes to add a tracing
statement showing the computed value (so that the user can use it to
replace the call). It also checks documented bounds of the second
parameter of these functions (if it's specified by a constant -
variable parameter is ignored).

View File

@@ -0,0 +1,5 @@
# hooks-float-one-pure
Hooks can obtain XFL enclosing number 1 by calling the float_one
function. Since the number never changes, a more efficient way is to
use its precomputed value.

View File

@@ -0,0 +1,12 @@
# hooks-float-pure
Hooks can use floating-point values in XFL format, creating them from
mantissa and exponent by calling the `float_set` function. If the
mantissa and exponent never change, a more efficient way to do this is
to precompute the floating-point value.
This check warns about calls of `float_set` with constant inputs and
proposes to add a tracing statement showing the computed value (so
that the user can use it to replace the call). In the special case of
0 mantissa and 0 exponent ("canonical 0"), a replacement value of 0 is
proposed directly, with no need to trace it.

View File

@@ -0,0 +1,5 @@
# hooks-guard-called
Every hook needs to import the guard function `_g` and use it at least once.
[Read documentation](https://xrpl-hooks.readme.io/docs/loops-and-guarding)

View File

@@ -0,0 +1,12 @@
# hooks-guard-in-for
Consider the following for-loop in C:
```c
#define GUARD(maxiter) _g(__LINE__, (maxiter)+1)
for (int i = 0; GUARD(3), i < 3; ++i)
```
This is the only way to satisfy the guard rule when using a for-loop
in C.

View File

@@ -0,0 +1,11 @@
# hooks-guard-in-while
Like for loops, while loops must have a guard in their condition:
```c
#define GUARD(maxiter) _g(__LINE__, (maxiter)+1)
int i = 0;
while (GUARD(3), i < 3)
```

View File

@@ -0,0 +1,7 @@
# hooks-hash-buf-len
Functions `util_sha512h`, `hook_hash`, `ledger_last_hash` and `nonce`
have fixed-size hash output.
This check warns about too-small size of their output buffer (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,8 @@
# hooks-keylet-buf-len
Computing a ripple keylet by calling `util_keylet` requires valid
parameters dependent on the keylet type.
This check does not fully parse these parameters, but warns about
invalid keylet type as well as buffer sizes that cannot be valid (if
they're specified by constants - variable parameters are ignored).

View File

@@ -0,0 +1,8 @@
# hooks-raddr-conv-buf-len
Hook API `util_accid` has upper limit on the length of its input
(because it expects it to be a raddr) and fixed-size account ID
output.
This check warns about invalid sizes of input and output parameters
(if they're specified by constants - variable parameters are ignored).

View File

@@ -0,0 +1,10 @@
# hooks-raddr-conv-pure
Hooks identify accounts by the 20 byte account ID, which can be
converted from a raddr using the `util_accid` function. If the raddr
never changes, a more efficient way to do this is precompute the
account-id from the raddr.
This check warns about calls of `util_accid` with constant input and
proposes to add a tracing statement showing the computed value (so
that the user can use it to replace the call).

View File

@@ -0,0 +1,9 @@
# hooks-reserve-limit
Hook API `etxn_reserve` takes a parameter specifying the number of
transactions intended to emit from the calling hook. Value of this
parameter is limited, and the function fails if the limit is exceeded.
This check warns about too-large values of the number of reserved
transactions (if they're specified by a constant - variable parameter
is ignored).

View File

@@ -0,0 +1,7 @@
# hooks-slot-hash-buf-len
Function `slot_id` has fixed-size canonical hash output.
This check warns about too-small size of its output buffer as well as
invalid values of the slot number parameter (if they're specified by
constants - variable parameters are ignored).

View File

@@ -0,0 +1,7 @@
# hooks-slot-keylet-buf-len
Function `slot_set` has structured keylet input.
This check does not parse the input, but warns about its sizes that
cannot be valid as well as invalid values of the slot number parameter
(if they're specified by constants - variable parameters are ignored).

View File

@@ -0,0 +1,9 @@
# hooks-slot-limit
Hook APIs `slot`, `slot_count`, `slot_clear`, `slot_size`,
`slot_float` and `trace_slot` take a parameter specifying the accessed
slot number. Value of this parameter is limited, and the functions
fail if the limit is exceeded.
This check warns about too-large values of the slot number (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,8 @@
# hooks-slot-sub-limit
Hook APIs `slot_subarray` and `slot_subfield` take parameters
specifying parent and child slot numbers. Values of these parameters
are limited, and the functions fail if the limit is exceeded.
This check warns about too-large values of the slot numbers (if
they're specified by a constant - variable parameters are ignored).

View File

@@ -0,0 +1,9 @@
# hooks-slot-type-limit
Hook API `slot_type` takes a parameter specifying the accessed slot
number. Value of this parameter is limited, and the function fails if
the limit is exceeded.
This check warns about too-large values of the slot number as well as
invalid values of the flags parameter (if they're specified by
constants - variable parameters are ignored).

View File

@@ -0,0 +1,6 @@
# hooks-state-buf-len
Functions state and state_set accept fixed-size Hook State key.
This check warns about invalid size of its input buffer (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,6 @@
# hooks-transaction-hash-buf-len
Function `otxn_id` has fixed-size canonical hash output.
This check warns about too-small size of its output buffer (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,8 @@
# hooks-transaction-slot-limit
Function `otxn_slot` takes a parameter specifying the accessed slot
number. Value of this parameter is limited, and the function fails if
the limit is exceeded.
This check warns about too-large values of the slot number (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,6 @@
# hooks-validate-buf-len
Hook API `sto_validate` requires non-empty input buffer.
This check warns about empty input in calls to `sto_validate` (if it's
specified by a constant - variable parameter is ignored).

View File

@@ -0,0 +1,8 @@
# hooks-verify-buf-len
Verifying a cryptographic signature by calling `util_verify` requires
valid public key & data signature.
This check does not fully parse these parameters, but warns about
their sizes that cannot be valid (if they're specified by constants -
variable parameters are ignored).

129
yarn.lock
View File

@@ -886,6 +886,11 @@
resolved "https://registry.yarnpkg.com/@types/file-saver/-/file-saver-2.0.4.tgz#aaf9b96296150d737b2fefa535ced05ed8013d84"
integrity sha512-sPZYQEIF/SOnLAvaz9lTuydniP+afBMtElRTdYkeV1QtEgvtJ7qolCPjly6O32QI8CbEmP5O/fztMXEDWfEcrg==
"@types/json-schema@^7.0.8":
version "7.0.9"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==
"@types/json5@^0.0.29":
version "0.0.29"
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
@@ -898,6 +903,18 @@
dependencies:
"@types/node" "*"
"@types/lodash.uniqby@^4.7.6":
version "4.7.6"
resolved "https://registry.yarnpkg.com/@types/lodash.uniqby/-/lodash.uniqby-4.7.6.tgz#672827a701403f07904fe37f0721ae92abfa80e8"
integrity sha512-9wBhrm1y6asW50Joj6tsySCNUgzK2tCqL7vtKIej0E9RyeBFdcte7fxUosmFuMoOU0eHqOMK76kCCrK99jxHgg==
dependencies:
"@types/lodash" "*"
"@types/lodash@*":
version "4.14.179"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.179.tgz#490ec3288088c91295780237d2497a3aa9dfb5c5"
integrity sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==
"@types/lodash@^4.14.136":
version "4.14.177"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.177.tgz#f70c0d19c30fab101cad46b52be60363c43c4578"
@@ -1049,7 +1066,12 @@ aggregate-error@^3.1.0:
clean-stack "^2.0.0"
indent-string "^4.0.0"
ajv@^6.10.0, ajv@^6.12.4:
ajv-keywords@^3.5.2:
version "3.5.2"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -1880,6 +1902,11 @@ emojis-list@^2.0.0:
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k=
emojis-list@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
encoding@0.1.13:
version "0.1.13"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
@@ -2273,6 +2300,11 @@ file-uri-to-path@1.0.0:
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
filesize@^8.0.7:
version "8.0.7"
resolved "https://registry.yarnpkg.com/filesize/-/filesize-8.0.7.tgz#695e70d80f4e47012c132d57a059e80c6b580bd8"
integrity sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
@@ -2833,6 +2865,13 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
javascript-time-ago@^2.3.11:
version "2.3.11"
resolved "https://registry.yarnpkg.com/javascript-time-ago/-/javascript-time-ago-2.3.11.tgz#b488ae765b7f42ff003f3830977a92411ef1fe92"
integrity sha512-R6Ux0IGv7RmAZ7vB04r/gevGaLDJeZY7+6jTYyYsPAF5x4PSv6up+dW2hLK99+OUY8xwdeXPItZFiUppIRUaoQ==
dependencies:
relative-time-format "^1.0.6"
jest-worker@27.0.0-next.5:
version "27.0.0-next.5"
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.0-next.5.tgz#5985ee29b12a4e191f4aae4bb73b97971d86ec28"
@@ -2882,6 +2921,13 @@ json5@^1.0.1:
dependencies:
minimist "^1.2.0"
json5@^2.1.2:
version "2.2.0"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==
dependencies:
minimist "^1.2.5"
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
@@ -2988,6 +3034,15 @@ loader-utils@1.2.3:
emojis-list "^2.0.0"
json5 "^1.0.1"
loader-utils@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129"
integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==
dependencies:
big.js "^5.2.2"
emojis-list "^3.0.0"
json5 "^2.1.2"
locate-path@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
@@ -3058,6 +3113,11 @@ lodash.truncate@^4.4.2:
resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=
lodash.uniqby@^4.7.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302"
integrity sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI=
lodash@^4.17.15, lodash@^4.17.4:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
@@ -3146,7 +3206,7 @@ minimatch@^3.0.4:
dependencies:
brace-expansion "^1.1.7"
minimist@^1.2.0:
minimist@^1.2.0, minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
@@ -3608,6 +3668,11 @@ pbkdf2@^3.0.3, pbkdf2@^3.0.9:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
performance-now@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
phosphor-react@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/phosphor-react/-/phosphor-react-1.3.1.tgz#96e33f44370d83cda15b60cccc17087ad0060684"
@@ -3688,6 +3753,15 @@ progress@^2.0.0:
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
prop-types@^15.5.7:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
dependencies:
loose-envify "^1.4.0"
object-assign "^4.1.1"
react-is "^16.13.1"
prop-types@^15.6.0, prop-types@^15.6.2:
version "15.8.0"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.0.tgz#d237e624c45a9846e469f5f31117f970017ff588"
@@ -3745,6 +3819,13 @@ queue@6.0.2:
dependencies:
inherits "~2.0.3"
raf@^3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==
dependencies:
performance-now "^2.1.0"
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
@@ -3770,6 +3851,14 @@ raw-body@2.4.1:
iconv-lite "0.4.24"
unpipe "1.0.0"
raw-loader@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6"
integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==
dependencies:
loader-utils "^2.0.0"
schema-utils "^3.0.0"
re-resizable@^6.9.1:
version "6.9.1"
resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.9.1.tgz#6be082b55d02364ca4bfee139e04feebdf52441c"
@@ -3855,6 +3944,14 @@ react-select@^5.2.1:
prop-types "^15.6.0"
react-transition-group "^4.3.0"
react-split@^2.0.14:
version "2.0.14"
resolved "https://registry.yarnpkg.com/react-split/-/react-split-2.0.14.tgz#ef198259bf43264d605f792fb3384f15f5b34432"
integrity sha512-bKWydgMgaKTg/2JGQnaJPg51T6dmumTWZppFgEbbY0Fbme0F5TuatAScCLaqommbGQQf/ZT1zaejuPDriscISA==
dependencies:
prop-types "^15.5.7"
split.js "^1.6.0"
react-stay-scrolled@^7.4.0:
version "7.4.0"
resolved "https://registry.yarnpkg.com/react-stay-scrolled/-/react-stay-scrolled-7.4.0.tgz#cb109b8dfd7834e5406d9c9322035ab40c398368"
@@ -3872,6 +3969,15 @@ react-style-singleton@^2.1.0:
invariant "^2.2.4"
tslib "^1.0.0"
react-time-ago@^7.1.9:
version "7.1.9"
resolved "https://registry.yarnpkg.com/react-time-ago/-/react-time-ago-7.1.9.tgz#df3999f1184b71ab09aaf1d05dda2a19b76e0bb8"
integrity sha512-jN4EsEgqm3a5eLJV0ZrRpom3iG+w4bullS2NHc2htIucGUIr2FbgHXjU0wIkuN9uWM87aFvIfkUDpS/ju7Mazg==
dependencies:
memoize-one "^6.0.0"
prop-types "^15.7.2"
raf "^3.4.1"
react-transition-group@^4.3.0:
version "4.4.2"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470"
@@ -3952,6 +4058,11 @@ regexpp@^3.1.0:
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
relative-time-format@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/relative-time-format/-/relative-time-format-1.0.6.tgz#4956f6aa161f63cbab8a7f2d8b2e92d7b6a888a3"
integrity sha512-voemOJLxlKun4P1fAo4PEg2WXNGjhqfE/G8Xen4gcy24Hyu/djn5bT5axmhx4MnjynoZ8f0HCOjk3RZpsY6X/g==
require-from-string@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
@@ -4145,6 +4256,15 @@ scheduler@^0.20.2:
loose-envify "^1.1.0"
object-assign "^4.1.1"
schema-utils@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281"
integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==
dependencies:
"@types/json-schema" "^7.0.8"
ajv "^6.12.5"
ajv-keywords "^3.5.2"
semver@^5.5.0, semver@^5.6.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
@@ -4259,6 +4379,11 @@ source-map@^0.6.1:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
split.js@^1.6.0:
version "1.6.5"
resolved "https://registry.yarnpkg.com/split.js/-/split.js-1.6.5.tgz#f7f61da1044c9984cb42947df4de4fadb5a3f300"
integrity sha512-mPTnGCiS/RiuTNsVhCm9De9cCAUsrNFFviRbADdKiiV+Kk8HKp/0fWu7Kr8pi3/yBmsqLFHuXGT9UUZ+CNLwFw==
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"