1
.gitignore
vendored
1
.gitignore
vendored
@@ -32,3 +32,4 @@ yarn-error.log*
|
|||||||
|
|
||||||
# vercel
|
# vercel
|
||||||
.vercel
|
.vercel
|
||||||
|
.vscode
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React, { useRef, useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useSnapshot, ref } from "valtio";
|
import { useSnapshot } from "valtio";
|
||||||
import Editor, { loader } from "@monaco-editor/react";
|
|
||||||
import type monaco from "monaco-editor";
|
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import NextLink from "next/link";
|
import NextLink from "next/link";
|
||||||
@@ -10,24 +9,16 @@ import filesize from "filesize";
|
|||||||
|
|
||||||
import Box from "./Box";
|
import Box from "./Box";
|
||||||
import Container from "./Container";
|
import Container from "./Container";
|
||||||
import dark from "../theme/editor/amy.json";
|
|
||||||
import light from "../theme/editor/xcode_default.json";
|
|
||||||
import state from "../state";
|
import state from "../state";
|
||||||
import wat from "../utils/wat-highlight";
|
import wat from "../utils/wat-highlight";
|
||||||
|
|
||||||
import EditorNavigation from "./EditorNavigation";
|
import EditorNavigation from "./EditorNavigation";
|
||||||
import { Button, Text, Link, Flex } from ".";
|
import { Button, Text, Link, Flex } from ".";
|
||||||
|
import Monaco from "./Monaco";
|
||||||
loader.config({
|
|
||||||
paths: {
|
|
||||||
vs: "https://cdn.jsdelivr.net/npm/monaco-editor@0.30.1/min/vs",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const FILESIZE_BREAKPOINTS: [number, number] = [2 * 1024, 5 * 1024];
|
const FILESIZE_BREAKPOINTS: [number, number] = [2 * 1024, 5 * 1024];
|
||||||
|
|
||||||
const DeployEditor = () => {
|
const DeployEditor = () => {
|
||||||
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>();
|
|
||||||
const snap = useSnapshot(state);
|
const snap = useSnapshot(state);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
@@ -36,7 +27,7 @@ const DeployEditor = () => {
|
|||||||
|
|
||||||
const activeFile = snap.files[snap.active]?.compiledContent
|
const activeFile = snap.files[snap.active]?.compiledContent
|
||||||
? snap.files[snap.active]
|
? snap.files[snap.active]
|
||||||
: snap.files.filter((file) => file.compiledContent)[0];
|
: 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]
|
||||||
@@ -45,6 +36,10 @@ const DeployEditor = () => {
|
|||||||
? "$warning"
|
? "$warning"
|
||||||
: "$success";
|
: "$success";
|
||||||
|
|
||||||
|
const isContentChanged =
|
||||||
|
activeFile && activeFile.compiledValueSnapshot !== activeFile.content;
|
||||||
|
// const hasDeployErros = activeFile && activeFile.containsErrors;
|
||||||
|
|
||||||
const CompiledStatView = activeFile && (
|
const CompiledStatView = activeFile && (
|
||||||
<Flex
|
<Flex
|
||||||
column
|
column
|
||||||
@@ -80,6 +75,12 @@ const DeployEditor = () => {
|
|||||||
<Button variant="link" onClick={() => setShowContent(true)}>
|
<Button variant="link" onClick={() => setShowContent(true)}>
|
||||||
View as WAT-file
|
View as WAT-file
|
||||||
</Button>
|
</Button>
|
||||||
|
{isContentChanged && (
|
||||||
|
<Text warning>
|
||||||
|
File contents were changed after last compile, compile again to
|
||||||
|
incorporate your latest changes in the build.
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
const NoContentView = !snap.loading && router.isReady && (
|
const NoContentView = !snap.loading && router.isReady && (
|
||||||
@@ -99,7 +100,7 @@ const DeployEditor = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
const isContent =
|
const isContent =
|
||||||
snap.files?.filter((file) => file.compiledWatContent).length > 0 &&
|
snap.files?.filter(file => file.compiledWatContent).length > 0 &&
|
||||||
router.isReady;
|
router.isReady;
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@@ -126,32 +127,38 @@ const DeployEditor = () => {
|
|||||||
) : !showContent ? (
|
) : !showContent ? (
|
||||||
CompiledStatView
|
CompiledStatView
|
||||||
) : (
|
) : (
|
||||||
<Editor
|
<Monaco
|
||||||
className="hooks-editor"
|
className="hooks-editor"
|
||||||
defaultLanguage={"wat"}
|
defaultLanguage={"wat"}
|
||||||
language={"wat"}
|
language={"wat"}
|
||||||
path={`file://tmp/c/${activeFile?.name}.wat`}
|
path={`file://tmp/c/${activeFile?.name}.wat`}
|
||||||
value={activeFile?.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);
|
||||||
monaco.languages.setMonarchTokensProvider("wat", wat.tokens);
|
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) => {
|
onMount={editor => {
|
||||||
editorRef.current = editor;
|
|
||||||
editor.updateOptions({
|
editor.updateOptions({
|
||||||
glyphMargin: true,
|
glyphMargin: true,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
theme={theme === "dark" ? "dark" : "light"}
|
theme={theme === "dark" ? "dark" : "light"}
|
||||||
|
overlay={
|
||||||
|
<Flex
|
||||||
|
css={{
|
||||||
|
m: "$1",
|
||||||
|
ml: "auto",
|
||||||
|
fontSize: "$sm",
|
||||||
|
color: "$textMuted",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Link onClick={() => setShowContent(false)}>
|
||||||
|
Exit editor mode
|
||||||
|
</Link>
|
||||||
|
</Flex>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { useEffect, useRef } from "react";
|
import React, { useEffect, useRef } from "react";
|
||||||
import { useSnapshot, ref } from "valtio";
|
import { useSnapshot, ref } from "valtio";
|
||||||
import Editor from "@monaco-editor/react";
|
|
||||||
import type monaco from "monaco-editor";
|
import type monaco from "monaco-editor";
|
||||||
import { ArrowBendLeftUp } from "phosphor-react";
|
import { ArrowBendLeftUp } from "phosphor-react";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
@@ -8,8 +7,6 @@ import { useRouter } from "next/router";
|
|||||||
|
|
||||||
import Box from "./Box";
|
import Box from "./Box";
|
||||||
import Container from "./Container";
|
import Container from "./Container";
|
||||||
import dark from "../theme/editor/amy.json";
|
|
||||||
import light from "../theme/editor/xcode_default.json";
|
|
||||||
import { saveFile } from "../state/actions";
|
import { saveFile } from "../state/actions";
|
||||||
import { apiHeaderFiles } from "../state/constants";
|
import { apiHeaderFiles } from "../state/constants";
|
||||||
import state from "../state";
|
import state from "../state";
|
||||||
@@ -22,10 +19,12 @@ import { listen } from "@codingame/monaco-jsonrpc";
|
|||||||
import ReconnectingWebSocket from "reconnecting-websocket";
|
import ReconnectingWebSocket from "reconnecting-websocket";
|
||||||
|
|
||||||
import docs from "../xrpl-hooks-docs/docs";
|
import docs from "../xrpl-hooks-docs/docs";
|
||||||
|
import Monaco from "./Monaco";
|
||||||
|
import { saveAllFiles } from '../state/actions/saveFile';
|
||||||
|
|
||||||
const validateWritability = (editor: monaco.editor.IStandaloneCodeEditor) => {
|
const validateWritability = (editor: monaco.editor.IStandaloneCodeEditor) => {
|
||||||
const currPath = editor.getModel()?.uri.path;
|
const currPath = editor.getModel()?.uri.path;
|
||||||
if (apiHeaderFiles.find((h) => currPath?.endsWith(h))) {
|
if (apiHeaderFiles.find(h => currPath?.endsWith(h))) {
|
||||||
editor.updateOptions({ readOnly: true });
|
editor.updateOptions({ readOnly: true });
|
||||||
} else {
|
} else {
|
||||||
editor.updateOptions({ readOnly: false });
|
editor.updateOptions({ readOnly: false });
|
||||||
@@ -42,7 +41,7 @@ const setMarkers = (monacoE: typeof monaco) => {
|
|||||||
.getModelMarkers({})
|
.getModelMarkers({})
|
||||||
// Filter out the markers that are hooks specific
|
// Filter out the markers that are hooks specific
|
||||||
.filter(
|
.filter(
|
||||||
(marker) =>
|
marker =>
|
||||||
typeof marker?.code === "string" &&
|
typeof marker?.code === "string" &&
|
||||||
// Take only markers that starts with "hooks-"
|
// Take only markers that starts with "hooks-"
|
||||||
marker?.code?.includes("hooks-")
|
marker?.code?.includes("hooks-")
|
||||||
@@ -56,16 +55,16 @@ const setMarkers = (monacoE: typeof monaco) => {
|
|||||||
// Add decoration (aka extra hoverMessages) to markers in the
|
// Add decoration (aka extra hoverMessages) to markers in the
|
||||||
// exact same range (location) where the markers are
|
// exact same range (location) where the markers are
|
||||||
const models = monacoE.editor.getModels();
|
const models = monacoE.editor.getModels();
|
||||||
models.forEach((model) => {
|
models.forEach(model => {
|
||||||
decorations[model.id] = model?.deltaDecorations(
|
decorations[model.id] = model?.deltaDecorations(
|
||||||
decorations?.[model.id] || [],
|
decorations?.[model.id] || [],
|
||||||
markers
|
markers
|
||||||
.filter((marker) =>
|
.filter(marker =>
|
||||||
marker?.resource.path
|
marker?.resource.path
|
||||||
.split("/")
|
.split("/")
|
||||||
.includes(`${state.files?.[state.active]?.name}`)
|
.includes(`${state.files?.[state.active]?.name}`)
|
||||||
)
|
)
|
||||||
.map((marker) => ({
|
.map(marker => ({
|
||||||
range: new monacoE.Range(
|
range: new monacoE.Range(
|
||||||
marker.startLineNumber,
|
marker.startLineNumber,
|
||||||
marker.startColumn,
|
marker.startColumn,
|
||||||
@@ -113,6 +112,13 @@ const HooksEditor = () => {
|
|||||||
setMarkers(monacoRef.current);
|
setMarkers(monacoRef.current);
|
||||||
}
|
}
|
||||||
}, [snap.active]);
|
}, [snap.active]);
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
saveAllFiles();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const file = snap.files[snap.active];
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
css={{
|
css={{
|
||||||
@@ -127,16 +133,16 @@ const HooksEditor = () => {
|
|||||||
>
|
>
|
||||||
<EditorNavigation />
|
<EditorNavigation />
|
||||||
{snap.files.length > 0 && router.isReady ? (
|
{snap.files.length > 0 && router.isReady ? (
|
||||||
<Editor
|
<Monaco
|
||||||
className="hooks-editor"
|
|
||||||
keepCurrentModel
|
keepCurrentModel
|
||||||
defaultLanguage={snap.files?.[snap.active]?.language}
|
defaultLanguage={file?.language}
|
||||||
language={snap.files?.[snap.active]?.language}
|
language={file?.language}
|
||||||
path={`file:///work/c/${snap.files?.[snap.active]?.name}`}
|
path={`file:///work/c/${file?.name}`}
|
||||||
defaultValue={snap.files?.[snap.active]?.content}
|
defaultValue={file?.content}
|
||||||
beforeMount={(monaco) => {
|
// onChange={val => (state.files[snap.active].content = val)} // Auto save?
|
||||||
|
beforeMount={monaco => {
|
||||||
if (!snap.editorCtx) {
|
if (!snap.editorCtx) {
|
||||||
snap.files.forEach((file) =>
|
snap.files.forEach(file =>
|
||||||
monaco.editor.createModel(
|
monaco.editor.createModel(
|
||||||
file.content,
|
file.content,
|
||||||
file.language,
|
file.language,
|
||||||
@@ -161,7 +167,7 @@ const HooksEditor = () => {
|
|||||||
// listen when the web socket is opened
|
// listen when the web socket is opened
|
||||||
listen({
|
listen({
|
||||||
webSocket: webSocket as WebSocket,
|
webSocket: webSocket as WebSocket,
|
||||||
onConnection: (connection) => {
|
onConnection: connection => {
|
||||||
// create and start the language client
|
// create and start the language client
|
||||||
const languageClient = createLanguageClient(connection);
|
const languageClient = createLanguageClient(connection);
|
||||||
const disposable = languageClient.start();
|
const disposable = languageClient.start();
|
||||||
@@ -177,7 +183,6 @@ const HooksEditor = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// // hook editor to global state
|
|
||||||
// editor.updateOptions({
|
// editor.updateOptions({
|
||||||
// minimap: {
|
// minimap: {
|
||||||
// enabled: false,
|
// enabled: false,
|
||||||
@@ -186,10 +191,6 @@ const HooksEditor = () => {
|
|||||||
// });
|
// });
|
||||||
if (!state.editorCtx) {
|
if (!state.editorCtx) {
|
||||||
state.editorCtx = ref(monaco.editor);
|
state.editorCtx = ref(monaco.editor);
|
||||||
// @ts-expect-error
|
|
||||||
monaco.editor.defineTheme("dark", dark);
|
|
||||||
// @ts-expect-error
|
|
||||||
monaco.editor.defineTheme("light", light);
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onMount={(editor, monaco) => {
|
onMount={(editor, monaco) => {
|
||||||
@@ -217,13 +218,13 @@ const HooksEditor = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Hacky way to hide Peek menu
|
// Hacky way to hide Peek menu
|
||||||
editor.onContextMenu((e) => {
|
editor.onContextMenu(e => {
|
||||||
const host =
|
const host =
|
||||||
document.querySelector<HTMLElement>(".shadow-root-host");
|
document.querySelector<HTMLElement>(".shadow-root-host");
|
||||||
|
|
||||||
const contextMenuItems =
|
const contextMenuItems =
|
||||||
host?.shadowRoot?.querySelectorAll("li.action-item");
|
host?.shadowRoot?.querySelectorAll("li.action-item");
|
||||||
contextMenuItems?.forEach((k) => {
|
contextMenuItems?.forEach(k => {
|
||||||
// If menu item contains "Peek" lets hide it
|
// If menu item contains "Peek" lets hide it
|
||||||
if (k.querySelector(".action-label")?.textContent === "Peek") {
|
if (k.querySelector(".action-label")?.textContent === "Peek") {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
|||||||
75
components/Monaco.tsx
Normal file
75
components/Monaco.tsx
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import Editor, { loader, EditorProps, Monaco } from "@monaco-editor/react";
|
||||||
|
import { CSS } from "@stitches/react";
|
||||||
|
import type monaco from "monaco-editor";
|
||||||
|
import { useTheme } from "next-themes";
|
||||||
|
import { FC, MutableRefObject, ReactNode } from "react";
|
||||||
|
import { Flex } from ".";
|
||||||
|
import dark from "../theme/editor/amy.json";
|
||||||
|
import light from "../theme/editor/xcode_default.json";
|
||||||
|
|
||||||
|
export type MonacoProps = EditorProps & {
|
||||||
|
id?: string;
|
||||||
|
rootProps?: { css: CSS } & Record<string, any>;
|
||||||
|
overlay?: ReactNode;
|
||||||
|
editorRef?: MutableRefObject<monaco.editor.IStandaloneCodeEditor>;
|
||||||
|
monacoRef?: MutableRefObject<typeof monaco>;
|
||||||
|
};
|
||||||
|
|
||||||
|
loader.config({
|
||||||
|
paths: {
|
||||||
|
vs: "https://cdn.jsdelivr.net/npm/monaco-editor@0.30.1/min/vs",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const Monaco: FC<MonacoProps> = ({
|
||||||
|
id,
|
||||||
|
path = `file:///${id}`,
|
||||||
|
className = id,
|
||||||
|
language = "json",
|
||||||
|
overlay,
|
||||||
|
editorRef,
|
||||||
|
monacoRef,
|
||||||
|
beforeMount,
|
||||||
|
rootProps,
|
||||||
|
...rest
|
||||||
|
}) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
const setTheme = (monaco: Monaco) => {
|
||||||
|
monaco.editor.defineTheme("dark", dark as any);
|
||||||
|
monaco.editor.defineTheme("light", light as any);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
fluid
|
||||||
|
column
|
||||||
|
{...rootProps}
|
||||||
|
css={{
|
||||||
|
position: "relative",
|
||||||
|
height: "100%",
|
||||||
|
...rootProps?.css,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Editor
|
||||||
|
className={className}
|
||||||
|
language={language}
|
||||||
|
path={path}
|
||||||
|
beforeMount={monaco => {
|
||||||
|
beforeMount?.(monaco);
|
||||||
|
|
||||||
|
setTheme(monaco);
|
||||||
|
}}
|
||||||
|
theme={theme === "dark" ? "dark" : "light"}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
{overlay && (
|
||||||
|
<Flex
|
||||||
|
css={{ position: "absolute", bottom: 0, right: 0, width: "100%" }}
|
||||||
|
>
|
||||||
|
{overlay}
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Monaco;
|
||||||
@@ -20,6 +20,11 @@ const Text = styled("span", {
|
|||||||
color: "$error",
|
color: "$error",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
warning: {
|
||||||
|
true: {
|
||||||
|
color: "$warning",
|
||||||
|
},
|
||||||
|
},
|
||||||
monospace: {
|
monospace: {
|
||||||
true: {
|
true: {
|
||||||
fontFamily: "$monospace",
|
fontFamily: "$monospace",
|
||||||
@@ -28,8 +33,8 @@ const Text = styled("span", {
|
|||||||
block: {
|
block: {
|
||||||
true: {
|
true: {
|
||||||
display: "block",
|
display: "block",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
import Editor, { loader, useMonaco } from "@monaco-editor/react";
|
|
||||||
import { FC, useCallback, useEffect, useState } from "react";
|
import { FC, useCallback, useEffect, useState } from "react";
|
||||||
import { useTheme } from "next-themes";
|
|
||||||
|
|
||||||
import dark from "../../theme/editor/amy.json";
|
|
||||||
import light from "../../theme/editor/xcode_default.json";
|
|
||||||
import { useSnapshot } from "valtio";
|
import { useSnapshot } from "valtio";
|
||||||
import state, {
|
import state, {
|
||||||
prepareState,
|
prepareState,
|
||||||
@@ -11,18 +6,13 @@ import state, {
|
|||||||
TransactionState,
|
TransactionState,
|
||||||
} from "../../state";
|
} from "../../state";
|
||||||
import Text from "../Text";
|
import Text from "../Text";
|
||||||
import Flex from "../Flex";
|
import { Flex, Link } from "..";
|
||||||
import { Link } from "..";
|
|
||||||
import { showAlert } from "../../state/actions/showAlert";
|
import { showAlert } from "../../state/actions/showAlert";
|
||||||
import { parseJSON } from "../../utils/json";
|
import { parseJSON } from "../../utils/json";
|
||||||
import { extractSchemaProps } from "../../utils/schema";
|
import { extractSchemaProps } from "../../utils/schema";
|
||||||
import amountSchema from "../../content/amount-schema.json";
|
import amountSchema from "../../content/amount-schema.json";
|
||||||
|
import Monaco from "../Monaco";
|
||||||
loader.config({
|
import type monaco from "monaco-editor";
|
||||||
paths: {
|
|
||||||
vs: "https://cdn.jsdelivr.net/npm/monaco-editor@0.30.1/min/vs",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
interface JsonProps {
|
interface JsonProps {
|
||||||
value?: string;
|
value?: string;
|
||||||
@@ -40,7 +30,6 @@ export const TxJson: FC<JsonProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { editorSettings, accounts } = useSnapshot(state);
|
const { editorSettings, accounts } = useSnapshot(state);
|
||||||
const { editorValue = value, estimatedFee } = txState;
|
const { editorValue = value, estimatedFee } = txState;
|
||||||
const { theme } = useTheme();
|
|
||||||
const [hasUnsaved, setHasUnsaved] = useState(false);
|
const [hasUnsaved, setHasUnsaved] = useState(false);
|
||||||
const [currTxType, setCurrTxType] = useState<string | undefined>(
|
const [currTxType, setCurrTxType] = useState<string | undefined>(
|
||||||
txState.selectedTransaction?.value
|
txState.selectedTransaction?.value
|
||||||
@@ -95,9 +84,6 @@ export const TxJson: FC<JsonProps> = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const path = `file:///${header}`;
|
|
||||||
const monaco = useMonaco();
|
|
||||||
|
|
||||||
const getSchemas = useCallback(async (): Promise<any[]> => {
|
const getSchemas = useCallback(async (): Promise<any[]> => {
|
||||||
const txObj = transactionsData.find(
|
const txObj = transactionsData.find(
|
||||||
td => td.TransactionType === currTxType
|
td => td.TransactionType === currTxType
|
||||||
@@ -177,55 +163,63 @@ export const TxJson: FC<JsonProps> = ({
|
|||||||
];
|
];
|
||||||
}, [accounts, currTxType, estimatedFee, header]);
|
}, [accounts, currTxType, estimatedFee, header]);
|
||||||
|
|
||||||
|
const [monacoInst, setMonacoInst] = useState<typeof monaco>();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!monaco) return;
|
if (!monacoInst) return;
|
||||||
getSchemas().then(schemas => {
|
getSchemas().then(schemas => {
|
||||||
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
|
monacoInst.languages.json.jsonDefaults.setDiagnosticsOptions({
|
||||||
validate: true,
|
validate: true,
|
||||||
schemas,
|
schemas,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, [getSchemas, monaco]);
|
}, [getSchemas, monacoInst]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Monaco
|
||||||
fluid
|
rootProps={{
|
||||||
column
|
css: { height: "calc(100% - 45px)" },
|
||||||
css={{ height: "calc(100% - 45px)", position: "relative" }}
|
}}
|
||||||
>
|
language={"json"}
|
||||||
<Editor
|
id={header}
|
||||||
className="hooks-editor"
|
height="100%"
|
||||||
language={"json"}
|
value={editorValue}
|
||||||
path={path}
|
onChange={val => setState({ editorValue: val })}
|
||||||
height="100%"
|
onMount={(editor, monaco) => {
|
||||||
beforeMount={monaco => {
|
editor.updateOptions({
|
||||||
monaco.editor.defineTheme("dark", dark as any);
|
minimap: { enabled: false },
|
||||||
monaco.editor.defineTheme("light", light as any);
|
glyphMargin: true,
|
||||||
}}
|
tabSize: editorSettings.tabSize,
|
||||||
value={editorValue}
|
dragAndDrop: true,
|
||||||
onChange={val => setState({ editorValue: val })}
|
fontSize: 14,
|
||||||
onMount={(editor, monaco) => {
|
});
|
||||||
editor.updateOptions({
|
|
||||||
minimap: { enabled: false },
|
|
||||||
glyphMargin: true,
|
|
||||||
tabSize: editorSettings.tabSize,
|
|
||||||
dragAndDrop: true,
|
|
||||||
fontSize: 14,
|
|
||||||
});
|
|
||||||
|
|
||||||
// register onExit cb
|
setMonacoInst(monaco);
|
||||||
const model = editor.getModel();
|
// register onExit cb
|
||||||
model?.onWillDispose(() => onExit(model.getValue()));
|
const model = editor.getModel();
|
||||||
}}
|
model?.onWillDispose(() => onExit(model.getValue()));
|
||||||
theme={theme === "dark" ? "dark" : "light"}
|
}}
|
||||||
/>
|
overlay={
|
||||||
{hasUnsaved && (
|
hasUnsaved ? (
|
||||||
<Text muted small css={{ position: "absolute", bottom: 0, right: 0 }}>
|
<Flex
|
||||||
This file has unsaved changes.{" "}
|
row
|
||||||
<Link onClick={() => saveState(editorValue, currTxType)}>save</Link>{" "}
|
align="center"
|
||||||
<Link onClick={discardChanges}>discard</Link>
|
css={{ fontSize: "$xs", color: "$textMuted", ml: 'auto' }}
|
||||||
</Text>
|
>
|
||||||
)}
|
<Text muted small>
|
||||||
</Flex>
|
This file has unsaved changes.
|
||||||
|
</Text>
|
||||||
|
<Link
|
||||||
|
css={{ ml: "$1" }}
|
||||||
|
onClick={() => saveState(editorValue, currTxType)}
|
||||||
|
>
|
||||||
|
save
|
||||||
|
</Link>
|
||||||
|
<Link css={{ ml: "$1" }} onClick={discardChanges}>
|
||||||
|
discard
|
||||||
|
</Link>
|
||||||
|
</Flex>
|
||||||
|
) : undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -38,22 +38,23 @@ 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);
|
||||||
|
|
||||||
@@ -97,32 +98,37 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
[estimateFee, handleSetField]
|
[estimateFee, handleSetField]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChangeTxType = (tt: SelectOption) => {
|
const handleChangeTxType = useCallback(
|
||||||
setState({ selectedTransaction: tt });
|
(tt: SelectOption) => {
|
||||||
|
setState({ selectedTransaction: tt });
|
||||||
|
|
||||||
const newState = resetOptions(tt.value);
|
const newState = resetOptions(tt.value);
|
||||||
|
|
||||||
handleEstimateFee(newState, true);
|
handleEstimateFee(newState, true);
|
||||||
};
|
},
|
||||||
|
[handleEstimateFee, resetOptions, setState]
|
||||||
|
);
|
||||||
|
|
||||||
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" });
|
||||||
|
|
||||||
|
// default tx
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (selectedTransaction?.value) return;
|
||||||
|
|
||||||
const defaultOption = transactionsOptions.find(
|
const defaultOption = transactionsOptions.find(
|
||||||
(tt) => tt.value === "Payment"
|
tt => tt.value === "Payment"
|
||||||
);
|
);
|
||||||
if (defaultOption) {
|
if (defaultOption) {
|
||||||
handleChangeTxType(defaultOption);
|
handleChangeTxType(defaultOption);
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [handleChangeTxType, selectedTransaction?.value, transactionsOptions]);
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
@@ -204,7 +210,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;
|
||||||
@@ -255,7 +261,7 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
<Input
|
<Input
|
||||||
type={isFee ? "number" : "text"}
|
type={isFee ? "number" : "text"}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => {
|
onChange={e => {
|
||||||
if (isFee) {
|
if (isFee) {
|
||||||
const val = e.target.value
|
const val = e.target.value
|
||||||
.replaceAll(".", "")
|
.replaceAll(".", "")
|
||||||
@@ -267,7 +273,7 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
}}
|
}}
|
||||||
onKeyPress={
|
onKeyPress={
|
||||||
isFee
|
isFee
|
||||||
? (e) => {
|
? e => {
|
||||||
if (e.key === "." || e.key === ",") {
|
if (e.key === "." || e.key === ",") {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,19 +14,21 @@ import { ref } from "valtio";
|
|||||||
*/
|
*/
|
||||||
export const compileCode = async (activeId: number) => {
|
export const compileCode = async (activeId: number) => {
|
||||||
// Save the file to global state
|
// Save the file to global state
|
||||||
saveFile(false);
|
saveFile(false, activeId);
|
||||||
if (!process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT) {
|
if (!process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT) {
|
||||||
throw Error("Missing env!");
|
throw Error("Missing env!");
|
||||||
}
|
}
|
||||||
// Bail out if we're already compiling
|
// Bail out if we're already compiling
|
||||||
if (state.compiling) {
|
if (state.compiling) {
|
||||||
// if compiling is ongoing return
|
// if compiling is ongoing return // TODO Inform user about it.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Set loading state to true
|
// Set loading state to true
|
||||||
state.compiling = true;
|
state.compiling = true;
|
||||||
state.logs = []
|
state.logs = []
|
||||||
|
const file = state.files[activeId]
|
||||||
try {
|
try {
|
||||||
|
file.containsErrors = false
|
||||||
const res = await fetch(process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT, {
|
const res = await fetch(process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -40,8 +42,8 @@ export const compileCode = async (activeId: number) => {
|
|||||||
{
|
{
|
||||||
type: "c",
|
type: "c",
|
||||||
options: state.compileOptions.optimizationLevel || '-O2',
|
options: state.compileOptions.optimizationLevel || '-O2',
|
||||||
name: state.files[activeId].name,
|
name: file.name,
|
||||||
src: state.files[activeId].content,
|
src: file.content,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
@@ -49,15 +51,15 @@ export const compileCode = async (activeId: number) => {
|
|||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
state.compiling = false;
|
state.compiling = false;
|
||||||
if (!json.success) {
|
if (!json.success) {
|
||||||
state.logs.push({ type: "error", message: json.message });
|
const errors = [json.message]
|
||||||
if (json.tasks && json.tasks.length > 0) {
|
if (json.tasks && json.tasks.length > 0) {
|
||||||
json.tasks.forEach((task: any) => {
|
json.tasks.forEach((task: any) => {
|
||||||
if (!task.success) {
|
if (!task.success) {
|
||||||
state.logs.push({ type: "error", message: task?.console });
|
errors.push(task?.console)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return toast.error(`Couldn't compile!`, { position: "bottom-center" });
|
throw errors
|
||||||
}
|
}
|
||||||
state.logs.push({
|
state.logs.push({
|
||||||
type: "success",
|
type: "success",
|
||||||
@@ -67,8 +69,9 @@ export const compileCode = async (activeId: number) => {
|
|||||||
});
|
});
|
||||||
// Decode base64 encoded wasm that is coming back from the endpoint
|
// Decode base64 encoded wasm that is coming back from the endpoint
|
||||||
const bufferData = await decodeBinary(json.output);
|
const bufferData = await decodeBinary(json.output);
|
||||||
state.files[state.active].compiledContent = ref(bufferData);
|
file.compiledContent = ref(bufferData);
|
||||||
state.files[state.active].lastCompiled = new Date();
|
file.lastCompiled = new Date();
|
||||||
|
file.compiledValueSnapshot = file.content
|
||||||
// Import wabt from and create human readable version of wasm file and
|
// Import wabt from and create human readable version of wasm file and
|
||||||
// put it into state
|
// put it into state
|
||||||
import("wabt").then((wabt) => {
|
import("wabt").then((wabt) => {
|
||||||
@@ -84,10 +87,23 @@ export const compileCode = async (activeId: number) => {
|
|||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
state.logs.push({
|
|
||||||
type: "error",
|
if (err instanceof Array && typeof err[0] === 'string') {
|
||||||
message: "Error occured while compiling!",
|
err.forEach(message => {
|
||||||
});
|
state.logs.push({
|
||||||
|
type: "error",
|
||||||
|
message,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
state.logs.push({
|
||||||
|
type: "error",
|
||||||
|
message: "Something went wrong, check your connection try again later!",
|
||||||
|
});
|
||||||
|
}
|
||||||
state.compiling = false;
|
state.compiling = false;
|
||||||
|
toast.error(`Error occurred while compiling!`, { position: "bottom-center" });
|
||||||
|
file.containsErrors = true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ export const deployHook = async (
|
|||||||
console.log(err);
|
console.log(err);
|
||||||
state.deployLogs.push({
|
state.deployLogs.push({
|
||||||
type: "error",
|
type: "error",
|
||||||
message: "Error occured while deploying",
|
message: "Error occurred while deploying",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (currentAccount) {
|
if (currentAccount) {
|
||||||
@@ -272,10 +272,10 @@ export const deleteHook = async (account: IAccount & { name?: string }) => {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
toast.error("Error occured while deleting hoook", { id: toastId });
|
toast.error("Error occurred while deleting hook", { id: toastId });
|
||||||
state.deployLogs.push({
|
state.deployLogs.push({
|
||||||
type: "error",
|
type: "error",
|
||||||
message: "Error occured while deleting hook",
|
message: "Error occurred while deleting hook",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (currentAccount) {
|
if (currentAccount) {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const downloadAsZip = async () => {
|
|||||||
const zipFileName = guessZipFileName(files);
|
const zipFileName = guessZipFileName(files);
|
||||||
zipped.saveFile(zipFileName);
|
zipped.saveFile(zipFileName);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error('Error occured while creating zip file, try again later')
|
toast.error('Error occurred while creating zip file, try again later')
|
||||||
} finally {
|
} finally {
|
||||||
state.zipLoading = false
|
state.zipLoading = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export const importAccount = (secret: string, name?: string) => {
|
|||||||
if (err?.message) {
|
if (err?.message) {
|
||||||
toast.error(err.message)
|
toast.error(err.message)
|
||||||
} else {
|
} else {
|
||||||
toast.error('Error occured while importing account')
|
toast.error('Error occurred while importing account')
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,15 @@ import toast from "react-hot-toast";
|
|||||||
import state from '../index';
|
import state from '../index';
|
||||||
|
|
||||||
// Saves the current editor content to global state
|
// Saves the current editor content to global state
|
||||||
export const saveFile = (showToast: boolean = true) => {
|
export const saveFile = (showToast: boolean = true, activeId?: number) => {
|
||||||
const editorModels = state.editorCtx?.getModels();
|
const editorModels = state.editorCtx?.getModels();
|
||||||
const sought = '/' + state.files[state.active].name;
|
const sought = '/' + state.files[state.active].name;
|
||||||
const currentModel = editorModels?.find((editorModel) => {
|
const currentModel = editorModels?.find((editorModel) => {
|
||||||
return editorModel.uri.path.endsWith(sought);
|
return editorModel.uri.path.endsWith(sought);
|
||||||
});
|
});
|
||||||
|
const file = state.files[activeId || state.active]
|
||||||
if (state.files.length > 0) {
|
if (state.files.length > 0) {
|
||||||
state.files[state.active].content = currentModel?.getValue() || "";
|
file.content = currentModel?.getValue() || "";
|
||||||
}
|
}
|
||||||
if (showToast) {
|
if (showToast) {
|
||||||
toast.success("Saved successfully", { position: "bottom-center" });
|
toast.success("Saved successfully", { position: "bottom-center" });
|
||||||
|
|||||||
@@ -13,9 +13,11 @@ export interface IFile {
|
|||||||
name: string;
|
name: string;
|
||||||
language: string;
|
language: string;
|
||||||
content: string;
|
content: string;
|
||||||
|
compiledValueSnapshot?: string
|
||||||
compiledContent?: ArrayBuffer | null;
|
compiledContent?: ArrayBuffer | null;
|
||||||
compiledWatContent?: string | null;
|
compiledWatContent?: string | null;
|
||||||
lastCompiled?: Date
|
lastCompiled?: Date
|
||||||
|
containsErrors?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FaucetAccountRes {
|
export interface FaucetAccountRes {
|
||||||
|
|||||||
@@ -19,6 +19,6 @@ export const getErrors = (source?: string): Error | undefined => {
|
|||||||
);
|
);
|
||||||
if (!probs.length) return undefined
|
if (!probs.length) return undefined
|
||||||
const errors = probs.map(prob => `[${prob.code}] on line ${prob.line}: ${prob.message}`)
|
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')}`)
|
const error = new Error(`The following error(s) occurred while parsing JSDOC: \n${errors.join('\n')}`)
|
||||||
return error
|
return error
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user