Skip to content

QuantUI Viewer: viewer mode, unsigned desktop installers, browser build on the docs site - #138

Merged
NCCU-Schultz-Lab merged 8 commits into
mainfrom
claude/quantui-lightweight-desktop-8syw5x
Oct 9, 2026
Merged

NCCU-Schultz-Lab merged 8 commits into
mainfrom
claude/quantui-lightweight-desktop-8syw5x

Conversation

@NCCU-Schultz-Lab

Copy link
Copy Markdown
Collaborator

A lightweight QuantUI for browsing saved results on any laptop: only the History and Analysis tabs, pointed at a results folder, no quantum engine. Delivered three ways.

What's in it

Viewer mode

  • QuantUIApp(viewer=True, results_dir=...) shows only History + Analysis, adds a results-folder bar, and skips calc-only startup work (GPU probe, resume offers, SLURM checks). New quantui/app_viewer.py.
  • quantui view [FOLDER] [--port] [--no-browser] starts Voilà on a free 127.0.0.1 port and opens the browser.
  • Browser support: Pyodide cannot start threads, so background renders (vibrational animation, isosurface, export, orbital gallery) and the activity timer go through _start_daemon, which runs inline when no thread can start. In the browser build the folder bar adds a .zip upload (path-traversal guarded) and hides Exit.

Desktop installers (packaging/viewer/installer/, conda constructor)

  • Windows .exe, macOS .pkg, Linux .sh with a "QuantUI Viewer" shortcut. Unsigned (no signing fee); first-run steps documented.

