diff --git a/source/npm/qsharp/package.json b/source/npm/qsharp/package.json index 46ba3ee1dc7..fb76ec65d18 100644 --- a/source/npm/qsharp/package.json +++ b/source/npm/qsharp/package.json @@ -24,6 +24,8 @@ "./katas-md": "./dist/katas-md.js", "./state-viz": "./ux/circuit-vis/state-viz/worker/index.ts", "./ux": "./ux/index.ts", + "./ux/bloch": "./ux/bloch/index.ts", + "./ux/chem": "./ux/chem/index.tsx", "./qdk-theme.css": "./ux/qdk-theme.css", "./rz-array.json": "./rz-array.json" }, diff --git a/source/npm/qsharp/ux/bloch/index.ts b/source/npm/qsharp/ux/bloch/index.ts new file mode 100644 index 00000000000..209269d344b --- /dev/null +++ b/source/npm/qsharp/ux/bloch/index.ts @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// Dedicated entry point for the Bloch sphere widget. Keeping this behind its +// own package subpath (qsharp-lang/ux/bloch) isolates three.js to a lazily +// loaded chunk instead of bundling it into the main ux barrel. +export { BlochSphere, type BlochSphereProps } from "./bloch.js"; diff --git a/source/npm/qsharp/ux/index.ts b/source/npm/qsharp/ux/index.ts index 709b29bb9f4..d30dd70ac6a 100644 --- a/source/npm/qsharp/ux/index.ts +++ b/source/npm/qsharp/ux/index.ts @@ -20,11 +20,9 @@ export { SpaceChart } from "./spaceChart.js"; export { ScatterChart } from "./scatterChart.js"; export { EstimatesOverview } from "./estimatesOverview.js"; export { EstimatesPanel } from "./estimatesPanel.js"; -export { BlochSphere } from "./bloch/bloch.js"; export { Circuit, CircuitPanel } from "./circuit.js"; export { setRenderer, Markdown } from "./renderers.js"; export { Atoms, type ZoneLayout, type TraceData } from "./atoms/index.js"; -export { MoleculeViewer } from "./chem/index.js"; export { Entanglement, type EntanglementProps } from "./entanglement.js"; export { ensureTheme, diff --git a/source/playground/src/main.tsx b/source/playground/src/main.tsx index 804fa55c5ee..41a8362fff4 100644 --- a/source/playground/src/main.tsx +++ b/source/playground/src/main.tsx @@ -49,7 +49,8 @@ import { // Set up the Markdown renderer with KaTeX support import mk from "@vscode/markdown-it-katex"; import markdownIt from "markdown-it"; -import { setRenderer, BlochSphere } from "qsharp-lang/ux"; +import { setRenderer } from "qsharp-lang/ux"; +import { BlochSphere } from "qsharp-lang/ux/bloch"; const md = markdownIt("commonmark"); md.use((mk as any).default, { diff --git a/source/vscode/build.mjs b/source/vscode/build.mjs index 7fee3349eed..cead55a5365 100644 --- a/source/vscode/build.mjs +++ b/source/vscode/build.mjs @@ -11,7 +11,13 @@ import { build as esbuildBuild, context } from "esbuild"; const thisDir = dirname(fileURLToPath(import.meta.url)); const libsDir = join(thisDir, "..", "..", "node_modules"); -// ── Shared esbuild options ────────────────────────────────────────── +// Watch builds skip minification so rebuilds stay fast and stack traces stay +// readable during development. One-shot builds (CI and `build.py`) minify to +// keep the shipped webview/extension bundles small. Linked source maps are +// emitted either way, so minified output remains debuggable. +const isWatch = process.argv.includes("--watch"); + +// ── Shared esbuild options ────────────────────────────────────── /** @type {import("esbuild").BuildOptions} */ const commonBuildOptions = { @@ -20,6 +26,7 @@ const commonBuildOptions = { format: "cjs", target: ["es2022"], sourcemap: "linked", + minify: !isWatch, }; // ── Per-platform build options ────────────────────────────────────── @@ -32,7 +39,6 @@ const platformBuildOptions = { outbase: join(thisDir, "src"), outdir: join(thisDir, "out"), entryPoints: [ - join(thisDir, "src", "webview/webview.tsx"), join(thisDir, "src", "webview/editor.tsx"), join(thisDir, "src", "learning/webview/webview-client.tsx"), ], @@ -42,6 +48,25 @@ const platformBuildOptions = { }, // plugins added at build time (needs inlineStateComputeWorkerPlugin) }, + // The main webview bundle is built as ESM with code splitting enabled so + // that heavy, rarely-used dependencies (e.g. three.js used only by the + // Bloch sphere) are emitted as separate chunks that are loaded on demand + // via dynamic import(), rather than bloating the shared webview.js. + webview: { + ...commonBuildOptions, + format: "esm", + splitting: true, + platform: "browser", + outbase: join(thisDir, "src"), + outdir: join(thisDir, "out"), + chunkNames: "webview/chunks/[name]-[hash]", + entryPoints: [join(thisDir, "src", "webview/webview.tsx")], + define: { + "import.meta.url": "undefined", + __PLATFORM__: JSON.stringify("browser"), + }, + // plugins added at build time (needs inlineStateComputeWorkerPlugin) + }, browser: { ...commonBuildOptions, entryPoints: [ @@ -209,8 +234,8 @@ async function buildPlatform(platform) { const options = platformBuildOptions[platform]; if (!options) throw new Error(`Invalid platform: ${platform}`); - // UI build needs the inline worker plugin - if (platform === "ui") { + // UI builds need the inline worker plugin + if (platform === "ui" || platform === "webview") { options.plugins = [inlineStateComputeWorkerPlugin]; } @@ -272,8 +297,6 @@ export async function watchVsCode() { (async () => { const thisFilePath = resolve(fileURLToPath(import.meta.url)); if (thisFilePath === resolve(process.argv[1])) { - const isWatch = process.argv.includes("--watch"); - if (isWatch) { await watchVsCode(); } else { @@ -282,6 +305,7 @@ export async function watchVsCode() { await Promise.all([ buildPlatform("ui"), + buildPlatform("webview"), buildPlatform("browser"), buildPlatform("node"), buildPlatform("node-worker"), diff --git a/source/vscode/src/webview/webview.tsx b/source/vscode/src/webview/webview.tsx index 7f7f0d0f615..aba03cf0143 100644 --- a/source/vscode/src/webview/webview.tsx +++ b/source/vscode/src/webview/webview.tsx @@ -6,12 +6,12 @@ const vscodeApi = acquireVsCodeApi(); import { render } from "preact"; +import { lazy, Suspense } from "preact/compat"; import { CircuitPanel, CircuitProps, EstimatesPanel, Histogram, - BlochSphere, setRenderer, detectThemeChange, updateStyleSheetTheme, @@ -21,6 +21,13 @@ import { HelpPage } from "./help"; import { DocumentationView, IDocFile } from "./docview"; import "./webview.css"; +// The Bloch sphere pulls in three.js, which is large and only needed when a +// Bloch sphere view is actually opened. Load it lazily via a dynamic import so +// esbuild emits it (and three.js) as a separate chunk kept out of webview.js. +const BlochSphere = lazy(() => + import("qsharp-lang/ux/bloch").then((m) => ({ default: m.BlochSphere })), +); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - there are no types for this import mk from "@vscode/markdown-it-katex"; @@ -234,7 +241,11 @@ function App({ state }: { state: State }) { case "help": return ; case "bloch": - return ; + return ( + Loading...}> + + + ); case "documentation": // Ideally we'd have this on all web views, but it makes the font a little // too large in the others right now. Something to unify later. diff --git a/source/vscode/src/webviewPanel.ts b/source/vscode/src/webviewPanel.ts index 58ab0159754..dfda7a5e864 100644 --- a/source/vscode/src/webviewPanel.ts +++ b/source/vscode/src/webviewPanel.ts @@ -363,7 +363,7 @@ export class QSharpWebViewPanel { - + diff --git a/source/widgets/js/index.tsx b/source/widgets/js/index.tsx index 012fd0676dd..da8cdbe5fb5 100644 --- a/source/widgets/js/index.tsx +++ b/source/widgets/js/index.tsx @@ -15,10 +15,10 @@ import { Atoms, type ZoneLayout, type TraceData, - MoleculeViewer, Entanglement, type EntanglementProps, } from "qsharp-lang/ux"; +import { MoleculeViewer } from "qsharp-lang/ux/chem"; import markdownIt from "markdown-it"; import "./widgets.css";