Skip to content

feat: generate viewer links dynamically for Zarr datasets#307

Open
allison-truhlar wants to merge 127 commits into
mainfrom
viewers-config
Open

feat: generate viewer links dynamically for Zarr datasets#307
allison-truhlar wants to merge 127 commits into
mainfrom
viewers-config

Conversation

@allison-truhlar

@allison-truhlar allison-truhlar commented Feb 2, 2026

Copy link
Copy Markdown
Collaborator

Clickup id: 86advt10e

This PR implements dynamic viewer configuration for Zarr datasets, using the @bioimagetools/capability-manifest library for automatic dataset-viewer compatibility detection. The PR has been edited since it was first created to support both build-time (original PR) and runtime (new) configuration. The latter will allow the customization of viewers in system installs that use Fileglancer from Pypi, using a new FGC_VIEWRES_CONFIG .env variable.

Changes

  • Created viewers.config.yaml with documentation for customizing viewer configuration. This file specifies available viewers for a Fileglancer deployment. The default configuration is our current viewers: Neuroglancer, Avivator, Vol-E, and OME-Zarr Validator. Default configuration is bundled at build time.

  • Runtime config API endpoint - added GET /api/viewers-config that serves a YAML config file from a path set via FGC_VIEWERS_CONFIG (or viewers_config in config.yaml). Returns 404 when not configured, allowing the frontend to fall back to the bundled default.

  • Added ViewersContext for managing viewer configuration. This context:

    • Loads and validates viewer configuration from YAML at initialization
    • Fetches capability manifests using @bioimagetools/capability-manifest library
    • Provides getViewersCompatibleWithImage() function to determine dataset-viewer compatibility using the capability-manifest library for compatibility detection
  • Refactored useZarrMetadata to dynamically generate openWithToolUrls using ViewersContext. URLs are generated only for compatible viewers based on the getCompatibleViewers function.

  • Updated DataToolLinks to render viewer buttons dynamically by mapping over valid viewers from ViewersContext. The "Copy URL" tool is always available when a data link is available.

  • Added unit tests for config parsing and component tests for DataToolLinks. Added backend tests for the /api/viewers-config endpoint.

  • Updated documentation with a ViewersConfiguration.md guide and updated CLAUDE.md with viewer configuration instructions.

- Custom viewers (validator, vol-e) now check for multiscales
- Ensures viewers only display for OME-Zarr datasets, not plain Zarr arrays
- Update DataToolLinks alt text to use displayName for E2E test compatibility
Add useCallback to memoize the getCompatibleViewers function in
ViewersContext to prevent unnecessary recalculations and improve
performance when filtering compatible viewers based on metadata.
Add more detailed error logging and ensure graceful degradation when:
- Capability manifests fail to load
- Viewer configuration parsing fails
- No valid viewers are configured

The application will continue with an empty viewer list and clear
console messages to help users troubleshoot configuration issues.

@neomorphic neomorphic left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good to me. I was able to enable and disable a viewer using the viewers.config.yaml. I added a few comments about some the code I wasn't sure on.

Comment thread frontend/src/config/viewerLogos.ts Outdated
Comment thread frontend/src/queries/n5Queries.ts
Comment thread frontend/src/hooks/useN5Metadata.ts
Comment thread frontend/src/config/viewersConfig.ts Outdated
Comment thread frontend/src/config/viewersConfig.ts
Comment thread frontend/src/contexts/ViewersContext.tsx Outdated
- previously, if a logo path wasn't found, a 404 URL was still created, so it was never falling through to the fallback_logo.
- includes checking that a fallback_logo is used when no logo is found for a viewer
@allison-truhlar

allison-truhlar commented Apr 15, 2026

Copy link
Copy Markdown
Collaborator Author

@krokicki This is ready for you to look at again. I made notes on all your comments about the commits that addressed them. I also modified the PR description slightly to reflect the current state of the PR.

Note that it should be reviewed in conjunction with the changes to the capability-manifest library proposed in this PR.

One outstanding question I have is how to implement the rfcs_supported check (see full question here).

allison-truhlar and others added 8 commits May 7, 2026 09:47
- passes through to useZarrMetadata, where validateViewer is run from compatibility manifest library
- define "Metadata" type in omezarr-helper.ts as the OmeZarrMetadata type from @bioimagetools/capability-manifest, plus {arr, shapes, scales, zarrVersion}. This eliminates the need to recast metadata in useZarrMetadata. Also normalize ome-zarr.js output at the fetch boundary and switch Neuroglancer helpers to user the canonical MultiscaleMetadata/OmeroMetadata types from @bioimagetools/capability-manifest

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a dynamic, manifest-driven “Open with…” viewer system for Zarr datasets, enabling both build-time and runtime viewer configuration and using @bioimagetools/capability-manifest to determine dataset/viewer compatibility.

Changes:

  • Adds a backend endpoint (GET /api/viewers-config) to optionally serve a runtime YAML viewer config.
  • Introduces a frontend ViewersContext + query layer to load/validate viewer config, fetch capability manifests, and filter compatible viewers.
  • Refactors Zarr metadata handling and UI rendering to generate viewer links/buttons dynamically, and updates unit/component/E2E tests and docs accordingly.

Reviewed changes

