microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.25.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/playground/src/kataViewer.tsx

235lines · modecode

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