qsharp-lang 1.0.23-dev → 1.0.25-dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,6 +19,12 @@ export function git_hash(): string;
19
19
  */
20
20
  export function get_qir(code: string): string;
21
21
  /**
22
+ * @param {string} code
23
+ * @param {string} params
24
+ * @returns {string}
25
+ */
26
+ export function get_estimates(code: string, params: string): string;
27
+ /**
22
28
  * @param {string} name
23
29
  * @returns {string | undefined}
24
30
  */
@@ -128,7 +134,7 @@ export interface IRelatedInformation {
128
134
  }
129
135
 
130
136
  export interface IWorkspaceConfiguration {
131
- targetProfile?: "full" | "base";
137
+ targetProfile?: TargetProfile;
132
138
  packageType?: "exe" | "lib";
133
139
  }
134
140
 
@@ -138,7 +144,7 @@ export interface ICompletionList {
138
144
 
139
145
  export interface ICompletionItem {
140
146
  label: string;
141
- kind: "function" | "interface" | "keyword" | "module" | "property";
147
+ kind: "function" | "interface" | "keyword" | "module" | "property" | "variable" | "typeParameter";
142
148
  sortText?: string;
143
149
  detail?: string;
144
150
  additionalTextEdits?: ITextEdit[];
@@ -191,6 +197,14 @@ export interface ICell {
191
197
  code: string;
192
198
  }
193
199
 
200
+ export interface INotebookMetadata {
201
+ targetProfile?: "unrestricted" | "base";
202
+ }
203
+
204
+
205
+ export type TargetProfile = "base" | "unrestricted";
206
+
207
+
194
208
  /**
195
209
  */
196
210
  export class DebugService {
@@ -272,9 +286,10 @@ export class LanguageService {
272
286
  close_document(uri: string): void;
273
287
  /**
274
288
  * @param {string} notebook_uri
289
+ * @param {INotebookMetadata} notebook_metadata
275
290
  * @param {(ICell)[]} cells
276
291
  */
277
- update_notebook_document(notebook_uri: string, cells: (ICell)[]): void;
292
+ update_notebook_document(notebook_uri: string, notebook_metadata: INotebookMetadata, cells: (ICell)[]): void;
278
293
  /**
279
294
  * @param {string} notebook_uri
280
295
  * @param {(string)[]} cell_uris
Binary file
@@ -19,6 +19,12 @@ export function git_hash(): string;
19
19
  */
20
20
  export function get_qir(code: string): string;
21
21
  /**
22
+ * @param {string} code
23
+ * @param {string} params
24
+ * @returns {string}
25
+ */
26
+ export function get_estimates(code: string, params: string): string;
27
+ /**
22
28
  * @param {string} name
23
29
  * @returns {string | undefined}
24
30
  */
@@ -128,7 +134,7 @@ export interface IRelatedInformation {
128
134
  }
129
135
 
130
136
  export interface IWorkspaceConfiguration {
131
- targetProfile?: "full" | "base";
137
+ targetProfile?: TargetProfile;
132
138
  packageType?: "exe" | "lib";
133
139
  }
134
140
 
@@ -138,7 +144,7 @@ export interface ICompletionList {
138
144
 
139
145
  export interface ICompletionItem {
140
146
  label: string;
141
- kind: "function" | "interface" | "keyword" | "module" | "property";
147
+ kind: "function" | "interface" | "keyword" | "module" | "property" | "variable" | "typeParameter";
142
148
  sortText?: string;
143
149
  detail?: string;
144
150
  additionalTextEdits?: ITextEdit[];
@@ -191,6 +197,14 @@ export interface ICell {
191
197
  code: string;
192
198
  }
193
199
 
200
+ export interface INotebookMetadata {
201
+ targetProfile?: "unrestricted" | "base";
202
+ }
203
+
204
+
205
+ export type TargetProfile = "base" | "unrestricted";
206
+
207
+
194
208
  /**
195
209
  */
196
210
  export class DebugService {
@@ -272,9 +286,10 @@ export class LanguageService {
272
286
  close_document(uri: string): void;
273
287
  /**
274
288
  * @param {string} notebook_uri
289
+ * @param {INotebookMetadata} notebook_metadata
275
290
  * @param {(ICell)[]} cells
276
291
  */
277
- update_notebook_document(notebook_uri: string, cells: (ICell)[]): void;
292
+ update_notebook_document(notebook_uri: string, notebook_metadata: INotebookMetadata, cells: (ICell)[]): void;
278
293
  /**
279
294
  * @param {string} notebook_uri
280
295
  * @param {(string)[]} cell_uris
@@ -346,7 +361,7 @@ export interface InitOutput {
346
361
  readonly languageservice_update_configuration: (a: number, b: number) => void;
347
362
  readonly languageservice_update_document: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
348
363
  readonly languageservice_close_document: (a: number, b: number, c: number) => void;
349
- readonly languageservice_update_notebook_document: (a: number, b: number, c: number, d: number, e: number) => void;
364
+ readonly languageservice_update_notebook_document: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
350
365
  readonly languageservice_close_notebook_document: (a: number, b: number, c: number, d: number, e: number) => void;
351
366
  readonly languageservice_get_completions: (a: number, b: number, c: number, d: number) => number;
352
367
  readonly languageservice_get_definition: (a: number, b: number, c: number, d: number) => number;
@@ -359,6 +374,7 @@ export interface InitOutput {
359
374
  readonly setLogLevel: (a: number) => void;
360
375
  readonly git_hash: (a: number) => void;
361
376
  readonly get_qir: (a: number, b: number, c: number) => void;
377
+ readonly get_estimates: (a: number, b: number, c: number, d: number, e: number) => void;
362
378
  readonly get_library_source_content: (a: number, b: number, c: number) => void;
363
379
  readonly get_hir: (a: number, b: number, c: number) => void;
364
380
  readonly run: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
@@ -309,6 +309,40 @@ export function get_qir(code) {
309
309
  }
310
310
  }
311
311
 
312
+ /**
313
+ * @param {string} code
314
+ * @param {string} params
315
+ * @returns {string}
316
+ */
317
+ export function get_estimates(code, params) {
318
+ let deferred4_0;
319
+ let deferred4_1;
320
+ try {
321
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
322
+ const ptr0 = passStringToWasm0(code, wasm.__wbindgen_export_0, wasm.__wbindgen_export_1);
323
+ const len0 = WASM_VECTOR_LEN;
324
+ const ptr1 = passStringToWasm0(params, wasm.__wbindgen_export_0, wasm.__wbindgen_export_1);
325
+ const len1 = WASM_VECTOR_LEN;
326
+ wasm.get_estimates(retptr, ptr0, len0, ptr1, len1);
327
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
328
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
329
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
330
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
331
+ var ptr3 = r0;
332
+ var len3 = r1;
333
+ if (r3) {
334
+ ptr3 = 0; len3 = 0;
335
+ throw takeObject(r2);
336
+ }
337
+ deferred4_0 = ptr3;
338
+ deferred4_1 = len3;
339
+ return getStringFromWasm0(ptr3, len3);
340
+ } finally {
341
+ wasm.__wbindgen_add_to_stack_pointer(16);
342
+ wasm.__wbindgen_export_2(deferred4_0, deferred4_1, 1);
343
+ }
344
+ }
345
+
312
346
  /**
313
347
  * @param {string} name
314
348
  * @returns {string | undefined}
@@ -652,14 +686,15 @@ export class LanguageService {
652
686
  }
653
687
  /**
654
688
  * @param {string} notebook_uri
689
+ * @param {INotebookMetadata} notebook_metadata
655
690
  * @param {(ICell)[]} cells
656
691
  */
657
- update_notebook_document(notebook_uri, cells) {
692
+ update_notebook_document(notebook_uri, notebook_metadata, cells) {
658
693
  const ptr0 = passStringToWasm0(notebook_uri, wasm.__wbindgen_export_0, wasm.__wbindgen_export_1);
659
694
  const len0 = WASM_VECTOR_LEN;
660
695
  const ptr1 = passArrayJsValueToWasm0(cells, wasm.__wbindgen_export_0);
661
696
  const len1 = WASM_VECTOR_LEN;
662
- wasm.languageservice_update_notebook_document(this.__wbg_ptr, ptr0, len0, ptr1, len1);
697
+ wasm.languageservice_update_notebook_document(this.__wbg_ptr, ptr0, len0, addHeapObject(notebook_metadata), ptr1, len1);
663
698
  }
664
699
  /**
665
700
  * @param {string} notebook_uri
Binary file
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "qsharp-lang",
3
3
  "description": "qsharp language package for quantum development",
4
- "version": "1.0.23-dev",
4
+ "version": "1.0.25-dev",
5
5
  "license": "MIT",
6
6
  "engines": {
7
7
  "node": ">=16.17.0"
@@ -19,7 +19,8 @@
19
19
  },
20
20
  "./compiler-worker": "./dist/compiler/worker-browser.js",
21
21
  "./language-service-worker": "./dist/language-service/worker-browser.js",
22
- "./debug-service-worker": "./dist/debug-service/worker-browser.js"
22
+ "./debug-service-worker": "./dist/debug-service/worker-browser.js",
23
+ "./ux": "./ux/index.ts"
23
24
  },
24
25
  "scripts": {
25
26
  "build": "npm run generate && npm run build:tsc",
@@ -31,6 +32,7 @@
31
32
  "type": "module",
32
33
  "files": [
33
34
  "dist",
34
- "lib"
35
+ "lib",
36
+ "ux"
35
37
  ]
36
38
  }
package/ux/README.md ADDED
@@ -0,0 +1,4 @@
1
+ # qsharp-lang/ux
2
+
3
+ This directory contains files in source form to be included in other projects
4
+ to provide UI components. See `vscode/src/webview/webview.tsx` for an example.
@@ -0,0 +1,438 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ import { useRef, useState } from "preact/hooks";
5
+
6
+ const enablePanning = false;
7
+ const altKeyPans = true;
8
+
9
+ const menuItems = [
10
+ {
11
+ category: "itemCount",
12
+ options: ["Show all", "Top 10", "Top 25"],
13
+ },
14
+ {
15
+ category: "sortOrder",
16
+ options: ["Sort a-z", "High to low", "Low to high"],
17
+ },
18
+ {
19
+ category: "labels",
20
+ options: ["Raw labels", "Ket labels", "No labels"],
21
+ },
22
+ ];
23
+ const maxMenuOptions = 3;
24
+ const defaultMenuSelection: { [idx: string]: number } = {
25
+ itemCount: 0,
26
+ sortOrder: 0,
27
+ labels: 0,
28
+ };
29
+
30
+ const reKetResult = /^\[(?:(Zero|One), *)*(Zero|One)\]$/;
31
+ function resultToKet(result: string): string {
32
+ if (typeof result !== "string") return "ERROR";
33
+
34
+ if (reKetResult.test(result)) {
35
+ // The result is a simple array of Zero and One
36
+ // The below will return an array of "Zero" or "One" in the order found
37
+ const matches = result.match(/(One|Zero)/g);
38
+ matches?.reverse();
39
+ let ket = "|";
40
+ matches?.forEach((digit) => (ket += digit == "One" ? "1" : "0"));
41
+ ket += "⟩";
42
+ return ket;
43
+ } else {
44
+ return result;
45
+ }
46
+ }
47
+
48
+ export function Histogram(props: {
49
+ shotCount: number;
50
+ data: Map<string, number>;
51
+ filter: string;
52
+ onFilter: (filter: string) => void;
53
+ shotsHeader: boolean;
54
+ }) {
55
+ const [hoverLabel, setHoverLabel] = useState("");
56
+ const [scale, setScale] = useState({ zoom: 1.0, offset: 1.0 });
57
+ const [menuSelection, setMenuSelection] = useState(defaultMenuSelection);
58
+
59
+ const gMenu = useRef<SVGGElement>(null);
60
+ const gInfo = useRef<SVGGElement>(null);
61
+
62
+ let maxItemsToShow = 0; // All
63
+ switch (menuSelection["itemCount"]) {
64
+ case 1:
65
+ maxItemsToShow = 10;
66
+ break;
67
+ case 2:
68
+ maxItemsToShow = 25;
69
+ break;
70
+ }
71
+ const showKetLabels = menuSelection["labels"] === 1;
72
+
73
+ const bucketArray = [...props.data];
74
+
75
+ // Calculate bucket percentages before truncating for display
76
+ let totalAllBuckets = 0;
77
+ let sizeBiggestBucket = 0;
78
+ bucketArray.forEach((x) => {
79
+ totalAllBuckets += x[1];
80
+ sizeBiggestBucket = Math.max(x[1], sizeBiggestBucket);
81
+ });
82
+
83
+ let histogramLabel = `${bucketArray.length} unique results`;
84
+ if (maxItemsToShow > 0) {
85
+ // Sort from high to low then take the first n
86
+ bucketArray.sort((a, b) => (a[1] < b[1] ? 1 : -1));
87
+ if (bucketArray.length > maxItemsToShow) {
88
+ histogramLabel = `Top ${maxItemsToShow} of ${histogramLabel}`;
89
+ bucketArray.length = maxItemsToShow;
90
+ }
91
+ }
92
+ if (props.filter) {
93
+ histogramLabel += `. Shot filter: ${
94
+ showKetLabels ? resultToKet(props.filter) : props.filter
95
+ }`;
96
+ }
97
+
98
+ bucketArray.sort((a, b) => {
99
+ // If they can be converted to numbers, then sort as numbers, else lexically
100
+ const ax = Number(a[0]);
101
+ const bx = Number(b[0]);
102
+ switch (menuSelection["sortOrder"]) {
103
+ case 1: // high-to-low
104
+ return a[1] < b[1] ? 1 : -1;
105
+ break;
106
+ case 2: // low-to-high
107
+ return a[1] > b[1] ? 1 : -1;
108
+ break;
109
+ default: // a-z
110
+ if (!isNaN(ax) && !isNaN(bx)) return ax < bx ? -1 : 1;
111
+ return a[0] < b[0] ? -1 : 1;
112
+ break;
113
+ }
114
+ });
115
+
116
+ function onMouseOverRect(evt: MouseEvent) {
117
+ const target = evt.target as SVGRectElement;
118
+ const title = target.querySelector("title")?.textContent;
119
+ setHoverLabel(title || "");
120
+ }
121
+
122
+ function onMouseOutRect() {
123
+ setHoverLabel("");
124
+ }
125
+
126
+ function onClickRect(evt: MouseEvent) {
127
+ const targetElem = evt.target as SVGRectElement;
128
+ const rawLabel = targetElem.getAttribute("data-raw-label");
129
+
130
+ if (rawLabel === props.filter) {
131
+ // Clicked the already selected bar. Clear the filter
132
+ props.onFilter("");
133
+ } else {
134
+ props.onFilter(rawLabel || "");
135
+ }
136
+ }
137
+
138
+ function toggleMenu() {
139
+ if (!gMenu.current) return;
140
+ if (gMenu.current.style.display === "inline") {
141
+ gMenu.current.style.display = "none";
142
+ } else {
143
+ gMenu.current.style.display = "inline";
144
+ if (gInfo.current) gInfo.current.style.display = "none";
145
+ }
146
+ }
147
+
148
+ function menuClicked(category: string, idx: number) {
149
+ if (!gMenu.current) return;
150
+ const newMenuSelection = { ...menuSelection };
151
+ newMenuSelection[category] = idx;
152
+ setMenuSelection(newMenuSelection);
153
+ if (category === "itemCount") {
154
+ setScale({ zoom: 1, offset: 1 });
155
+ }
156
+ gMenu.current.style.display = "none";
157
+ }
158
+
159
+ function toggleInfo() {
160
+ if (!gInfo.current) return;
161
+
162
+ gInfo.current.style.display === "inline"
163
+ ? (gInfo.current.style.display = "none")
164
+ : (gInfo.current.style.display = "inline");
165
+ }
166
+
167
+ // Each menu item has a width of 32px and a height of 10px
168
+ // Menu items are 38px apart on the x-axis, and 11px on the y-axis.
169
+ const menuItemWidth = 38;
170
+ const menuItemHeight = 11;
171
+ const menuBoxWidth = menuItems.length * menuItemWidth - 2;
172
+ const menuBoxHeight = maxMenuOptions * menuItemHeight + 3;
173
+
174
+ const barAreaWidth = 163;
175
+ const barAreaHeight = 72;
176
+ const fontOffset = 1.2;
177
+
178
+ // Scale the below for when zoomed
179
+ const barBoxWidth = (barAreaWidth * scale.zoom) / bucketArray.length;
180
+ const barPaddingPercent = 0.1; // 10%
181
+ const barPaddingSize = barBoxWidth * barPaddingPercent;
182
+ const barFillWidth = barBoxWidth - 2 * barPaddingSize;
183
+ const showLabels = barBoxWidth > 5 && menuSelection["labels"] !== 2;
184
+
185
+ function onWheel(e: WheelEvent): void {
186
+ e.preventDefault();
187
+
188
+ // currentTarget is the element the listener is attached to, the main svg
189
+ // element in this case.
190
+ const svgElem = e.currentTarget as SVGSVGElement;
191
+
192
+ // Below gets the mouse location in the svg element coordinates. This stays
193
+ // consistent while the scroll is occuring (i.e. it is the point the mouse
194
+ // was at when scrolling started).
195
+ const mousePoint = new DOMPoint(e.clientX, e.clientY).matrixTransform(
196
+ svgElem.getScreenCTM()?.inverse(),
197
+ );
198
+
199
+ /*
200
+ While zooming, we want is to track the point the mouse is at when scrolling, and pin
201
+ that location on the screen. That means adjusting the scroll offset.
202
+
203
+ SVG translation is used to pan left and right, but zooming is done manually (making the
204
+ bars wider or thinner) to keep the fonts from getting streched, which occurs with scaling.
205
+
206
+ deltaX and deltaY do not accumulate across events, they are a new delta each time.
207
+ */
208
+
209
+ let newScrollOffset = scale.offset;
210
+ let newZoom = scale.zoom;
211
+
212
+ // *** First handle any zooming ***
213
+ if (!altKeyPans || !e.altKey) {
214
+ newZoom = scale.zoom + e.deltaY * 0.05;
215
+ newZoom = Math.min(Math.max(1, newZoom), 50);
216
+
217
+ // On zooming in, need to shift left to maintain mouse point, and vice verca.
218
+ const oldChartWidth = barAreaWidth * scale.zoom;
219
+ const mousePointOnChart = 0 - scale.offset + mousePoint.x;
220
+ const percentRightOnChart = mousePointOnChart / oldChartWidth;
221
+ const chartWidthGrowth =
222
+ newZoom * barAreaWidth - scale.zoom * barAreaWidth;
223
+ const shiftLeftAdjust = percentRightOnChart * chartWidthGrowth;
224
+ newScrollOffset = scale.offset - shiftLeftAdjust;
225
+ }
226
+
227
+ // *** Then handle any panning ***
228
+ if (enablePanning) {
229
+ newScrollOffset -= e.deltaX;
230
+ }
231
+ if (!enablePanning && altKeyPans && e.altKey) {
232
+ newScrollOffset -= e.deltaY;
233
+ }
234
+
235
+ // Don't allow offset > 1 (scrolls the first bar right of the left edge of the area)
236
+ // Don't allow for less than 0 - barwidths + screen width (scrolls last bar left of the right edge)
237
+ const maxScrollRight = 1 - (barAreaWidth * newZoom - barAreaWidth);
238
+ const boundScrollOffset = Math.min(
239
+ Math.max(newScrollOffset, maxScrollRight),
240
+ 1,
241
+ );
242
+
243
+ setScale({ zoom: newZoom, offset: boundScrollOffset });
244
+ }
245
+
246
+ return (
247
+ <>
248
+ {props.shotsHeader ? (
249
+ <h4 style="margin: 8px 0px">Total shots: {props.shotCount}</h4>
250
+ ) : null}
251
+ <svg class="histogram" viewBox="0 0 165 100" onWheel={onWheel}>
252
+ <g transform={`translate(${scale.offset},4)`}>
253
+ {bucketArray.map((entry, idx) => {
254
+ const label = showKetLabels ? resultToKet(entry[0]) : entry[0];
255
+
256
+ const height = barAreaHeight * (entry[1] / sizeBiggestBucket);
257
+ const x = barBoxWidth * idx + barPaddingSize;
258
+ const labelX = barBoxWidth * idx + barBoxWidth / 2 - fontOffset;
259
+ const y = barAreaHeight + 15 - height;
260
+ const barLabel = `${label} at ${(
261
+ (entry[1] / totalAllBuckets) *
262
+ 100
263
+ ).toFixed(2)}%`;
264
+ let barClass = "bar";
265
+
266
+ if (entry[0] === props.filter) {
267
+ barClass += " bar-selected";
268
+ }
269
+
270
+ return (
271
+ <>
272
+ <rect
273
+ class={barClass}
274
+ x={x}
275
+ y={y}
276
+ width={barFillWidth}
277
+ height={height}
278
+ onMouseOver={onMouseOverRect}
279
+ onMouseOut={onMouseOutRect}
280
+ onClick={onClickRect}
281
+ data-raw-label={entry[0]}
282
+ >
283
+ <title>{barLabel}</title>
284
+ </rect>
285
+ {
286
+ <text
287
+ class="bar-label"
288
+ x={labelX}
289
+ y="85"
290
+ visibility={showLabels ? "visible" : "hidden"}
291
+ transform={`rotate(90, ${labelX}, 85)`}
292
+ >
293
+ {label}
294
+ </text>
295
+ }
296
+ </>
297
+ );
298
+ })}
299
+ </g>
300
+
301
+ <text class="histo-label" x="2" y="97">
302
+ {histogramLabel}
303
+ </text>
304
+ <text class="hover-text" x="85" y="6">
305
+ {hoverLabel}
306
+ </text>
307
+
308
+ {/* The settings icon */}
309
+ <g
310
+ class="menu-icon"
311
+ transform="translate(2, 2) scale(0.3 0.3)"
312
+ onClick={toggleMenu}
313
+ >
314
+ <rect width="24" height="24" fill="white" stroke-widths="0.5"></rect>
315
+ <path
316
+ d="M3 5 H21 M3 12 H21 M3 19 H21"
317
+ stroke-width="1.75"
318
+ stroke-linecap="round"
319
+ />
320
+ <rect x="6" y="3" width="4" height="4" rx="1" stroke-width="1.5" />
321
+ <rect x="15" y="10" width="4" height="4" rx="1" stroke-width="1.5" />
322
+ <rect x="9" y="17" width="4" height="4" rx="1" stroke-width="1.5" />
323
+ </g>
324
+
325
+ {/* The info icon */}
326
+ <g
327
+ class="menu-icon"
328
+ transform="translate(156, 2) scale(0.3 0.3)"
329
+ onClick={toggleInfo}
330
+ >
331
+ <rect width="24" height="24" stroke-width="0"></rect>
332
+ <circle cx="12" cy="13" r="10" stroke-width="1.5" />
333
+ <path
334
+ stroke-width="2.5"
335
+ stroke-linecap="round"
336
+ d="M12 8 V8 M12 12.5 V18"
337
+ />
338
+ </g>
339
+
340
+ {/* The menu box */}
341
+ <g
342
+ id="menu"
343
+ ref={gMenu}
344
+ transform="translate(8, 2)"
345
+ style="display: none;"
346
+ >
347
+ <rect
348
+ x="0"
349
+ y="0"
350
+ rx="2"
351
+ width={menuBoxWidth}
352
+ height={menuBoxHeight}
353
+ class="menu-box"
354
+ ></rect>
355
+
356
+ {
357
+ // Menu items
358
+ menuItems.map((item, col) => {
359
+ return item.options.map((option, row) => {
360
+ let classList = "menu-item";
361
+ if (menuSelection[item.category] === row)
362
+ classList += " menu-selected";
363
+ return (
364
+ <>
365
+ <rect
366
+ x={2 + col * menuItemWidth}
367
+ y={2 + row * menuItemHeight}
368
+ rx="1"
369
+ class={classList}
370
+ onClick={() => menuClicked(item.category, row)}
371
+ ></rect>
372
+ <text
373
+ x={5 + col * menuItemWidth}
374
+ y={9 + row * menuItemHeight}
375
+ class="menu-text"
376
+ >
377
+ {option}
378
+ </text>
379
+ </>
380
+ );
381
+ });
382
+ })
383
+ }
384
+ {
385
+ // Column separators
386
+ menuItems.map((item, idx) => {
387
+ return idx >= menuItems.length - 1 ? null : (
388
+ <line
389
+ class="menu-separator"
390
+ x1={37 + idx * menuItemWidth}
391
+ y1="2"
392
+ x2={37 + idx * menuItemWidth}
393
+ y2={maxMenuOptions * menuItemHeight + 1}
394
+ ></line>
395
+ );
396
+ })
397
+ }
398
+ </g>
399
+
400
+ {/* The info box */}
401
+ <g ref={gInfo} style="display: none;">
402
+ <rect
403
+ width="155"
404
+ height="76"
405
+ rx="5"
406
+ x="5"
407
+ y="6"
408
+ class="help-info"
409
+ onClick={toggleInfo}
410
+ />
411
+ <text y="6" class="help-info-text">
412
+ <tspan x="10" dy="10">
413
+ This histogram shows the frequency of unique 'shot' results.
414
+ </tspan>
415
+ <tspan x="10" dy="10">
416
+ Click the top-left 'settings' icon for display options.
417
+ </tspan>
418
+ <tspan x="10" dy="10">
419
+ You can zoom the chart using the mouse scroll wheel.
420
+ </tspan>
421
+ <tspan x="10" dy="7">
422
+ (Or using a trackpad gesture).
423
+ </tspan>
424
+ <tspan x="10" dy="10">
425
+ When zoomed, to pan left &amp; right, press 'Alt' while scrolling.
426
+ </tspan>
427
+ <tspan x="10" dy="10">
428
+ Click on a bar to filter the shot details to that result.
429
+ </tspan>
430
+ <tspan x="10" dy="12">
431
+ Click anywhere in this box to dismiss it.
432
+ </tspan>
433
+ </text>
434
+ </g>
435
+ </svg>
436
+ </>
437
+ );
438
+ }
package/ux/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ // By importing the CSS here, esbuild will by default bundle it up and copy it
5
+ // to a CSS file adjacent to the JS bundle and with the same name.
6
+ import "./qsharp-ux.css";
7
+
8
+ export { Histogram } from "./histogram.js";
9
+ export { ReTable, type ReData } from "./reTable.js";
10
+ export { SpaceChart } from "./spaceChart.js";
11
+ export { ResultsTable, type CellValue } from "./resultsTable.js";