11import * as vscode from "vscode" ;
22// Allow using JSX within this file by overriding our own createElement function
33import React from "../utils/FakeReact" ;
4+ import ReactDOMServer from "react-dom/server" ;
45
56import Messages , {
67 MessageType ,
@@ -14,6 +15,7 @@ import { Editor } from "../utils/editor";
1415import { FRONTEND_ELEMENT_ID } from "../constants" ;
1516import { client } from "../extension" ;
1617import _ from "lodash" ;
18+ import { McqPanelWithLogging as McqPanel } from "../webview/components/McqPanel" ;
1719
1820let mcqPanel : vscode . WebviewPanel | null = null ;
1921
@@ -44,37 +46,49 @@ async function handleMessage(
4446 case MessageTypeNames . ExtensionPing :
4547 sendToFrontend ( panel , Messages . ExtensionPong ( null ) ) ;
4648 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" ,
49+ case MessageTypeNames . MCQQuestion : {
50+ if ( mcqPanel === null ) {
51+ mcqPanel = vscode . window . createWebviewPanel (
52+ "mcq-question-panel" ,
53+ `Question ${ message . questionId + 1 } ` ,
54+ vscode . ViewColumn . One ,
55+ { enableScripts : true , retainContextWhenHidden : true } ,
6556 ) ;
6657
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 ) ;
58+ mcqPanel . onDidDispose ( ( ) => {
59+ mcqPanel = null ;
60+ } ) ;
7661 }
62+ mcqPanel . title = `Question ${ message . questionId + 1 } ` ;
63+ mcqPanel . iconPath = vscode . Uri . joinPath (
64+ context . extensionUri ,
65+ "assets" ,
66+ "icon.png" ,
67+ ) ;
68+ let activePanel = await McqPanel ( {
69+ data : {
70+ assessmentName : message . assessmentName ?? "" ,
71+ questionId : message . questionId ,
72+ question : message . question ,
73+ choices : message . options ,
74+ workspaceLocation : message . workspaceLocation ?? "assessment" ,
75+ } ,
76+ } ) ;
77+ setWebviewContent (
78+ mcqPanel ,
79+ context ,
80+ < div
81+ // @ts -ignore: Our FakeReact doesn't modify the style attribute
82+ style = "width: 100%; height: calc(100vh - 10px)"
83+ id = "mcq-panel"
84+ >
85+ { ReactDOMServer . renderToString ( activePanel ) }
86+ </ div > ,
87+ ) ;
88+ mcqPanel . reveal ( vscode . ViewColumn . One ) ;
89+
7790 break ;
91+ }
7892
7993 case MessageTypeNames . NewEditor :
8094 // console.log(message.questionType + " questionType \n");
@@ -172,7 +186,6 @@ export async function showPanel(context: vscode.ExtensionContext) {
172186
173187 const frontendUrl = new URL ( "/playground" , config . frontendBaseUrl ) . href ;
174188
175- // equivalent to panel.webview.html = ...
176189 setWebviewContent (
177190 panel ,
178191 context ,
@@ -201,141 +214,6 @@ export async function showPanel(context: vscode.ExtensionContext) {
201214 ) ;
202215}
203216
204- // TODO: Move this to a util file
205- function getMcqHtml (
206- _webview : vscode . Webview ,
207- question : string ,
208- options : string [ ] ,
209- questionId : string ,
210- ) : string {
211- return `<!DOCTYPE html>
212- <html lang="en">
213- <head>
214- <meta charset="UTF-8" />
215- <meta name="viewport" content="width=device-width, initial-scale=1" />
216- <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;" />
217- <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
218- <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
219- <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
220- <script src="https://unpkg.com/[email protected] /marked.min.js"></script> 221- <script>window.marked.setOptions({ breaks: true });</script>
222- <style>
223- .mcq-option {
224- color: black !important;
225- }
226- .mcq-option p {
227- margin: 0;
228- display: inline;
229- }
230- </style>
231- </head>
232- <body>
233- <div id="root"></div>
234- <script type="text/babel" data-type="module">
235- const { useState } = React;
236-
237- function McqPanel({ question, options, questionId }) {
238- const [selected, setSelected] = useState(null);
239-
240- const handleSubmit = (e) => {
241- e.preventDefault();
242- if (selected === null) return;
243- const vscode = acquireVsCodeApi();
244- vscode.postMessage({ type: 'answer', answer: selected });
245- };
246-
247- return (
248- <div style={{
249- padding: '1rem',
250- fontFamily: 'sans-serif',
251- maxWidth: '800px',
252- margin: '0 auto'
253- }}>
254- <h3>Question {parseInt(questionId) + 1}</h3>
255- <div dangerouslySetInnerHTML={{ __html: window.marked.parse(question) }} />
256- <form onSubmit={handleSubmit}>
257- <ul style={{
258- listStyle: 'none',
259- padding: 0,
260- margin: '1rem 0 1.5rem 0'
261- }}>
262- {options.map((option, index) => (
263- <li
264- key={index}
265- style={{
266- margin: '0.5rem 0',
267- padding: '0.75rem',
268- border: '1px solid #e1e4e8',
269- borderRadius: '6px',
270- backgroundColor: selected === index ? '#f6f8fa' : 'white',
271- cursor: 'pointer',
272- transition: 'background-color 0.2s'
273- }}
274- onClick={() => setSelected(index)}
275- >
276- <label style={{
277- display: 'flex',
278- alignItems: 'center',
279- cursor: 'pointer',
280- margin: 0
281- }}>
282- <input
283- type="radio"
284- name="mcq-option"
285- checked={selected === index}
286- onChange={() => setSelected(index)}
287- style={{
288- marginRight: '0.75rem',
289- width: '1.25rem',
290- height: '1.25rem',
291- cursor: 'pointer'
292- }}
293- />
294- <span
295- className="mcq-option"
296- dangerouslySetInnerHTML={{ __html: window.marked.parse(option) }}
297- />
298- </label>
299- </li>
300- ))}
301- </ul>
302- <button
303- type="submit"
304- disabled={selected === null}
305- style={{
306- padding: '0.5rem 1.5rem',
307- backgroundColor: selected !== null ? '#2ea043' : '#94d3a2',
308- color: 'white',
309- border: 'none',
310- borderRadius: '6px',
311- cursor: selected !== null ? 'pointer' : 'not-allowed',
312- fontSize: '1rem',
313- fontWeight: '500',
314- transition: 'background-color 0.2s',
315- opacity: selected !== null ? 1 : 0.7
316- }}
317- >
318- Submit Answer
319- </button>
320- </form>
321- </div>
322- );
323- }
324-
325- // Render the component
326- const root = ReactDOM.createRoot(document.getElementById('root'));
327- root.render(
328- <McqPanel
329- question="${ question . replace ( / " / g, """ ) } "
330- questionId="${ questionId } "
331- options={${ JSON . stringify ( options ) } }
332- />
333- );
334- </script>
335- </body>
336- </html>` ;
337- }
338-
339217export async function sendToFrontendWrapped ( message : MessageType ) {
340218 if ( ! panel ) {
341219 console . error ( "ERROR: panel is not set" ) ;
0 commit comments