Skip to content

lazy imports for better performance#848

Merged
PythonFZ merged 12 commits intomainfrom
housekeeping/lazy-imports
Jan 15, 2026
Merged

lazy imports for better performance#848
PythonFZ merged 12 commits intomainfrom
housekeeping/lazy-imports

Conversation

@PythonFZ
Copy link
Copy Markdown
Member

@PythonFZ PythonFZ commented Jan 15, 2026

Lazy import python modules for faster load times
split and lazy load js modules for faster site load times

Summary by CodeRabbit

  • New Features
    • Added a Distance analysis producing interactive distance-over-time plots.
  • Performance Improvements
    • Implemented lazy loading for several heavy UI components and added loading indicators.
    • Introduced optimized bundle splitting to improve caching and load times.
  • Bug Fixes / UX
    • Avoids rendering empty window UI when nothing is open.
  • Chores
    • Improved log formatting for molecule additions and expanded test coverage for distance analysis.

✏️ Tip: You can customize this high-level summary in your review settings.

PythonFZ and others added 8 commits January 15, 2026 21:23
Backend: Replace @requires_lock with @check_lock decorator
- Operations proceed unless blocked by another session's lock
- FIFO ordering handles concurrency when no lock exists
- Fine-grained locks: geometry:{key}, step, bookmarks, global
- Remove deprecated @requires_lock decorator

Frontend: Remove automatic lock acquisition
- Simplify createGeometry/deleteGeometry - remove withAutoLock
- Remove withAutoLock function from client.ts
- Simplify useStepControl - remove auto-lock acquisition
- Delete useLockManager.ts hook (no longer needed)
- Remove lockToken parameter from geometry persistence
- Add 423 error handling with snackbar notifications
- Fix GeometryListResponse type to include types property

Bug fix:
- Fix AttributeError in socket_manager.py bookmark exception handling

Tests:
- Add test_check_lock_decorator.py for new behavior
- Update test_step_endpoints.py for @check_lock semantics
- Update test_partial_frame_update.py
- Remove test_requires_lock_decorator.py

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 15, 2026

📝 Walkthrough

Walkthrough

Adds React lazy-loading and Suspense fallbacks for several UI components, adds Vite manualChunks for bundle splitting, defers heavy Python imports into analysis/building run methods, and introduces a new Distance analysis extension that computes inter-atomic distances and produces a Plotly figure.

Changes

Cohort / File(s) Summary
React: Window & dialog lazy loading
app/src/components/WindowManager.tsx, app/src/components/jsonforms-renderers/CustomSmilesEditor.tsx, app/src/components/jsonforms-renderers/CustomSmilesPackEditor.tsx
Replaced direct imports with React.lazy and wrapped usages in Suspense with centered CircularProgress fallbacks. WindowManager returns early when no windows are open.
React: Page-level lazy loading
app/src/pages/landingPage.tsx
ChatWindow changed to lazy-loaded component and rendered only when chatOpen is true; wrapped in Suspense with CircularProgress.
Build config: manual chunking
app/vite.config.ts
Added build.rollupOptions.output.manualChunks grouping dependencies into named chunks (three, ketcher, plotly, mui, vendor).
Python: analysis extensions & new Distance analysis
src/zndraw/extensions/analysis.py, tests/test_vis_figures.py
Added Distance(Analysis) class and registered it. Moved pandas/plotly.express imports into run methods for lazy runtime import. Added tests for Distance and selection error handling.
Python: molecule building lazy imports & log formatting
src/zndraw/extensions/molecule_building.py
Moved molify imports into run methods for AddFromSMILES and PackBox. Updated AddFromSMILES logging to include fenced SMILES code block.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant UI as React UI
  participant Server as Analysis Runner
  participant Plotly as Plotly (fig)
  participant Vis as Vis Manager

  User->>UI: Open dialog / trigger Distance analysis
  UI->>UI: React.lazy loads component (Suspense shows CircularProgress)
  UI->>Server: submit analysis job with selected atoms
  Server->>Server: (run) import pandas, plotly.express locally
  Server->>Plotly: compute distances and build figure
  Plotly-->>Server: returns figure object (with data, customdata, meta)
  Server->>Vis: store figure in vis.figures["Distance"]
  Vis-->>UI: updated figures list / event
  UI->>User: render figure viewer with interactive Plotly figure
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped through code with curious paws,

