Spaces:
Running
Running
File size: 3,489 Bytes
9a9d700 6a3b521 0234ec8 6a3b521 9a9d700 0234ec8 9a9d700 0234ec8 9a9d700 78397e9 dadb124 78397e9 dadb124 78397e9 6a3b521 dadb124 78397e9 6a3b521 78397e9 6a3b521 78397e9 6a3b521 3daa013 78397e9 6a3b521 78397e9 6a3b521 78397e9 6a3b521 9a9d700 dadb124 9a9d700 dadb124 9a9d700 81529d5 9a9d700 81529d5 9a9d700 81529d5 dadb124 9a9d700 dadb124 9a9d700 6a3b521 78397e9 6a3b521 9a9d700 |
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>
);
}
|