Browser build (packaging/viewer/browser/, Voici + Pyodide)

  • Published with the docs: new page docs/viewer.md at /viewer/, app at /viewer/app/voici/render/viewer.html. pages.yml builds it after mkdocs build --strict in its own venv (CDN Pyodide, demo water results) and now also runs on release: published and packaging/viewer/** changes.

CI

  • New viewer-builds.yml (runs only on packaging/viewer/** / workflow changes, on demand, or on release — not on PRs): builds the wheel, the three installers (each silently installed + smoke-tested) and the browser site. On a published release it attaches the installers to that release.
  • check-yaml excludes construct.yaml (a constructor jinja/selector template).

Fix along the way

  • History → View Results / View Analysis navigated by hard-coded tab index, landing one tab early when the Cluster Jobs tab is visible. Now looked up by name.

Testing

  • tests/test_app_viewer.py (16 tests). Full suite locally after merging main 0.10.0: 3739 passed, 21 skipped, 23 failed — the 23 are the NMR/Raman tests needing pyscf-properties, which does not build in the dev container; they fail identically without this change.
  • viewer-builds run 37175808013: all jobs green on Windows, macOS and Linux runners (the only later commit, ceab5ba, edits a comment).
  • Browser build driven in headless Chromium under the /QuantUI/viewer/app/ subpath: loads, zip upload works, Freq analysis renders. mkdocs build --strict passes.

Not verified yet (needs merge / real devices)

  • The Pages deploy itself and loading Pyodide from the jsDelivr CDN (blocked in the dev container; local tests self-hosted Pyodide).
  • Real Windows/macOS installs (SmartScreen / Gatekeeper first-run prompts).
  • Orbital isosurfaces, density/ESP surfaces and the orbital gallery need PySCF and are unavailable in the viewer.
  • Installers attach on the next release; v0.10.0 predates viewer mode.

🤖 Generated with Claude Code


Generated by Claude Code

NCCU-Schultz-Lab and others added 8 commits September 30, 2026 02:25
QuantUIApp(viewer=True, results_dir=...) builds the normal app but shows
only the History and Analysis tabs, skips calc-only startup work (GPU
probe, resume offers, SLURM checks) and adds a results-folder bar above
the tabs. The folder goes through QUANTUI_RESULTS_DIR, the seam every
results reader already uses. No quantum engine is required.

`quantui view [FOLDER] [--port] [--no-browser]` writes
~/.quantui/viewer.ipynb and starts Voila on a free 127.0.0.1 port,
opening the browser once the port accepts connections.

Browser (Pyodide) support, found by running the viewer under Voici:
- Thread.start() raises there, so background renders (vibrational
  animation, isosurface, export) go through _start_daemon, which runs
  the target inline when no thread can start; the activity-light timer
  does the same. Before this the isosurface button stuck on
  "Generating...".
- On emscripten the folder bar adds a .zip upload (unpacked with a
  path-traversal guard) and hides Exit, which has no server to stop.

Also fixes History -> View Results / View Analysis navigating by fixed
tab index (1/2): with the Cluster Jobs tab visible they landed one tab
early. They now use _tab_index(name).

Tests: tests/test_app_viewer.py (16). Full suite: 3418 passed,
18 skipped, 23 failed; the 23 are the NMR/Raman tests that need
pyscf-properties, which does not build in this environment, and they
fail identically without this change.

Contributions:
- Claude (Opus 5.5): code edits, review, and conceptual discussion
- Jonathan Schultz: overall vision, planning, review, and orchestration

Co-authored-by: Jonathan Schultz <nccu-schultz-lab@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
packaging/viewer/ holds both delivery routes for the viewer:

- installer/: conda-constructor recipe (Windows .exe, macOS .pkg, Linux
  .sh) bundling Python, Voila and conda-forge deps; post_install.*
  installs the QuantUI wheel and adds a "QuantUI Viewer" shortcut
  running `quantui view`. Unsigned by design (no signing fee); README
  documents the SmartScreen / Gatekeeper first-run steps. The Linux
  installer was built and installed in a clean prefix here: 238 MB
  download, 994 MB installed, launches and serves the viewer.
- browser/: Voici + Pyodide static site with the QuantUI wheel and its
  pure-Python deps bundled (build.sh, viewer.ipynb, make_samples.py).
  Verified in headless Chromium: loads in ~21 s from localhost, History,
  IR, Raman, UV-Vis, 3D and vibrational viewers render, .zip upload
  works. Pins: jupyterlite-pyodide-kernel 0.7.2 does not load under
  voici 0.10.0; 0.7.0 does.

.github/workflows/viewer-builds.yml builds the wheel, the three
installers (each silently installed and smoke-tested) and the browser
site. It runs only when packaging/viewer/ or the workflow changes, or
on demand, so the normal PR CI is untouched.

construct.yaml is a constructor template (jinja + "# [win]" selector
lines that repeat keys), so check-yaml now excludes it.

Contributions:
- Claude (Opus 5.5): code edits, review, and conceptual discussion
- Jonathan Schultz: overall vision, planning, review, and orchestration

Co-authored-by: Jonathan Schultz <nccu-schultz-lab@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
First viewer-builds run: wheel, Linux and macOS installers passed
(built, silently installed, `quantui view --help` and viewer import OK,
launcher created). Two failures fixed here:

- Browser job: make_samples.py runs a geometry optimization, which needs
  ASE; install ".[pyscf,ase]".
- Windows: the installer, wheel install and viewer import passed, but no
  Start-menu shortcut appeared. The inline PowerShell in post_install.bat
  failed silently. It is now create_shortcuts.ps1 (shipped only in the
  Windows installer), called with the full powershell.exe path and
  logging to <prefix>\post_install.log, which CI prints.

Also: the viewer header no longer points at a System Settings tab it
does not have.

Contributions:
- Claude (Opus 5.5): code edits, review, and conceptual discussion
- Jonathan Schultz: overall vision, planning, review, and orchestration

Co-authored-by: Jonathan Schultz <nccu-schultz-lab@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Brings the branch up to date with the 0.10.0 release prep. Only
CHANGELOG.md conflicted: the viewer entries stay under [Unreleased],
above the new [0.10.0] section.

One follow-up in the merged code: the new orbital gallery starts its
worker with threading.Thread directly; it now goes through
_start_daemon like the other background renders, so in the browser
build (no threads) it reports its error instead of hanging.

Contributions:
- Claude (Opus 5.5): code edits, review, and conceptual discussion
- Jonathan Schultz: overall vision, planning, review, and orchestration

Co-authored-by: Jonathan Schultz <nccu-schultz-lab@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Hosting decision (M-LITE LITE.8): the browser Viewer lives on the existing
GitHub Pages site.

- docs/viewer.md (nav: Getting Started -> QuantUI Viewer, URL /viewer/):
  what the Viewer is, a button to the browser Viewer, zip-upload steps,
  installer downloads and the unsigned-installer first-run steps.
- pages.yml builds the Voici site into site/viewer/app/ after
  `mkdocs build --strict`, in its own venv (QuantUI, PySCF and Voici stay
  out of the docs toolchain), with demo results from make_samples.py and
  CDN Pyodide. Also triggers on release: published and on
  packaging/viewer/ changes, so the hosted Viewer is rebuilt from each
  release tag. The app sits under /viewer/app/ because MkDocs already
  renders the docs page to site/viewer/index.html.
- viewer-builds.yml: on release: published, a new attach job uploads the
  three installers to the release (contents: write for that job only);
  the browser job is skipped there since pages.yml publishes it.

Checked locally: mkdocs --strict passes; the combined site served under
/QuantUI/ loads the Viewer from /QuantUI/viewer/app/ in headless Chromium
(UI in ~25 s, zip upload, Freq analysis renders). Locally Pyodide was
self-hosted because jsDelivr is blocked here; the CDN path itself is not
exercised until the site is live. pages.yml only runs on main, so the
deploy itself is untested until merge.

Contributions:
- Claude (Opus 5.5): code edits, review, and conceptual discussion
- Jonathan Schultz: overall vision, planning, review, and orchestration

Co-authored-by: Jonathan Schultz <nccu-schultz-lab@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The planning milestone for the viewer was renumbered to M-VIEWER (was
"M-LITE" when 72093fc was written); keep the workflow comment in step.

Contributions:
- Claude (Opus 5.5): code edits, review, and conceptual discussion
- Jonathan Schultz: overall vision, planning, review, and orchestration

Co-authored-by: Jonathan Schultz <nccu-schultz-lab@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
CI's `mypy --ignore-missing-imports quantui/` flagged two errors from
this branch:
- app.py: _viewer_folder_bar was not declared among QuantUIApp's
  TYPE_CHECKING attributes (it is set by app_viewer.build_viewer_folder_bar).
- app_launcher._viewer_notebook returned the Any from json.loads as a dict.

mypy now reports no issues in 102 source files.

Contributions:
- Claude (Opus 5.5): code edits, review, and conceptual discussion
- Jonathan Schultz: overall vision, planning, review, and orchestration

Co-authored-by: Jonathan Schultz <nccu-schultz-lab@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Brings PR #138 level with main: two dependabot bumps in pyproject.toml
(numba <0.69 in the pyfock extra, mypy <2.5 in dev). No conflicts; no
viewer code touched.

Contributions:
- Claude (Opus 5.5): code edits, review, and conceptual discussion
- Jonathan Schultz: overall vision, planning, review, and orchestration

Co-authored-by: Jonathan Schultz <nccu-schultz-lab@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@NCCU-Schultz-Lab
NCCU-Schultz-Lab merged commit 149834f into main Oct 9, 2026
6 checks passed
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.

1 participant