Lazy-load dreams and tidy configs I applause.
Chunks and plots now wait till they're asked,
Distance traced and SMILES unmasked.
A nibble, a skip — efficient applause! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'lazy imports for better performance' directly reflects the main change across the entire changeset, which consistently implements lazy loading patterns in both frontend (React components and Vite config) and backend (Python modules) code.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings


📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between db79e11 and a2e5bf7.

📒 Files selected for processing (4)
  • app/src/pages/landingPage.tsx
  • src/zndraw/extensions/analysis.py
  • src/zndraw/extensions/molecule_building.py
  • tests/test_vis_figures.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/zndraw/extensions/molecule_building.py
  • app/src/pages/landingPage.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: You MUST NEVER @pytest.mark.xfail or similar - all tests must pass!
All default values (e.g., camera, particles) must be defined exclusively within the Pydantic model. Do not scatter fallback logic throughout the codebase.
Do not perform null checks combined with hardcoded literals for default values. Rely entirely on the schema to populate default values during initialization.
You can not use LUA scripts with Redis!
If sensible, implement collections.abc interfaces for your classes, such as MutableMapping or MutableSequence.
Use numpy style docstrings in Python code.
Docstrings must be concise and to the point.
Use type hints wherever possible in Python. Use list[int|float] | None instead of t.Optional[t.List[int|float]]!
Imports should always be at the top of the file in Python.

Files:

  • src/zndraw/extensions/analysis.py
  • tests/test_vis_figures.py
**/test_*.py

📄 CodeRabbit inference engine (AGENTS.md)

When designing new tests, read the old tests first to understand the existing patterns. Use pytest.mark.parametrize to avoid code duplication. Tests should be very specific and test only one thing. Avoid complex test setups. Each test must be a function, not a method of a class!

Files:

  • tests/test_vis_figures.py
🧬 Code graph analysis (2)
src/zndraw/extensions/analysis.py (3)
src/zndraw/extensions/molecule_building.py (2)
  • run (20-29)
  • run (55-72)
src/zndraw/zndraw.py (4)
  • run (1592-1637)
  • atoms (757-759)
  • atoms (762-764)
  • append (1311-1317)
src/zndraw/extensions/abc.py (1)
  • run (22-31)
tests/test_vis_figures.py (1)
src/zndraw/extensions/analysis.py (7)
  • Distance (18-89)
  • Properties2D (259-373)
  • run (27-89)
  • run (101-167)
  • run (192-256)
  • run (287-373)
  • run (398-478)
🪛 Ruff (0.14.11)
src/zndraw/extensions/analysis.py

41-41: Avoid specifying long messages outside the exception class

(TRY003)

tests/test_vis_figures.py

382-382: Unused function argument: celery_worker

(ARG001)


424-424: Unused function argument: celery_worker

