microsoft/qdk
Publicmirrored from https://github.com/microsoft/qdkAvailable
source/vscode/src/webview/webview.tsx
236lines · modecode
| 1 | // Copyright (c) Microsoft Corporation. |
| 2 | // Licensed under the MIT License. |
| 3 | |
| 4 | /// <reference types="@types/vscode-webview"/> |
| 5 | |
| 6 | const vscodeApi = acquireVsCodeApi(); |
| 7 | |
| 8 | import { render } from "preact"; |
| 9 | import { |
| 10 | CircuitPanel, |
| 11 | CircuitProps, |
| 12 | EstimatesPanel, |
| 13 | Histogram, |
| 14 | setRenderer, |
| 15 | type ReData, |
| 16 | } from "qsharp-lang/ux"; |
| 17 | import { HelpPage } from "./help"; |
| 18 | import { DocumentationView, IDocFile } from "./docview"; |
| 19 | import "./webview.css"; |
| 20 | |
| 21 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment |
| 22 | // @ts-ignore - there are no types for this |
| 23 | import mk from "@vscode/markdown-it-katex"; |
| 24 | import markdownIt from "markdown-it"; |
| 25 | import { setThemeStylesheet } from "./theme"; |
| 26 | const md = markdownIt("commonmark"); |
| 27 | md.use(mk, { |
| 28 | enableMathBlockInHtml: true, |
| 29 | enableMathInlineInHtml: true, |
| 30 | }); |
| 31 | setRenderer((input: string) => md.render(input)); |
| 32 | |
| 33 | window.addEventListener("message", onMessage); |
| 34 | window.addEventListener("load", main); |
| 35 | |
| 36 | type HistogramState = { |
| 37 | viewType: "histogram"; |
| 38 | panelId: string; |
| 39 | buckets: Array<[string, number]>; |
| 40 | shotCount: number; |
| 41 | suppressSettings?: boolean; |
| 42 | }; |
| 43 | |
| 44 | type EstimatesState = { |
| 45 | viewType: "estimates"; |
| 46 | estimatesData: { |
| 47 | calculating: boolean; |
| 48 | estimates: ReData[]; |
| 49 | }; |
| 50 | }; |
| 51 | |
| 52 | type CircuitState = { |
| 53 | viewType: "circuit"; |
| 54 | panelId: string; |
| 55 | props: CircuitProps; |
| 56 | }; |
| 57 | |
| 58 | type DocumentationState = { |
| 59 | viewType: "documentation"; |
| 60 | fragmentsToRender: IDocFile[]; |
| 61 | projectName: string; |
| 62 | }; |
| 63 | |
| 64 | type State = |
| 65 | | { viewType: "loading"; panelId: string } |
| 66 | | { viewType: "help" } |
| 67 | | HistogramState |
| 68 | | EstimatesState |
| 69 | | CircuitState |
| 70 | | DocumentationState; |
| 71 | const loadingState: State = { viewType: "loading", panelId: "" }; |
| 72 | const helpState: State = { viewType: "help" }; |
| 73 | let state: State = loadingState; |
| 74 | |
| 75 | function main() { |
| 76 | state = (vscodeApi.getState() as any) || loadingState; |
| 77 | render(<App state={state} />, document.body); |
| 78 | setThemeStylesheet(); |
| 79 | vscodeApi.postMessage({ command: "ready" }); |
| 80 | } |
| 81 | |
| 82 | function onMessage(event: any) { |
| 83 | const message = event.data; |
| 84 | if (!message?.command) { |
| 85 | console.error("Unknown message: ", message); |
| 86 | return; |
| 87 | } |
| 88 | switch (message.command) { |
| 89 | case "histogram": { |
| 90 | if (!message.buckets || typeof message.shotCount !== "number") { |
| 91 | console.error("No buckets in message: ", message); |
| 92 | return; |
| 93 | } |
| 94 | state = { |
| 95 | viewType: "histogram", |
| 96 | panelId: message.panelId, |
| 97 | buckets: message.buckets as Array<[string, number]>, |
| 98 | shotCount: message.shotCount, |
| 99 | suppressSettings: message.suppressSettings, |
| 100 | }; |
| 101 | break; |
| 102 | } |
| 103 | case "estimates": |
| 104 | { |
| 105 | const newState: EstimatesState = { |
| 106 | viewType: "estimates", |
| 107 | estimatesData: { |
| 108 | calculating: !!message.calculating, |
| 109 | estimates: [], |
| 110 | }, |
| 111 | }; |
| 112 | // Copy over any existing estimates |
| 113 | if ((state as EstimatesState).estimatesData?.estimates) { |
| 114 | newState.estimatesData.estimates.push( |
| 115 | ...(state as EstimatesState).estimatesData.estimates, |
| 116 | ); |
| 117 | } |
| 118 | // Append any new estimates |
| 119 | if (message.estimates) { |
| 120 | if (Array.isArray(message.estimates)) { |
| 121 | newState.estimatesData.estimates.push(...message.estimates); |
| 122 | } else { |
| 123 | newState.estimatesData.estimates.push(message.estimates); |
| 124 | } |
| 125 | } |
| 126 | state = newState; |
| 127 | } |
| 128 | break; |
| 129 | case "help": |
| 130 | state = helpState; |
| 131 | break; |
| 132 | case "circuit": |
| 133 | { |
| 134 | state = { |
| 135 | viewType: "circuit", |
| 136 | ...message, |
| 137 | }; |
| 138 | } |
| 139 | break; |
| 140 | case "showDocumentationCommand": |
| 141 | { |
| 142 | state = { |
| 143 | viewType: "documentation", |
| 144 | fragmentsToRender: message.fragmentsToRender, |
| 145 | projectName: message.projectName, |
| 146 | }; |
| 147 | } |
| 148 | break; |
| 149 | default: |
| 150 | console.error("Unknown command: ", message.command); |
| 151 | return; |
| 152 | } |
| 153 | |
| 154 | vscodeApi.setState(state); |
| 155 | render(<App state={state} />, document.body); |
| 156 | } |
| 157 | |
| 158 | function onRowDeleted(rowId: string) { |
| 159 | // Clone all the state to a new object |
| 160 | const newState: State = JSON.parse(JSON.stringify(state)); |
| 161 | |
| 162 | // Splice out the estimate that was deleted |
| 163 | const estimates = (newState as EstimatesState).estimatesData.estimates; |
| 164 | const index = estimates.findIndex( |
| 165 | (estimate) => estimate.jobParams.runName === rowId, |
| 166 | ); |
| 167 | if (index >= 0) { |
| 168 | estimates.splice(index, 1); |
| 169 | } |
| 170 | state = newState; |
| 171 | |
| 172 | vscodeApi.setState(state); |
| 173 | render(<App state={state} />, document.body); |
| 174 | } |
| 175 | |
| 176 | function App({ state }: { state: State }) { |
| 177 | const onFilter = () => undefined; |
| 178 | |
| 179 | switch (state.viewType) { |
| 180 | case "loading": |
| 181 | return <div>Loading...</div>; |
| 182 | case "histogram": |
| 183 | return ( |
| 184 | <> |
| 185 | <Histogram |
| 186 | data={new Map(state.buckets)} |
| 187 | shotCount={state.shotCount} |
| 188 | filter="" |
| 189 | onFilter={onFilter} |
| 190 | shotsHeader={true} |
| 191 | ></Histogram> |
| 192 | {state.suppressSettings ? null : ( |
| 193 | <p style="margin-top: 8px; font-size: 0.8em"> |
| 194 | Note: If a{" "} |
| 195 | <a href="vscode://settings/Q%23.simulation.pauliNoise"> |
| 196 | noise model |
| 197 | </a>{" "} |
| 198 | or any{" "} |
| 199 | <a href="vscode://settings/Q%23.simulation.qubitLoss"> |
| 200 | qubit loss |
| 201 | </a>{" "} |
| 202 | has been configured, this may impact results |
| 203 | </p> |
| 204 | )} |
| 205 | </> |
| 206 | ); |
| 207 | case "estimates": |
| 208 | return ( |
| 209 | <EstimatesPanel |
| 210 | calculating={state.estimatesData.calculating} |
| 211 | estimatesData={state.estimatesData.estimates} |
| 212 | onRowDeleted={onRowDeleted} |
| 213 | colors={[]} |
| 214 | runNames={[]} |
| 215 | /> |
| 216 | ); |
| 217 | case "circuit": |
| 218 | return <CircuitPanel {...state.props}></CircuitPanel>; |
| 219 | case "help": |
| 220 | return <HelpPage />; |
| 221 | case "documentation": |
| 222 | // Ideally we'd have this on all web views, but it makes the font a little |
| 223 | // too large in the others right now. Something to unify later. |
| 224 | document.body.classList.add("markdown-body"); |
| 225 | document.body.style.fontSize = "0.8em"; |
| 226 | return ( |
| 227 | <DocumentationView |
| 228 | fragmentsToRender={state.fragmentsToRender} |
| 229 | projectName={state.projectName} |
| 230 | /> |
| 231 | ); |
| 232 | default: |
| 233 | console.error("Unknown view type in state", state); |
| 234 | return <div>Loading error</div>; |
| 235 | } |
| 236 | } |
| 237 | |