@@ -15,6 +15,8 @@ import { FRONTEND_ELEMENT_ID } from "../constants";
1515import { client } from "../extension" ;
1616import _ from "lodash" ;
1717
18+ let mcqPanel : vscode . WebviewPanel | null = null ;
19+
1820let panel : vscode . WebviewPanel | null = null ;
1921// This needs to be a reference to active
2022// TODO: Fix this ugly code!
@@ -42,11 +44,43 @@ async function handleMessage(
4244 case MessageTypeNames . ExtensionPing :
4345 sendToFrontend ( panel , Messages . ExtensionPong ( null ) ) ;
4446 break ;
45- case MessageTypeNames . NewEditor :
46- console . log ( message . questionType + " questionType \n" ) ;
47- if ( message . questionType == "mcq" ) {
48- break ;
47+ case MessageTypeNames . MCQQuestion :
48+ {
49+ if ( mcqPanel === null ) {
50+ mcqPanel = vscode . window . createWebviewPanel (
51+ "mcq-question-panel" ,
52+ `Question ${ message . questionId + 1 } ` ,
53+ vscode . ViewColumn . One ,
54+ { enableScripts : true , retainContextWhenHidden : true } ,
55+ ) ;
56+ mcqPanel . onDidDispose ( ( ) => {
57+ mcqPanel = null ;
58+ } ) ;
59+ }
60+ mcqPanel . title = `Question ${ message . questionId + 1 } ` ;
61+ mcqPanel . iconPath = vscode . Uri . joinPath (
62+ context . extensionUri ,
63+ "assets" ,
64+ "icon.png" ,
65+ ) ;
66+
67+ // Cast message to ensure properties exist
68+ const mcqMsg = message as any ;
69+ mcqPanel . webview . html = getMcqHtml (
70+ mcqPanel . webview ,
71+ mcqMsg . question ,
72+ mcqMsg . options ,
73+ mcqMsg . questionId ,
74+ ) ;
75+ mcqPanel . reveal ( vscode . ViewColumn . One ) ;
4976 }
77+ break ;
78+
79+ case MessageTypeNames . NewEditor :
80+ // console.log(message.questionType + " questionType \n");
81+ // if (message.questionType == "mcq") {
82+ // break;
83+ // }
5084 activeEditor = await Editor . create (
5185 message . workspaceLocation ,
5286 message . assessmentName ,
@@ -73,12 +107,7 @@ async function handleMessage(
73107 console . log (
74108 `EXTENSION: NewEditor: activeEditor set to ${ activeEditor . assessmentName } _${ activeEditor . questionId } ` ,
75109 ) ;
76- if ( activeEditor ) {
77- console . log ( "activeEditor keys and values:" ) ;
78- Object . entries ( activeEditor ) . forEach ( ( [ key , value ] ) => {
79- console . log ( `${ key } :` , value ) ;
80- } ) ;
81- }
110+
82111 activeEditor . onChange ( ( editor ) => {
83112 const workspaceLocation = editor . workspaceLocation ;
84113 const code = editor . getText ( ) ;
@@ -172,6 +201,140 @@ export async function showPanel(context: vscode.ExtensionContext) {
172201}
173202
174203// TODO: Move this to a util file
204+ function getMcqHtml (
205+ _webview : vscode . Webview ,
206+ question : string ,
207+ options : string [ ] ,
208+ questionId : string ,
209+ ) : string {
210+ return `<!DOCTYPE html>
211+ <html lang="en">
212+ <head>
213+ <meta charset="UTF-8" />
214+ <meta name="viewport" content="width=device-width, initial-scale=1" />
215+ <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-eval' 'unsafe-inline' https://unpkg.com; style-src 'unsafe-inline' https://unpkg.com;" />
216+ <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
217+ <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
218+ <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
219+ <script src="https://unpkg.com/marked@4.0.0/marked.min.js"></script>
220+ <script>window.marked.setOptions({ breaks: true });</script>
221+ <style>
222+ .mcq-option {
223+ color: black !important;
224+ }
225+ .mcq-option p {
226+ margin: 0;
227+ display: inline;
228+ }
229+ </style>
230+ </head>
231+ <body>
232+ <div id="root"></div>
233+ <script type="text/babel" data-type="module">
234+ const { useState } = React;
235+
236+ function McqPanel({ question, options, questionId }) {
237+ const [selected, setSelected] = useState(null);
238+
239+ const handleSubmit = (e) => {
240+ e.preventDefault();
241+ if (selected === null) return;
242+ const vscode = acquireVsCodeApi();
243+ vscode.postMessage({ type: 'answer', answer: selected });
244+ };
245+
246+ return (
247+ <div style={{
248+ padding: '1rem',
249+ fontFamily: 'sans-serif',
250+ maxWidth: '800px',
251+ margin: '0 auto'
252+ }}>
253+ <h3>Question {parseInt(questionId) + 1}</h3>
254+ <div dangerouslySetInnerHTML={{ __html: window.marked.parse(question) }} />
255+ <form onSubmit={handleSubmit}>
256+ <ul style={{
257+ listStyle: 'none',
258+ padding: 0,
259+ margin: '1rem 0 1.5rem 0'
260+ }}>
261+ {options.map((option, index) => (
262+ <li
263+ key={index}
264+ style={{
265+ margin: '0.5rem 0',
266+ padding: '0.75rem',
267+ border: '1px solid #e1e4e8',
268+ borderRadius: '6px',
269+ backgroundColor: selected === index ? '#f6f8fa' : 'white',
270+ cursor: 'pointer',
271+ transition: 'background-color 0.2s'
272+ }}
273+ onClick={() => setSelected(index)}
274+ >
275+ <label style={{
276+ display: 'flex',
277+ alignItems: 'center',
278+ cursor: 'pointer',
279+ margin: 0
280+ }}>
281+ <input
282+ type="radio"
283+ name="mcq-option"
284+ checked={selected === index}
285+ onChange={() => setSelected(index)}
286+ style={{
287+ marginRight: '0.75rem',
288+ width: '1.25rem',
289+ height: '1.25rem',
290+ cursor: 'pointer'
291+ }}
292+ />
293+ <span
294+ className="mcq-option"
295+ dangerouslySetInnerHTML={{ __html: window.marked.parse(option) }}
296+ />
297+ </label>
298+ </li>
299+ ))}
300+ </ul>
301+ <button
302+ type="submit"
303+ disabled={selected === null}
304+ style={{
305+ padding: '0.5rem 1.5rem',
306+ backgroundColor: selected !== null ? '#2ea043' : '#94d3a2',
307+ color: 'white',
308+ border: 'none',
309+ borderRadius: '6px',
310+ cursor: selected !== null ? 'pointer' : 'not-allowed',
311+ fontSize: '1rem',
312+ fontWeight: '500',
313+ transition: 'background-color 0.2s',
314+ opacity: selected !== null ? 1 : 0.7
315+ }}
316+ >
317+ Submit Answer
318+ </button>
319+ </form>
320+ </div>
321+ );
322+ }
323+
324+ // Render the component
325+ const root = ReactDOM.createRoot(document.getElementById('root'));
326+ root.render(
327+ <McqPanel
328+ question="${ question . replace ( / " / g, """ ) } "
329+ questionId="${ questionId } "
330+ options={${ JSON . stringify ( options ) } }
331+ />
332+ );
333+ </script>
334+ </body>
335+ </html>` ;
336+ }
337+
175338export async function sendToFrontendWrapped ( message : MessageType ) {
176339 if ( ! panel ) {
177340 console . error ( "ERROR: panel is not set" ) ;
0 commit comments