(ARG001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: pytest (3.11, ubuntu-latest)
  • GitHub Check: pytest (3.12, ubuntu-latest)
  • GitHub Check: pytest (3.13, ubuntu-latest)
🔇 Additional comments (6)
src/zndraw/extensions/analysis.py (3)

18-89: LGTM! Well-structured Distance analysis class.

The implementation correctly follows the established pattern from other analysis classes (DihedralAngle, Properties1D, etc.). The lazy imports align with the PR objective for better performance, and the interaction schema is consistent with other analyses.

Minor note: The static analysis hint (TRY003) about the exception message on line 41 is a style suggestion. Given that other analysis classes in this file use the same pattern (e.g., line 115), maintaining consistency here is reasonable.


106-107: LGTM! Consistent lazy import pattern across all analysis classes.

The lazy imports for pandas and plotly.express are consistently applied to all analysis run() methods, which aligns with the PR objective for faster module load times.


481-487: LGTM! Distance correctly registered.

The Distance class is properly added to the analysis registry, making it available as a public analysis type.

tests/test_vis_figures.py (3)

9-9: LGTM! Import updated correctly.

The Distance class is properly imported alongside Properties2D for use in the new tests.


382-421: LGTM! Comprehensive test for Distance analysis.

The test follows the established pattern from test_properties_2d_analysis and thoroughly verifies:

  • Figure creation with correct key
  • Figure type validation
  • Presence of data, customdata, and interaction metadata

The celery_worker parameter (flagged by static analysis) is a pytest fixture pattern—it ensures the worker is running even without direct reference in the test body. This matches the existing tests at lines 297 and 345.


424-443: LGTM! Good error handling test.

This test properly validates the error case when an incorrect number of atoms (3 instead of 2) is selected. It correctly verifies both the job failure state and the absence of the figure in vis.figures.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@PythonFZ PythonFZ marked this pull request as draft January 15, 2026 20:24
@PythonFZ PythonFZ marked this pull request as ready for review January 15, 2026 20:25
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@app/src/pages/landingPage.tsx`:
- Around line 40-47: The file uses React.lazy to define ChatWindow before
importing React, causing a TDZ runtime error; move the React import (import
React, { useState, useEffect, Suspense } from "react") to the top of the import
block so React is available before calling React.lazy (affecting the ChatWindow
declaration), or alternatively replace React.lazy usage with a direct named
import pattern only after confirming React is imported first; ensure references
to ChatWindow, useState, useEffect, and Suspense remain correct after
reordering.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e66be9d and db79e11.

📒 Files selected for processing (7)
  • app/src/components/WindowManager.tsx
  • app/src/components/jsonforms-renderers/CustomSmilesEditor.tsx
  • app/src/components/jsonforms-renderers/CustomSmilesPackEditor.tsx
  • app/src/pages/landingPage.tsx
  • app/vite.config.ts
  • src/zndraw/extensions/analysis.py
  • src/zndraw/extensions/molecule_building.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: You MUST NEVER @pytest.mark.xfail or similar - all tests must pass!
All default values (e.g., camera, particles) must be defined exclusively within the Pydantic model. Do not scatter fallback logic throughout the codebase.
Do not perform null checks combined with hardcoded literals for default values. Rely entirely on the schema to populate default values during initialization.
You can not use LUA scripts with Redis!
If sensible, implement collections.abc interfaces for your classes, such as MutableMapping or MutableSequence.
Use numpy style docstrings in Python code.
Docstrings must be concise and to the point.
Use type hints wherever possible in Python. Use list[int|float] | None instead of t.Optional[t.List[int|float]]!
Imports should always be at the top of the file in Python.

Files:

  • src/zndraw/extensions/analysis.py
  • src/zndraw/extensions/molecule_building.py
🧬 Code graph analysis (4)
app/src/components/jsonforms-renderers/CustomSmilesEditor.tsx (1)
app/src/components/jsonforms-renderers/SmilesEditDialog.tsx (1)
  • SmilesEditDialog (43-109)
app/src/components/jsonforms-renderers/CustomSmilesPackEditor.tsx (1)
app/src/components/jsonforms-renderers/SmilesEditDialog.tsx (1)
  • SmilesEditDialog (43-109)
app/src/components/WindowManager.tsx (1)
app/src/stores/windowManagerStore.ts (1)
  • useWindowManagerStore (27-132)
src/zndraw/extensions/molecule_building.py (1)
src/zndraw/zndraw.py (1)
  • log (1720-1722)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: pytest (3.13, ubuntu-latest)
  • GitHub Check: pytest (3.11, ubuntu-latest)
  • GitHub Check: pytest (3.12, ubuntu-latest)
🔇 Additional comments (17)
src/zndraw/extensions/molecule_building.py (3)

21-22: Lazy import deviates from coding guidelines.

The lazy import pattern conflicts with the guideline stating "Imports should always be at the top of the file in Python." However, for optional heavy dependencies like molify, this is a recognized optimization pattern that improves load times and allows graceful handling when the dependency isn't installed.

Please confirm this deviation is acceptable for performance-critical optional dependencies.


25-25: LGTM!

The markdown fenced code block with smiles language identifier will render nicely in the chat interface, improving readability of the SMILES notation.


52-54: LGTM!

Consistent lazy import pattern with AddFromSMILES.run. Same guideline consideration applies as noted above.

src/zndraw/extensions/analysis.py (4)

32-34: Lazy imports approved for performance optimization.

Moving pandas and plotly.express imports into the run methods is a valid pattern for deferring heavy dependency loading until needed. Python caches imported modules, so subsequent calls incur minimal overhead.

Note: This intentionally deviates from the coding guideline stating "Imports should always be at the top of the file in Python." Given the explicit PR objective of lazy imports for faster load times, this is an acceptable exception.


126-128: Consistent lazy import pattern.


219-221: Consistent lazy import pattern.


330-332: Consistent lazy import pattern.

app/src/components/WindowManager.tsx (3)

1-7: LGTM! Clean lazy loading setup for Plotly.

The React.lazy import with the descriptive comment about the 4.8MB Plotly bundle is helpful for understanding the motivation behind this optimization.


14-14: Good optimization: early return prevents unnecessary Suspense boundary.

Returning null when there are no open windows avoids mounting the Suspense boundary unnecessarily, which is a small but worthwhile optimization.


17-35: Suspense fallback implementation looks good.

The centered CircularProgress with zIndex: 9999 provides clear loading feedback. The Suspense boundary correctly wraps all FigureWindow instances.

app/src/pages/landingPage.tsx (1)

528-547: Lazy loading pattern for ChatWindow is consistent with the rest of the codebase.

The Suspense boundary with centered CircularProgress fallback matches the pattern used in WindowManager and the SMILES editors.

app/src/components/jsonforms-renderers/CustomSmilesEditor.tsx (2)

7-13: LGTM! Correct lazy loading setup.

Imports are properly ordered, and the lazy-loaded SmilesEditDialog with the Ketcher editor will only load when the user clicks "Draw".


100-125: Good pattern: conditional render triggers lazy load only when needed.

The {dialogOpen && <Suspense>...} pattern ensures the Ketcher module is fetched only when the user opens the dialog, maximizing the performance benefit.

app/src/components/jsonforms-renderers/CustomSmilesPackEditor.tsx (3)

6-13: LGTM! Imports are correctly structured.

The lazy and Suspense imports along with CircularProgress are properly positioned before usage.

Also applies to: 22-22


36-37: Good: Same lazy-loading pattern as CustomSmilesEditor.

Both SMILES editor components now share the same deferred loading strategy for SmilesEditDialog, ensuring consistency across the codebase.


356-384: Suspense implementation is consistent and correct.

The conditional rendering with Suspense boundary matches the pattern in CustomSmilesEditor.tsx, ensuring the Ketcher module loads only when the edit dialog is opened.

app/vite.config.ts (1)

40-66: Manual chunk configuration is correct and well-organized.

The chunking strategy effectively separates heavy dependencies into cacheable groups, which aligns well with the PR's lazy loading objectives. The comments explaining each chunk's purpose are helpful for maintainability.

Both plotly.js and plotly.js-dist-min are legitimately used in the codebase: FigureWindow.tsx imports the minified distribution for runtime functionality while also importing internal modules directly from the full plotly.js package (plotly.js/src/lib/array.js for the decodeTypedArraySpec utility). Including both in the chunk is correct.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread app/src/pages/landingPage.tsx Outdated
PythonFZ and others added 3 commits January 15, 2026 21:34
Add new analysis extension that calculates distances between 2 selected
atoms across all frames with interactive frame selection support.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move React import before React.lazy usage to prevent temporal dead zone
runtime error when loading ChatWindow component.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Jan 15, 2026

Codecov Report

❌ Patch coverage is 57.81250% with 27 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.96%. Comparing base (e66be9d) to head (a2e5bf7).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/zndraw/extensions/analysis.py 7.69% 24 Missing ⚠️
src/zndraw/extensions/molecule_building.py 0.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #848      +/-   ##
==========================================
- Coverage   80.02%   79.96%   -0.07%     
==========================================
  Files         165      165              
  Lines       20084    20143      +59     
==========================================
+ Hits        16073    16108      +35     
- Misses       4011     4035      +24     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@PythonFZ PythonFZ merged commit a847bf2 into main Jan 15, 2026
6 checks passed
@PythonFZ PythonFZ deleted the housekeeping/lazy-imports branch January 15, 2026 21:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants