Compare commits

...

36 Commits

Author SHA1 Message Date
Joni Juup
a6fc730de6 add plausible analytics to builder 2022-06-29 12:58:17 +03:00
Valtteri Karesto
c6ced424d8 Merge pull request #226 from XRPLF/feat/long-navigation-support
Fix #215 scrollbar issues
2022-06-28 14:59:35 +03:00
Valtteri Karesto
3a1159cffc Make thumbs more visible 2022-06-28 14:49:34 +03:00
Valtteri Karesto
3136de1bd1 Slight style adjustments 2022-06-28 14:38:59 +03:00
Valtteri Karesto
67ffd3f1b4 Fix #215 scrollbar issues 2022-06-28 14:01:08 +03:00
Valtteri Karesto
8508cb69c4 Merge pull request #224 from XRPLF/feat/debug-stream-fixes
Feat/debug stream fixes
2022-06-28 11:31:03 +03:00
Valtteri Karesto
89217d2633 Remove console.log 2022-06-28 11:03:56 +03:00
Valtteri Karesto
ba1b64391c ping socket connection 2022-06-28 09:36:45 +03:00
Valtteri Karesto
098d919a77 Bring back dispose 2022-06-27 15:03:49 +03:00
Valtteri Karesto
b2af37ab4b Use reconnecting-websocket and refactor debug stream 2022-06-27 15:03:42 +03:00
Valtteri Karesto
dcb7e94e86 New gists provided by XRPL (#223)
* Prepare logic for new gists

* Remove unused imports

* updated gist IDs for xrplfgists

* Add macro.h to apiheaderfiles

* Update headers

Co-authored-by: Vaclav Barta <vbarta@mangrove.cz>
2022-06-27 08:25:25 +02:00
Valtteri Karesto
67848b3d8d Merge pull request #222 from XRPLF/fix/suggest-button-disabled
Fix/suggest button disabled
2022-06-23 11:12:04 +03:00
Valtteri Karesto
31a86263a1 Disable suggest if no account selected 2022-06-22 14:42:50 +03:00
Valtteri Karesto
4d0025afc1 Fix splitscreen error 2022-06-22 14:42:39 +03:00
Valtteri Karesto
f85bd2398d Merge pull request #220 from XRPLF/fix/decimals-again
fixes #201
2022-06-22 12:28:12 +03:00
Valtteri Karesto
a2a6596cc5 Prevent pasting decimals 2022-06-22 12:16:36 +03:00
Valtteri Karesto
37208ce97e fixes #201 2022-06-22 12:03:43 +03:00
Valtteri Karesto
bf4042926d Merge pull request #218 from XRPLF/feat/ui-fixes
Fixes #213 and fixes #200
2022-06-22 11:41:31 +03:00
Valtteri Karesto
3ccc1c16ac Fixed deploy 2022-06-22 11:32:07 +03:00
Valtteri Karesto
135f0c91a1 Fixes #213 and fixes #200 2022-06-22 11:06:15 +03:00
Valtteri Karesto
8f5786e242 Merge pull request #216 from XRPLF/feat/change-default-optimization
fixes #214 change default optimization
2022-06-22 09:56:15 +03:00
Valtteri Karesto
810eb4ca27 Add new default to compileCode as well 2022-06-22 09:52:47 +03:00
Valtteri Karesto
e6574f9f12 fixes #214 change default optimization 2022-06-22 09:47:30 +03:00
Valtteri Karesto
1a6726fabf Merge pull request #212 from XRPLF/feat/improve-supp-js
Improve supplementary JS feature
2022-06-21 11:28:42 +03:00
Valtteri Karesto
89f8671217 Clear log should now work 2022-06-21 10:53:52 +03:00
Valtteri Karesto
fb5259221b Changed color of starting running 2022-06-21 09:43:29 +03:00
Valtteri Karesto
fd17f59616 Show LogBoxForScrips if js file active 2022-06-20 23:54:56 +03:00
Valtteri Karesto
91bbc7ea61 Catch template errors, add better labels, styling 2022-06-20 23:54:33 +03:00
Valtteri Karesto
783d832c6d Remove export for unused component 2022-06-20 23:53:32 +03:00
Valtteri Karesto
698ca376e7 Add showbuttons prop to LogBoxForScripts 2022-06-20 23:53:15 +03:00
Valtteri Karesto
bfd9e21ab8 Remove unused component 2022-06-20 23:52:45 +03:00
Valtteri Karesto
e46411f245 Rename template helpers 2022-06-20 14:53:30 +03:00
Valtteri Karesto
08447c6b29 Add support for select parameters 2022-06-20 14:16:16 +03:00
Valtteri Karesto
9216cc6bf7 When downloading zip, include wasm if it exists 2022-06-20 11:01:13 +03:00
Valtteri Karesto
5108b08e39 Do not show scripts panel if no supplementary scripts 2022-06-20 10:40:47 +03:00
Valtteri Karesto
6c46a4f809 Merge pull request #211 from XRPLF/feat/user-provided-scripts
Feat/user provided scripts
2022-06-20 10:16:06 +03:00
23 changed files with 552 additions and 421 deletions

View File

@@ -512,6 +512,7 @@ const ImportAccountDialog = () => {
<Input <Input
name="secret" name="secret"
type="password" type="password"
autoComplete="new-password"
value={value} value={value}
onChange={(e) => setValue(e.target.value)} onChange={(e) => setValue(e.target.value)}
/> />

View File

@@ -1,5 +1,7 @@
import { useCallback, useEffect } from "react"; import { useEffect } from "react";
import ReconnectingWebSocket, { CloseEvent } from "reconnecting-websocket";
import { proxy, ref, useSnapshot } from "valtio"; import { proxy, ref, useSnapshot } from "valtio";
import { subscribeKey } from "valtio/utils";
import { Select } from "."; import { Select } from ".";
import state, { ILog, transactionsState } from "../state"; import state, { ILog, transactionsState } from "../state";
import { extractJSON } from "../utils/json"; import { extractJSON } from "../utils/json";
@@ -15,7 +17,7 @@ export interface IStreamState {
status: "idle" | "opened" | "closed"; status: "idle" | "opened" | "closed";
statusChangeTimestamp?: number; statusChangeTimestamp?: number;
logs: ILog[]; logs: ILog[];
socket?: WebSocket; socket?: ReconnectingWebSocket;
} }
export const streamState = proxy<IStreamState>({ export const streamState = proxy<IStreamState>({
@@ -24,12 +26,85 @@ export const streamState = proxy<IStreamState>({
logs: [] as ILog[], logs: [] as ILog[],
}); });
const onOpen = (account: ISelect | null) => {
if (!account) {
return;
}
// streamState.logs = [];
streamState.status = "opened";
streamState.statusChangeTimestamp = Date.now();
pushLog(`Debug stream opened for account ${account?.value}`, {
type: "success",
});
};
const onError = () => {
pushLog("Something went wrong! Check your connection and try again.", {
type: "error",
});
};
const onClose = (e: CloseEvent) => {
// 999 = closed websocket connection by switching account
if (e.code !== 4999) {
pushLog(`Connection was closed. [code: ${e.code}]`, {
type: "error",
});
}
streamState.status = "closed";
streamState.statusChangeTimestamp = Date.now();
};
const onMessage = (event: any) => {
// Ping returns just account address, if we get that
// response we don't need to log anything
if (event.data !== streamState.selectedAccount?.value) {
pushLog(event.data);
}
};
let interval: NodeJS.Timer | null = null;
const addListeners = (account: ISelect | null) => {
if (account?.value && streamState.socket?.url.endsWith(account?.value)) {
return;
}
streamState.logs = [];
if (account?.value) {
if (interval) {
clearInterval(interval);
}
if (streamState.socket) {
streamState.socket?.removeEventListener("open", () => onOpen(account));
streamState.socket?.removeEventListener("close", onClose);
streamState.socket?.removeEventListener("error", onError);
streamState.socket?.removeEventListener("message", onMessage);
}
streamState.socket = ref(
new ReconnectingWebSocket(
`wss://${process.env.NEXT_PUBLIC_DEBUG_STREAM_URL}/${account?.value}`
)
);
if (streamState.socket) {
interval = setInterval(() => {
streamState.socket?.send("");
}, 10000);
}
streamState.socket.addEventListener("open", () => onOpen(account));
streamState.socket.addEventListener("close", onClose);
streamState.socket.addEventListener("error", onError);
streamState.socket.addEventListener("message", onMessage);
}
};
subscribeKey(streamState, "selectedAccount", addListeners);
const DebugStream = () => { const DebugStream = () => {
const { selectedAccount, logs, socket } = useSnapshot(streamState); const { selectedAccount, logs } = useSnapshot(streamState);
const { activeHeader: activeTxTab } = useSnapshot(transactionsState); const { activeHeader: activeTxTab } = useSnapshot(transactionsState);
const { accounts } = useSnapshot(state); const { accounts } = useSnapshot(state);
const accountOptions = accounts.map(acc => ({ const accountOptions = accounts.map((acc) => ({
label: acc.name, label: acc.name,
value: acc.address, value: acc.address,
})); }));
@@ -42,117 +117,21 @@ const DebugStream = () => {
options={accountOptions} options={accountOptions}
hideSelectedOptions hideSelectedOptions
value={selectedAccount} value={selectedAccount}
onChange={acc => (streamState.selectedAccount = acc as any)} onChange={(acc) => {
streamState.socket?.close(
4999,
"Old connection closed because user switched account"
);
streamState.selectedAccount = acc as any;
}}
css={{ width: "100%" }} css={{ width: "100%" }}
/> />
</> </>
); );
useEffect(() => {
const account = selectedAccount?.value;
if (account && (!socket || !socket.url.endsWith(account))) {
socket?.close();
streamState.socket = ref(
new WebSocket(
`wss://${process.env.NEXT_PUBLIC_DEBUG_STREAM_URL}/${account}`
)
);
} else if (!account && socket) {
socket.close();
streamState.socket = undefined;
}
}, [selectedAccount?.value, socket]);
const onMount = useCallback(async () => {
// deliberately using `proxy` values and not the `useSnapshot` ones to have no dep list
const acc = streamState.selectedAccount;
const status = streamState.status;
if (status === "opened" && acc) {
// fetch the missing ones
try {
const url = `https://${process.env.NEXT_PUBLIC_DEBUG_STREAM_URL}/recent/${acc?.value}`;
// TODO Remove after api sets cors properly
const res = await fetch("/api/proxy", {
method: "POST",
body: JSON.stringify({ url }),
headers: {
"Content-Type": "application/json",
},
});
if (!res.ok) return;
const body = await res.json();
if (!body?.logs) return;
const start = streamState.statusChangeTimestamp || 0;
streamState.logs = [];
pushLog(`Debug stream opened for account ${acc.value}`, {
type: "success",
});
const logs = Object.entries(body.logs).filter(([tm]) => +tm >= start);
logs.forEach(([tm, log]) => pushLog(log));
} catch (error) {
console.error(error);
}
}
}, []);
useEffect(() => {
onMount();
}, [onMount]);
useEffect(() => {
const account = selectedAccount?.value;
const socket = streamState.socket;
if (!socket) return;
const onOpen = () => {
streamState.logs = [];
streamState.status = "opened";
streamState.statusChangeTimestamp = Date.now();
pushLog(`Debug stream opened for account ${account}`, {
type: "success",
});
};
const onError = () => {
pushLog("Something went wrong! Check your connection and try again.", {
type: "error",
});
};
const onClose = (e: CloseEvent) => {
pushLog(`Connection was closed. [code: ${e.code}]`, {
type: "error",
});
streamState.selectedAccount = null;
streamState.status = "closed";
streamState.statusChangeTimestamp = Date.now();
};
const onMessage = (event: any) => {
pushLog(event.data);
};
socket.addEventListener("open", onOpen);
socket.addEventListener("close", onClose);
socket.addEventListener("error", onError);
socket.addEventListener("message", onMessage);
return () => {
socket.removeEventListener("open", onOpen);
socket.removeEventListener("close", onClose);
socket.removeEventListener("message", onMessage);
socket.removeEventListener("error", onError);
};
}, [selectedAccount?.value, socket]);
useEffect(() => { useEffect(() => {
const account = transactionsState.transactions.find( const account = transactionsState.transactions.find(
tx => tx.header === activeTxTab (tx) => tx.header === activeTxTab
)?.state.selectedAccount; )?.state.selectedAccount;
if (account && account.value !== streamState.selectedAccount?.value) if (account && account.value !== streamState.selectedAccount?.value)

View File

@@ -34,7 +34,9 @@ const DeployEditor = () => {
const [showContent, setShowContent] = useState(false); const [showContent, setShowContent] = useState(false);
const activeFile = snap.files[snap.active]; const activeFile = snap.files[snap.active]?.compiledContent
? snap.files[snap.active]
: snap.files.filter((file) => file.compiledContent)[0];
const compiledSize = activeFile?.compiledContent?.byteLength || 0; const compiledSize = activeFile?.compiledContent?.byteLength || 0;
const color = const color =
compiledSize > FILESIZE_BREAKPOINTS[1] compiledSize > FILESIZE_BREAKPOINTS[1]
@@ -60,12 +62,21 @@ const DeployEditor = () => {
{activeFile?.lastCompiled && ( {activeFile?.lastCompiled && (
<ReactTimeAgo date={activeFile.lastCompiled} locale="en-US" /> <ReactTimeAgo date={activeFile.lastCompiled} locale="en-US" />
)} )}
{activeFile.compiledContent?.byteLength && ( {activeFile.compiledContent?.byteLength && (
<Text css={{ ml: "$2", color }}> <Text css={{ ml: "$2", color }}>
({filesize(activeFile.compiledContent.byteLength)}) ({filesize(activeFile.compiledContent.byteLength)})
</Text> </Text>
)} )}
</Flex> </Flex>
{activeFile.compiledContent?.byteLength &&
activeFile.compiledContent?.byteLength >= 64000 && (
<Flex css={{ flexDirection: "column", py: "$3", pb: "$1" }}>
<Text css={{ ml: "$2", color: "$error" }}>
File size is larger than 64kB, cannot set hook!
</Text>
</Flex>
)}
<Button variant="link" onClick={() => setShowContent(true)}> <Button variant="link" onClick={() => setShowContent(true)}>
View as WAT-file View as WAT-file
</Button> </Button>
@@ -119,8 +130,8 @@ const DeployEditor = () => {
className="hooks-editor" className="hooks-editor"
defaultLanguage={"wat"} defaultLanguage={"wat"}
language={"wat"} language={"wat"}
path={`file://tmp/c/${snap.files?.[snap.active]?.name}.wat`} path={`file://tmp/c/${activeFile?.name}.wat`}
value={snap.files?.[snap.active]?.compiledWatContent || ""} value={activeFile?.compiledWatContent || ""}
beforeMount={(monaco) => { beforeMount={(monaco) => {
monaco.languages.register({ id: "wat" }); monaco.languages.register({ id: "wat" });
monaco.languages.setLanguageConfiguration("wat", wat.config); monaco.languages.setLanguageConfiguration("wat", wat.config);

View File

@@ -1,103 +0,0 @@
import React, { useRef, useLayoutEffect } from "react";
import { useSnapshot } from "valtio";
import { Play, Prohibit } from "phosphor-react";
import useStayScrolled from "react-stay-scrolled";
import Container from "./Container";
import Box from "./Box";
import LogText from "./LogText";
import { compileCode } from "../state/actions";
import state from "../state";
import Button from "./Button";
import Heading from "./Heading";
const Footer = () => {
const snap = useSnapshot(state);
const logRef = useRef<HTMLPreElement>(null);
const { stayScrolled /*, scrollBottom*/ } = useStayScrolled(logRef);
useLayoutEffect(() => {
stayScrolled();
}, [snap.logs, stayScrolled]);
return (
<Box
as="footer"
css={{
display: "flex",
borderTop: "1px solid $mauve6",
background: "$mauve1",
position: "relative",
}}
>
<Container css={{ py: "$3", flexShrink: 1 }}>
<Heading
as="h3"
css={{ fontWeight: 300, m: 0, fontSize: "11px", color: "$mauve9" }}
>
DEVELOPMENT LOG
</Heading>
<Button
ghost
size="xs"
css={{
position: "absolute",
right: "$3",
top: "$2",
color: "$mauve10",
}}
onClick={() => {
state.logs = [];
}}
>
<Prohibit size="14px" />
</Button>
<Box
as="pre"
ref={logRef}
css={{
display: "flex",
flexDirection: "column",
width: "100%",
height: "160px",
fontSize: "13px",
fontWeight: "$body",
fontFamily: "$monospace",
overflowY: "auto",
wordWrap: "break-word",
py: 3,
}}
>
{snap.logs?.map((log, index) => (
<Box as="span" key={log.type + index}>
<LogText capitalize variant={log.type}>
{log.type}:{" "}
</LogText>
<LogText>{log.message}</LogText>
</Box>
))}
</Box>
<Button
variant="primary"
uppercase
disabled={!snap.files.length}
isLoading={snap.compiling}
onClick={() => compileCode(snap.active)}
css={{
position: "absolute",
bottom: "$4",
left: "$4",
alignItems: "center",
display: "flex",
cursor: "pointer",
}}
>
<Play weight="bold" size="16px" />
Compile to Wasm
</Button>
</Container>
</Box>
);
};
export default Footer;

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback } from "react"; import React, { useState, useEffect, useCallback, useRef } from "react";
import { import {
Plus, Plus,
Share, Share,
@@ -101,7 +101,7 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
if (!filename) { if (!filename) {
return { error: "You need to add filename" }; return { error: "You need to add filename" };
} }
if (snap.files.find(file => file.name === filename)) { if (snap.files.find((file) => file.name === filename)) {
return { error: "Filename already exists." }; return { error: "Filename already exists." };
} }
@@ -132,22 +132,55 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
createNewFile(filename); createNewFile(filename);
setFilename(""); setFilename("");
}, [filename, setIsNewfileDialogOpen, setFilename, validateFilename]); }, [filename, setIsNewfileDialogOpen, setFilename, validateFilename]);
const scrollRef = useRef<HTMLDivElement>(null);
const containerRef = useRef<HTMLDivElement>(null);
const files = snap.files; const files = snap.files;
return ( return (
<Flex css={{ flexShrink: 0, gap: "$0" }}> <Flex css={{ flexShrink: 0, gap: "$0" }}>
<Flex <Flex
id="kissa"
ref={scrollRef}
css={{ css={{
overflowX: "scroll", overflowX: "scroll",
overflowY: "hidden",
py: "$3", py: "$3",
pb: "$0",
flex: 1, flex: 1,
"&::-webkit-scrollbar": { "&::-webkit-scrollbar": {
height: 0, height: "0.3em",
background: "transparent", background: "rgba(0,0,0,.0)",
},
"&::-webkit-scrollbar-gutter": "stable",
"&::-webkit-scrollbar-thumb": {
backgroundColor: "rgba(0,0,0,.2)",
outline: "0px",
borderRadius: "9999px",
},
scrollbarColor: "rgba(0,0,0,.2) rgba(0,0,0,0)",
scrollbarGutter: "stable",
scrollbarWidth: "thin",
".dark &": {
"&::-webkit-scrollbar": {
background: "rgba(0,0,0,.0)",
},
"&::-webkit-scrollbar-gutter": "stable",
"&::-webkit-scrollbar-thumb": {
backgroundColor: "rgba(255,255,255,.2)",
outline: "0px",
borderRadius: "9999px",
},
scrollbarColor: "rgba(255,255,255,.2) rgba(0,0,0,0)",
scrollbarGutter: "stable",
scrollbarWidth: "thin",
}, },
}} }}
onWheelCapture={(e) => {
if (scrollRef.current) {
scrollRef.current.scrollLeft += e.deltaY;
}
}}
> >
<Container css={{ flex: 1 }}> <Container css={{ flex: 1 }} ref={containerRef}>
<Stack <Stack
css={{ css={{
gap: "$3", gap: "$3",
@@ -233,8 +266,8 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
<Label>Filename</Label> <Label>Filename</Label>
<Input <Input
value={filename} value={filename}
onChange={e => setFilename(e.target.value)} onChange={(e) => setFilename(e.target.value)}
onKeyPress={e => { onKeyPress={(e) => {
if (e.key === "Enter") { if (e.key === "Enter") {
handleConfirm(); handleConfirm();
} }
@@ -509,8 +542,8 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
type="number" type="number"
min="1" min="1"
value={editorSettings.tabSize} value={editorSettings.tabSize}
onChange={e => onChange={(e) =>
setEditorSettings(curr => ({ setEditorSettings((curr) => ({
...curr, ...curr,
tabSize: Number(e.target.value), tabSize: Number(e.target.value),
})) }))

View File

@@ -164,21 +164,15 @@ const HooksEditor = () => {
onConnection: (connection) => { onConnection: (connection) => {
// create and start the language client // create and start the language client
const languageClient = createLanguageClient(connection); const languageClient = createLanguageClient(connection);
languageClient.start(); const disposable = languageClient.start();
// connection.onDispose((d) => {
// console.log("disposed: ", d); connection.onClose(() => {
// }); try {
// connection.onError((ee) => { disposable.dispose();
// console.log(ee =) } catch (err) {
// }) console.log("err", err);
// connection.onClose(() => { }
// try { });
// // disposable.stop();
// disposable.dispose();
// } catch (err) {
// console.log("err", err);
// }
// });
}, },
}); });
} }

View File

@@ -25,6 +25,7 @@ interface ILogBox {
logs: ILog[]; logs: ILog[];
renderNav?: () => ReactNode; renderNav?: () => ReactNode;
enhanced?: boolean; enhanced?: boolean;
showButtons?: boolean;
} }
const LogBox: FC<ILogBox> = ({ const LogBox: FC<ILogBox> = ({
@@ -34,6 +35,7 @@ const LogBox: FC<ILogBox> = ({
children, children,
renderNav, renderNav,
enhanced, enhanced,
showButtons = true,
}) => { }) => {
const logRef = useRef<HTMLPreElement>(null); const logRef = useRef<HTMLPreElement>(null);
const { stayScrolled /*, scrollBottom*/ } = useStayScrolled(logRef); const { stayScrolled /*, scrollBottom*/ } = useStayScrolled(logRef);
@@ -86,13 +88,15 @@ const LogBox: FC<ILogBox> = ({
> >
<FileJs size="15px" /> <Text css={{ lineHeight: 1 }}>{title}</Text> <FileJs size="15px" /> <Text css={{ lineHeight: 1 }}>{title}</Text>
</Heading> </Heading>
<Flex css={{ gap: "$3" }}> {showButtons && (
{snap.files <Flex css={{ gap: "$3" }}>
.filter((f) => f.name.endsWith(".js")) {snap.files
.map((file) => ( .filter((f) => f.name.endsWith(".js"))
<RunScript file={file} key={file.name} /> .map((file) => (
))} <RunScript file={file} key={file.name} />
</Flex> ))}
</Flex>
)}
<Flex css={{ ml: "auto", gap: "$3", marginRight: "$3" }}> <Flex css={{ ml: "auto", gap: "$3", marginRight: "$3" }}>
{clearLog && ( {clearLog && (
<Button ghost size="xs" onClick={clearLog}> <Button ghost size="xs" onClick={clearLog}>

View File

@@ -30,12 +30,6 @@ import PanelBox from "./PanelBox";
import { templateFileIds } from "../state/constants"; import { templateFileIds } from "../state/constants";
import { styled } from "../stitches.config"; import { styled } from "../stitches.config";
import Starter from "../components/icons/Starter";
import Firewall from "../components/icons/Firewall";
import Notary from "../components/icons/Notary";
import Carbon from "../components/icons/Carbon";
import Peggy from "../components/icons/Peggy";
const ImageWrapper = styled(Flex, { const ImageWrapper = styled(Flex, {
position: "relative", position: "relative",
mt: "$2", mt: "$2",
@@ -301,66 +295,18 @@ const Navigation = () => {
}, },
}} }}
> >
<PanelBox {Object.values(templateFileIds).map((template) => (
as="a" <PanelBox
href={`/develop/${templateFileIds.starter}`} key={template.id}
> as="a"
<ImageWrapper> href={`/develop/${template.id}`}
<Starter /> >
</ImageWrapper> <ImageWrapper>{template.icon()}</ImageWrapper>
<Heading>Starter</Heading> <Heading>{template.name}</Heading>
<Text> <Text>{template.description}</Text>
Just a basic starter with essential imports, just </PanelBox>
accepts any transaction coming through ))}
</Text>
</PanelBox>
<PanelBox
as="a"
href={`/develop/${templateFileIds.firewall}`}
css={{ alignItems: "flex-start" }}
>
<ImageWrapper>
<Firewall />
</ImageWrapper>
<Heading>Firewall</Heading>
<Text>
This Hook essentially checks a blacklist of accounts
</Text>
</PanelBox>
<PanelBox
as="a"
href={`/develop/${templateFileIds.notary}`}
>
<ImageWrapper>
<Notary />
</ImageWrapper>
<Heading>Notary</Heading>
<Text>
Collecting signatures for multi-sign transactions
</Text>
</PanelBox>
<PanelBox
as="a"
href={`/develop/${templateFileIds.carbon}`}
>
<ImageWrapper>
<Carbon />
</ImageWrapper>
<Heading>Carbon</Heading>
<Text>Send a percentage of sum to an address</Text>
</PanelBox>
<PanelBox
as="a"
href={`/develop/${templateFileIds.peggy}`}
>
<ImageWrapper>
<Peggy />
</ImageWrapper>
<Heading>Peggy</Heading>
<Text>An oracle based stable coin hook</Text>
</PanelBox>
</Flex> </Flex>
</Flex> </Flex>
<DialogClose asChild> <DialogClose asChild>
@@ -394,6 +340,8 @@ const Navigation = () => {
height: 0, height: 0,
background: "transparent", background: "transparent",
}, },
scrollbarColor: "transparent",
scrollbarWidth: "none",
}} }}
> >
<Stack <Stack

View File

@@ -1,6 +1,6 @@
import Handlebars from "handlebars"; import * as Handlebars from "handlebars";
import { Play, X } from "phosphor-react"; import { Play, X } from "phosphor-react";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import state, { IFile, ILog } from "../../state"; import state, { IFile, ILog } from "../../state";
import Button from "../Button"; import Button from "../Button";
import Box from "../Box"; import Box from "../Box";
@@ -16,6 +16,15 @@ import {
} from "../Dialog"; } from "../Dialog";
import Flex from "../Flex"; import Flex from "../Flex";
import { useSnapshot } from "valtio"; import { useSnapshot } from "valtio";
import Select from "../Select";
import { saveFile } from "../../state/actions/saveFile";
Handlebars.registerHelper(
"customize_input",
function (/* dynamic arguments */) {
return new Handlebars.SafeString(arguments[0]);
}
);
const generateHtmlTemplate = (code: string) => { const generateHtmlTemplate = (code: string) => {
return ` return `
@@ -48,7 +57,7 @@ const generateHtmlTemplate = (code: string) => {
} }
</script> </script>
<script type="module"> <script type="module">
${code} ${code}
</script> </script>
</head> </head>
<body> <body>
@@ -56,22 +65,87 @@ const generateHtmlTemplate = (code: string) => {
</html> </html>
`; `;
}; };
const RunScript: React.FC<{ file: IFile }> = ({ file }) => {
type Fields = Record<
string,
{
key: string;
value: string;
label?: string;
type?: string;
attach?: "account_secret" | "account_address" | string;
}
>;
const RunScript: React.FC<{ file: IFile }> = ({ file: { content, name } }) => {
const snap = useSnapshot(state); const snap = useSnapshot(state);
const parsed = Handlebars.parse(file.content); const [templateError, setTemplateError] = useState("");
const names = parsed.body const getFieldValues = useCallback(() => {
.filter((i) => i.type === "MustacheStatement") try {
// @ts-expect-error const parsed = Handlebars.parse(content);
.map((block) => block?.path?.original); const names = parsed.body
const defaultState: Record<string, string> = {}; .filter((i) => i.type === "MustacheStatement")
names.forEach((name) => (defaultState[name] = "")); .map((block) => {
const [fields, setFields] = useState<Record<string, string>>(defaultState); // @ts-expect-error
const type = block.hash?.pairs?.find((i) => i.key == "type");
// @ts-expect-error
const attach = block.hash?.pairs?.find((i) => i.key == "attach");
// @ts-expect-error
const label = block.hash?.pairs?.find((i) => i.key == "label");
const key =
// @ts-expect-error
block?.path?.original === "customize_input"
? // @ts-expect-error
block?.params?.[0].original
: // @ts-expect-error
block?.path?.original;
return {
key,
label: label?.value?.original || key,
attach: attach?.value?.original,
type: type?.value?.original,
value: "",
};
});
const defaultState: Fields = {};
if (names) {
names.forEach((field) => (defaultState[field.key] = field));
}
setTemplateError("");
return defaultState;
} catch (err) {
console.log(err);
setTemplateError("Could not parse template");
return undefined;
}
}, [content]);
// const defaultFieldValues = getFieldValues();
const [fields, setFields] = useState<Fields>({});
const [iFrameCode, setIframeCode] = useState(""); const [iFrameCode, setIframeCode] = useState("");
const [isDialogOpen, setIsDialogOpen] = useState(false); const [isDialogOpen, setIsDialogOpen] = useState(false);
const runScript = () => { const runScript = () => {
const template = Handlebars.compile(file.content); const fieldsToSend: Record<string, string> = {};
const code = template(fields); Object.entries(fields).map(([key, obj]) => {
setIframeCode(generateHtmlTemplate(code)); fieldsToSend[key] = obj.value;
});
const template = Handlebars.compile(content, { strict: false });
try {
const code = template(fieldsToSend);
setIframeCode(generateHtmlTemplate(code));
state.scriptLogs = [
...snap.scriptLogs,
{ type: "success", message: "Started running..." },
];
} catch (err) {
state.scriptLogs = [
...snap.scriptLogs,
// @ts-expect-error
{ type: "error", message: err?.message || "Could not parse template" },
];
}
}; };
useEffect(() => { useEffect(() => {
@@ -88,6 +162,18 @@ const RunScript: React.FC<{ file: IFile }> = ({ file }) => {
return () => window.removeEventListener("message", handleEvent); return () => window.removeEventListener("message", handleEvent);
}, [snap.scriptLogs]); }, [snap.scriptLogs]);
useEffect(() => {
const newDefaultState = getFieldValues();
setFields(newDefaultState || {});
}, [content, setFields, getFieldValues]);
const options = snap.accounts?.map((acc) => ({
label: acc.name,
secret: acc.secret,
address: acc.address,
value: acc.address,
}));
return ( return (
<> <>
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}> <Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
@@ -95,18 +181,28 @@ const RunScript: React.FC<{ file: IFile }> = ({ file }) => {
<Button <Button
variant="primary" variant="primary"
onClick={() => { onClick={() => {
saveFile(false);
setIframeCode(""); setIframeCode("");
}} }}
> >
{file.name} <Play weight="bold" size="16px" /> <Play weight="bold" size="16px" /> {name}
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent> <DialogContent>
<DialogTitle>Run {file.name} script</DialogTitle> <DialogTitle>Run {name} script</DialogTitle>
<DialogDescription> <DialogDescription>
You are about to run scripts provided by the developer of the hook, You are about to run scripts provided by the developer of the hook,
make sure you know what you are doing. make sure you know what you are doing.
<br /> <br />
{templateError && (
<Box
as="span"
css={{ display: "block", color: "$error", mt: "$3" }}
>
Error occured while parsing template, modify script and try
again!
</Box>
)}
<br /> <br />
{Object.keys(fields).length > 0 {Object.keys(fields).length > 0
? `You also need to fill in following parameters to run the script` ? `You also need to fill in following parameters to run the script`
@@ -115,15 +211,52 @@ const RunScript: React.FC<{ file: IFile }> = ({ file }) => {
<Stack css={{ width: "100%" }}> <Stack css={{ width: "100%" }}>
{Object.keys(fields).map((key) => ( {Object.keys(fields).map((key) => (
<Box key={key} css={{ width: "100%" }}> <Box key={key} css={{ width: "100%" }}>
<label>{key}</label> <label>
<Input {fields[key]?.label || key}{" "}
type="text" {fields[key].attach === "account_secret" &&
value={fields[key]} `(Script uses account secret)`}
css={{ mt: "$1" }} </label>
onChange={(e) => {fields[key].attach === "account_secret" ||
setFields({ ...fields, [key]: e.target.value }) fields[key].attach === "account_address" ? (
} <Select
/> css={{ mt: "$1" }}
options={options}
onChange={(val: any) => {
setFields({
...fields,
[key]: {
...fields[key],
value:
fields[key].attach === "account_secret"
? val.secret
: val.address,
},
});
}}
value={options.find(
(opt) =>
opt.address === fields[key].value ||
opt.secret === fields[key].value
)}
/>
) : (
<Input
type={fields[key].type || "text"}
value={
typeof fields[key].value !== "string"
? // @ts-expect-error
fields[key].value.value
: fields[key].value
}
css={{ mt: "$1" }}
onChange={(e) => {
setFields({
...fields,
[key]: { ...fields[key], value: e.target.value },
});
}}
/>
)}
</Box> </Box>
))} ))}
<Flex <Flex
@@ -135,10 +268,9 @@ const RunScript: React.FC<{ file: IFile }> = ({ file }) => {
<Button <Button
variant="primary" variant="primary"
isDisabled={ isDisabled={
Object.entries(fields).length > 0 && (Object.entries(fields).length > 0 &&
Object.entries(fields).every( Object.entries(fields).some(([key, obj]) => !obj.value)) ||
([key, value]: [string, string]) => !value Boolean(templateError)
)
} }
onClick={() => { onClick={() => {
state.scriptLogs = []; state.scriptLogs = [];

View File

@@ -140,6 +140,16 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
return null; return null;
} }
const tooLargeFile = () => {
const activeFile = snap.files[snap.active].compiledContent
? snap.files[snap.active]
: snap.files.filter((file) => file.compiledContent)[0];
return Boolean(
activeFile?.compiledContent?.byteLength &&
activeFile?.compiledContent?.byteLength >= 64000
);
};
const onSubmit: SubmitHandler<SetHookData> = async (data) => { const onSubmit: SubmitHandler<SetHookData> = async (data) => {
const currAccount = state.accounts.find( const currAccount = state.accounts.find(
(acc) => acc.address === account.address (acc) => acc.address === account.address
@@ -164,7 +174,8 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
variant={"secondary"} variant={"secondary"}
disabled={ disabled={
account.isLoading || account.isLoading ||
!snap.files.filter((file) => file.compiledWatContent).length !snap.files.filter((file) => file.compiledWatContent).length ||
tooLargeFile()
} }
> >
Set Hook Set Hook

View File

@@ -38,22 +38,22 @@ export const TxUI: FC<UIProps> = ({
txFields, txFields,
} = txState; } = txState;
const transactionsOptions = transactionsData.map(tx => ({ const transactionsOptions = transactionsData.map((tx) => ({
value: tx.TransactionType, value: tx.TransactionType,
label: tx.TransactionType, label: tx.TransactionType,
})); }));
const accountOptions: SelectOption[] = accounts.map(acc => ({ const accountOptions: SelectOption[] = accounts.map((acc) => ({
label: acc.name, label: acc.name,
value: acc.address, value: acc.address,
})); }));
const destAccountOptions: SelectOption[] = accounts const destAccountOptions: SelectOption[] = accounts
.map(acc => ({ .map((acc) => ({
label: acc.name, label: acc.name,
value: acc.address, value: acc.address,
})) }))
.filter(acc => acc.value !== selectedAccount?.value); .filter((acc) => acc.value !== selectedAccount?.value);
const [feeLoading, setFeeLoading] = useState(false); const [feeLoading, setFeeLoading] = useState(false);
@@ -108,15 +108,15 @@ export const TxUI: FC<UIProps> = ({
const specialFields = ["TransactionType", "Account", "Destination"]; const specialFields = ["TransactionType", "Account", "Destination"];
const otherFields = Object.keys(txFields).filter( const otherFields = Object.keys(txFields).filter(
k => !specialFields.includes(k) (k) => !specialFields.includes(k)
) as [keyof TxFields]; ) as [keyof TxFields];
const switchToJson = () => const switchToJson = () =>
setState({ editorSavedValue: null, viewType: "json" }); setState({ editorSavedValue: null, viewType: "json" });
useEffect(() => { useEffect(() => {
const defaultOption = transactionsOptions.find( const defaultOption = transactionsOptions.find(
tt => tt.value === "Payment" (tt) => tt.value === "Payment"
); );
if (defaultOption) { if (defaultOption) {
handleChangeTxType(defaultOption); handleChangeTxType(defaultOption);
@@ -204,7 +204,7 @@ export const TxUI: FC<UIProps> = ({
/> />
</Flex> </Flex>
)} )}
{otherFields.map(field => { {otherFields.map((field) => {
let _value = txFields[field]; let _value = txFields[field];
let value: string | undefined; let value: string | undefined;
@@ -253,13 +253,39 @@ export const TxUI: FC<UIProps> = ({
/> />
) : ( ) : (
<Input <Input
type={isFee ? "number" : "text"}
value={value} value={value}
onChange={e => { onChange={(e) => {
handleSetField(field, e.target.value); if (isFee) {
const val = e.target.value
.replaceAll(".", "")
.replaceAll(",", "");
handleSetField(field, val);
} else {
handleSetField(field, e.target.value);
}
}} }}
onKeyPress={
isFee
? (e) => {
if (e.key === "." || e.key === ",") {
e.preventDefault();
}
}
: undefined
}
css={{ css={{
width: "70%", width: "70%",
flex: "inherit", flex: "inherit",
"-moz-appearance": "textfield",
"&::-webkit-outer-spin-button": {
"-webkit-appearance": "none",
margin: 0,
},
"&::-webkit-inner-spin-button ": {
"-webkit-appearance": "none",
margin: 0,
},
}} }}
/> />
)} )}
@@ -268,6 +294,8 @@ export const TxUI: FC<UIProps> = ({
size="xs" size="xs"
variant="primary" variant="primary"
outline outline
disabled={txState.txIsDisabled}
isDisabled={txState.txIsDisabled}
isLoading={feeLoading} isLoading={feeLoading}
css={{ css={{
position: "absolute", position: "absolute",

View File

@@ -12,6 +12,5 @@ export { default as Box } from "./Box";
export { default as Button } from "./Button"; export { default as Button } from "./Button";
export { default as Pre } from "./Pre"; export { default as Pre } from "./Pre";
export { default as ButtonGroup } from "./ButtonGroup"; export { default as ButtonGroup } from "./ButtonGroup";
export { default as DeployFooter } from "./DeployFooter";
export * from "./Dialog"; export * from "./Dialog";
export * from "./DropdownMenu"; export * from "./DropdownMenu";

View File

@@ -36,6 +36,7 @@
"monaco-editor": "^0.33.0", "monaco-editor": "^0.33.0",
"next": "^12.0.4", "next": "^12.0.4",
"next-auth": "^4.0.0-beta.5", "next-auth": "^4.0.0-beta.5",
"next-plausible": "^3.2.0",
"next-themes": "^0.1.1", "next-themes": "^0.1.1",
"normalize-url": "^7.0.2", "normalize-url": "^7.0.2",
"octokit": "^1.7.0", "octokit": "^1.7.0",

View File

@@ -7,6 +7,7 @@ import { ThemeProvider } from "next-themes";
import { Toaster } from "react-hot-toast"; import { Toaster } from "react-hot-toast";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { IdProvider } from "@radix-ui/react-id"; import { IdProvider } from "@radix-ui/react-id";
import PlausibleProvider from "next-plausible";
import { darkTheme, css } from "../stitches.config"; import { darkTheme, css } from "../stitches.config";
import Navigation from "../components/Navigation"; import Navigation from "../components/Navigation";
@@ -114,6 +115,7 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
media="(prefers-color-scheme: light)" media="(prefers-color-scheme: light)"
/> />
</Head> </Head>
<IdProvider> <IdProvider>
<SessionProvider session={session}> <SessionProvider session={session}>
<ThemeProvider <ThemeProvider
@@ -125,23 +127,28 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
dark: darkTheme.className, dark: darkTheme.className,
}} }}
> >
<Navigation /> <PlausibleProvider
<Component {...pageProps} /> domain="hooks-builder.xrpl.org"
<Toaster trackOutboundLinks
toastOptions={{ >
className: css({ <Navigation />
backgroundColor: "$mauve1", <Component {...pageProps} />
color: "$mauve10", <Toaster
fontSize: "$sm", toastOptions={{
zIndex: 9999, className: css({
".dark &": { backgroundColor: "$mauve1",
backgroundColor: "$mauve4", color: "$mauve10",
color: "$mauve12", fontSize: "$sm",
}, zIndex: 9999,
})(), ".dark &": {
}} backgroundColor: "$mauve4",
/> color: "$mauve12",
<Alert /> },
})(),
}}
/>
<Alert />
</PlausibleProvider>
</ThemeProvider> </ThemeProvider>
</SessionProvider> </SessionProvider>
</IdProvider> </IdProvider>

View File

@@ -8,7 +8,9 @@ import { useSnapshot } from "valtio";
import { ButtonGroup, Flex } from "../../components"; import { ButtonGroup, Flex } from "../../components";
import Box from "../../components/Box"; import Box from "../../components/Box";
import Button from "../../components/Button"; import Button from "../../components/Button";
import LogBoxForScripts from "../../components/LogBoxForScripts";
import Popover from "../../components/Popover"; import Popover from "../../components/Popover";
import RunScript from "../../components/RunScript";
import state from "../../state"; import state from "../../state";
import { compileCode } from "../../state/actions"; import { compileCode } from "../../state/actions";
import { getSplit, saveSplit } from "../../state/actions/persistSplits"; import { getSplit, saveSplit } from "../../state/actions/persistSplits";
@@ -196,20 +198,61 @@ const Home: NextPage = () => {
</Flex> </Flex>
</Hotkeys> </Hotkeys>
)} )}
{snap.files[snap.active]?.name?.split(".")?.[1]?.toLowerCase() ===
"js" && (
<Hotkeys
keyName="command+b,ctrl+b"
onKeyDown={() =>
!snap.compiling && snap.files.length && compileCode(snap.active)
}
>
<Flex
css={{
position: "absolute",
bottom: "$4",
left: "$4",
alignItems: "center",
display: "flex",
cursor: "pointer",
gap: "$2",
}}
>
<RunScript file={snap.files[snap.active]} />
</Flex>
</Hotkeys>
)}
</main> </main>
<Box <Flex css={{ width: "100%" }}>
css={{ <Flex
display: "flex", css={{
background: "$mauve1", flex: 1,
position: "relative", background: "$mauve1",
}} position: "relative",
> borderRight: "1px solid $mauve8",
<LogBox }}
title="Development Log" >
clearLog={() => (state.logs = [])} <LogBox
logs={snap.logs} title="Development Log"
/> clearLog={() => (state.logs = [])}
</Box> logs={snap.logs}
/>
</Flex>
{snap.files[snap.active]?.name?.split(".")?.[1]?.toLowerCase() ===
"js" && (
<Flex
css={{
flex: 1,
}}
>
<LogBoxForScripts
showButtons={false}
title="Script Log"
logs={snap.scriptLogs}
clearLog={() => (state.scriptLogs = [])}
/>
</Flex>
)}
</Flex>
</Split> </Split>
); );
}; };

View File

@@ -32,11 +32,21 @@ const Test = () => {
if (!showComponent) { if (!showComponent) {
return null; return null;
} }
const hasScripts = Boolean(
snap.files.filter((f) => f.name.toLowerCase()?.endsWith(".js")).length
);
return ( return (
<Container css={{ px: 0 }}> <Container css={{ px: 0 }}>
<Split <Split
direction="vertical" direction="vertical"
sizes={getSplit("testVertical") || [50, 20, 30]} sizes={
hasScripts && getSplit("testVertical")?.length === 2
? [50, 20, 30]
: hasScripts
? [50, 20, 50]
: [50, 50]
}
gutterSize={4} gutterSize={4}
gutterAlign="center" gutterAlign="center"
style={{ height: "calc(100vh - 60px)" }} style={{ height: "calc(100vh - 60px)" }}
@@ -91,16 +101,22 @@ const Test = () => {
</Box> </Box>
</Split> </Split>
</Flex> </Flex>
<Flex {hasScripts ? (
as="div" <Flex
css={{ as="div"
borderTop: "1px solid $mauve6", css={{
background: "$mauve1", borderTop: "1px solid $mauve6",
flexDirection: "column", background: "$mauve1",
}} flexDirection: "column",
> }}
<LogBoxForScripts title="Helper scripts" logs={snap.scriptLogs} /> >
</Flex> <LogBoxForScripts
title="Helper scripts"
logs={snap.scriptLogs}
clearLog={() => (state.scriptLogs = [])}
/>
</Flex>
) : null}
<Flex> <Flex>
<Split <Split
direction="horizontal" direction="horizontal"

View File

@@ -39,7 +39,7 @@ export const compileCode = async (activeId: number) => {
files: [ files: [
{ {
type: "c", type: "c",
options: state.compileOptions.optimizationLevel || '-O0', options: state.compileOptions.optimizationLevel || '-O2',
name: state.files[activeId].name, name: state.files[activeId].name,
src: state.files[activeId].content, src: state.files[activeId].content,
}, },

View File

@@ -54,15 +54,15 @@ export const prepareDeployHookTx = async (
account: IAccount & { name?: string }, account: IAccount & { name?: string },
data: SetHookData data: SetHookData
) => { ) => {
if ( const activeFile = state.files[state.active]?.compiledContent
!state.files || ? state.files[state.active]
state.files.length === 0 || : state.files.filter((file) => file.compiledContent)[0];
!state.files?.[state.active]?.compiledContent
) { if (!state.files || state.files.length === 0) {
return; return;
} }
if (!state.files?.[state.active]?.compiledContent) { if (!activeFile?.compiledContent) {
return; return;
} }
if (!state.client) { if (!state.client) {
@@ -99,7 +99,7 @@ export const prepareDeployHookTx = async (
{ {
Hook: { Hook: {
CreateCode: arrayBufferToHex( CreateCode: arrayBufferToHex(
state.files?.[state.active]?.compiledContent activeFile?.compiledContent
).toUpperCase(), ).toUpperCase(),
HookOn: calculateHookOn(hookOnValues), HookOn: calculateHookOn(hookOnValues),
HookNamespace, HookNamespace,

View File

@@ -8,7 +8,8 @@ export const downloadAsZip = async () => {
state.zipLoading = true state.zipLoading = true
// TODO do something about file/gist loading state // TODO do something about file/gist loading state
const files = state.files.map(({ name, content }) => ({ name, content })); const files = state.files.map(({ name, content }) => ({ name, content }));
const zipped = await createZip(files); const wasmFiles = state.files.filter(i => i.compiledContent).map(({ name, compiledContent }) => ({ name: `${name}.wasm`, content: compiledContent }));
const zipped = await createZip([...files, ...wasmFiles]);
const zipFileName = guessZipFileName(files); const zipFileName = guessZipFileName(files);
zipped.saveFile(zipFileName); zipped.saveFile(zipFileName);
} catch (error) { } catch (error) {

View File

@@ -19,7 +19,7 @@ export const fetchFiles = (gistId: string) => {
octokit octokit
.request("GET /gists/{gist_id}", { gist_id: gistId }) .request("GET /gists/{gist_id}", { gist_id: gistId })
.then(async res => { .then(async res => {
if (!Object.values(templateFileIds).includes(gistId)) { if (!Object.values(templateFileIds).map(v => v.id).includes(gistId)) {
return res return res
} }
// in case of templates, fetch header file(s) and append to res // in case of templates, fetch header file(s) and append to res

View File

@@ -1,20 +1,41 @@
// export const templateFileIds = { import Carbon from "../../components/icons/Carbon";
// 'starter': '1d14e51e2e02dc0a508cb0733767a914', // TODO currently same as accept import Firewall from "../../components/icons/Firewall";
// 'firewall': 'bcd6d0c0fcbe52545ddb802481ff9d26', import Notary from "../../components/icons/Notary";
// 'notary': 'a789c75f591eeab7932fd702ed8cf9ea', import Peggy from "../../components/icons/Peggy";
// 'carbon': '43925143fa19735d8c6505c34d3a6a47', import Starter from "../../components/icons/Starter";
// 'peggy': 'ceaf352e2a65741341033ab7ef05c448',
// 'headers': '9b448e8a55fab11ef5d1274cb59f9cf3'
// }
export const templateFileIds = { export const templateFileIds = {
'starter': '1f7d2963d9e342ea092286115274f3e3', 'starter': {
'firewall': '70edec690f0de4dd315fad1f4f996d8c', id: '9106f1fe60482d90475bfe8f1315affe',
'notary': '3d5677768fe8a54c4f6317e185d9ba66', name: 'Starter',
'carbon': 'a9fbcaf1b816b198c7fc0f62962bebf2', description: 'Just a basic starter with essential imports, just accepts any transaction coming through',
'doubler': '56b86174aeb70b2b48eee962bad3e355', icon: Starter
'peggy': 'd21298a37e1550b781682014762a567b',
'headers': '55f639bce59a49c58c45e663776b5138' },
'firewall': {
id: '741816f53eddac862ef1ba400e1b9b84',
name: 'Firewall',
description: 'This Hook essentially checks a blacklist of accounts',
icon: Firewall
},
'notary': {
id: '0dfe12adb0aa75cff24c3c19497fb95a',
name: 'Notary',
description: 'Collecting signatures for multi-sign transactions',
icon: Notary
},
'carbon': {
id: '5941c19dce3e147948f564e224553c02',
name: 'Carbon',
description: 'Send a percentage of sum to an address',
icon: Carbon
},
'peggy': {
id: '52e61c02e777c44c913808981a4ca61f',
name: 'Peggy',
description: 'An oracle based stable coin hook',
icon: Peggy
},
} }
export const apiHeaderFiles = ['hookapi.h', 'sfcodes.h', 'hookmacro.h'] export const apiHeaderFiles = ['hookapi.h', 'sfcodes.h', 'macro.h', 'extern.h', 'error.h'];

View File

@@ -114,7 +114,7 @@ let initialState: IState = {
mainModalShowed: false, mainModalShowed: false,
accounts: [], accounts: [],
compileOptions: { compileOptions: {
optimizationLevel: '-O0', optimizationLevel: '-O2',
strip: true strip: true
} }
}; };

View File

@@ -2981,6 +2981,11 @@ next-auth@^4.0.0-beta.5:
preact-render-to-string "^5.1.19" preact-render-to-string "^5.1.19"
uuid "^8.3.2" uuid "^8.3.2"
next-plausible@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/next-plausible/-/next-plausible-3.2.0.tgz#d801346253e0c1cf64a02b9fc3a42050455cbc47"
integrity sha512-OlYcLXBG3kKd/fKMpm8SZ5IkUKSFm1/8t7cv6e5bewIqlpdZpdWuSrjbdJpbmutb2KPLXHzilKp09zmDGjy9KQ==
next-themes@^0.1.1: next-themes@^0.1.1:
version "0.1.1" version "0.1.1"
resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.1.1.tgz#122113a458bf1d1be5ffed66778ab924c106f82a" resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.1.1.tgz#122113a458bf1d1be5ffed66778ab924c106f82a"