From 8fd211471308d14dacd019283b8065101f000f33 Mon Sep 17 00:00:00 2001 From: Benjamin Leonard Date: Tue, 17 Dec 2024 14:11:58 +0000 Subject: [PATCH] Switch to monaco --- app/components/note/Editor.tsx | 105 ++ app/components/note/NoteForm.tsx | 29 +- app/components/note/oxide-dark.json | 1386 +++++++++++++++++++++++++++ app/routes/notes.$id.publish.tsx | 2 +- app/routes/notes_.$id.delete.tsx | 2 +- app/routes/notes_.$id_.edit.tsx | 8 +- app/routes/notes_._index.tsx | 4 +- app/routes/notes_.new.tsx | 2 +- app/routes/notes_.tsx | 2 +- package.json | 5 +- 10 files changed, 1515 insertions(+), 30 deletions(-) create mode 100644 app/components/note/Editor.tsx create mode 100644 app/components/note/oxide-dark.json diff --git a/app/components/note/Editor.tsx b/app/components/note/Editor.tsx new file mode 100644 index 0000000..5d3ad6a --- /dev/null +++ b/app/components/note/Editor.tsx @@ -0,0 +1,105 @@ +import { Editor, useMonaco } from '@monaco-editor/react' +import { shikiToMonaco } from '@shikijs/monaco' +import { useEffect } from 'react' +import { getHighlighter } from 'shiki' + +// import asciidocLang from './asciidoc-lang.json' +import theme from './oxide-dark.json' + +const EditorWrapper = ({ + body, + onChange, +}: { + body: string + onChange: (string) => void +}) => { + const monaco = useMonaco() + + useEffect(() => { + if (!monaco) { + return + } + + const highlight = async () => { + const highlighter = await getHighlighter({ + themes: [theme], + langs: ['asciidoc'], + }) + + monaco.languages.register({ id: 'asciidoc' }) + shikiToMonaco(highlighter, monaco) + } + + highlight() + }, [monaco]) + + return ( + + ) +} + +export default EditorWrapper + +// import loader from '@monaco-editor/loader' +// import { shikiToMonaco } from '@shikijs/monaco' +// import { type editor } from 'monaco-editor' +// import type * as Monaco from 'monaco-editor/esm/vs/editor/editor.api' +// import { useCallback, useEffect, useRef, useState } from 'react' +// import { getHighlighter } from 'shiki' + +// const Editor = () => { +// const containerRef = useRef(null) +// const monacoRef = useRef(null) +// const [isMonacoMounting, setIsMonacoMounting] = useState(true) +// const editorRef = useRef(null) +// const preventCreation = useRef(false) +// const [isEditorReady, setIsEditorReady] = useState(false) + +// useEffect(() => { +// const cancelable = loader.init() + +// cancelable +// .then((monaco) => (monacoRef.current = monaco) && setIsMonacoMounting(false)) +// .catch( +// (error) => +// error?.type !== 'cancelation' && +// console.error('Monaco initialization: error:', error), +// ) + +// return () => (editorRef.current ? editorRef.current!.dispose() : cancelable.cancel()) +// }, [containerRef]) + +// const createEditor = useCallback(() => { +// if (!containerRef.current || !monacoRef.current) return +// if (!preventCreation.current) { +// editorRef.current = monacoRef.current?.editor.create(containerRef.current, { +// automaticLayout: true, +// }) + +// monacoRef.current.editor.setTheme('vs-dark') + +// setIsEditorReady(true) +// preventCreation.current = true +// } +// }, []) + +// useEffect(() => { +// !isMonacoMounting && !isEditorReady && createEditor() +// }, [isMonacoMounting, isEditorReady, createEditor]) + +// console.log(monacoRef, containerRef) + +// return ( +//
+//
+//
+// ) +// } + +// export default Editor diff --git a/app/components/note/NoteForm.tsx b/app/components/note/NoteForm.tsx index 4891e47..e145464 100644 --- a/app/components/note/NoteForm.tsx +++ b/app/components/note/NoteForm.tsx @@ -6,19 +6,22 @@ * Copyright Oxide Computer Company */ import { useDialogStore, type DialogStore } from '@ariakit/react' -import { EditorView } from '@codemirror/view' +// import { StreamLanguage } from '@codemirror/language' +// import { EditorView } from '@codemirror/view' +// import Editor from '@monaco-editor/react' import Asciidoc, { asciidoctor } from '@oxide/react-asciidoc' import * as Dropdown from '@radix-ui/react-dropdown-menu' import { useFetcher, useLoaderData } from '@remix-run/react' -import CodeMirror, { type ReactCodeMirrorRef } from '@uiw/react-codemirror' +// import CodeMirror, { basicSetup, type ReactCodeMirrorRef } from '@uiw/react-codemirror' import cn from 'classnames' +// import { asciidoc } from 'codemirror-asciidoc' import dayjs from 'dayjs' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { opts } from '~/components/AsciidocBlocks' import { DropdownItem, DropdownLink, DropdownMenu } from '~/components/Dropdown' import Icon from '~/components/Icon' -import { editorTheme } from '~/components/note/EditorTheme' +// import { editorTheme } from '~/components/note/EditorTheme' import { useDebounce } from '~/hooks/use-debounce' import { useRootLoaderData } from '~/root' @@ -26,6 +29,7 @@ import { MinimalDocument } from '../AsciidocBlocks/MinimalDocument' import Modal from '../Modal' import Spinner from '../Spinner' import { TextInput } from '../TextInput' +import EditorWrapper from './Editor' import { SidebarIcon } from './Sidebar' const ad = asciidoctor() @@ -174,32 +178,17 @@ export const NoteForm = ({
{ - if ((el.target as HTMLElement).id === 'code_mirror_wrapper') { - inputRef.current && - inputRef.current.editor && - inputRef.current.editor.focus() - } - }} > - { setStatus('unsaved') setBody(val) }} - theme={editorTheme} - className="!normal-case !tracking-normal text-mono-md" - readOnly={false} - basicSetup - autoFocus - extensions={[EditorView.lineWrapping]} />
{sidebarOpen && } diff --git a/app/routes/notes_._index.tsx b/app/routes/notes_._index.tsx index 709c7e5..f442fa6 100644 --- a/app/routes/notes_._index.tsx +++ b/app/routes/notes_._index.tsx @@ -15,9 +15,11 @@ export const loader = async ({ request }: LoaderArgs) => { if (!user) throw new Response('Not authorized', { status: 401 }) + console.log(`http://localhost:8000/user/${user.id}`) + const response = await fetch(`http://localhost:8000/user/${user.id}`, { headers: { - 'x-api-key': process.env.TOME_API_KEY || '', + 'x-api-key': process.env.NOTES_API_KEY || '', }, }) if (!response.ok) { diff --git a/app/routes/notes_.new.tsx b/app/routes/notes_.new.tsx index e1d27ce..4ac5e3f 100644 --- a/app/routes/notes_.new.tsx +++ b/app/routes/notes_.new.tsx @@ -17,7 +17,7 @@ export async function action({ request }: ActionArgs) { const response = await fetch('http://localhost:8000/notes', { method: 'POST', headers: { - 'x-api-key': process.env.TOME_API_KEY || '', + 'x-api-key': process.env.NOTES_API_KEY || '', 'Content-Type': 'application/json; charset=utf-8', }, body: JSON.stringify({ title: 'Untitled', user: user.id, body: '' }), diff --git a/app/routes/notes_.tsx b/app/routes/notes_.tsx index 318d5f9..f3e37b5 100644 --- a/app/routes/notes_.tsx +++ b/app/routes/notes_.tsx @@ -18,7 +18,7 @@ export const loader = async ({ request }: LoaderArgs) => { const response = await fetch(`http://localhost:8000/user/${user.id}`, { headers: { - 'x-api-key': process.env.TOME_API_KEY || '', + 'x-api-key': process.env.NOTES_API_KEY || '', }, }) diff --git a/package.json b/package.json index 0f3eac1..bdc0317 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,9 @@ "@remix-run/node": "2.13.1", "@remix-run/react": "2.13.1", "@remix-run/serve": "2.13.1", + "@monaco-editor/loader": "^1.4.0", + "@monaco-editor/react": "^4.6.0", + "@shikijs/monaco": "^1.5.1", "@tanstack/react-query": "^4.3.9", "@vercel/remix": "^2.13.1", "classnames": "^2.3.1", @@ -44,7 +47,7 @@ "remeda": "^2.17.4", "remix-auth": "^3.6.0", "remix-auth-oauth2": "^1.11.1", - "shiki": "^1.23.1", + "shiki": "^1.5.1", "simple-text-diff": "^1.7.0", "tunnel-rat": "^0.1.2", "zod": "^3.22.3"