Add a notebook renderer for QDK Learning multiple-choice questions - #3690
Dhairya Patel (HABER7789) wants to merge 7 commits into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Untrusted renderer content can reach Copilot, while payload and stale-output validation gaps can produce incorrect quiz behavior.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds an interactive, kernel-independent notebook renderer for QDK Learning quizzes.
Changes:
- Implements multiple-choice rendering, grading, retry, accessibility, and Copilot actions.
- Adds Python quiz authoring, validation, conversion, and baked-output tooling.
- Converts IQPE self-check content and integrates renderer builds and messaging.
File summaries
| File | Description |
|---|---|
source/vscode/tsconfig.json |
Separates renderer type-checking. |
source/vscode/src/notebookRenderer/tsconfig.json |
Configures renderer TypeScript checks. |
source/vscode/src/notebookRenderer/styles.css |
Styles quiz states and controls. |
source/vscode/src/notebookRenderer/schema.ts |
Defines payload and messaging contracts. |
source/vscode/src/notebookRenderer/rendering.ts |
Adds safe DOM helpers. |
source/vscode/src/notebookRenderer/rendererApi.d.ts |
Declares notebook renderer APIs. |
source/vscode/src/notebookRenderer/multipleChoice.ts |
Implements quiz interaction and grading. |
source/vscode/src/notebookRenderer/index.ts |
Activates rendering and manages lifecycle. |
source/vscode/src/notebookRenderer/css.d.ts |
Types CSS text imports. |
source/vscode/src/learning/notebookRendererMessaging.ts |
Bridges renderer actions to Copilot. |
source/vscode/src/learning/notebookExercises.ts |
Excludes quizzes from tracked activities. |
source/vscode/src/learning/index.ts |
Registers renderer messaging. |
source/vscode/resources/qdk-learning/utils/chemistry-qpe/verify_course.py |
Recognizes baked quiz cells. |
source/vscode/resources/qdk-learning/utils/chemistry-qpe/README.md |
Documents quiz conversion workflow. |
source/vscode/resources/qdk-learning/utils/chemistry-qpe/details_to_quiz.py |
Converts and rebakes quiz outputs. |
source/vscode/resources/qdk-learning/courses/chemistry-qpe/06-iterative-phase-estimation/_unit.py |
Registers IQPE quiz content. |
source/vscode/resources/qdk-learning/courses/chemistry-qpe/_learning_output.py |
Defines quiz payloads and fallbacks. |
source/vscode/package.json |
Registers and type-checks the renderer. |
source/vscode/build.mjs |
Builds the renderer and checks contracts. |
source/vscode/authoring-courses.md |
Documents quiz authoring. |
source/npm/qsharp/ux/qdk-theme.css |
Adds quiz theme colors. |
Review details
- Files reviewed: 19/22 changed files
- Comments generated: 3
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
🟡 Changes recommended
The stale-output checker misses deleted single-quiz calls, and the Copilot action omits the learner’s selected answer.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
source/vscode/resources/qdk-learning/utils/chemistry-qpe/details_to_quiz.py:328
- When the last
quiz(...)call is removed from a cell,idsis empty and this early return skips its existing baked output. The deleted question therefore remains visible while--checkreports the notebook as up to date; most generated quiz cells contain only one call, so this is a common deletion case. Detect cells that still contain the custom quiz MIME output even when no IDs remain, and report or clear those outputs.
ids = _cell_quiz_ids(cell)
if not ids:
continue
- Files reviewed: 20/23 changed files
- Comments generated: 1
- Review effort level: Balanced
# Conflicts: # source/vscode/resources/qdk-learning/courses/chemistry-qpe/06-iterative-phase-estimation/iterative_phase_estimation.ipynb # source/vscode/resources/qdk-learning/utils/chemistry-qpe/README.md
There was a problem hiding this comment.
🟡 Changes recommended
Stale deleted outputs can remain visible, and the Python emitter can produce payloads that the renderer rejects.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (1)
source/vscode/resources/qdk-learning/utils/chemistry-qpe/details_to_quiz.py:328
- The leftover-output fix still skips the case where the last
quiz()call is removed from a quiz cell:idsis empty, so the function returns before inspecting that cell's baked outputs. In that case--checkstill reports up to date and VS Code continues rendering the deleted question. Track stale cells (not only surviving quiz IDs) so an empty quiz cell can have its outputs cleared.
if not ids:
continue
- Files reviewed: 20/23 changed files
- Comments generated: 4
- Review effort level: Balanced
- Reject empty prompts, empty option text and malformed ids when a quiz is authored, so mistakes fail for the author instead of the learner - Extend the build-time contract check to the manifest, the runtime kind list, the quiz cell tag, the id grammar and the option cap - Clear baked questions left behind when a quiz call is removed, and refuse rather than delete when a call cannot be parsed - Rename the payload's cellId to payloadId - it is a quiz id, and cellId means an ipynb cell id everywhere else in src/learning - Drop the duplicate workbook check in favour of the existing isCourseWorkbook
| ) | ||
|
|
||
|
|
||
| # --------------------------------------------------------------------------- |
There was a problem hiding this comment.
I don't love that a bunch of course content now has to live on the side, in _unit.py.
|
|
||
| const tryAgainButton = document.createElement("button"); | ||
| tryAgainButton.type = "button"; | ||
| tryAgainButton.textContent = "Try again"; |
There was a problem hiding this comment.
What's the try again button for if the solution is already displayed?
| ? ` I picked ${optionIds.map((id) => `"${id}"`).join(" and ")}.` | ||
| : ""; | ||
| return ( | ||
| `/qdk-learning I answered the quiz "${quizId}" in this notebook incorrectly.${picked}` + |
There was a problem hiding this comment.
I know we have to be really explicit for copilot, but the user can see this, so we might want to try to find a gentler way to ask the same thing.
Each section of the IQPE chapter ends with a self-check question. Today it's a dropdown you click to reveal the answer. This turns those 12 into multiple choice questions a learner answers, gets marked on, and can retry.
application/vnd.qdk.learning+json_unit.py, so the notebook cell only saysquiz("id")details_to_quiz.pybakes a chapter's questions;--checkreports stale outputschema.ts,_learning_output.pyand thepackage.jsoncontribution with nothing linking them, socheckRendererContract()compares them at build time and fails if the MIME type, the payload fields, the id grammar or the quiz cell tag stop matchingNot in this PR
_learning_output.pywouldn't be covered.