Copilot reviewed 40 out of 46 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
tests/test_endpoints.py Adds backend tests for /api/viewers-config responses.
frontend/vite.config.ts Adds build-time override resolution for bundled viewers.config.yaml via aliasing.
frontend/ui-tests/utils/navigation.ts Stabilizes navigation helpers by waiting for UI readiness.
frontend/ui-tests/tests/load-zarr-files.spec.ts Updates assertions to match dynamic viewer button rendering (alt-text based).
frontend/ui-tests/tests/data-link-operations.spec.ts Updates viewer/link expectations and reduces custom timeouts relying on global expect timeout.
frontend/ui-tests/playwright.config.js Sets a global expect.timeout for Playwright tests.
frontend/src/queries/zarrQueries.ts Expands Zarr/OME-NGFF version detection and enriches metadata for capability checks (codecs/compressor/plate/well).
frontend/src/queries/viewersConfigQueries.ts Adds query to load runtime viewers config (fallback to bundled config).
frontend/src/queries/n5Queries.ts Simplifies N5 tool URL typing in line with dynamic viewers approach.
frontend/src/omezarr-helper.ts Extends Metadata type with fields needed for capability validation.
frontend/src/layouts/MainLayout.tsx Wires in ViewersProvider at the app layout level.
frontend/src/hooks/useZarrMetadata.ts Generates openWithToolUrls dynamically based on compatible viewers + manifests.
frontend/src/hooks/useN5Metadata.ts Simplifies N5 tool URL generation.
frontend/src/contexts/ViewersContext.tsx New context that loads config, fetches manifests, validates viewers, and computes compatibility.
frontend/src/config/viewersConfig.ts Adds YAML parsing + Zod validation for viewer config entries.
frontend/src/config/viewers.config.yaml Adds default bundled viewer config (manifest URLs).
frontend/src/config/resolveViewersConfigPath.ts Adds build-time override resolution for frontend/viewers.config.yaml.
frontend/src/components/ui/Dialogs/dataLinkUsage/DataLinkUsageDialog.tsx Updates Zarr detection/version selection logic to new helpers.
frontend/src/components/ui/BrowsePage/ZarrPreview.tsx Renames/retargets props for available Zarr version reporting.
frontend/src/components/ui/BrowsePage/ZarrMetadataTable.tsx Updates version display logic and switches to app logger.
frontend/src/components/ui/BrowsePage/FileBrowser.tsx Uses new Zarr marker detection helper and renames available versions prop.
frontend/src/components/ui/BrowsePage/DataToolLinks.tsx Renders viewer buttons dynamically from ViewersContext and adds logo fallback behavior.
frontend/src/tests/unitTests/zarrVersionDetection.test.ts Updates unit tests for new Zarr/NGFF version helper functions.
frontend/src/tests/unitTests/viewersConfigOverride.test.ts Adds unit tests for build-time config override path resolution.
frontend/src/tests/unitTests/viewersConfig.test.ts Adds unit tests for YAML parsing/validation.
frontend/src/tests/test-utils.tsx Wraps test render utilities with ViewersProvider.
frontend/src/tests/mocks/viewers.config.yaml Adds a fixture config for override tests.
frontend/src/tests/mocks/handlers.ts Adds MSW handler for /api/viewers-config 404 fallback behavior.
frontend/src/tests/componentTests/ZarrMetadataTable.test.tsx Updates component test expectations for new version array formatting.
frontend/src/tests/componentTests/DataToolLinks.test.tsx Adds component tests for dynamic viewer rendering and error scenarios.
frontend/package.json Adds dependencies for capability manifests, YAML parsing, and Zod.
frontend/CLAUDE.md Updates frontend-specific development guidance (notably URL/build conventions).
fileglancer/settings.py Adds viewers_config setting for runtime config path.
fileglancer/server.py Adds /api/viewers-config endpoint to serve YAML config when configured.
docs/ViewersConfiguration.md Adds end-user/admin documentation for configuring viewers.
docs/Development.md Adds a development doc section describing viewer configuration workflow.
clean.sh Expands cleanup to include more frontend caches/artifacts.
CLAUDE.md Adds top-level documentation about the viewer configuration system.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/test_endpoints.py
Comment thread tests/test_endpoints.py
Comment thread frontend/vite.config.ts Outdated
Comment thread frontend/vite.config.ts Outdated
Comment thread frontend/src/hooks/useZarrMetadata.ts Outdated
Comment thread CLAUDE.md
Comment thread frontend/src/config/viewers.config.yaml Outdated
…liable testing

- the old appraoch used a real filesystem fixture file in src/__tests__/mocks/ to make existsSync return true. This broke in the Windows testing environment as the vite.config.ts enabled nodePolyfills for path, so in the test environment path was the posix polyfill. path.resolve behaves differently in Windows and in posix, and existsSync returned false for the mock file. By injecting the existence as an optional parametr, the test can control the outcome of existsSyn without a fixture file. The updated test mostly just pins the desired behavior that the override file for viewers.config.yaml  should end up under frontend/viewers.config.yaml (not frontend/src/config/viewers.config.ymal - that's the default file) and that it beats the committed default.
…ifest main branch

previously pointed to a feature branch, which has been merged
__dirname does not work when the the vite config is loaded as an ES module. move resolution to fallback above the viewersConfigPath so that function doesn't throw
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.

5 participants