Skip to content
Open
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
174 changes: 98 additions & 76 deletions externals/styles/fonts.tsx
Original file line number Diff line number Diff line change
@@ -1,80 +1,102 @@
import { Fira_Sans, Fira_Mono, Lato, Roboto } from 'next/font/google'
// import { Fira_Sans, Fira_Mono, Lato, Roboto } from 'next/font/google'

export const roboto = Roboto({
subsets: ['latin'],
weight: ['400', '700'],
style: ['normal', 'italic'],
fallback: [
'-apple-system',
'BlinkMacSystemFont',
'Segoe UI',
'Roboto',
'Helvetica Neue',
'Arial',
'sans-serif',
'Apple Color Emoji',
'Segoe UI Emoji',
'Segoe UI Symbol',
],
variable: '--font-roboto',
preload: false,
})
// export const roboto = Roboto({
// subsets: ['latin'],
// weight: ['400', '700'],
// style: ['normal', 'italic'],
// fallback: [
// '-apple-system',
// 'BlinkMacSystemFont',
// 'Segoe UI',
// 'Roboto',
// 'Helvetica Neue',
// 'Arial',
// 'sans-serif',
// 'Apple Color Emoji',
// 'Segoe UI Emoji',
// 'Segoe UI Symbol',
// ],
// variable: '--font-roboto',
// preload: false,
// })

export const firaSans = Fira_Sans({
subsets: ['latin'],
weight: ['300', '400', '500', '600', '700'],
style: ['normal', 'italic'],
fallback: [
'-apple-system',
'BlinkMacSystemFont',
'Segoe UI',
'Roboto',
'Helvetica Neue',
'Arial',
'sans-serif',
'Apple Color Emoji',
'Segoe UI Emoji',
'Segoe UI Symbol',
],
variable: '--font-fira-sans',
preload: false,
})
// export const firaSans = Fira_Sans({
// subsets: ['latin'],
// weight: ['300', '400', '500', '600', '700'],
// style: ['normal', 'italic'],
// fallback: [
// '-apple-system',
// 'BlinkMacSystemFont',
// 'Segoe UI',
// 'Roboto',
// 'Helvetica Neue',
// 'Arial',
// 'sans-serif',
// 'Apple Color Emoji',
// 'Segoe UI Emoji',
// 'Segoe UI Symbol',
// ],
// variable: '--font-fira-sans',
// preload: false,
// })

export const firaMono = Fira_Mono({
subsets: ['latin'],
weight: ['400'],
fallback: [
'-apple-system',
'BlinkMacSystemFont',
'Segoe UI',
'Roboto',
'Helvetica Neue',
'Arial',
'sans-serif',
'Apple Color Emoji',
'Segoe UI Emoji',
'Segoe UI Symbol',
],
variable: '--font-fira-mono',
preload: false,
})
// export const firaMono = Fira_Mono({
// subsets: ['latin'],
// weight: ['400'],
// fallback: [
// '-apple-system',
// 'BlinkMacSystemFont',
// 'Segoe UI',
// 'Roboto',
// 'Helvetica Neue',
// 'Arial',
// 'sans-serif',
// 'Apple Color Emoji',
// 'Segoe UI Emoji',
// 'Segoe UI Symbol',
// ],
// variable: '--font-fira-mono',
// preload: false,
// })

export const lato = Lato({
subsets: ['latin'],
weight: ['400'],
style: ['normal', 'italic'],
fallback: [
'-apple-system',
'BlinkMacSystemFont',
'Segoe UI',
'Roboto',
'Helvetica Neue',
'Arial',
'sans-serif',
'Apple Color Emoji',
'Segoe UI Emoji',
'Segoe UI Symbol',
],
variable: '--font-lato',
preload: false,
})
// export const lato = Lato({
// subsets: ['latin'],
// weight: ['400'],
// style: ['normal', 'italic'],
// fallback: [
// '-apple-system',
// 'BlinkMacSystemFont',
// 'Segoe UI',
// 'Roboto',
// 'Helvetica Neue',
// 'Arial',
// 'sans-serif',
// 'Apple Color Emoji',
// 'Segoe UI Emoji',
// 'Segoe UI Symbol',
// ],
// variable: '--font-lato',
// preload: false,
// })

// please see the sandbox issue #2 for this

export const firaSans = {
className: 'font-fira-sans',
style: { fontFamily: 'Fira Sans, sans-serif' },
}

export const firaMono = {
className: 'font-fira-mono',
style: { fontFamily: 'Fira Mono, monospace' },
}

export const lato = {
className: 'font-lato',
style: { fontFamily: 'Lato, sans-serif' },
}

