Run format again on new changes.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useSnapshot, ref } from "valtio";
|
||||
import type monaco from "monaco-editor";
|
||||
import { ArrowBendLeftUp } from "phosphor-react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { useSnapshot, ref } from 'valtio'
|
||||
import type monaco from 'monaco-editor'
|
||||
import { ArrowBendLeftUp } from 'phosphor-react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
import Box from './Box'
|
||||
import Container from './Container'
|
||||
@@ -18,13 +18,13 @@ import { createLanguageClient, createWebSocket } from '../utils/languageClient'
|
||||
import { listen } from '@codingame/monaco-jsonrpc'
|
||||
import ReconnectingWebSocket from 'reconnecting-websocket'
|
||||
|
||||
import docs from "../xrpl-hooks-docs/docs";
|
||||
import Monaco from "./Monaco";
|
||||
import { saveAllFiles } from "../state/actions/saveFile";
|
||||
import { Tab, Tabs } from "./Tabs";
|
||||
import { renameFile } from "../state/actions/createNewFile";
|
||||
import { Link } from ".";
|
||||
import Markdown from "./Markdown";
|
||||
import docs from '../xrpl-hooks-docs/docs'
|
||||
import Monaco from './Monaco'
|
||||
import { saveAllFiles } from '../state/actions/saveFile'
|
||||
import { Tab, Tabs } from './Tabs'
|
||||
import { renameFile } from '../state/actions/createNewFile'
|
||||
import { Link } from '.'
|
||||
import Markdown from './Markdown'
|
||||
|
||||
const checkWritable = (filename?: string): boolean => {
|
||||
if (apiHeaderFiles.find(file => file === filename)) {
|
||||
@@ -34,10 +34,10 @@ const checkWritable = (filename?: string): boolean => {
|
||||
}
|
||||
|
||||
const validateWritability = (editor: monaco.editor.IStandaloneCodeEditor) => {
|
||||
const filename = editor.getModel()?.uri.path.split("/").pop();
|
||||
const isWritable = checkWritable(filename);
|
||||
editor.updateOptions({ readOnly: !isWritable });
|
||||
};
|
||||
const filename = editor.getModel()?.uri.path.split('/').pop()
|
||||
const isWritable = checkWritable(filename)
|
||||
editor.updateOptions({ readOnly: !isWritable })
|
||||
}
|
||||
|
||||
let decorations: { [key: string]: string[] } = {}
|
||||
|
||||
@@ -95,13 +95,13 @@ const setMarkers = (monacoE: typeof monaco) => {
|
||||
}
|
||||
|
||||
const HooksEditor = () => {
|
||||
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>();
|
||||
const monacoRef = useRef<typeof monaco>();
|
||||
const subscriptionRef = useRef<ReconnectingWebSocket | null>(null);
|
||||
const snap = useSnapshot(state);
|
||||
const router = useRouter();
|
||||
const { theme } = useTheme();
|
||||
const [isMdPreview, setIsMdPreview] = useState(true);
|
||||
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>()
|
||||
const monacoRef = useRef<typeof monaco>()
|
||||
const subscriptionRef = useRef<ReconnectingWebSocket | null>(null)
|
||||
const snap = useSnapshot(state)
|
||||
const router = useRouter()
|
||||
const { theme } = useTheme()
|
||||
const [isMdPreview, setIsMdPreview] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
if (editorRef.current) validateWritability(editorRef.current)
|
||||
@@ -140,36 +140,30 @@ const HooksEditor = () => {
|
||||
}}
|
||||
>
|
||||
{snap.files.map((file, index) => {
|
||||
return (
|
||||
<Tab
|
||||
key={file.name}
|
||||
header={file.name}
|
||||
renameDisabled={!checkWritable(file.name)}
|
||||
/>
|
||||
);
|
||||
return <Tab key={file.name} header={file.name} renameDisabled={!checkWritable(file.name)} />
|
||||
})}
|
||||
</Tabs>
|
||||
);
|
||||
)
|
||||
const previewToggle = (
|
||||
<Link
|
||||
onClick={() => {
|
||||
if (!isMdPreview) {
|
||||
saveFile(false);
|
||||
saveFile(false)
|
||||
}
|
||||
setIsMdPreview(!isMdPreview);
|
||||
setIsMdPreview(!isMdPreview)
|
||||
}}
|
||||
css={{
|
||||
position: "absolute",
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
zIndex: 10,
|
||||
m: "$1",
|
||||
fontSize: "$sm",
|
||||
m: '$1',
|
||||
fontSize: '$sm'
|
||||
}}
|
||||
>
|
||||
{isMdPreview ? "Exit Preview" : "View Preview"}
|
||||
{isMdPreview ? 'Exit Preview' : 'View Preview'}
|
||||
</Link>
|
||||
);
|
||||
)
|
||||
return (
|
||||
<Box
|
||||
css={{
|
||||
@@ -183,16 +177,16 @@ const HooksEditor = () => {
|
||||
}}
|
||||
>
|
||||
<EditorNavigation renderNav={renderNav} />
|
||||
{file?.language === "markdown" && previewToggle}
|
||||
{file?.language === 'markdown' && previewToggle}
|
||||
{snap.files.length > 0 && router.isReady ? (
|
||||
isMdPreview && file?.language === "markdown" ? (
|
||||
isMdPreview && file?.language === 'markdown' ? (
|
||||
<Markdown
|
||||
components={{
|
||||
a: ({ href, children }) => (
|
||||
<Link target="_blank" rel="noopener noreferrer" href={href}>
|
||||
{children}
|
||||
</Link>
|
||||
),
|
||||
)
|
||||
}}
|
||||
>
|
||||
{file.content}
|
||||
@@ -213,39 +207,39 @@ const HooksEditor = () => {
|
||||
file.language,
|
||||
monaco.Uri.parse(`file:///work/c/${file.name}`)
|
||||
)
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
// create the web socket
|
||||
if (!subscriptionRef.current) {
|
||||
monaco.languages.register({
|
||||
id: "c",
|
||||
extensions: [".c", ".h"],
|
||||
aliases: ["C", "c", "H", "h"],
|
||||
mimetypes: ["text/plain"],
|
||||
});
|
||||
MonacoServices.install(monaco);
|
||||
id: 'c',
|
||||
extensions: ['.c', '.h'],
|
||||
aliases: ['C', 'c', 'H', 'h'],
|
||||
mimetypes: ['text/plain']
|
||||
})
|
||||
MonacoServices.install(monaco)
|
||||
const webSocket = createWebSocket(
|
||||
process.env.NEXT_PUBLIC_LANGUAGE_SERVER_API_ENDPOINT || ""
|
||||
);
|
||||
subscriptionRef.current = webSocket;
|
||||
process.env.NEXT_PUBLIC_LANGUAGE_SERVER_API_ENDPOINT || ''
|
||||
)
|
||||
subscriptionRef.current = webSocket
|
||||
// listen when the web socket is opened
|
||||
listen({
|
||||
webSocket: webSocket as WebSocket,
|
||||
onConnection: connection => {
|
||||
// create and start the language client
|
||||
const languageClient = createLanguageClient(connection);
|
||||
const disposable = languageClient.start();
|
||||
const languageClient = createLanguageClient(connection)
|
||||
const disposable = languageClient.start()
|
||||
|
||||
connection.onClose(() => {
|
||||
try {
|
||||
disposable.dispose();
|
||||
disposable.dispose()
|
||||
} catch (err) {
|
||||
console.log("err", err);
|
||||
console.log('err', err)
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// editor.updateOptions({
|
||||
@@ -255,54 +249,47 @@ const HooksEditor = () => {
|
||||
// ...snap.editorSettings,
|
||||
// });
|
||||
if (!state.editorCtx) {
|
||||
state.editorCtx = ref(monaco.editor);
|
||||
state.editorCtx = ref(monaco.editor)
|
||||
}
|
||||
}}
|
||||
onMount={(editor, monaco) => {
|
||||
editorRef.current = editor;
|
||||
monacoRef.current = monaco;
|
||||
editorRef.current = editor
|
||||
monacoRef.current = monaco
|
||||
editor.updateOptions({
|
||||
glyphMargin: true,
|
||||
lightbulb: {
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
editor.addCommand(
|
||||
monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS,
|
||||
() => {
|
||||
saveFile();
|
||||
enabled: true
|
||||
}
|
||||
);
|
||||
})
|
||||
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {
|
||||
saveFile()
|
||||
})
|
||||
// When the markers (errors/warnings from clangd language server) change
|
||||
// Lets improve the markers by adding extra content to them from related
|
||||
// md files
|
||||
monaco.editor.onDidChangeMarkers(() => {
|
||||
if (monacoRef.current) {
|
||||
setMarkers(monacoRef.current);
|
||||
setMarkers(monacoRef.current)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
// Hacky way to hide Peek menu
|
||||
editor.onContextMenu(e => {
|
||||
const host =
|
||||
document.querySelector<HTMLElement>(".shadow-root-host");
|
||||
const host = document.querySelector<HTMLElement>('.shadow-root-host')
|
||||
|
||||
const contextMenuItems =
|
||||
host?.shadowRoot?.querySelectorAll("li.action-item");
|
||||
const contextMenuItems = host?.shadowRoot?.querySelectorAll('li.action-item')
|
||||
contextMenuItems?.forEach(k => {
|
||||
// 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
|
||||
k["style"].display = "none";
|
||||
k['style'].display = 'none'
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
validateWritability(editor);
|
||||
validateWritability(editor)
|
||||
}}
|
||||
theme={theme === "dark" ? "dark" : "light"}
|
||||
theme={theme === 'dark' ? 'dark' : 'light'}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { styled } from '../stitches.config';
|
||||
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import { styled } from '../stitches.config'
|
||||
|
||||
const Markdown = styled(ReactMarkdown, {
|
||||
px: "$8",
|
||||
"@md": {
|
||||
px: "$20",
|
||||
px: '$8',
|
||||
'@md': {
|
||||
px: '$20'
|
||||
},
|
||||
pb: "$5",
|
||||
height: "100%",
|
||||
overflowY: "auto"
|
||||
});
|
||||
|
||||
export default Markdown;
|
||||
pb: '$5',
|
||||
height: '100%',
|
||||
overflowY: 'auto'
|
||||
})
|
||||
|
||||
export default Markdown
|
||||
|
||||
@@ -101,13 +101,9 @@ const Navigation = () => {
|
||||
<Spinner />
|
||||
) : (
|
||||
<>
|
||||
<Heading css={{ lineHeight: 1 }}>
|
||||
{snap.gistName || "XRPL Hooks"}
|
||||
</Heading>
|
||||
<Text
|
||||
css={{ fontSize: "$xs", color: "$mauve10", lineHeight: 1 }}
|
||||
>
|
||||
{snap.files.length > 0 ? "Gist: " : "Builder"}
|
||||
<Heading css={{ lineHeight: 1 }}>{snap.gistName || 'XRPL Hooks'}</Heading>
|
||||
<Text css={{ fontSize: '$xs', color: '$mauve10', lineHeight: 1 }}>
|
||||
{snap.files.length > 0 ? 'Gist: ' : 'Builder'}
|
||||
{snap.files.length > 0 && (
|
||||
<Link
|
||||
href={`https://gist.github.com/${snap.gistOwner || ''}/${snap.gistId || ''}`}
|
||||
|
||||
@@ -1,57 +1,62 @@
|
||||
import { Octokit } from "@octokit/core";
|
||||
import state, { IFile } from '../index';
|
||||
import { templateFileIds } from '../constants';
|
||||
import { Octokit } from '@octokit/core'
|
||||
import state, { IFile } from '../index'
|
||||
import { templateFileIds } from '../constants'
|
||||
|
||||
const octokit = new Octokit()
|
||||
|
||||
/**
|
||||
/**
|
||||
* Fetches files from Github Gists based on gistId and stores them in global state
|
||||
*/
|
||||
export const fetchFiles = async (gistId: string) => {
|
||||
if (!gistId || state.files.length) return
|
||||
|
||||
state.loading = true;
|
||||
state.loading = true
|
||||
state.logs.push({
|
||||
type: "log",
|
||||
message: `Fetching Gist with id: ${gistId}`,
|
||||
});
|
||||
type: 'log',
|
||||
message: `Fetching Gist with id: ${gistId}`
|
||||
})
|
||||
try {
|
||||
const res = await octokit
|
||||
.request("GET /gists/{gist_id}", { gist_id: gistId })
|
||||
const res = await octokit.request('GET /gists/{gist_id}', { gist_id: gistId })
|
||||
|
||||
const isTemplate = (id: string) => Object.values(templateFileIds).map(v => v.id).includes(id)
|
||||
const isTemplate = (id: string) =>
|
||||
Object.values(templateFileIds)
|
||||
.map(v => v.id)
|
||||
.includes(id)
|
||||
|
||||
if (isTemplate(gistId)) {
|
||||
// fetch headers
|
||||
const headerRes = await fetch(`${process.env.NEXT_PUBLIC_COMPILE_API_BASE_URL}/api/header-files`);
|
||||
if (!headerRes.ok) throw Error("Failed to fetch headers");;
|
||||
const headerRes = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_COMPILE_API_BASE_URL}/api/header-files`
|
||||
)
|
||||
if (!headerRes.ok) throw Error('Failed to fetch headers')
|
||||
|
||||
const headerJson = await headerRes.json()
|
||||
const headerFiles: Record<string, { filename: string; content: string; language: string }> = {};
|
||||
const headerFiles: Record<string, { filename: string; content: string; language: string }> =
|
||||
{}
|
||||
Object.entries(headerJson).forEach(([key, value]) => {
|
||||
const fname = `${key}.h`;
|
||||
const fname = `${key}.h`
|
||||
headerFiles[fname] = { filename: fname, content: value as string, language: 'C' }
|
||||
})
|
||||
const files = {
|
||||
...res.data.files,
|
||||
...headerFiles
|
||||
};
|
||||
res.data.files = files;
|
||||
}
|
||||
res.data.files = files
|
||||
}
|
||||
|
||||
if (!res.data.files) throw Error("No files could be fetched from given gist id!")
|
||||
if (!res.data.files) throw Error('No files could be fetched from given gist id!')
|
||||
|
||||
const files: IFile[] = Object.keys(res.data.files).map((filename) => ({
|
||||
name: res.data.files?.[filename]?.filename || "untitled.c",
|
||||
language: res.data.files?.[filename]?.language?.toLowerCase() || "",
|
||||
content: res.data.files?.[filename]?.content || "",
|
||||
}));
|
||||
const files: IFile[] = Object.keys(res.data.files).map(filename => ({
|
||||
name: res.data.files?.[filename]?.filename || 'untitled.c',
|
||||
language: res.data.files?.[filename]?.language?.toLowerCase() || '',
|
||||
content: res.data.files?.[filename]?.content || ''
|
||||
}))
|
||||
|
||||
files.sort((a, b) => {
|
||||
const aBasename = a.name.split('.')?.[0];
|
||||
const aExt = a.name.split('.').pop() || '';
|
||||
const bBasename = b.name.split('.')?.[0];
|
||||
const bExt = b.name.split('.').pop() || '';
|
||||
const aBasename = a.name.split('.')?.[0]
|
||||
const aExt = a.name.split('.').pop() || ''
|
||||
const bBasename = b.name.split('.')?.[0]
|
||||
const bExt = b.name.split('.').pop() || ''
|
||||
|
||||
// default priority is undefined == 0
|
||||
const extPriority: Record<string, number> = {
|
||||
@@ -63,20 +68,22 @@ export const fetchFiles = async (gistId: string) => {
|
||||
// Sort based on extention priorities
|
||||
const comp = (extPriority[bExt] || 0) - (extPriority[aExt] || 0)
|
||||
if (comp !== 0) return comp
|
||||
|
||||
|
||||
// Otherwise fallback to alphabetical sorting
|
||||
return aBasename.localeCompare(bBasename)
|
||||
})
|
||||
|
||||
state.logs.push({
|
||||
type: "success",
|
||||
message: "Fetched successfully ✅",
|
||||
});
|
||||
state.files = files;
|
||||
state.gistId = gistId;
|
||||
state.gistOwner = res.data.owner?.login;
|
||||
type: 'success',
|
||||
message: 'Fetched successfully ✅'
|
||||
})
|
||||
state.files = files
|
||||
state.gistId = gistId
|
||||
state.gistOwner = res.data.owner?.login
|
||||
|
||||
const gistName = files.find(file => file.language === 'c' || file.language === 'javascript')?.name || "untitled";
|
||||
const gistName =
|
||||
files.find(file => file.language === 'c' || file.language === 'javascript')?.name ||
|
||||
'untitled'
|
||||
state.gistName = gistName
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
@@ -84,9 +91,9 @@ export const fetchFiles = async (gistId: string) => {
|
||||
if (err instanceof Error) message = err.message
|
||||
else message = `Something went wrong, try again later!`
|
||||
state.logs.push({
|
||||
type: "error",
|
||||
message: `Error: ${message}`,
|
||||
});
|
||||
type: 'error',
|
||||
message: `Error: ${message}`
|
||||
})
|
||||
}
|
||||
state.loading = false
|
||||
}
|
||||
|
||||
@@ -5,37 +5,37 @@ import Peggy from '../../components/icons/Peggy'
|
||||
import Starter from '../../components/icons/Starter'
|
||||
|
||||
export const templateFileIds = {
|
||||
'starter': {
|
||||
id: '1f8109c80f504e6326db2735df2f0ad6', // Forked
|
||||
name: 'Starter',
|
||||
description: 'Just a basic starter with essential imports, just accepts any transaction coming through',
|
||||
icon: Starter
|
||||
|
||||
},
|
||||
'firewall': {
|
||||
id: '1cc30f39c8a0b9c55b88c312669ca45e', // Forked
|
||||
name: 'Firewall',
|
||||
description: 'This Hook essentially checks a blacklist of accounts',
|
||||
icon: Firewall
|
||||
},
|
||||
'notary': {
|
||||
id: '87b6f5a8c2f5038fb0f20b8b510efa10', // Forked
|
||||
name: 'Notary',
|
||||
description: 'Collecting signatures for multi-sign transactions',
|
||||
icon: Notary
|
||||
},
|
||||
'carbon': {
|
||||
id: '953662b22d065449f8ab6f69bc2afe41', // Forked
|
||||
name: 'Carbon',
|
||||
description: 'Send a percentage of sum to an address',
|
||||
icon: Carbon
|
||||
},
|
||||
'peggy': {
|
||||
id: '049784a83fa068faf7912f663f7b6471', // Forked
|
||||
name: 'Peggy',
|
||||
description: 'An oracle based stable coin hook',
|
||||
icon: Peggy
|
||||
},
|
||||
starter: {
|
||||
id: '1f8109c80f504e6326db2735df2f0ad6', // Forked
|
||||
name: 'Starter',
|
||||
description:
|
||||
'Just a basic starter with essential imports, just accepts any transaction coming through',
|
||||
icon: Starter
|
||||
},
|
||||
firewall: {
|
||||
id: '1cc30f39c8a0b9c55b88c312669ca45e', // Forked
|
||||
name: 'Firewall',
|
||||
description: 'This Hook essentially checks a blacklist of accounts',
|
||||
icon: Firewall
|
||||
},
|
||||
notary: {
|
||||
id: '87b6f5a8c2f5038fb0f20b8b510efa10', // Forked
|
||||
name: 'Notary',
|
||||
description: 'Collecting signatures for multi-sign transactions',
|
||||
icon: Notary
|
||||
},
|
||||
carbon: {
|
||||
id: '953662b22d065449f8ab6f69bc2afe41', // Forked
|
||||
name: 'Carbon',
|
||||
description: 'Send a percentage of sum to an address',
|
||||
icon: Carbon
|
||||
},
|
||||
peggy: {
|
||||
id: '049784a83fa068faf7912f663f7b6471', // Forked
|
||||
name: 'Peggy',
|
||||
description: 'An oracle based stable coin hook',
|
||||
icon: Peggy
|
||||
}
|
||||
}
|
||||
|
||||
export const apiHeaderFiles = ['hookapi.h', 'sfcodes.h', 'macro.h', 'extern.h', 'error.h']
|
||||
|
||||
Reference in New Issue
Block a user