@@ -15,6 +15,8 @@ import { FRONTEND_ELEMENT_ID } from "../constants";
15
15
import { client } from "../extension" ;
16
16
import _ from "lodash" ;
17
17
18
+ let mcqPanel : vscode . WebviewPanel | null = null ;
19
+
18
20
let panel : vscode . WebviewPanel | null = null ;
19
21
// This needs to be a reference to active
20
22
// TODO: Fix this ugly code!
@@ -42,11 +44,43 @@ async function handleMessage(
42
44
case MessageTypeNames . ExtensionPing :
43
45
sendToFrontend ( panel , Messages . ExtensionPong ( null ) ) ;
44
46
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 ) ;
49
76
}
77
+ break ;
78
+
79
+ case MessageTypeNames . NewEditor :
80
+ // console.log(message.questionType + " questionType \n");
81
+ // if (message.questionType == "mcq") {
82
+ // break;
83
+ // }
50
84
activeEditor = await Editor . create (
51
85
message . workspaceLocation ,
52
86
message . assessmentName ,
@@ -73,12 +107,7 @@ async function handleMessage(
73
107
console . log (
74
108
`EXTENSION: NewEditor: activeEditor set to ${ activeEditor . assessmentName } _${ activeEditor . questionId } ` ,
75
109
) ;
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
+
82
111
activeEditor . onChange ( ( editor ) => {
83
112
const workspaceLocation = editor . workspaceLocation ;
84
113
const code = editor . getText ( ) ;
@@ -172,6 +201,140 @@ export async function showPanel(context: vscode.ExtensionContext) {
172
201
}
173
202
174
203
// 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
+
175
338
export async function sendToFrontendWrapped ( message : MessageType ) {
176
339
if ( ! panel ) {
177
340
console . error ( "ERROR: panel is not set" ) ;
0 commit comments