Compare commits

..

2 Commits

Author SHA1 Message Date
muzam1l
b08b66529c Fix split error 2022-07-05 17:31:49 +05:30
Valtteri Karesto
3af2bad536 Make ping interval longer (#232)
* Make ping interval 45s

Co-authored-by: Vaclav Barta <vbarta@mangrove.cz>
2022-07-04 12:22:06 +02:00
12 changed files with 449 additions and 257 deletions

View File

@@ -87,7 +87,7 @@ const addListeners = (account: ISelect | null) => {
if (streamState.socket) { if (streamState.socket) {
interval = setInterval(() => { interval = setInterval(() => {
streamState.socket?.send(""); streamState.socket?.send("");
}, 10000); }, 45000);
} }
streamState.socket.addEventListener("open", () => onOpen(account)); streamState.socket.addEventListener("open", () => onOpen(account));

View File

@@ -6,7 +6,7 @@ import {
useState, useState,
useCallback, useCallback,
} from "react"; } from "react";
import { IconProps, Notepad, Prohibit } from "phosphor-react"; import { Notepad, Prohibit } from "phosphor-react";
import useStayScrolled from "react-stay-scrolled"; import useStayScrolled from "react-stay-scrolled";
import NextLink from "next/link"; import NextLink from "next/link";
@@ -24,7 +24,6 @@ interface ILogBox {
logs: ILog[]; logs: ILog[];
renderNav?: () => ReactNode; renderNav?: () => ReactNode;
enhanced?: boolean; enhanced?: boolean;
Icon?: FC<IconProps>;
} }
const LogBox: FC<ILogBox> = ({ const LogBox: FC<ILogBox> = ({
@@ -34,7 +33,6 @@ const LogBox: FC<ILogBox> = ({
children, children,
renderNav, renderNav,
enhanced, enhanced,
Icon = Notepad,
}) => { }) => {
const logRef = useRef<HTMLPreElement>(null); const logRef = useRef<HTMLPreElement>(null);
const { stayScrolled /*, scrollBottom*/ } = useStayScrolled(logRef); const { stayScrolled /*, scrollBottom*/ } = useStayScrolled(logRef);
@@ -84,14 +82,14 @@ const LogBox: FC<ILogBox> = ({
gap: "$3", gap: "$3",
}} }}
> >
<Icon size="15px" /> <Text css={{ lineHeight: 1 }}>{title}</Text> <Notepad size="15px" /> <Text css={{ lineHeight: 1 }}>{title}</Text>
</Heading> </Heading>
<Flex <Flex
row row
align="center" align="center"
// css={{ css={{
// maxWidth: "100%", // TODO make it max without breaking layout! width: "50%", // TODO make it max without breaking layout!
// }} }}
> >
{renderNav?.()} {renderNav?.()}
</Flex> </Flex>
@@ -164,11 +162,11 @@ export const Log: FC<ILog> = ({
(str?: string): ReactNode => { (str?: string): ReactNode => {
if (!str || !accounts.length) return null; if (!str || !accounts.length) return null;
const pattern = `(${accounts.map(acc => acc.address).join("|")})`; const pattern = `(${accounts.map((acc) => acc.address).join("|")})`;
const res = regexifyString({ const res = regexifyString({
pattern: new RegExp(pattern, "gim"), pattern: new RegExp(pattern, "gim"),
decorator: (match, idx) => { decorator: (match, idx) => {
const name = accounts.find(acc => acc.address === match)?.name; const name = accounts.find((acc) => acc.address === match)?.name;
return ( return (
<Link <Link
key={match + idx} key={match + idx}
@@ -191,12 +189,12 @@ export const Log: FC<ILog> = ({
let message: ReactNode; let message: ReactNode;
if (typeof _message === "string") { if (typeof _message === 'string') {
_message = _message.trim().replace(/\n /gi, "\n"); _message = _message.trim().replace(/\n /gi, "\n");
if (_message) message = enrichAccounts(_message); message = enrichAccounts(_message)
else message = <Text muted>{'""'}</Text> }
} else { else {
message = _message; message = _message
} }
const jsonData = enrichAccounts(_jsonData); const jsonData = enrichAccounts(_jsonData);

View File

@@ -0,0 +1,234 @@
import {
useRef,
useLayoutEffect,
ReactNode,
FC,
useState,
useCallback,
} from "react";
import { FileJs, Prohibit } from "phosphor-react";
import useStayScrolled from "react-stay-scrolled";
import NextLink from "next/link";
import Container from "./Container";
import LogText from "./LogText";
import state, { ILog } from "../state";
import { Pre, Link, Heading, Button, Text, Flex, Box } from ".";
import regexifyString from "regexify-string";
import { useSnapshot } from "valtio";
import { AccountDialog } from "./Accounts";
import RunScript from "./RunScript";
interface ILogBox {
title: string;
clearLog?: () => void;
logs: ILog[];
renderNav?: () => ReactNode;
enhanced?: boolean;
showButtons?: boolean;
}
const LogBox: FC<ILogBox> = ({
title,
clearLog,
logs,
children,
renderNav,
enhanced,
showButtons = true,
}) => {
const logRef = useRef<HTMLPreElement>(null);
const { stayScrolled /*, scrollBottom*/ } = useStayScrolled(logRef);
const snap = useSnapshot(state);
useLayoutEffect(() => {
stayScrolled();
}, [stayScrolled, logs]);
return (
<Flex
as="div"
css={{
display: "flex",
borderTop: "1px solid $mauve6",
background: "$mauve1",
position: "relative",
flex: 1,
height: "100%",
}}
>
<Container
css={{
px: 0,
height: "100%",
}}
>
<Flex
fluid
css={{
height: "48px",
alignItems: "center",
fontSize: "$sm",
fontWeight: 300,
}}
>
<Heading
as="h3"
css={{
fontWeight: 300,
m: 0,
fontSize: "11px",
color: "$mauve12",
px: "$3",
textTransform: "uppercase",
alignItems: "center",
display: "inline-flex",
gap: "$3",
mr: "$3",
}}
>
<FileJs size="15px" /> <Text css={{ lineHeight: 1 }}>{title}</Text>
</Heading>
{showButtons && (
<Flex css={{ gap: "$3" }}>
{snap.files
.filter((f) => f.name.endsWith(".js"))
.map((file) => (
<RunScript file={file} key={file.name} />
))}
</Flex>
)}
<Flex css={{ ml: "auto", gap: "$3", marginRight: "$3" }}>
{clearLog && (
<Button ghost size="xs" onClick={clearLog}>
<Prohibit size="14px" />
</Button>
)}
</Flex>
</Flex>
<Box
as="pre"
ref={logRef}
css={{
margin: 0,
// display: "inline-block",
display: "flex",
flexDirection: "column",
width: "100%",
height: "calc(100% - 48px)", // 100% minus the logbox header height
overflowY: "auto",
fontSize: "13px",
fontWeight: "$body",
fontFamily: "$monospace",
px: "$3",
pb: "$2",
whiteSpace: "normal",
}}
>
{logs?.map((log, index) => (
<Box
as="span"
key={log.type + index}
css={{
"@hover": {
"&:hover": {
backgroundColor: enhanced ? "$backgroundAlt" : undefined,
},
},
p: enhanced ? "$1" : undefined,
my: enhanced ? "$1" : undefined,
}}
>
<Log {...log} />
</Box>
))}
{children}
</Box>
</Container>
</Flex>
);
};
export const Log: FC<ILog> = ({
type,
timestring,
message: _message,
link,
linkText,
defaultCollapsed,
jsonData: _jsonData,
}) => {
const [expanded, setExpanded] = useState(!defaultCollapsed);
const { accounts } = useSnapshot(state);
const [dialogAccount, setDialogAccount] = useState<string | null>(null);
const enrichAccounts = useCallback(
(str?: string): ReactNode => {
if (!str || !accounts.length) return null;
const pattern = `(${accounts.map((acc) => acc.address).join("|")})`;
const res = regexifyString({
pattern: new RegExp(pattern, "gim"),
decorator: (match, idx) => {
const name = accounts.find((acc) => acc.address === match)?.name;
return (
<Link
key={match + idx}
as="a"
onClick={() => setDialogAccount(match)}
title={match}
highlighted
>
{name || match}
</Link>
);
},
input: str,
});
return <>{res}</>;
},
[accounts]
);
let message: ReactNode;
if (typeof _message === "string") {
_message = _message.trim().replace(/\n /gi, "\n");
message = enrichAccounts(_message);
} else {
message = _message;
}
const jsonData = enrichAccounts(_jsonData);
return (
<>
<AccountDialog
setActiveAccountAddress={setDialogAccount}
activeAccountAddress={dialogAccount}
/>
<LogText variant={type}>
{timestring && (
<Text muted monospace>
{timestring}{" "}
</Text>
)}
<Pre>{message} </Pre>
{link && (
<NextLink href={link} shallow passHref>
<Link as="a">{linkText}</Link>
</NextLink>
)}
{jsonData && (
<Link onClick={() => setExpanded(!expanded)} as="a">
{expanded ? "Collapse" : "Expand"}
</Link>
)}
{expanded && jsonData && <Pre block>{jsonData}</Pre>}
</LogText>
<br />
</>
);
};
export default LogBox;

View File

@@ -1,14 +1,10 @@
import * as Handlebars from "handlebars";
import { Play, X } from "phosphor-react"; import { Play, X } from "phosphor-react";
import { import { useCallback, useEffect, useState } from "react";
HTMLInputTypeAttribute, import state, { IFile, ILog } from "../../state";
useCallback,
useEffect,
useState,
} from "react";
import state, { IAccount, IFile, ILog } from "../../state";
import Button from "../Button"; import Button from "../Button";
import Box from "../Box"; import Box from "../Box";
import Input, { Label } from "../Input"; import Input from "../Input";
import Stack from "../Stack"; import Stack from "../Stack";
import { import {
Dialog, Dialog,
@@ -21,21 +17,16 @@ import {
import Flex from "../Flex"; import Flex from "../Flex";
import { useSnapshot } from "valtio"; import { useSnapshot } from "valtio";
import Select from "../Select"; import Select from "../Select";
import Text from "../Text";
import { saveFile } from "../../state/actions/saveFile"; import { saveFile } from "../../state/actions/saveFile";
import { getErrors, getTags } from "../../utils/comment-parser";
import toast from "react-hot-toast";
const generateHtmlTemplate = (code: string, data?: Record<string, any>) => { Handlebars.registerHelper(
let processString: string | undefined; "customize_input",
const process = { env: { NODE_ENV: "production" } } as any; function (/* dynamic arguments */) {
if (data) { return new Handlebars.SafeString(arguments[0]);
Object.keys(data).forEach(key => {
process.env[key] = data[key];
});
} }
processString = JSON.stringify(process); );
const generateHtmlTemplate = (code: string) => {
return ` return `
<html> <html>
<head> <head>
@@ -64,20 +55,7 @@ const generateHtmlTemplate = (code: string, data?: Record<string, any>) => {
parent.window.postMessage({ type: 'warning', args: args || [] }, '*'); parent.window.postMessage({ type: 'warning', args: args || [] }, '*');
warnLog.apply(console, args); warnLog.apply(console, args);
} }
var process = '${processString || "{}"}';
process = JSON.parse(process);
window.process = process
function windowErrorHandler(event) {
event.preventDefault() // to prevent automatically logging to console
console.error(event.error?.toString())
}
window.addEventListener('error', windowErrorHandler);
</script> </script>
<script type="module"> <script type="module">
${code} ${code}
</script> </script>
@@ -91,57 +69,72 @@ const generateHtmlTemplate = (code: string, data?: Record<string, any>) => {
type Fields = Record< type Fields = Record<
string, string,
{ {
name: string; key: string;
value: string; value: string;
type?: "Account" | `Account.${keyof IAccount}` | HTMLInputTypeAttribute; label?: string;
description?: string; type?: string;
required?: boolean; attach?: "account_secret" | "account_address" | string;
} }
>; >;
const RunScript: React.FC<{ file: IFile }> = ({ file: { content, name } }) => { const RunScript: React.FC<{ file: IFile }> = ({ file: { content, name } }) => {
const snap = useSnapshot(state); const snap = useSnapshot(state);
const [templateError, setTemplateError] = useState(""); const [templateError, setTemplateError] = useState("");
const getFieldValues = useCallback(() => {
try {
const parsed = Handlebars.parse(content);
const names = parsed.body
.filter((i) => i.type === "MustacheStatement")
.map((block) => {
// @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 [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 getFields = useCallback(() => { const fieldsToSend: Record<string, string> = {};
const inputTags = ["input", "param", "arg", "argument"]; Object.entries(fields).map(([key, obj]) => {
const tags = getTags(content) fieldsToSend[key] = obj.value;
.filter(tag => inputTags.includes(tag.tag)) });
.filter(tag => !!tag.name); const template = Handlebars.compile(content, { strict: false });
let _fields = tags.map(tag => ({
name: tag.name,
value: tag.default || "",
type: tag.type,
description: tag.description,
required: !tag.optional,
}));
const fields: Fields = _fields.reduce((acc, field) => {
acc[field.name] = field;
return acc;
}, {} as Fields);
const error = getErrors(content);
if (error) setTemplateError(error.message);
else setTemplateError("");
return fields;
}, [content]);
const runScript = useCallback(() => {
try { try {
let data: any = {}; const code = template(fieldsToSend);
Object.keys(fields).forEach(key => { setIframeCode(generateHtmlTemplate(code));
data[key] = fields[key].value;
});
const template = generateHtmlTemplate(content, data);
setIframeCode(template);
state.scriptLogs = [ state.scriptLogs = [
...snap.scriptLogs, ...snap.scriptLogs,
{ type: "success", message: "Started running..." }, { type: "success", message: "Started running..." },
@@ -153,7 +146,7 @@ const RunScript: React.FC<{ file: IFile }> = ({ file: { content, name } }) => {
{ type: "error", message: err?.message || "Could not parse template" }, { type: "error", message: err?.message || "Could not parse template" },
]; ];
} }
}, [content, fields, snap.scriptLogs]); };
useEffect(() => { useEffect(() => {
const handleEvent = (e: any) => { const handleEvent = (e: any) => {
@@ -170,29 +163,17 @@ const RunScript: React.FC<{ file: IFile }> = ({ file: { content, name } }) => {
}, [snap.scriptLogs]); }, [snap.scriptLogs]);
useEffect(() => { useEffect(() => {
const defaultFields = getFields() || {}; const newDefaultState = getFieldValues();
setFields(defaultFields); setFields(newDefaultState || {});
}, [content, setFields, getFields]); }, [content, setFields, getFieldValues]);
const accOptions = snap.accounts?.map(acc => ({ const options = snap.accounts?.map((acc) => ({
...acc,
label: acc.name, label: acc.name,
secret: acc.secret,
address: acc.address,
value: acc.address, value: acc.address,
})); }));
const isDisabled = Object.values(fields).some(
field => field.required && !field.value
);
const handleRun = useCallback(() => {
if (isDisabled)
return toast.error("Please fill in all the required fields.");
state.scriptLogs = [];
runScript();
setIsDialogOpen(false);
}, [isDisabled, runScript]);
return ( return (
<> <>
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}> <Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
@@ -210,87 +191,74 @@ const RunScript: React.FC<{ file: IFile }> = ({ file: { content, name } }) => {
<DialogContent> <DialogContent>
<DialogTitle>Run {name} script</DialogTitle> <DialogTitle>Run {name} script</DialogTitle>
<DialogDescription> <DialogDescription>
<Box> 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 make sure you know what you are doing.
hook, make sure you trust the author before you continue. <br />
</Box>
{templateError && ( {templateError && (
<Box <Box
as="span" as="span"
css={{ css={{ display: "block", color: "$error", mt: "$3" }}
display: "block",
color: "$error",
mt: "$3",
whiteSpace: "pre",
}}
> >
{templateError} Error occured while parsing template, modify script and try
</Box> again!
)}
{Object.keys(fields).length > 0 && (
<Box css={{ mt: "$4", mb: 0 }}>
Fill in the following parameters to run the script.
</Box> </Box>
)} )}
<br />
{Object.keys(fields).length > 0
? `You also need to fill in following parameters to run the script`
: ""}
</DialogDescription> </DialogDescription>
<Stack css={{ width: "100%" }}> <Stack css={{ width: "100%" }}>
{Object.keys(fields).map(key => { {Object.keys(fields).map((key) => (
const { name, value, type, description, required } = fields[key]; <Box key={key} css={{ width: "100%" }}>
<label>
const isAccount = type?.startsWith("Account"); {fields[key]?.label || key}{" "}
const isAccountSecret = type === "Account.secret"; {fields[key].attach === "account_secret" &&
`(Script uses account secret)`}
const accountField = </label>
(isAccount && type?.split(".")[1]) || "address"; {fields[key].attach === "account_secret" ||
fields[key].attach === "account_address" ? (
return ( <Select
<Box key={name} css={{ width: "100%" }}> css={{ mt: "$1" }}
<Label options={options}
css={{ display: "flex", justifyContent: "space-between" }} onChange={(val: any) => {
> setFields({
<span> ...fields,
{description || name} {required && <Text error>*</Text>} [key]: {
</span> ...fields[key],
{isAccountSecret && ( value:
<Text error small css={{ alignSelf: "end" }}> fields[key].attach === "account_secret"
can access account secret key ? val.secret
</Text> : val.address,
},
});
}}
value={options.find(
(opt) =>
opt.address === fields[key].value ||
opt.secret === fields[key].value
)} )}
</Label> />
{isAccount ? ( ) : (
<Select <Input
css={{ mt: "$1" }} type={fields[key].type || "text"}
options={accOptions} value={
onChange={(val: any) => { typeof fields[key].value !== "string"
setFields({ ? // @ts-expect-error
...fields, fields[key].value.value
[key]: { : fields[key].value
...fields[key], }
value: val[accountField], css={{ mt: "$1" }}
}, onChange={(e) => {
}); setFields({
}} ...fields,
value={accOptions.find( [key]: { ...fields[key], value: e.target.value },
(acc: any) => acc[accountField] === value });
)} }}
/> />
) : ( )}
<Input </Box>
type={type || "text"} ))}
value={value}
css={{ mt: "$1" }}
onChange={e => {
setFields({
...fields,
[key]: { ...fields[key], value: e.target.value },
});
}}
/>
)}
</Box>
);
})}
<Flex <Flex
css={{ justifyContent: "flex-end", width: "100%", gap: "$3" }} css={{ justifyContent: "flex-end", width: "100%", gap: "$3" }}
> >
@@ -299,8 +267,16 @@ const RunScript: React.FC<{ file: IFile }> = ({ file: { content, name } }) => {
</DialogClose> </DialogClose>
<Button <Button
variant="primary" variant="primary"
isDisabled={isDisabled} isDisabled={
onClick={handleRun} (Object.entries(fields).length > 0 &&
Object.entries(fields).some(([key, obj]) => !obj.value)) ||
Boolean(templateError)
}
onClick={() => {
state.scriptLogs = [];
runScript();
setIsDialogOpen(false);
}}
> >
Run script Run script
</Button> </Button>

View File

@@ -7,30 +7,20 @@ const Text = styled("span", {
variants: { variants: {
small: { small: {
true: { true: {
fontSize: "$xs", fontSize: '$xs'
}, }
}, },
muted: { muted: {
true: { true: {
color: "$mauve9", color: '$mauve9'
}, }
},
error: {
true: {
color: "$error",
},
}, },
monospace: { monospace: {
true: { true: {
fontFamily: "$monospace", fontFamily: '$monospace'
},
},
block: {
true: {
display: "block",
} }
} }
}, }
}); });
export default Text; export default Text;

View File

@@ -8,6 +8,9 @@ module.exports = {
config.resolve.alias["vscode"] = require.resolve( config.resolve.alias["vscode"] = require.resolve(
"@codingame/monaco-languageclient/lib/vscode-compatibility" "@codingame/monaco-languageclient/lib/vscode-compatibility"
); );
config.resolve.alias["handlebars"] = require.resolve(
"handlebars/dist/handlebars.js"
);
if (!isServer) { if (!isServer) {
config.resolve.fallback.fs = false; config.resolve.fallback.fs = false;
} }

View File

@@ -25,10 +25,10 @@
"@radix-ui/react-tooltip": "^0.1.7", "@radix-ui/react-tooltip": "^0.1.7",
"@stitches/react": "^1.2.8", "@stitches/react": "^1.2.8",
"base64-js": "^1.5.1", "base64-js": "^1.5.1",
"comment-parser": "^1.3.1",
"dinero.js": "^1.9.1", "dinero.js": "^1.9.1",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"filesize": "^8.0.7", "filesize": "^8.0.7",
"handlebars": "^4.7.7",
"javascript-time-ago": "^2.3.11", "javascript-time-ago": "^2.3.11",
"jszip": "^3.7.1", "jszip": "^3.7.1",
"lodash.uniqby": "^4.7.0", "lodash.uniqby": "^4.7.0",

View File

@@ -1,13 +1,14 @@
import { Label } from "@radix-ui/react-label"; import { Label } from "@radix-ui/react-label";
import type { NextPage } from "next"; import type { NextPage } from "next";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import { FileJs, Gear, Play } from "phosphor-react"; import { Gear, Play } from "phosphor-react";
import Hotkeys from "react-hot-keys"; import Hotkeys from "react-hot-keys";
import Split from "react-split"; import Split from "react-split";
import { useSnapshot } from "valtio"; 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 RunScript from "../../components/RunScript";
import state from "../../state"; import state from "../../state";
@@ -243,8 +244,8 @@ const Home: NextPage = () => {
flex: 1, flex: 1,
}} }}
> >
<LogBox <LogBoxForScripts
Icon={FileJs} showButtons={false}
title="Script Log" title="Script Log"
logs={snap.scriptLogs} logs={snap.scriptLogs}
clearLog={() => (state.scriptLogs = [])} clearLog={() => (state.scriptLogs = [])}

View File

@@ -6,9 +6,8 @@ import Transaction from "../../components/Transaction";
import state from "../../state"; import state from "../../state";
import { getSplit, saveSplit } from "../../state/actions/persistSplits"; import { getSplit, saveSplit } from "../../state/actions/persistSplits";
import { transactionsState, modifyTransaction } from "../../state"; import { transactionsState, modifyTransaction } from "../../state";
import LogBoxForScripts from "../../components/LogBoxForScripts";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { FileJs } from "phosphor-react";
import RunScript from '../../components/RunScript';
const DebugStream = dynamic(() => import("../../components/DebugStream"), { const DebugStream = dynamic(() => import("../../components/DebugStream"), {
ssr: false, ssr: false,
@@ -34,17 +33,7 @@ const Test = () => {
return null; return null;
} }
const hasScripts = Boolean( const hasScripts = Boolean(
snap.files.filter(f => f.name.toLowerCase()?.endsWith(".js")).length snap.files.filter((f) => f.name.toLowerCase()?.endsWith(".js")).length
);
const renderNav = () => (
<Flex css={{ gap: "$3" }}>
{snap.files
.filter(f => f.name.endsWith(".js"))
.map(file => (
<RunScript file={file} key={file.name} />
))}
</Flex>
); );
return ( return (
@@ -56,12 +45,12 @@ const Test = () => {
? [50, 20, 30] ? [50, 20, 30]
: hasScripts : hasScripts
? [50, 20, 50] ? [50, 20, 50]
: [50, 50] : [50, 50, 0]
} }
gutterSize={4} gutterSize={4}
gutterAlign="center" gutterAlign="center"
style={{ height: "calc(100vh - 60px)" }} style={{ height: "calc(100vh - 60px)" }}
onDragEnd={e => saveSplit("testVertical", e)} onDragEnd={(e) => saveSplit("testVertical", e)}
> >
<Flex <Flex
row row
@@ -83,11 +72,11 @@ const Test = () => {
width: "100%", width: "100%",
height: "100%", height: "100%",
}} }}
onDragEnd={e => saveSplit("testHorizontal", e)} onDragEnd={(e) => saveSplit("testHorizontal", e)}
> >
<Box css={{ width: "55%", px: "$2" }}> <Box css={{ width: "55%", px: "$2" }}>
<Tabs <Tabs
label="Transaction" label='Transaction'
activeHeader={activeHeader} activeHeader={activeHeader}
// TODO make header a required field // TODO make header a required field
onChangeActive={(idx, header) => { onChangeActive={(idx, header) => {
@@ -95,8 +84,8 @@ const Test = () => {
}} }}
keepAllAlive keepAllAlive
defaultExtension="json" defaultExtension="json"
allowedExtensions={["json"]} allowedExtensions={['json']}
onCreateNewTab={header => modifyTransaction(header, {})} onCreateNewTab={(header) => modifyTransaction(header, {})}
onCloseTab={(idx, header) => onCloseTab={(idx, header) =>
header && modifyTransaction(header, undefined) header && modifyTransaction(header, undefined)
} }
@@ -122,12 +111,10 @@ const Test = () => {
flexDirection: "column", flexDirection: "column",
}} }}
> >
<LogBox <LogBoxForScripts
Icon={FileJs}
title="Helper scripts" title="Helper scripts"
logs={snap.scriptLogs} logs={snap.scriptLogs}
clearLog={() => (state.scriptLogs = [])} clearLog={() => (state.scriptLogs = [])}
renderNav={renderNav}
/> />
</Flex> </Flex>
) : null} ) : null}

View File

@@ -13,13 +13,13 @@ export const templateFileIds = {
}, },
'firewall': { 'firewall': {
id: '1cc30f39c8a0b9c55b88c312669ca45e', // Forked id: '741816f53eddac862ef1ba400e1b9b84',
name: 'Firewall', name: 'Firewall',
description: 'This Hook essentially checks a blacklist of accounts', description: 'This Hook essentially checks a blacklist of accounts',
icon: Firewall icon: Firewall
}, },
'notary': { 'notary': {
id: '87b6f5a8c2f5038fb0f20b8b510efa10', // Forked id: '0dfe12adb0aa75cff24c3c19497fb95a',
name: 'Notary', name: 'Notary',
description: 'Collecting signatures for multi-sign transactions', description: 'Collecting signatures for multi-sign transactions',
icon: Notary icon: Notary
@@ -31,7 +31,7 @@ export const templateFileIds = {
icon: Carbon icon: Carbon
}, },
'peggy': { 'peggy': {
id: '049784a83fa068faf7912f663f7b6471', // Forked id: '52e61c02e777c44c913808981a4ca61f',
name: 'Peggy', name: 'Peggy',
description: 'An oracle based stable coin hook', description: 'An oracle based stable coin hook',
icon: Peggy icon: Peggy

View File

@@ -1,24 +0,0 @@
import { Spec, parse, Problem } from "comment-parser"
export const getTags = (source?: string): Spec[] => {
if (!source) return []
const blocks = parse(source)
const tags = blocks.reduce(
(acc, block) => acc.concat(block.tags),
[] as Spec[]
);
return tags
}
export const getErrors = (source?: string): Error | undefined => {
if (!source) return undefined
const blocks = parse(source)
const probs = blocks.reduce(
(acc, block) => acc.concat(block.problems),
[] as Problem[]
);
if (!probs.length) return undefined
const errors = probs.map(prob => `[${prob.code}] on line ${prob.line}: ${prob.message}`)
const error = new Error(`The following error(s) occured while parsing JSDOC: \n${errors.join('\n')}`)
return error
}

View File

@@ -1525,11 +1525,6 @@ color-name@~1.1.4:
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
comment-parser@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.3.1.tgz#3d7ea3adaf9345594aedee6563f422348f165c1b"
integrity sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==
concat-map@0.0.1: concat-map@0.0.1:
version "0.0.1" version "0.0.1"
resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
@@ -2286,6 +2281,18 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6:
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz"
integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==
handlebars@^4.7.7:
version "4.7.7"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1"
integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==
dependencies:
minimist "^1.2.5"
neo-async "^2.6.0"
source-map "^0.6.1"
wordwrap "^1.0.0"
optionalDependencies:
uglify-js "^3.1.4"
has-bigints@^1.0.1: has-bigints@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz" resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz"
@@ -2954,6 +2961,11 @@ natural-compare@^1.4.0:
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
neo-async@^2.6.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
next-auth@^4.0.0-beta.5: next-auth@^4.0.0-beta.5:
version "4.2.1" version "4.2.1"
resolved "https://registry.npmjs.org/next-auth/-/next-auth-4.2.1.tgz" resolved "https://registry.npmjs.org/next-auth/-/next-auth-4.2.1.tgz"
@@ -3468,7 +3480,7 @@ react-select@^5.2.1:
react-split@^2.0.14: react-split@^2.0.14:
version "2.0.14" version "2.0.14"
resolved "https://registry.npmjs.org/react-split/-/react-split-2.0.14.tgz" resolved "https://registry.yarnpkg.com/react-split/-/react-split-2.0.14.tgz#ef198259bf43264d605f792fb3384f15f5b34432"
integrity sha512-bKWydgMgaKTg/2JGQnaJPg51T6dmumTWZppFgEbbY0Fbme0F5TuatAScCLaqommbGQQf/ZT1zaejuPDriscISA== integrity sha512-bKWydgMgaKTg/2JGQnaJPg51T6dmumTWZppFgEbbY0Fbme0F5TuatAScCLaqommbGQQf/ZT1zaejuPDriscISA==
dependencies: dependencies:
prop-types "^15.5.7" prop-types "^15.5.7"
@@ -3868,6 +3880,11 @@ source-map@^0.5.7:
resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
source-map@^0.6.1:
version "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: split.js@^1.6.0:
version "1.6.5" version "1.6.5"
resolved "https://registry.npmjs.org/split.js/-/split.js-1.6.5.tgz" resolved "https://registry.npmjs.org/split.js/-/split.js-1.6.5.tgz"
@@ -4099,6 +4116,11 @@ typescript@4.4.4:
resolved "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz" resolved "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz"
integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA== integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==
uglify-js@^3.1.4:
version "3.16.0"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.16.0.tgz#b778ba0831ca102c1d8ecbdec2d2bdfcc7353190"
integrity sha512-FEikl6bR30n0T3amyBh3LoiBdqHRy/f4H80+My34HOesOKyHfOsxAPAxOoqC0JUnC1amnO0IwkYC3sko51caSw==
unbox-primitive@^1.0.1: unbox-primitive@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz" resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz"
@@ -4318,6 +4340,11 @@ word-wrap@^1.2.3:
resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz"
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
wordwrap@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==
wrappy@1: wrappy@1:
version "1.0.2" version "1.0.2"
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"