microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
9831093db0098b3a3e55cbadf3929222d7dd4805

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/playground/src/kataViewer.tsx

214lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4// import "preact/debug"; // Include this line only when debugging rendering
5
6import { render } from "preact";
7import { useEffect } from "preact/hooks";
8
9// This viewer uses the html version of the katas bundle and MathJax, as quantum.microsoft.com does
10import {
11 Exercise,
12 ExplainedSolutionItem,
13 Kata,
14 Lesson,
15 getAllKatas,
16} from "qsharp-lang/katas";
17
18declare global {
19 // The below are added by the MathJax and Highlight.js scripts
20 interface Window {
21 MathJax: any;
22 hljs: any;
23 }
24}
25
26window.MathJax = {
27 loader: {
28 load: ["[tex]/color", "[tex]/braket"],
29 },
30 tex: {
31 packages: { "[+]": ["color", "braket"] },
32 inlineMath: [
33 ["$", "$"],
34 ["\\(", "\\)"],
35 ],
36 formatError: (jax: any, err: any) => {
37 console.log("LaTeX processing error occurred. ", err, jax);
38 const errorNode = document.createElement("div");
39 errorNode.innerText = `LaTeX processing error: ${err.message}.\nLaTeX: ${jax.latex}\n\n`;
40 errorNode.style.fontSize = "20px";
41 errorNode.style.color = "red";
42 document.querySelector("#errors")?.appendChild(errorNode);
43 window.scroll(0, 0);
44 jax.formatError(err);
45 },
46 },
47 startup: {
48 pageReady: async () => {
49 await onload();
50 return window.MathJax.startup.defaultPageReady();
51 },
52 },
53};
54
55function Nav(props: {
56 katas: Kata[];
57 onnav: (index: number) => void;
58 selected: number;
59}) {
60 return (
61 <div class="nav">
62 {props.katas.map((kata, idx) => (
63 <>
64 <div
65 className={
66 idx === props.selected ? "nav-item nav-selected" : "nav-item"
67 }
68 onClick={() => props.onnav(idx)}
69 >
70 {kata.title}
71 </div>
72 </>
73 ))}
74 </div>
75 );
76}
77
78function KataEl(props: { kata: Kata }) {
79 useEffect(() => {
80 window.hljs.highlightAll();
81 window.MathJax.texReset();
82 window.MathJax.typesetClear();
83 window.MathJax.typesetPromise([".content"]);
84 }, [props.kata.id]);
85 window.scrollTo(0, 0);
86 return (
87 <div class="content" key={props.kata.id}>
88 <div id="errors"></div>
89 <h1>{props.kata.title}</h1>
90 {props.kata.sections.map((section) =>
91 section.type === "lesson" ? (
92 <LessonEl lesson={section} />
93 ) : (
94 <ExerciseEl exercise={section} />
95 ),
96 )}
97 </div>
98 );
99}
100
101function LessonEl(props: { lesson: Lesson }) {
102 const item = props.lesson;
103 return (
104 <>
105 <h2>{item.title}</h2>
106 {item.items.map((item) => {
107 switch (item.type) {
108 case "text-content":
109 return (
110 <div dangerouslySetInnerHTML={{ __html: item.content }}></div>
111 );
112 case "question":
113 return (
114 <>
115 <h3>Question</h3>
116 <div
117 dangerouslySetInnerHTML={{ __html: item.description.content }}
118 />
119 <h3>Answer</h3>
120 {item.answer.items.map((answer) => (
121 <ExplainedSolution item={answer} />
122 ))}
123 </>
124 );
125 case "example":
126 return (
127 <pre>
128 <code>{item.code}</code>
129 </pre>
130 );
131 }
132 })}
133 </>
134 );
135}
136
137function ExerciseEl(props: { exercise: Exercise }) {
138 const item = props.exercise;
139 return (
140 <>
141 <h2>{"Exercise: " + item.title}</h2>
142 <div dangerouslySetInnerHTML={{ __html: item.description.content }} />
143 <pre>
144 <code>{item.placeholderCode}</code>
145 </pre>
146 <h4>Solution</h4>
147 {item.explainedSolution.items.map((item) => (
148 <ExplainedSolution item={item} />
149 ))}
150 </>
151 );
152}
153
154function ExplainedSolution(props: { item: ExplainedSolutionItem }) {
155 const item = props.item;
156 return (
157 <div>
158 {item.type === "text-content" ? (
159 <div dangerouslySetInnerHTML={{ __html: item.content }}></div>
160 ) : (
161 <pre>
162 <code>{item.code}</code>
163 </pre>
164 )}
165 </div>
166 );
167}
168
169async function onload() {
170 const katas = await getAllKatas({ includeUnpublished: true });
171 const app = document.querySelector("#app") as HTMLDivElement;
172
173 function onRender(index: number) {
174 render(
175 <>
176 <Nav katas={katas} onnav={onNav} selected={index} />
177 <KataEl kata={katas[index]} />
178 </>,
179 app,
180 );
181 }
182
183 // Update the history and URL fragment if the user navigates katas
184 function onNav(index: number) {
185 history.pushState(null, "", "#" + katas[index].id);
186 onRender(index);
187 }
188
189 // Handle back/forward navigation
190 window.addEventListener("popstate", () => {
191 loadFromUrl();
192 });
193
194 function loadFromUrl() {
195 let kataIndex = 0;
196 if (window.location.hash) {
197 const kataId = window.location.hash.slice(1);
198 kataIndex = katas.findIndex((kata) => kata.id === kataId);
199 }
200 if (kataIndex < 0) kataIndex = 0;
201
202 // Allow the user to pass ?theme={dark|light} on the URL to set the theme
203 const urlParams = new URLSearchParams(window.location.search);
204 const theme = urlParams.get("theme");
205 if (theme) {
206 document.body.setAttribute("data-theme", theme);
207 }
208
209 onRender(kataIndex);
210 }
211
212 // Do initial load
213 loadFromUrl();
214}
215