# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## What This Project Does A Python package that generates responsive static HTML galleries from scientific plot collections (PDFs and HTMLs). It converts PDFs to PNGs via ImageMagick, organizes plots hierarchically, propagates YAML/JSON metadata through directory trees, and renders everything via a Jinja2 template into a static website served from a web directory. ## Commands ```bash # Install the package (editable) pip install -e ".[dev]" # Run all tests pytest tests/ # Run a single test file pytest tests/test_generate_gallery.py -v # Run a single test by name pytest tests/test_generate_gallery.py::test_needs_update_missing_target -v # Generate gallery (CLI entry point after install) gallery --config config.yaml --verbose # Generate gallery (legacy script) python generate_gallery.py --verbose # Incremental update for one source only gallery --source /path/to/plots --verbose # Clean regeneration gallery --clean --verbose # Serve output locally python -m http.server 8000 -d /web/kschmidt/public_html/ ``` Code style: black with `line-length = 120`. ## Architecture ### Execution Flow ``` generate() [api.py] └── copy_assets() [builder.py] — copies assets/ to web output dir └── get_template() [builder.py] — loads gallery/templates/gallery.html └── build_gallery() [builder.py] — recursive per-source-directory walk └── load_folder_metadata() [utils/metadata.py] └── merge_metadata() [utils/metadata.py] — inherits from parent └── process_plot_files() [utils/processing.py] — PDF→PNG, copy files └── save_metadata_cache() [utils/metadata.py] └── render_gallery_page() [utils/processing.py] — writes index.html └── recurse into subdirs ``` ### Key Files | File | Role | |------|------| | `gallery/api.py` | `generate()` — primary public entry point; orchestrates everything | | `gallery/builder.py` | `build_gallery()` — recursive traversal; `get_template()`, `copy_assets()` | | `gallery/config.py` | `GalleryConfig`, `GallerySource`, `GalleryDefaults` dataclasses; YAML loading | | `gallery/cli.py` | CLI (`gallery` command) wrapping `generate()` | | `gallery/utils/processing.py` | PDF→PNG via ImageMagick subprocess; `needs_update()` timestamp check; `render_gallery_page()` | | `gallery/utils/metadata.py` | Load/merge/cache YAML+JSON metadata; per-plot metadata resolution | | `gallery/utils/stats.py` | Directory size/count statistics | | `gallery/templates/gallery.html` | Single Jinja2 template for all gallery pages | | `gallery/assets/js/` | Vanilla JS modules loaded as ES modules; `GalleryApp` in `gallery-app.js` orchestrates all managers | | `gallery/assets/css/` | Modular CSS; `main.css` imports all others via `@import` | | `config.yaml` | Local deployment config (paths are machine-specific) | ### Config File Format The YAML config uses a specific structure (not flat — must match `GalleryConfig.from_yaml()`): ```yaml paths: web_folder: "/web/user/public_html" # required gallery: plot_root: "gallery" png_dpi: 400 sources: - name: "my_plots" path: "/path/to/plots" metadata: cache_enabled: true inherit_from_parent: true ``` ### Incremental Updates `needs_update(source, target)` uses a **30-second buffer** on mtime comparisons to handle filesystem timing. This is intentional — avoid tightening it. When `source_to_update` is passed to `generate()`, only that source's subdirectory is deleted and rebuilt; all other sources stay intact and the root index is re-rendered to include them. ### Metadata Inheritance `metadata.yaml` (or `.yml`/`.json`) in any source directory is loaded and **merged with parent metadata** (`inherit_from_parent=True` by default). Child directories override parent keys. Per-plot overrides can live in `.yaml` files alongside the plot. ### Frontend (Static JS/CSS) The frontend is vanilla ES modules — no build step. `assets/js/main.js` imports `GalleryApp` from `gallery-app.js`, which instantiates all manager classes (`ThemeManager`, `SearchManager`, `NavigationManager`, etc.). Each manager is self-contained. The template embeds gallery data as JSON in the page; JS reads it at runtime. ### Deployment The project ships a `Singularity.def` / `web.sif` Apptainer container for HPC environments. CI (`.gitlab-ci.yml`) builds the container and runs pytest inside it. For local development the `.venv` is sufficient.