Spaces:
Running
Running
File size: 3,489 Bytes
544e2ab ca97c6b 90b31da ca97c6b 544e2ab 90b31da 544e2ab 90b31da 544e2ab f350e4d d1700e9 f350e4d d1700e9 f350e4d ca97c6b d1700e9 f350e4d ca97c6b f350e4d ca97c6b f350e4d ca97c6b 0ea55ea f350e4d ca97c6b f350e4d ca97c6b f350e4d ca97c6b 544e2ab d1700e9 544e2ab d1700e9 544e2ab 1d6d85b 544e2ab 1d6d85b 544e2ab 1d6d85b d1700e9 544e2ab d1700e9 544e2ab ca97c6b f350e4d ca97c6b 544e2ab |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
// Full-page editor for code files.
import Editor, { type Monaco } from "@monaco-editor/react";
import type { editor } from "monaco-editor";
import { useEffect, useRef } from "react";
import { Link } from "react-router";
import { WebsocketProvider } from "y-websocket";
import * as Y from "yjs";
// @ts-ignore
import Atom from "~icons/tabler/atom.jsx";
// @ts-ignore
import Backspace from "~icons/tabler/backspace.jsx";
// @ts-ignore
import Close from "~icons/tabler/x.jsx";
import favicon from "./assets/favicon.ico";
import theme from "./code-theme.ts";
import { usePath } from "./common.ts";
export default function Code() {
const path = usePath().replace(/^[/]code[/]/, "");
const parentDir = path!.split("/").slice(0, -1).join("/");
const yDocRef = useRef<any>();
const wsProviderRef = useRef<any>();
const monacoBindingRef = useRef<any>();
const yMonacoRef = useRef<any>();
const yMonacoLoadingRef = useRef(false);
const editorRef = useRef<any>();
useEffect(() => {
const loadMonaco = async () => {
if (yMonacoLoadingRef.current) return;
yMonacoLoadingRef.current = true;
// y-monaco is gigantic. The other Monaco packages are small.
yMonacoRef.current = await import("y-monaco");
initCRDT();
};
loadMonaco();
}, []);
function beforeMount(monaco: Monaco) {
monaco.editor.defineTheme("lynxkite", theme);
}
function onMount(_editor: editor.IStandaloneCodeEditor, monaco: Monaco) {
// Do nothing on Ctrl+S. We save after every keypress anyway.
_editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {});
editorRef.current = _editor;
initCRDT();
}
function initCRDT() {
if (!yMonacoRef.current || !editorRef.current) return;
if (yDocRef.current) return;
yDocRef.current = new Y.Doc();
const text = yDocRef.current.getText("text");
const proto = location.protocol === "https:" ? "wss:" : "ws:";
wsProviderRef.current = new WebsocketProvider(
`${proto}//${location.host}/ws/code/crdt`,
path!,
yDocRef.current,
);
editorRef.current.getModel()!.setEOL(0); // https://github.com/yjs/y-monaco/issues/6
monacoBindingRef.current = new yMonacoRef.current.MonacoBinding(
text,
editorRef.current.getModel()!,
new Set([editorRef.current]),
wsProviderRef.current.awareness,
);
}
useEffect(() => {
return () => {
yDocRef.current?.destroy();
wsProviderRef.current?.destroy();
monacoBindingRef.current?.destroy();
};
});
return (
<div className="workspace">
<div className="top-bar bg-neutral">
<Link className="logo" to="">
<img alt="" src={favicon} />
</Link>
<div className="ws-name">{path}</div>
<div className="tools text-secondary">
<button className="btn btn-link">
<Atom />
</button>
<button className="btn btn-link">
<Backspace />
</button>
<Link to={`/dir/${parentDir}`} className="btn btn-link">
<Close />
</Link>
</div>
</div>
<Editor
defaultLanguage="python"
theme="lynxkite"
path={path}
beforeMount={beforeMount}
onMount={onMount}
loading={null}
options={{
cursorStyle: "block",
cursorBlinking: "solid",
minimap: { enabled: false },
renderLineHighlight: "none",
}}
/>
</div>
);
}
|