SW-2007 Cut heavy eager deps: lazy Plotly, slim Shiki, deferred mermaid/KaTeX#184
Open
owilliams-tetrascience wants to merge 1 commit into
Open
SW-2007 Cut heavy eager deps: lazy Plotly, slim Shiki, deferred mermaid/KaTeX#184owilliams-tetrascience wants to merge 1 commit into
owilliams-tetrascience wants to merge 1 commit into
Conversation
…id/KaTeX The kit ships preserved ES modules with all deps externalized, so static imports of heavy libraries land in every consumer's main chunk. On the identical consumer fixture this cut the main chunk from 6.63 MB (1.97 MB gzip) to 0.70 MB (0.22 MB gzip) and the build from 655 to 68 chunks at the default Node heap. - Plotly: memoized dynamic-import loader (plotly-loader.ts); all charts use type-only Plotly imports and draw after loadPlotly() resolves. Consolidates the triple declaration: drops plotly.js and unused react-plotly.js, keeps plotly.js-dist. - Shiki: shared slim highlighter (lib/shiki.ts) on shiki/core + JS regex engine + explicit lazy grammar set, extensible via new registerCodeBlockLanguage(); code-block.tsx uses it, and a slim streamdown code plugin replaces @streamdown/code (which statically pulled a second full Shiki copy). - mermaid/KaTeX: MessageResponse/ReasoningContent load streamdown plugins through useStreamdownPlugins() (dynamic import), so markdown streams immediately and code/math/mermaid upgrade in place. - Tests: chart unit tests flush the async draw via async act; chart story plays gate their first plot assertion behind waitFor. - AGENTS.md documents the heavy-deps-must-stay-lazy rule. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
owilliams-tetrascience
deployed
to
artifactory-prod
July 17, 2026 21:31 — with
GitHub Actions
Active
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
There was a problem hiding this comment.
Pull request overview
This PR addresses SW-2007 by moving several heavyweight dependencies (Plotly, Shiki, Streamdown plugin set incl. mermaid/KaTeX) behind dynamic imports so they no longer land in consumers’ main bundle for preserved-ESM builds.
Changes:
- Introduces a lazy Plotly loader and updates chart components/tests/stories to render asynchronously after Plotly is loaded.
- Adds a shared “slim Shiki” setup (
shiki/core+ dynamic grammar/theme imports) and wires it intoCodeBlockand a custom Streamdown code plugin. - Defers Streamdown plugin loading via
useStreamdownPlugins()and removes eager imports of mermaid/math/code plugins from AI components.
Reviewed changes
Copilot reviewed 43 out of 44 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/lib/shiki.ts | Adds shared slim Shiki core with dynamic grammar/theme loading and a public language-extension API. |
| src/index.ts | Re-exports Shiki language-extension helpers from the public entrypoint. |
| src/components/ui/code-block.tsx | Switches code highlighting to the shared slim Shiki highlighter. |
| src/components/charts/StackedChromatogram/StackedChromatogram.stories.tsx | Updates plays to wait for async Plotly-backed render. |
| src/components/charts/ScatterPlotInteractive/ScatterPlotInteractive.tsx | Lazily loads Plotly before draw; updates cleanup/selection sync for async draw. |
| src/components/charts/ScatterPlotInteractive/ScatterPlotInteractive.stories.tsx | Adds waits/timeouts to accommodate lazy Plotly load. |
| src/components/charts/ScatterPlotInteractive/tests/ScatterPlotInteractive.test.tsx | Updates tests to async act so lazy Plotly draw/effects flush before assertions. |
| src/components/charts/ScatterPlot/ScatterPlot.tsx | Lazily loads Plotly and uses synchronous getLoadedPlotly() only after init. |
| src/components/charts/ScatterPlot/ScatterPlot.stories.tsx | Updates plays to wait for async Plotly-backed render. |
| src/components/charts/ScatterPlot/tests/ScatterPlot.test.tsx | Updates tests to async act for lazy Plotly draw. |
| src/components/charts/plotly-loader.ts | Adds memoized dynamic import loader with synchronous accessor for post-load code paths. |
| src/components/charts/PlateMap/PlateMap.tsx | Converts Plotly usage to lazy loader; updates cleanup to purge via loaded Plotly. |
| src/components/charts/PlateMap/PlateMap.stories.tsx | Updates plays to wait for async Plotly-backed render. |
| src/components/charts/PieChart/PieChart.tsx | Converts Plotly usage to lazy loader and uses loaded Plotly for purge. |
| src/components/charts/PieChart/PieChart.stories.tsx | Updates plays to wait for async Plotly-backed render. |
| src/components/charts/LinePlot/LinePlot.tsx | Converts Plotly usage to lazy loader; keeps resize relayout via loaded Plotly. |
| src/components/charts/LinePlot/LinePlot.stories.tsx | Updates plays to wait for async Plotly-backed render. |
| src/components/charts/LinePlot/tests/LinePlot.test.tsx | Updates tests to async act for lazy Plotly draw. |
| src/components/charts/Histogram/Histogram.tsx | Converts Plotly usage to lazy loader; keeps resize relayout via loaded Plotly. |
| src/components/charts/Histogram/Histogram.stories.tsx | Updates plays to wait for async Plotly-backed render. |
| src/components/charts/Electropherogram/Electropherogram.tsx | Converts Plotly usage to lazy loader and uses loaded Plotly for purge. |
| src/components/charts/Electropherogram/Electropherogram.stories.tsx | Updates plays to wait for async Plotly-backed render. |
| src/components/charts/Electropherogram/tests/Electropherogram.test.tsx | Updates tests to async act for lazy Plotly draw. |
| src/components/charts/Chromatogram/plotBuilder.ts | Switches restyle calls inside handlers to getLoadedPlotly() post-draw. |
| src/components/charts/Chromatogram/Chromatogram.tsx | Converts Plotly usage to lazy loader; updates annotation relayout to use loaded Plotly post-draw. |
| src/components/charts/Chromatogram/Chromatogram.stories.tsx | Updates plays to wait for async Plotly-backed render. |
| src/components/charts/Chromatogram/tests/plotBuilder.test.ts | Primes the Plotly loader in tests so handler code can use getLoadedPlotly(). |
| src/components/charts/ChartTooltip/ChartTooltip.tsx | Uses getLoadedPlotly() for relayout inside bindTooltip post-newPlot. |
| src/components/charts/ChartTooltip/tests/ChartTooltip.test.tsx | Primes the Plotly loader in tests before rendering the hook. |
| src/components/charts/BoxPlot/BoxPlot.tsx | Converts Plotly usage to lazy loader; keeps resize relayout via loaded Plotly. |
| src/components/charts/BoxPlot/BoxPlot.stories.tsx | Updates plays to wait for async Plotly-backed render. |
| src/components/charts/BarChart/BarChart.tsx | Converts Plotly usage to lazy loader; keeps resize relayout via loaded Plotly. |
| src/components/charts/BarChart/BarChart.stories.tsx | Updates plays to wait for async Plotly-backed render. |
| src/components/charts/AreaPlot/AreaPlot.tsx | Converts Plotly usage to lazy loader; keeps resize relayout via loaded Plotly. |
| src/components/charts/AreaPlot/AreaPlot.stories.tsx | Updates plays to wait for async Plotly-backed render. |
| src/components/charts/AreaPlot/tests/AreaPlot.test.tsx | Updates tests to async act for lazy Plotly draw and resize flows. |
| src/components/ai/use-streamdown-plugins.ts | Adds lazy plugin loader hook with module-level cache for Streamdown plugins. |
| src/components/ai/streamdown-plugins.ts | Defines full Streamdown plugin set in a lazily-imported module. |
| src/components/ai/streamdown-code-plugin.ts | Replaces @streamdown/code with a slim plugin backed by the shared Shiki setup. |
| src/components/ai/reasoning.tsx | Switches reasoning markdown rendering to lazy-loaded Streamdown plugins. |
| src/components/ai/message.tsx | Switches message markdown rendering to lazy-loaded Streamdown plugins. |
| package.json | Adds Shiki langs/themes packages; removes unused Plotly/react-plotly and streamdown code deps/types. |
| AGENTS.md | Documents the “heavy dependencies must stay lazy” rule to prevent regressions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+74
to
+77
| languageLoaders.set(language, loader); | ||
| for (const alias of aliases) { | ||
| languageAliases.set(alias, language); | ||
| } |
Comment on lines
+21
to
+30
| export function loadPlotly(): Promise<PlotlyModule> { | ||
| plotlyPromise ??= import("plotly.js-dist").then((mod) => { | ||
| // plotly.js-dist is CJS; depending on the consumer's bundler interop the | ||
| // API surface is either the namespace itself or its `default` export. | ||
| const withDefault = mod as PlotlyModule & { default?: PlotlyModule }; | ||
| loadedPlotly = withDefault.default ?? withDefault; | ||
| return loadedPlotly; | ||
| }); | ||
| return plotlyPromise; | ||
| } |
Comment on lines
+10
to
+16
| function loadStreamdownPlugins(): Promise<PluginConfig> { | ||
| pluginsPromise ??= import("./streamdown-plugins").then((mod) => { | ||
| cachedPlugins = mod.streamdownPlugins; | ||
| return cachedPlugins; | ||
| }); | ||
| return pluginsPromise; | ||
| } |
Coverage Report
File Coverage |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes SW-2007: the kit ships preserved ES modules with all dependencies externalized, so a static import of a heavy library lands in every consumer's main chunk. This PR makes the three offenders lazy:
src/components/charts/plotly-loader.tswith a memoized dynamicimport("plotly.js-dist"); all 12 chart components +ChartTooltip+ Chromatogram'splotBuildernow use type-only Plotly imports and draw afterloadPlotly()resolves (with cancellation guards; post-draw code paths use the synchronousgetLoadedPlotly()). The triple Plotly declaration is consolidated —plotly.jsand the never-importedreact-plotly.jsare removed;plotly.js-distremains (types still come from@types/plotly.js; the publishedindex.d.tshas zero Plotly references, so consumer typing is unaffected).code-block.tsxand v3 via@streamdown/codeinsideMessageResponse. New shared slim highlightersrc/lib/shiki.ts(shiki/core+ JS regex engine + explicit lazy grammar set: bash/javascript/json/markdown/python/sql/tsx/typescript/yaml + aliases, extensible via the newly exportedregisterCodeBlockLanguage()).code-block.tsxuses it, and a slim custom streamdown plugin (streamdown-code-plugin.ts) replaces@streamdown/code. Unknown languages degrade to plaintext.MessageResponse/ReasoningContentnow obtain streamdown plugins fromuseStreamdownPlugins(), which dynamic-imports the plugin set — markdown streams immediately; code/math/mermaid rendering upgrades in place when the lazy chunk arrives.AGENTS.mddocuments the heavy-deps-must-stay-lazy rule to prevent regressions.Measured impact (identical minimal consumer fixture — one chart +
MessageResponse+CodeBlock— built with Vite 7 at the default Node heap)origin/mainRuntime-verified on the built fixture: main chunk loads first; Plotly, the streamdown plugin chunk, shiki core/engine/themes, and only the grammars actually used are fetched on demand; console clean.
Type of Change
Not marked breaking: the public API is additive (
registerCodeBlockLanguage,getSupportedCodeBlockLanguages). Note for reviewers: code highlighting now covers an explicit language set (extensible at runtime) instead of all ~200 bundled grammars — anything outside the set renders as plaintext rather than highlighted.Checklist
yarn lintpassesyarn buildpassesyarn test:allpasses (841 unit + 690 storybook)waitForfor the async draw; first story per file gets a 15s timeout for the cold Plotly fetch)Testing
Existing Zephyr-linked chart/story test cases unchanged (no
parameters.zephyr.testCaseIdvalues touched; no assertions changed). Chart unit tests flush the async draw via asyncact;ChartTooltip/plotBuildertests prime the loader withawait loadPlotly().Verification
🤖 Generated with Claude Code