@tetrascience-npm/tetrascience-react-ui 1.0.0-beta.140.1 → 1.0.0-beta.142.1

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.
package/README.md CHANGED
@@ -36,7 +36,7 @@ This library provides:
36
36
  > The `/server` utilities (JWT auth, provider helpers) require a running TDP instance of v4.x or later.
37
37
  > Browser support follows React 19's matrix (modern evergreen browsers).
38
38
  >
39
- > As of v1.0.0, heavy dependencies are **optional peer dependencies**: install `plotly.js-dist` if you use the chart components, `@streamdown/mermaid` / `@streamdown/math` if you use the AI markdown components, `@rdkit/rdkit` if you use `MoleculeStructure`, and the provider SDKs (`@aws-sdk/client-athena`, `@databricks/sql`, `snowflake-sdk`) only for the `/server` utilities you use. Apps that don't use these components don't need to install them.
39
+ > As of v1.0.0, heavy dependencies are **optional peer dependencies** see [Optional peer dependencies](#optional-peer-dependencies) below for what to install and when. Upgrading from v0.7.x? Chart components were renamed and four components were removed: read the [v0.7.x v1.0.0 migration guide](./MIGRATION.md#migrating-from-v07x-to-v100) first.
40
40
 
41
41
  ## Installation
42
42
 
@@ -44,6 +44,36 @@ This library provides:
44
44
  yarn add @tetrascience-npm/tetrascience-react-ui
45
45
  ```
46
46
 
47
+ ### Optional peer dependencies
48
+
49
+ The kit does not install heavy dependencies for you. Add only the ones your app uses:
50
+
51
+ | You use… | Install |
52
+ | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
53
+ | Any `charts/` component | `plotly.js-dist` |
54
+ | `MessageResponse` / `Reasoning` (AI markdown) | `@streamdown/math`, `@streamdown/mermaid` |
55
+ | `MoleculeStructure` | `@rdkit/rdkit` — **plus a served WASM, [see below](#moleculestructure-requires-a-served-rdkit-wasm)** |
56
+ | **Any import from the package root** | `@streamdown/math`, `@streamdown/mermaid` — see the caveat below |
57
+ | `/server` Athena / Snowflake / Databricks provider | `@aws-sdk/client-athena` / `snowflake-sdk` / `@databricks/sql` |
58
+
59
+ > **Root-entry imports pull in the streamdown peers whether or not you use them.**
60
+ > The AI markdown plugins are loaded through a dynamic import, but a dynamic-import target is still
61
+ > part of your bundler's module graph and its _named_ static imports must resolve. With
62
+ > `@streamdown/math` absent, a root-entry build fails even when your only kit import is `AreaPlot`:
63
+ >
64
+ > ```
65
+ > dist/components/ai/streamdown-plugins.js (2:9): "math" is not exported by
66
+ > "__vite-optional-peer-dep:@streamdown/math:@tetrascience-npm/tetrascience-react-ui"
67
+ > ```
68
+ >
69
+ > Install the two packages, or use [per-component imports](#per-component-imports), which avoid the
70
+ > barrel entirely. This fails at build time, so it can never reach production unnoticed.
71
+ > Tracked in [SW-2472](https://tetrascience.atlassian.net/browse/SW-2472).
72
+
73
+ A missing `plotly.js-dist` behaves differently: it does **not** fail the build under Vite/Rollup — it
74
+ resolves to an empty stub and the chart fails at runtime with a console error from the loader
75
+ (`Failed to load 'plotly.js-dist' …`). If your charts render blank after upgrading, check this first.
76
+
47
77
  ## Quick Start
48
78
 
49
79
  ```tsx
@@ -93,6 +123,12 @@ The subpath name always matches the component's directory/file under
93
123
  [Storybook](https://ts-lib-ui-kit-storybook.vercel.app/) sidebar for the
94
124
  exact name.
95
125
 
126
+ > **Known gap in v1.0.0:** `./ui/progress` and `./ui/snippet` resolve their _types_ but ship no
127
+ > runtime module, so importing either typechecks cleanly and then fails your build. Neither component
128
+ > is exported from the package root either, so nothing regressed — but the subpath makes them look
129
+ > available. Don't import them.
130
+ > Tracked in [SW-2472](https://tetrascience.atlassian.net/browse/SW-2472).
131
+
96
132
  ## Styling & CSS
97
133
 
98
134
  This library uses **Tailwind CSS 4** with design tokens defined as CSS custom properties (oklch color space). All CSS files are declared as [`sideEffects`](https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free) in `package.json`, so bundlers will preserve them while still tree-shaking unused JavaScript.
@@ -132,7 +168,34 @@ Accordion, Alert, AlertDialog, AspectRatio, Avatar, Badge, Breadcrumb, Button, B
132
168
 
133
169
  TetraScience-specific compositions built from UI primitives:
134
170
 
135
- AppHeader, AppLayout, AssistantModal, CodeScriptEditorButton, LaunchContent, Main, Navbar, ProcessFlow, ProtocolConfiguration, ProtocolYamlCard, PythonEditorModal, Sidebar, TdpLink, TdpSearch, TdpUrl
171
+ AssistantLayout, Chat, ConfirmDialog, DataAppShell (with PrimaryNav, SecondaryNav, RightPanel), EmptyState, FormPatterns, MoleculeStructure, PlateMapEditor, ProcessFlow, RichListItem, StatCard, TdpLink, TdpSearch, TdpUrl, TopBar, UserMenu
172
+
173
+ #### `MoleculeStructure` requires a served RDKit WASM
174
+
175
+ Installing `@rdkit/rdkit` is **not sufficient**. RDKit is a ~6.6 MB WebAssembly module that the
176
+ package does not place anywhere your app serves it, so the loader's fetch for `RDKit_minimal.wasm`
177
+ falls through to your dev server's SPA fallback and gets `index.html` back. The component then
178
+ renders its `errorContent` — by default **"Invalid structure"** — for a perfectly valid SMILES.
179
+
180
+ Point the loader at a served copy once, at app startup:
181
+
182
+ ```ts
183
+ import { configureRDKit } from "@tetrascience-npm/tetrascience-react-ui";
184
+
185
+ // Option A — let your bundler emit and fingerprint it (Vite):
186
+ import wasmSrc from "@rdkit/rdkit/dist/RDKit_minimal.wasm?url";
187
+ configureRDKit({ wasmSrc });
188
+
189
+ // Option B — copy node_modules/@rdkit/rdkit/dist/RDKit_minimal.wasm into public/
190
+ configureRDKit({ wasmSrc: "/RDKit_minimal.wasm" });
191
+ ```
192
+
193
+ To confirm it worked, the request for `RDKit_minimal.wasm` should return `Content-Type:
194
+ application/wasm` at ~6.9 MB — not `text/html` at a few hundred bytes.
195
+
196
+ > `errorContent` currently covers both an invalid SMILES **and** a failed RDKit load, so a molecule
197
+ > you trust showing as invalid almost always means the WASM isn't being served. Splitting the two
198
+ > messages is tracked in [SW-2472](https://tetrascience.atlassian.net/browse/SW-2472).
136
199
 
137
200
  #### ProcessFlow
138
201
 
@@ -400,7 +463,7 @@ import type { ButtonProps, BarChartProps, BarDataSeries } from "@tetrascience-np
400
463
 
401
464
  ## Testing your app with Jest
402
465
 
403
- The kit ships dual ESM + CJS output, so Jest's CommonJS runtime can load every component directly — no need to mock the package. What Jest *can't* load are a few third-party dependencies that publish ESM-only (the streamdown/markdown stack, shiki, `use-stick-to-bottom`, `react-resizable-panels`) and optional peers you may not have installed (`plotly.js-dist`, `@rdkit/rdkit`). The kit ships a single setup file that stubs exactly those, plus the jsdom shims Radix-based components need (ResizeObserver, matchMedia, pointer capture, …).
466
+ The kit ships dual ESM + CJS output, so Jest's CommonJS runtime can load every component directly — no need to mock the package. What Jest _can't_ load are a few third-party dependencies that publish ESM-only (the streamdown/markdown stack, shiki, `use-stick-to-bottom`, `react-resizable-panels`) and optional peers you may not have installed (`plotly.js-dist`, `@rdkit/rdkit`). The kit ships a single setup file that stubs exactly those, plus the jsdom shims Radix-based components need (ResizeObserver, matchMedia, pointer capture, …).
404
467
 
405
468
  Add one line to `jest.config.js`:
406
469
 
@@ -442,7 +505,8 @@ Visit <http://localhost:6006>.
442
505
 
443
506
  - [Storybook – Live Component Demos](https://ts-lib-ui-kit-storybook.vercel.app/) - Browse all components with interactive examples
444
507
  - [NPM Package](https://www.npmjs.com/package/@tetrascience-npm/tetrascience-react-ui) - Installation and version info
445
- - [Migration Guide](./MIGRATION.md) - Migrating from the old atom/molecule/organism architecture
508
+ - [Migration Guide](./MIGRATION.md#migrating-from-v07x-to-v100) - Upgrading from v0.7.x to v1.0.0 (chart renames, removed components, optional peers)
509
+ - [Changelog](./CHANGELOG.md) - What changed in each release, including v1.0.0's breaking changes
446
510
  - [Theming Guide](./THEMING.md) - Customise the design system
447
511
  - [Contributing](./CONTRIBUTING.md#development-setup) - Clone the repo and run `yarn storybook`
448
512
 
@@ -455,10 +519,10 @@ reducing hallucinated component APIs when scaffolding a data app.
455
519
 
456
520
  There are two endpoints. Pick whichever fits; you can add both.
457
521
 
458
- | Endpoint | URL | Tools |
459
- | --- | --- | --- |
460
- | **Deployed** (no local checkout needed) | `https://ts-lib-ui-kit-storybook.vercel.app/api/mcp` | docs: `list_components`, `get_component`, `search_components` |
461
- | **Local** (needs `yarn storybook` running) | `http://localhost:6006/mcp` | full set: docs **+** write/preview/test stories |
522
+ | Endpoint | URL | Tools |
523
+ | ------------------------------------------ | ---------------------------------------------------- | ------------------------------------------------------------- |
524
+ | **Deployed** (no local checkout needed) | `https://ts-lib-ui-kit-storybook.vercel.app/api/mcp` | docs: `list_components`, `get_component`, `search_components` |
525
+ | **Local** (needs `yarn storybook` running) | `http://localhost:6006/mcp` | full set: docs **+** write/preview/test stories |
462
526
 
463
527
  ### Add the connection
464
528
 
@@ -497,8 +561,8 @@ client's MCP config (e.g. Cursor's `.cursor/mcp.json`, or Claude Desktop's
497
561
  npx mcp-add --type http --url "https://ts-lib-ui-kit-storybook.vercel.app/api/mcp"
498
562
  ```
499
563
 
500
- Then ask your agent something like *"using the ts-ui-kit MCP, list the available
501
- components"* or *"build a form using ts-ui-kit primitives"* to confirm it's wired
564
+ Then ask your agent something like _"using the ts-ui-kit MCP, list the available
565
+ components"_ or _"build a form using ts-ui-kit primitives"_ to confirm it's wired
502
566
  up.
503
567
 
504
568
  ## Tech Stack
@@ -1 +1 @@
1
- {"version":3,"file":"plotly-loader.cjs","sources":["../../../src/components/charts/plotly-loader.ts"],"sourcesContent":["/**\n * Lazy Plotly loader (SW-2007).\n *\n * The kit ships preserved ES modules with all dependencies externalized, so a\n * static `import Plotly from \"plotly.js-dist\"` in any chart lands the full\n * ~4.7 MB Plotly dist in the consumer's main chunk — before any chart renders.\n * Loading it through this module's dynamic `import()` instead lets consumer\n * bundlers split Plotly into its own chunk, fetched only when a chart mounts.\n *\n * Chart components must call `loadPlotly()` before drawing. Code that only\n * runs after a plot exists (hover handlers, resize relayouts, unmount purges)\n * can use the synchronous `getLoadedPlotly()`.\n *\n * `plotly.js-dist` is an *optional* peer dependency (SW-2007): consumers that\n * use the kit's charts must add it to their own app; consumers that don't\n * never install its ~4.7 MB. A missing peer surfaces at build time as an\n * unresolved-import error from the consumer's bundler; the runtime guard below\n * covers the rarer case where the import resolves but fails to evaluate.\n */\n\ntype PlotlyModule = typeof import(\"plotly.js-dist\");\n\nconst MISSING_PLOTLY_MESSAGE =\n \"Failed to load 'plotly.js-dist'. It is an optional peer dependency of \" +\n \"@tetrascience-npm/tetrascience-react-ui required by the chart components — \" +\n \"install it in your app (e.g. `yarn add plotly.js-dist`).\";\n\nlet loadedPlotly: PlotlyModule | null = null;\nlet plotlyPromise: Promise<PlotlyModule> | null = null;\n\n/** Import Plotly on first use; subsequent calls reuse the same promise. */\nexport function loadPlotly(): Promise<PlotlyModule> {\n plotlyPromise ??= import(\"plotly.js-dist\")\n .then((mod) => {\n // plotly.js-dist is CJS; depending on the consumer's bundler interop the\n // API surface is either the namespace itself or its `default` export.\n const withDefault = mod as PlotlyModule & { default?: PlotlyModule };\n loadedPlotly = withDefault.default ?? withDefault;\n return loadedPlotly;\n })\n .catch((error: unknown) => {\n // Reset so a later mount can retry, and rethrow with install guidance.\n plotlyPromise = null;\n console.error(MISSING_PLOTLY_MESSAGE, error);\n throw new Error(MISSING_PLOTLY_MESSAGE);\n });\n return plotlyPromise;\n}\n\n/**\n * Synchronous access to the loaded Plotly module. Only valid after\n * `loadPlotly()` has resolved — i.e. in code paths that can only run once a\n * plot has been drawn (event handlers, cleanups, resize effects).\n */\nexport function getLoadedPlotly(): PlotlyModule {\n if (!loadedPlotly) {\n throw new Error(\n \"Plotly accessed before loadPlotly() resolved. Call loadPlotly() and draw the plot first.\",\n );\n }\n return loadedPlotly;\n}\n\nexport type { PlotlyModule };\n"],"names":["MISSING_PLOTLY_MESSAGE","loadedPlotly","plotlyPromise","loadPlotly","_interopNamespaceDefault","mod","withDefault","error","getLoadedPlotly"],"mappings":"6VAsBA,MAAMA,EACJ,4MAIF,IAAIC,EAAoC,KACpCC,EAA8C,KAG3C,SAASC,GAAoC,CAClD,OAAAD,IAAkB,QAAA,QAAA,EAAA,KAAA,IAAAE,EAAA,QAAO,gBAAgB,CAAA,CAAA,EACtC,KAAMC,GAAQ,CAGb,MAAMC,EAAcD,EACpB,OAAAJ,EAAeK,EAAY,SAAWA,EAC/BL,CACT,CAAC,EACA,MAAOM,GAAmB,CAEzB,MAAAL,EAAgB,KAChB,QAAQ,MAAMF,EAAwBO,CAAK,EACrC,IAAI,MAAMP,CAAsB,CACxC,CAAC,EACIE,CACT,CAOO,SAASM,GAAgC,CAC9C,GAAI,CAACP,EACH,MAAM,IAAI,MACR,0FAAA,EAGJ,OAAOA,CACT"}
1
+ {"version":3,"file":"plotly-loader.cjs","sources":["../../../src/components/charts/plotly-loader.ts"],"sourcesContent":["/**\n * Lazy Plotly loader (SW-2007).\n *\n * The kit ships preserved ES modules with all dependencies externalized, so a\n * static `import Plotly from \"plotly.js-dist\"` in any chart lands the full\n * ~4.7 MB Plotly dist in the consumer's main chunk — before any chart renders.\n * Loading it through this module's dynamic `import()` instead lets consumer\n * bundlers split Plotly into its own chunk, fetched only when a chart mounts.\n *\n * Chart components must call `loadPlotly()` before drawing. Code that only\n * runs after a plot exists (hover handlers, resize relayouts, unmount purges)\n * can use the synchronous `getLoadedPlotly()`.\n *\n * `plotly.js-dist` is an *optional* peer dependency (SW-2007): consumers that\n * use the kit's charts must add it to their own app; consumers that don't\n * never install its ~4.7 MB.\n *\n * A missing peer does **not** fail the consumer's build (SW-2472). Because this\n * is a dynamic, default-only import, Vite/Rollup resolve it to an empty\n * optional-peer stub and exit 0 — so the runtime guard below is the *primary*\n * failure path, not a fallback for the rarer case.\n *\n * TODO(SW-2472): that guard is currently incomplete. `.catch()` covers a module\n * that won't resolve, but a module that *resolves to a stub* with no `newPlot`\n * is cached as-is and surfaces later as a `TypeError` from the first draw call\n * rather than as MISSING_PLOTLY_MESSAGE. Closing it means shape-checking the\n * resolved module before caching it:\n *\n * const candidate = withDefault.default ?? withDefault;\n * if (typeof candidate?.newPlot !== \"function\") throw new Error(MISSING_PLOTLY_MESSAGE);\n *\n * Deliberately not done here — this was a docs-only change and the fix wants a\n * test that builds against an absent peer.\n */\n\ntype PlotlyModule = typeof import(\"plotly.js-dist\");\n\nconst MISSING_PLOTLY_MESSAGE =\n \"Failed to load 'plotly.js-dist'. It is an optional peer dependency of \" +\n \"@tetrascience-npm/tetrascience-react-ui required by the chart components — \" +\n \"install it in your app (e.g. `yarn add plotly.js-dist`).\";\n\nlet loadedPlotly: PlotlyModule | null = null;\nlet plotlyPromise: Promise<PlotlyModule> | null = null;\n\n/** Import Plotly on first use; subsequent calls reuse the same promise. */\nexport function loadPlotly(): Promise<PlotlyModule> {\n plotlyPromise ??= import(\"plotly.js-dist\")\n .then((mod) => {\n // plotly.js-dist is CJS; depending on the consumer's bundler interop the\n // API surface is either the namespace itself or its `default` export.\n const withDefault = mod as PlotlyModule & { default?: PlotlyModule };\n loadedPlotly = withDefault.default ?? withDefault;\n return loadedPlotly;\n })\n .catch((error: unknown) => {\n // Reset so a later mount can retry, and rethrow with install guidance.\n plotlyPromise = null;\n console.error(MISSING_PLOTLY_MESSAGE, error);\n throw new Error(MISSING_PLOTLY_MESSAGE);\n });\n return plotlyPromise;\n}\n\n/**\n * Synchronous access to the loaded Plotly module. Only valid after\n * `loadPlotly()` has resolved — i.e. in code paths that can only run once a\n * plot has been drawn (event handlers, cleanups, resize effects).\n */\nexport function getLoadedPlotly(): PlotlyModule {\n if (!loadedPlotly) {\n throw new Error(\"Plotly accessed before loadPlotly() resolved. Call loadPlotly() and draw the plot first.\");\n }\n return loadedPlotly;\n}\n\nexport type { PlotlyModule };\n"],"names":["MISSING_PLOTLY_MESSAGE","loadedPlotly","plotlyPromise","loadPlotly","_interopNamespaceDefault","mod","withDefault","error","getLoadedPlotly"],"mappings":"6VAqCA,MAAMA,EACJ,4MAIF,IAAIC,EAAoC,KACpCC,EAA8C,KAG3C,SAASC,GAAoC,CAClD,OAAAD,IAAkB,QAAA,QAAA,EAAA,KAAA,IAAAE,EAAA,QAAO,gBAAgB,CAAA,CAAA,EACtC,KAAMC,GAAQ,CAGb,MAAMC,EAAcD,EACpB,OAAAJ,EAAeK,EAAY,SAAWA,EAC/BL,CACT,CAAC,EACA,MAAOM,GAAmB,CAEzB,MAAAL,EAAgB,KAChB,QAAQ,MAAMF,EAAwBO,CAAK,EACrC,IAAI,MAAMP,CAAsB,CACxC,CAAC,EACIE,CACT,CAOO,SAASM,GAAgC,CAC9C,GAAI,CAACP,EACH,MAAM,IAAI,MAAM,0FAA0F,EAE5G,OAAOA,CACT"}
@@ -13,9 +13,24 @@
13
13
  *
14
14
  * `plotly.js-dist` is an *optional* peer dependency (SW-2007): consumers that
15
15
  * use the kit's charts must add it to their own app; consumers that don't
16
- * never install its ~4.7 MB. A missing peer surfaces at build time as an
17
- * unresolved-import error from the consumer's bundler; the runtime guard below
18
- * covers the rarer case where the import resolves but fails to evaluate.
16
+ * never install its ~4.7 MB.
17
+ *
18
+ * A missing peer does **not** fail the consumer's build (SW-2472). Because this
19
+ * is a dynamic, default-only import, Vite/Rollup resolve it to an empty
20
+ * optional-peer stub and exit 0 — so the runtime guard below is the *primary*
21
+ * failure path, not a fallback for the rarer case.
22
+ *
23
+ * TODO(SW-2472): that guard is currently incomplete. `.catch()` covers a module
24
+ * that won't resolve, but a module that *resolves to a stub* with no `newPlot`
25
+ * is cached as-is and surfaces later as a `TypeError` from the first draw call
26
+ * rather than as MISSING_PLOTLY_MESSAGE. Closing it means shape-checking the
27
+ * resolved module before caching it:
28
+ *
29
+ * const candidate = withDefault.default ?? withDefault;
30
+ * if (typeof candidate?.newPlot !== "function") throw new Error(MISSING_PLOTLY_MESSAGE);
31
+ *
32
+ * Deliberately not done here — this was a docs-only change and the fix wants a
33
+ * test that builds against an absent peer.
19
34
  */
20
35
  type PlotlyModule = typeof import("plotly.js-dist");
21
36
  /** Import Plotly on first use; subsequent calls reuse the same promise. */
@@ -10,9 +10,7 @@ function n() {
10
10
  }
11
11
  function a() {
12
12
  if (!t)
13
- throw new Error(
14
- "Plotly accessed before loadPlotly() resolved. Call loadPlotly() and draw the plot first."
15
- );
13
+ throw new Error("Plotly accessed before loadPlotly() resolved. Call loadPlotly() and draw the plot first.");
16
14
  return t;
17
15
  }
18
16
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"plotly-loader.js","sources":["../../../src/components/charts/plotly-loader.ts"],"sourcesContent":["/**\n * Lazy Plotly loader (SW-2007).\n *\n * The kit ships preserved ES modules with all dependencies externalized, so a\n * static `import Plotly from \"plotly.js-dist\"` in any chart lands the full\n * ~4.7 MB Plotly dist in the consumer's main chunk — before any chart renders.\n * Loading it through this module's dynamic `import()` instead lets consumer\n * bundlers split Plotly into its own chunk, fetched only when a chart mounts.\n *\n * Chart components must call `loadPlotly()` before drawing. Code that only\n * runs after a plot exists (hover handlers, resize relayouts, unmount purges)\n * can use the synchronous `getLoadedPlotly()`.\n *\n * `plotly.js-dist` is an *optional* peer dependency (SW-2007): consumers that\n * use the kit's charts must add it to their own app; consumers that don't\n * never install its ~4.7 MB. A missing peer surfaces at build time as an\n * unresolved-import error from the consumer's bundler; the runtime guard below\n * covers the rarer case where the import resolves but fails to evaluate.\n */\n\ntype PlotlyModule = typeof import(\"plotly.js-dist\");\n\nconst MISSING_PLOTLY_MESSAGE =\n \"Failed to load 'plotly.js-dist'. It is an optional peer dependency of \" +\n \"@tetrascience-npm/tetrascience-react-ui required by the chart components — \" +\n \"install it in your app (e.g. `yarn add plotly.js-dist`).\";\n\nlet loadedPlotly: PlotlyModule | null = null;\nlet plotlyPromise: Promise<PlotlyModule> | null = null;\n\n/** Import Plotly on first use; subsequent calls reuse the same promise. */\nexport function loadPlotly(): Promise<PlotlyModule> {\n plotlyPromise ??= import(\"plotly.js-dist\")\n .then((mod) => {\n // plotly.js-dist is CJS; depending on the consumer's bundler interop the\n // API surface is either the namespace itself or its `default` export.\n const withDefault = mod as PlotlyModule & { default?: PlotlyModule };\n loadedPlotly = withDefault.default ?? withDefault;\n return loadedPlotly;\n })\n .catch((error: unknown) => {\n // Reset so a later mount can retry, and rethrow with install guidance.\n plotlyPromise = null;\n console.error(MISSING_PLOTLY_MESSAGE, error);\n throw new Error(MISSING_PLOTLY_MESSAGE);\n });\n return plotlyPromise;\n}\n\n/**\n * Synchronous access to the loaded Plotly module. Only valid after\n * `loadPlotly()` has resolved — i.e. in code paths that can only run once a\n * plot has been drawn (event handlers, cleanups, resize effects).\n */\nexport function getLoadedPlotly(): PlotlyModule {\n if (!loadedPlotly) {\n throw new Error(\n \"Plotly accessed before loadPlotly() resolved. Call loadPlotly() and draw the plot first.\",\n );\n }\n return loadedPlotly;\n}\n\nexport type { PlotlyModule };\n"],"names":["MISSING_PLOTLY_MESSAGE","loadedPlotly","plotlyPromise","loadPlotly","mod","withDefault","error","getLoadedPlotly"],"mappings":"AAsBA,MAAMA,IACJ;AAIF,IAAIC,IAAoC,MACpCC,IAA8C;AAG3C,SAASC,IAAoC;AAClD,SAAAD,MAAkB,OAAO,gBAAgB,EACtC,KAAK,CAACE,MAAQ;AAGb,UAAMC,IAAcD;AACpB,WAAAH,IAAeI,EAAY,WAAWA,GAC/BJ;AAAA,EACT,CAAC,EACA,MAAM,CAACK,MAAmB;AAEzB,UAAAJ,IAAgB,MAChB,QAAQ,MAAMF,GAAwBM,CAAK,GACrC,IAAI,MAAMN,CAAsB;AAAA,EACxC,CAAC,GACIE;AACT;AAOO,SAASK,IAAgC;AAC9C,MAAI,CAACN;AACH,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAGJ,SAAOA;AACT;"}
1
+ {"version":3,"file":"plotly-loader.js","sources":["../../../src/components/charts/plotly-loader.ts"],"sourcesContent":["/**\n * Lazy Plotly loader (SW-2007).\n *\n * The kit ships preserved ES modules with all dependencies externalized, so a\n * static `import Plotly from \"plotly.js-dist\"` in any chart lands the full\n * ~4.7 MB Plotly dist in the consumer's main chunk — before any chart renders.\n * Loading it through this module's dynamic `import()` instead lets consumer\n * bundlers split Plotly into its own chunk, fetched only when a chart mounts.\n *\n * Chart components must call `loadPlotly()` before drawing. Code that only\n * runs after a plot exists (hover handlers, resize relayouts, unmount purges)\n * can use the synchronous `getLoadedPlotly()`.\n *\n * `plotly.js-dist` is an *optional* peer dependency (SW-2007): consumers that\n * use the kit's charts must add it to their own app; consumers that don't\n * never install its ~4.7 MB.\n *\n * A missing peer does **not** fail the consumer's build (SW-2472). Because this\n * is a dynamic, default-only import, Vite/Rollup resolve it to an empty\n * optional-peer stub and exit 0 — so the runtime guard below is the *primary*\n * failure path, not a fallback for the rarer case.\n *\n * TODO(SW-2472): that guard is currently incomplete. `.catch()` covers a module\n * that won't resolve, but a module that *resolves to a stub* with no `newPlot`\n * is cached as-is and surfaces later as a `TypeError` from the first draw call\n * rather than as MISSING_PLOTLY_MESSAGE. Closing it means shape-checking the\n * resolved module before caching it:\n *\n * const candidate = withDefault.default ?? withDefault;\n * if (typeof candidate?.newPlot !== \"function\") throw new Error(MISSING_PLOTLY_MESSAGE);\n *\n * Deliberately not done here — this was a docs-only change and the fix wants a\n * test that builds against an absent peer.\n */\n\ntype PlotlyModule = typeof import(\"plotly.js-dist\");\n\nconst MISSING_PLOTLY_MESSAGE =\n \"Failed to load 'plotly.js-dist'. It is an optional peer dependency of \" +\n \"@tetrascience-npm/tetrascience-react-ui required by the chart components — \" +\n \"install it in your app (e.g. `yarn add plotly.js-dist`).\";\n\nlet loadedPlotly: PlotlyModule | null = null;\nlet plotlyPromise: Promise<PlotlyModule> | null = null;\n\n/** Import Plotly on first use; subsequent calls reuse the same promise. */\nexport function loadPlotly(): Promise<PlotlyModule> {\n plotlyPromise ??= import(\"plotly.js-dist\")\n .then((mod) => {\n // plotly.js-dist is CJS; depending on the consumer's bundler interop the\n // API surface is either the namespace itself or its `default` export.\n const withDefault = mod as PlotlyModule & { default?: PlotlyModule };\n loadedPlotly = withDefault.default ?? withDefault;\n return loadedPlotly;\n })\n .catch((error: unknown) => {\n // Reset so a later mount can retry, and rethrow with install guidance.\n plotlyPromise = null;\n console.error(MISSING_PLOTLY_MESSAGE, error);\n throw new Error(MISSING_PLOTLY_MESSAGE);\n });\n return plotlyPromise;\n}\n\n/**\n * Synchronous access to the loaded Plotly module. Only valid after\n * `loadPlotly()` has resolved — i.e. in code paths that can only run once a\n * plot has been drawn (event handlers, cleanups, resize effects).\n */\nexport function getLoadedPlotly(): PlotlyModule {\n if (!loadedPlotly) {\n throw new Error(\"Plotly accessed before loadPlotly() resolved. Call loadPlotly() and draw the plot first.\");\n }\n return loadedPlotly;\n}\n\nexport type { PlotlyModule };\n"],"names":["MISSING_PLOTLY_MESSAGE","loadedPlotly","plotlyPromise","loadPlotly","mod","withDefault","error","getLoadedPlotly"],"mappings":"AAqCA,MAAMA,IACJ;AAIF,IAAIC,IAAoC,MACpCC,IAA8C;AAG3C,SAASC,IAAoC;AAClD,SAAAD,MAAkB,OAAO,gBAAgB,EACtC,KAAK,CAACE,MAAQ;AAGb,UAAMC,IAAcD;AACpB,WAAAH,IAAeI,EAAY,WAAWA,GAC/BJ;AAAA,EACT,CAAC,EACA,MAAM,CAACK,MAAmB;AAEzB,UAAAJ,IAAgB,MAChB,QAAQ,MAAMF,GAAwBM,CAAK,GACrC,IAAI,MAAMN,CAAsB;AAAA,EACxC,CAAC,GACIE;AACT;AAOO,SAASK,IAAgC;AAC9C,MAAI,CAACN;AACH,UAAM,IAAI,MAAM,0FAA0F;AAE5G,SAAOA;AACT;"}