Add features & update project

This commit is contained in:
Gabriel Brown
2026-06-23 01:46:08 -04:00
parent 930fbf5965
commit fe72fc2957
39 changed files with 3106 additions and 178 deletions
@@ -5,6 +5,8 @@ import dynamic from 'next/dynamic';
import { Button, Switch } from '@spoon/ui';
import { languageForPath } from './languages';
const MonacoEditor = dynamic(async () => await import('@monaco-editor/react'), {
ssr: false,
});
@@ -20,27 +22,27 @@ type VimMode = {
export const CodeEditor = ({
path,
content,
savedContent,
readOnly,
vimEnabled,
onSave,
onChange,
onVimEnabledChange,
}: {
path?: string;
content: string;
savedContent: string;
readOnly: boolean;
vimEnabled: boolean;
onSave: (content: string) => Promise<void>;
onChange: (content: string) => void;
onVimEnabledChange: (enabled: boolean) => void;
}) => {
const [value, setValue] = useState(content);
const [saving, setSaving] = useState(false);
const [vimEnabled, setVimEnabled] = useState(false);
const [dirty, setDirty] = useState(false);
const editorRef = useRef<MonacoEditorInstance | null>(null);
const vimRef = useRef<VimMode | null>(null);
const statusRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
setValue(content);
setDirty(false);
}, [content, path]);
useEffect(() => {
const editor = editorRef.current;
if (!editor) return;
@@ -71,13 +73,14 @@ export const CodeEditor = ({
const save = async () => {
setSaving(true);
try {
await onSave(value);
setDirty(false);
await onSave(content);
} finally {
setSaving(false);
}
};
const dirty = content !== savedContent;
return (
<div className='flex h-full min-h-0 flex-col'>
<div className='border-border flex h-11 items-center justify-between gap-3 border-b px-3'>
@@ -90,7 +93,7 @@ export const CodeEditor = ({
<div className='flex items-center gap-3'>
<label className='flex items-center gap-2 text-xs'>
Vim
<Switch checked={vimEnabled} onCheckedChange={setVimEnabled} />
<Switch checked={vimEnabled} onCheckedChange={onVimEnabledChange} />
</label>
<Button
type='button'
@@ -107,7 +110,8 @@ export const CodeEditor = ({
height='100%'
width='100%'
path={path}
value={value}
language={languageForPath(path)}
value={content}
theme='vs-dark'
options={{
readOnly,
@@ -116,13 +120,20 @@ export const CodeEditor = ({
scrollBeyondLastLine: false,
wordWrap: 'on',
automaticLayout: true,
scrollbar: { alwaysConsumeMouseWheel: false },
quickSuggestions: true,
suggestOnTriggerCharacters: true,
tabCompletion: 'on',
wordBasedSuggestions: 'matchingDocuments',
bracketPairColorization: { enabled: true },
renderWhitespace: 'selection',
}}
onMount={(editor) => {
editorRef.current = editor as MonacoEditorInstance;
}}
onChange={(next) => {
setValue(next ?? '');
setDirty((next ?? '') !== content);
const nextValue = next ?? '';
onChange(nextValue);
}}
/>
</div>