Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,12 @@ jobs:
workspaces: "./client/src-tauri -> target"

- name: install frontend dependencies
# If you don't have `beforeBuildCommand` configured you may want to build your frontend here too.
run: npm install # change this to npm or pnpm depending on which one you use.
run: npm install
working-directory: ./client

# Skipped because of https://github.com/basalt-rs/basalt/issues/65
# - name: build frontend
# run: npm run build
# working-directory: ./client
- name: build frontend
run: npm run build
working-directory: ./client

- name: ensure app builds
run: cargo check --release
Expand Down
15 changes: 11 additions & 4 deletions client/app/competitor/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ const EditorButtons = () => {

// Defaults to first language if no language selected
useEffect(() => {
if (currQuestion?.languages?.length) {
setSelectedLanguage((prev) => prev ?? currQuestion?.languages?.[0]?.name);
if (currQuestion?.languages.length) {
setSelectedLanguage((prev) => prev ?? currQuestion.languages[0]);
}
}, [currQuestion, setSelectedLanguage]);

Expand Down Expand Up @@ -173,12 +173,19 @@ const EditorButtons = () => {
</Button>
</Tooltip>
<span className="ml-auto">
<Select value={selectedLanguage} onValueChange={setSelectedLanguage}>
<Select
value={selectedLanguage?.name}
onValueChange={(v) =>
setSelectedLanguage(
currQuestion.languages.find((l) => l.name === v) ?? null
)
}
>
<SelectTrigger className="w-56">
<SelectValue placeholder="Programming Language" />
</SelectTrigger>
<SelectContent>
{currQuestion?.languages?.map((l) => (
{currQuestion?.languages.map((l) => (
<SelectItem key={l.name} value={l.name}>
{l.name}
</SelectItem>
Expand Down
75 changes: 33 additions & 42 deletions client/components/CodeViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,44 @@
import React, { useEffect, useState } from 'react';
import AceEditor from 'react-ace';
import React, { useEffect, useRef, useState } from 'react';
import CodeMirror, { Extension, ReactCodeMirrorRef } from '@uiw/react-codemirror';
import { useAtom } from 'jotai';
import { editorSettingsAtom } from '@/lib/competitor-state';
import 'ace-builds/src-noconflict/theme-monokai';
import('ace-builds/src-noconflict/mode-javascript');
import 'ace-builds/src-noconflict/keybinding-vim';
import 'ace-builds/src-noconflict/keybinding-emacs';
import 'ace-builds/src-noconflict/keybinding-sublime';
import 'ace-builds/src-noconflict/ext-language_tools';
import { cn } from '@/lib/utils';
import { type LanguageSyntax } from '@/lib/editor/langs';
import { getExtensions } from '@/lib/editor';

export const CodeViewer = ({ code, className = '' }: { code: string; className?: string }) => {
const [editorSettings] = useAtom(editorSettingsAtom);
const [editorTheme, setEditorTheme] = useState(editorSettings.theme);
export const CodeViewer = ({
code,
language = 'javascript',
className = '',
}: {
code: string;
language?: LanguageSyntax;
className?: string;
}) => {
const [settings] = useAtom(editorSettingsAtom);
const $editor = useRef<ReactCodeMirrorRef>(null);
const [extensions, setExtensions] = useState<Extension[]>([]);

useEffect(() => {
(async () => {
await import(`ace-builds/src-noconflict/theme-${editorSettings.theme}`);
setEditorTheme(editorSettings.theme);

if (editorSettings.keybind !== 'ace') {
await import(`ace-builds/src-noconflict/keybinding-${editorSettings.keybind}`);
}
})();
}, [editorSettings]);
setExtensions(getExtensions(settings, language));
if ($editor.current) {
// TODO: this seems less than ideal
$editor.current.editor?.querySelectorAll<HTMLElement>('.cm-editor *').forEach((e) => {
e.style.fontSize = `${settings.fontSize}px`;
});
}
}, [language, settings, $editor]);

return (
<AceEditor
mode="javascript"
theme={editorTheme}
name="code-editor"
editorProps={{ $blockScrolling: true }}
width="100%"
height="100%"
<CodeMirror
extensions={extensions}
ref={$editor}
autoFocus
readOnly
theme="none"
className={cn('h-full w-full', className)}
value={code}
className={className}
setOptions={{
readOnly: true,
fontSize: editorSettings.fontSize,
tabSize: editorSettings.tabSize,
useSoftTabs: editorSettings.useSoftTabs,
enableBasicAutocompletion: editorSettings.enableBasicAutocompletion,
enableLiveAutocompletion: editorSettings.enableLiveAutocompletion,
highlightActiveLine: editorSettings.highlightActiveLine,
showGutter: editorSettings.showGutter,
displayIndentGuides: editorSettings.displayIndentGuides,
relativeLineNumbers: editorSettings.relativeLineNumbers,
foldStyle: editorSettings.foldStyle,
}}
keyboardHandler={editorSettings.keybind}
basicSetup={false}
/>
);
};
66 changes: 22 additions & 44 deletions client/components/Editor.tsx
Original file line number Diff line number Diff line change
@@ -1,62 +1,40 @@
import React, { useEffect, useState } from 'react';
import AceEditor from 'react-ace';
import React, { useEffect, useRef, useState } from 'react';
import CodeMirror, { Extension, ReactCodeMirrorRef } from '@uiw/react-codemirror';
import { useAtom } from 'jotai';
import {
editorContentAtom,
editorSettingsAtom,
selectedLanguageAtom,
} from '@/lib/competitor-state';
import 'ace-builds/esm-resolver';
import 'ace-builds/src-noconflict/theme-monokai';
import 'ace-builds/src-noconflict/ext-language_tools';
import { currQuestionAtom } from '@/lib/services/questions';
import { getExtensions } from '@/lib/editor';

export default function CodeEditor() {
const [editorContent, setEditorContent] = useAtom(editorContentAtom);
const [editorSettings] = useAtom(editorSettingsAtom);
const [languageValue] = useAtom(selectedLanguageAtom);
const [editorTheme, setEditorTheme] = useState(editorSettings.theme);
const [question] = useAtom(currQuestionAtom);
const [settings] = useAtom(editorSettingsAtom);
const [language] = useAtom(selectedLanguageAtom);
const $editor = useRef<ReactCodeMirrorRef>(null);
const [extensions, setExtensions] = useState<Extension[]>([]);

useEffect(() => {
(async () => {
await import(`ace-builds/src-noconflict/theme-${editorSettings.theme}`);
setEditorTheme(editorSettings.theme);

if (editorSettings.keybind !== 'ace') {
await import(`ace-builds/src-noconflict/keybinding-${editorSettings.keybind}`);
}
if (languageValue) {
await import(
`ace-builds/src-noconflict/mode-${question?.languages?.find((l) => l.name === languageValue)?.syntax || 'plaintext'}`
);
}
})();
}, [editorSettings, languageValue, question]);
setExtensions(getExtensions(settings, language?.syntax));
if ($editor.current) {
// TODO: this seems less than ideal
$editor.current.editor?.querySelectorAll<HTMLElement>('.cm-editor *').forEach((e) => {
e.style.fontSize = `${settings.fontSize}px`;
});
}
}, [language, settings, $editor]);

return (
<AceEditor
mode={question?.languages?.find((l) => l.name === languageValue)?.syntax || 'plaintext'}
theme={editorTheme}
name="code-editor"
editorProps={{ $blockScrolling: true }}
width="100%"
height="100%"
<CodeMirror
extensions={extensions}
ref={$editor}
autoFocus
theme="none"
className="h-full w-full"
value={editorContent}
onChange={setEditorContent}
setOptions={{
fontSize: editorSettings.fontSize,
tabSize: editorSettings.tabSize,
useSoftTabs: editorSettings.useSoftTabs,
enableBasicAutocompletion: editorSettings.enableBasicAutocompletion,
enableLiveAutocompletion: editorSettings.enableLiveAutocompletion,
highlightActiveLine: editorSettings.highlightActiveLine,
showGutter: editorSettings.showGutter,
displayIndentGuides: editorSettings.displayIndentGuides,
relativeLineNumbers: editorSettings.relativeLineNumbers,
foldStyle: editorSettings.foldStyle,
}}
keyboardHandler={editorSettings.keybind}
basicSetup={false}
/>
);
}
Loading