export const roboto = {
className: 'font-roboto',
style: { fontFamily: 'Roboto, sans-serif' },
}
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,16 @@
"dev": "run-p build:watch preview"
},
"dependencies": {
"@codemirror/basic-setup": "^0.20.0",
"@codemirror/lang-javascript": "^6.2.4",
"@codemirror/language": "^6.12.1",
"@codemirror/state": "^6.5.4",
"@codemirror/view": "^6.39.12",
"@date-io/date-fns": "^2.13.2",
"@emotion/react": "^11.4.1",
"@emotion/server": "^11.4.0",
"@emotion/styled": "^11.3.0",
"@lezer/highlight": "^1.2.3",
"@monaco-editor/react": "^4.6.0",
"@mui/icons-material": "^5.15.20",
"@mui/lab": "^5.0.0-alpha.100",
Expand All @@ -35,6 +41,7 @@
"@nivo/core": "^0.88.0",
"@nivo/line": "^0.88.0",
"@nivo/pie": "^0.88.0",
"codemirror": "^6.0.2",
"axios": "^1.7.2",
"date-fns": "^2.28.0",
"firebase": "^11.0.1",
Expand All @@ -49,8 +56,8 @@
"notistack": "^2.0.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-dropzone": "^14.2.3",
"react-hook-form": "^7.31.2",
"react-dropzone": "^14.2.3",
"react-image-crop": "^11.0.10",
"react-katex": "^3.0.1",
"react-query": "^3.39.0",
Expand Down
4 changes: 3 additions & 1 deletion src/sandbox-component.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { ThemeProvider } from '@styles/minimal/theme-provider'

import { PseudocodeResponseAreaTub } from './types/Pseudocode'
import { SandboxResponseAreaTub } from './types/Sandbox/index'

function ResponseAreaInputWrapper({ children }: { children: React.ReactNode }) {
return <ThemeProvider>{children}</ThemeProvider>
}

// wrap the components with the necessary providers; only in the sandbox
class WrappedSandboxResponseAreaTub extends SandboxResponseAreaTub {
class WrappedSandboxResponseAreaTub extends PseudocodeResponseAreaTub {
constructor() {
super()

Expand Down
1 change: 0 additions & 1 deletion src/types/Essay/EssayWizard.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ export const EssayWizard: React.FC<EssayWizardProps> = props => {
onChange,
} = props
const { classes, cx } = useStyles()
console.log('answer', answer)

const updateAnswer = (newAnswer: string) => {
onChange({
Expand Down
136 changes: 136 additions & 0 deletions src/types/Pseudocode/Pseudocode.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { foldGutter, foldService, syntaxHighlighting } from '@codemirror/language';
import { EditorState } from '@codemirror/state';
import { placeholder } from '@codemirror/view';
import { EditorView, basicSetup } from 'codemirror';
import { useEffect, useRef, useState } from 'react';

import { BaseResponseAreaProps } from '../base-props.type';

import { PseudocodeFeedbackPanel } from './components/PseudocodeFeedbackPanel';
import { autoIndentAfterColon } from './plugins/autoIndent';
import { pseudocodeFoldFunc } from './plugins/fold';
import { pseudocodeHighlightStyle } from './plugins/highlight';
import { pseudocodeLanguage } from './plugins/language';
import { pseudocodeTheme } from './plugins/pseudocode.theme';
import { StudentResponse } from './types/input';
// import { defaultStudentResponse } from './utils/consts';
import { EvaluationResult } from './types/output';
import { usePseudocodeStyles } from './utils/styles';

type PseudocodeInputProps = Omit<BaseResponseAreaProps, 'handleChange' | 'answer' | 'feedback'> & {
handleChange: (val: StudentResponse) => void;
feedback: EvaluationResult | null;
answer: StudentResponse
};

export const PseudocodeInput: React.FC<PseudocodeInputProps> = ({
handleChange,
feedback,
answer
}) => {
console.log(answer)
const { classes } = usePseudocodeStyles();

// Internal state fully managed in this component
const [internalAnswer, setInternalAnswer] = useState<StudentResponse>(answer);

const editorRef = useRef<HTMLDivElement | null>(null);
const viewRef = useRef<EditorView | null>(null);

// Initialize CodeMirror once
useEffect(() => {
if (!editorRef.current) return;

const state = EditorState.create({
doc: internalAnswer.pseudocode,
extensions: [
foldGutter(),
foldService.of(pseudocodeFoldFunc),
autoIndentAfterColon,
basicSetup,
pseudocodeLanguage,
syntaxHighlighting(pseudocodeHighlightStyle),
pseudocodeTheme,
placeholder('Write your pseudocode here...'),
EditorView.updateListener.of((update) => {
if (!update.docChanged) return;
const newCode = update.state.doc.toString();
setInternalAnswer((prev) => {
const updated = { ...prev, pseudocode: newCode };
handleChange(updated); // notify parent immediately
return updated;
});
}),
EditorView.theme({
'&': { height: '100%' },
'.cm-scroller': { overflow: 'auto' },
}),
],
});

const view = new EditorView({
state,
parent: editorRef.current,
});

viewRef.current = view;
return () => {
view.destroy();
viewRef.current = null;
};
}, []); // only runs once

// Report change for complexity/explanation fields
const handleFieldChange = (field: keyof StudentResponse, value: string) => {
setInternalAnswer((prev) => {
const updated = { ...prev, [field]: value };
handleChange(updated); // immediate update to parent
return updated;
});
};

return (
<div className={classes.root}>
{/* ================= Editor ================= */}
<div className={classes.editorWrapper}>
<div ref={editorRef} className={classes.editor} />
</div>

{/* ================= Complexity Inputs ================= */}
<div className={classes.complexityRow}>
<input
className={classes.field}
value={internalAnswer.time_complexity ?? ''}
placeholder="Time Complexity (e.g. O(n log n))"
onChange={(e) => handleFieldChange('time_complexity', e.target.value)}
/>
<input
className={classes.field}
value={internalAnswer.space_complexity ?? ''}
placeholder="Space Complexity (e.g. O(n))"
onChange={(e) => handleFieldChange('space_complexity', e.target.value)}
/>
</div>

{/* ================= Explanation ================= */}
<textarea
className={classes.textarea}
value={internalAnswer.explanation ?? ''}
placeholder="Explain your reasoning (optional)"
onChange={(e) => handleFieldChange('explanation', e.target.value)}
/>

{/* ================= Action =================
<button onClick={callAPI}>
Check Answer
</button> */}

{/* ================= Feedback ================= */}
{feedback && (
<div className={classes.feedbackPanel}>
<PseudocodeFeedbackPanel result={feedback} />
</div>
)}
</div>
);
};
Loading