Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
5f1a624
feat: add viewers configuration template
allison-truhlar Feb 2, 2026
d0bf67c
feat: add viewer configuration types and YAML parser
allison-truhlar Feb 2, 2026
6620486
feat: add ViewersContext for dynamic viewer configuration
allison-truhlar Feb 2, 2026
24e030e
refactor: make OpenWithToolUrls dynamic using Record type
allison-truhlar Feb 2, 2026
9f5d1eb
docs: add comment explaining type assertion in parseViewersConfig
allison-truhlar Feb 2, 2026
28173ea
refactor: use ViewersContext in useZarrMetadata for dynamic viewer URLs
allison-truhlar Feb 2, 2026
3fd00d7
refactor: use ViewersContext in DataToolLinks for dynamic viewer icons
allison-truhlar Feb 2, 2026
9160eb4
chore: prettier formatting
allison-truhlar Feb 2, 2026
ecf1b62
chore: update logo names; add fallback logo
allison-truhlar Feb 2, 2026
7698348
docs: add viewers configuration guide
allison-truhlar Feb 2, 2026
69ae1b6
fix: require OME metadata for custom viewers without manifests
allison-truhlar Feb 2, 2026
3e15ae7
fix: update locators to match new defaults
allison-truhlar Feb 2, 2026
4292cdf
efactor: use committed config file instead of template
allison-truhlar Feb 2, 2026
d545e28
docs: add viewers configuration to CLAUDE.md
allison-truhlar Feb 2, 2026
d5c24e9
refactor: remove unused keys from N5OpenWithToolUrls type
allison-truhlar Feb 2, 2026
e6e1cd4
perf: memoize getCompatibleViewers function
allison-truhlar Feb 2, 2026
be8bbd5
feat: improve error handling in viewers initialization
allison-truhlar Feb 2, 2026
58d163d
chore: fix eslint warnings
allison-truhlar Feb 2, 2026
5ae87ae
fix: add validation for ome-zarr verison numbers
allison-truhlar Feb 2, 2026
4b6db2e
tests: unit tests for the viewer config parsing func
allison-truhlar Feb 2, 2026
fef30cd
tests: component tests for DataToolLinks
allison-truhlar Feb 2, 2026
82b9cfb
Merge branch 'main' into viewers-config
allison-truhlar Feb 2, 2026
d2a4e84
fix: add ViewersProvider to unit test setup for zarr tests
allison-truhlar Feb 3, 2026
b0e97a3
fix: change how logos urls are found
allison-truhlar Feb 3, 2026
8b2af82
tests: add unit tests for the getViewerLogo func
allison-truhlar Feb 3, 2026
1ebfb3e
chore: prettier formatting
allison-truhlar Feb 3, 2026
5cacb67
refactor: move valid ome zarr versions to config file
allison-truhlar Feb 3, 2026
7ec34ec
refactor: add zod for viewer config validation
allison-truhlar Feb 3, 2026
30f0853
refactor: update tests for ome zarr version in config; zod error msgs
allison-truhlar Feb 3, 2026
bc867c8
docs: add valid_ome_zarr_versions to viewer config docs
allison-truhlar Feb 3, 2026
83dd34f
refactor: move metadata.multiscale check back to useZarrMetadata hook
allison-truhlar Feb 3, 2026
da8dd86
feat: add capability manifest files for all viewers
allison-truhlar Feb 9, 2026
10bafa7
refactor: simplify viewers config to manifest_url entries
allison-truhlar Feb 9, 2026
cd05c36
refactor: update ViewersContext for new capability-manifest API
allison-truhlar Feb 9, 2026
d892ca1
test: rewrite viewersConfig unit tests for manifest_url API
allison-truhlar Feb 9, 2026
d5261a5
test: rewrite DataToolLinks component tests for manifest_url API
allison-truhlar Feb 9, 2026
05957ba
docs: update viewer configuration docs for manifest-based system
allison-truhlar Feb 9, 2026
0fce05c
chore: prettier formatting
allison-truhlar Feb 9, 2026
4ab4fe6
chore: bump capability-manifest version
allison-truhlar Feb 9, 2026
d7ea968
fix: update capability-manifest in package-lock.json
allison-truhlar Feb 12, 2026
c104803
tests: fix vole to vol-e in expected text
allison-truhlar Feb 12, 2026
0bd1731
test: remove timeout to rely on longer global timeout value
allison-truhlar Feb 12, 2026
2f86cab
chore: bump capability-manifest version
allison-truhlar Feb 12, 2026
9c629d2
fix: use relative paths for viewer manifest URLs to fix CI failures
allison-truhlar Feb 12, 2026
f4e7f51
fix: capitalize viewer names in manifests (validator → Validator, vol…
allison-truhlar Feb 12, 2026
3080e8a
fix: use viewer label for logo alt text and update test locators to m…
allison-truhlar Feb 12, 2026
06c0ab1
chore: prettier formatting
allison-truhlar Feb 12, 2026
7e46003
Merge branch 'main' into viewers-config
allison-truhlar Feb 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
},
"mounts": [
"source=${localEnv:HOME}/.claude,target=/home/vscode/.claude,type=bind",
"source=fileglancer-pixi,target=${containerWorkspaceFolder}/.pixi,type=volume"
"source=fileglancer-pixi,target=${containerWorkspaceFolder}/.pixi,type=volume",
"source=/groups/scicompsoft/home/truhlara/gh-repos/capability-manifest,target=/workspaces/capability-manifest,type=bind"
],
"remoteEnv": {
"NODE_OPTIONS": "--max-old-space-size=4096",
Expand Down
16 changes: 16 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,22 @@ Key settings:
- `db_url`: Database connection string
- SSL certificates for HTTPS mode

## Viewers Configuration

Fileglancer uses a manifest-based viewer configuration system. Each viewer is defined by a **capability manifest** (a YAML file describing the viewer's name, URL template, and capabilities). The config file lists manifest URLs and optional overrides.

- **Configuration file**: `frontend/src/config/viewers.config.yaml` -- lists viewers by `manifest_url` with optional `instance_template_url`, `label`, and `logo` overrides
- **Manifest files**: `frontend/public/viewers/*.yaml` -- capability manifest YAML files defining each viewer's identity and supported features
- **Compatibility**: Handled by the `@bioimagetools/capability-manifest` library, which checks dataset metadata against manifest capabilities at runtime
- **Documentation**: See `docs/ViewersConfiguration.md`

To customize viewers:

1. Edit `frontend/src/config/viewers.config.yaml` (add/remove `manifest_url` entries, override URLs or labels)
2. Rebuild application: `pixi run node-build`

The default configuration includes Neuroglancer, Avivator, OME-Zarr Validator, and Vol-E viewers. The config file is bundled at build time.

## Pixi Environments

- `default`: Standard development
Expand Down
22 changes: 18 additions & 4 deletions docs/Development.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,16 @@ By default, Fileglancer provides access to each user's home directory without re

```yaml
file_share_mounts:
- "~/" # User's home directory (default)
- "~/" # User's home directory (default)
```

You can add additional file share paths by editing your `config.yaml`:

```yaml
file_share_mounts:
- "~/" # User's home directory
- "/groups/scicomp/data" # Shared data directory
- "/opt/data" # Another shared directory
- "~/" # User's home directory
- "/groups/scicomp/data" # Shared data directory
- "/opt/data" # Another shared directory
```

**How Home Directories Work:**
Expand All @@ -66,6 +66,20 @@ file_share_mounts:

Instead of using the `file_share_mounts` setting, you can configure file share paths in the database. This is useful for production deployments where you want centralized management of file share paths. To use the paths in the database, set `file_share_mounts: []`. See [fileglancer-janelia](https://github.com/JaneliaSciComp/fileglancer-janelia) for an example of populating the file share paths in the database, using a private wiki source.

### Viewers Configuration

Fileglancer supports dynamic configuration of OME-Zarr viewers through `viewers.config.yaml`. This allows you to customize which viewers are available in your deployment and configure custom viewer URLs.

**Quick Setup:**

1. Edit the configuration file at `frontend/src/config/viewers.config.yaml` to enable/disable viewers or customize URLs

2. Rebuild the application: `pixi run node-build` or use watch mode in development: `pixi run dev-watch`

**Note:** The configuration file is bundled at build time, so changes require rebuilding the application. The default configuration includes Neuroglancer, Avivator, OME-Zarr Validator, and Vol-E viewers.

For detailed configuration options, examples, and documentation on adding custom viewers, see [ViewersConfiguration.md](ViewersConfiguration.md).

### Feature Flags

Optional features can be enabled via Vite environment variables. Create or edit `frontend/.env`:
Expand Down
252 changes: 252 additions & 0 deletions docs/ViewersConfiguration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
# Viewers Configuration Guide

Fileglancer supports dynamic configuration of OME-Zarr viewers. This allows administrators to customize which viewers are available in their deployment, override viewer URLs, and control how compatibility is determined.

## Overview

The viewer system is built on capability manifests:

- **`viewers.config.yaml`**: Configuration file listing viewers and their manifest URLs
- **Capability manifest files**: YAML files describing each viewer's name, URL template, and capabilities
- **`@bioimagetools/capability-manifest`**: Library that loads manifests and checks dataset compatibility
- **`ViewersContext`**: React context that provides viewer information to the application

Each viewer is defined by a **capability manifest** hosted at a URL. The configuration file simply lists manifest URLs and optional overrides. At runtime, the manifests are fetched, and the `@bioimagetools/capability-manifest` library determines which viewers are compatible with a given dataset based on the manifest's declared capabilities.

## Quick Start

1. Edit the configuration file at `frontend/src/config/viewers.config.yaml`
2. Rebuild the application: `pixi run node-build`

## Configuration File

### Location

`frontend/src/config/viewers.config.yaml`

**Important:** This file is bundled at build time. Changes require rebuilding the application.

### Structure

The configuration file has a single top-level key, `viewers`, containing a list of viewer entries. Each entry requires a `manifest_url` and supports optional overrides.

#### Viewer Entry Fields

| Field | Required | Description |
| ----------------------- | -------- | -------------------------------------------------------------------------------- |
| `manifest_url` | Yes | URL to a capability manifest YAML file |
| `instance_template_url` | No | Override the viewer's `template_url` from the manifest |
| `label` | No | Custom tooltip text (defaults to "View in {Name}") |
| `logo` | No | Filename of logo in `frontend/src/assets/` (defaults to `{normalized_name}.png`) |

### Default Configuration

The default `viewers.config.yaml` configures four viewers:

```yaml
viewers:
- manifest_url: "https://raw.githubusercontent.com/JaneliaSciComp/fileglancer/main/frontend/public/viewers/neuroglancer.yaml"

- manifest_url: "https://raw.githubusercontent.com/JaneliaSciComp/fileglancer/main/frontend/public/viewers/vizarr.yaml"
instance_template_url: "https://janeliascicomp.github.io/viv/"

- manifest_url: "https://raw.githubusercontent.com/JaneliaSciComp/fileglancer/main/frontend/public/viewers/validator.yaml"
label: "View in OME-Zarr Validator"

- manifest_url: "https://raw.githubusercontent.com/JaneliaSciComp/fileglancer/main/frontend/public/viewers/vole.yaml"
label: "View in Vol-E"
```

## Capability Manifest Files

Manifest files describe a viewer's identity and capabilities. The default manifests are stored in `frontend/public/viewers/` and are hosted via GitHub. You can host your own manifest files anywhere accessible via URL.

### Manifest Structure

A manifest has two sections: `viewer` (identity) and `capabilities` (what the viewer supports).

#### Example: `neuroglancer.yaml`

```yaml
viewer:
name: "Neuroglancer"
version: "2.41.2"
repo: "https://github.com/google/neuroglancer"
template_url: https://neuroglancer-demo.appspot.com/#!{"layers":[{"name":"image","source":"{DATA_URL}","type":"image"}]}

capabilities:
ome_zarr_versions: [0.4, 0.5]
compression_codecs: ["blosc", "zstd", "zlib", "lz4", "gzip"]
rfcs_supported: []
axes: true
scale: true
translation: true
channels: true
timepoints: true
labels: false
hcs_plates: false
bioformats2raw_layout: false
omero_metadata: false
```

### Viewer Section

| Field | Description |
| -------------- | -------------------------------------------------------------- |
| `name` | Display name for the viewer |
| `version` | Viewer version |
| `repo` | Repository URL |
| `template_url` | URL template with `{DATA_URL}` placeholder for the dataset URL |

### Capabilities Section

| Field | Type | Description |
| ----------------------- | -------- | ------------------------------------------------------------ |
| `ome_zarr_versions` | number[] | Supported OME-Zarr specification versions |
| `compression_codecs` | string[] | Supported compression codecs (e.g., "blosc", "zstd", "gzip") |
| `rfcs_supported` | string[] | Additional RFCs supported |
| `axes` | boolean | Whether axis names and units are respected |
| `scale` | boolean | Whether scaling factors on multiscales are respected |
| `translation` | boolean | Whether translation factors on multiscales are respected |
| `channels` | boolean | Whether multiple channels are supported |
| `timepoints` | boolean | Whether multiple timepoints are supported |
| `labels` | boolean | Whether labels are loaded when available |
| `hcs_plates` | boolean | Whether HCS plates are loaded when available |
| `bioformats2raw_layout` | boolean | Whether bioformats2raw layout is handled |
| `omero_metadata` | boolean | Whether OMERO metadata is used (e.g., channel colors) |

## URL Templates and `{DATA_URL}` Placeholder

The `{DATA_URL}` placeholder in a manifest's `template_url` (or a config entry's `instance_template_url`) is replaced at runtime with the actual dataset URL. Internally, `{DATA_URL}` is normalized to `{dataLink}` for consistency with the rest of the application.

For example, given this manifest `template_url`:

```
https://neuroglancer-demo.appspot.com/#!{"layers":[{"name":"image","source":"{DATA_URL}","type":"image"}]}
```

When a user clicks the viewer link for a dataset at `https://example.com/data.zarr`, the final URL becomes:

```
https://neuroglancer-demo.appspot.com/#!{"layers":[{"name":"image","source":"https://example.com/data.zarr","type":"image"}]}
```

## Configuration Examples

### Minimal: single viewer

```yaml
viewers:
- manifest_url: "https://raw.githubusercontent.com/JaneliaSciComp/fileglancer/main/frontend/public/viewers/neuroglancer.yaml"
```

### Override a viewer's URL

Use `instance_template_url` to point to a custom deployment of a viewer while still using its manifest for capability matching:

```yaml
viewers:
- manifest_url: "https://raw.githubusercontent.com/JaneliaSciComp/fileglancer/main/frontend/public/viewers/vizarr.yaml"
instance_template_url: "https://my-avivator-instance.example.com/?image_url={dataLink}"
logo: avivator.png
```

### Add a custom viewer

To add a new viewer, create a capability manifest YAML file, host it at a URL, and reference it in the config:

1. Create a manifest file (e.g., `my-viewer.yaml`):

```yaml
viewer:
name: "My Viewer"
version: "1.0.0"
repo: "https://github.com/example/my-viewer"
template_url: "https://viewer.example.com/?data={DATA_URL}"

capabilities:
ome_zarr_versions: [0.4, 0.5]
compression_codecs: ["blosc", "gzip"]
rfcs_supported: []
axes: true
scale: true
translation: true
channels: true
timepoints: false
labels: false
hcs_plates: false
bioformats2raw_layout: false
omero_metadata: false
```

2. Host the manifest at an accessible URL (e.g., in your own `frontend/public/viewers/` directory, on GitHub, or any web server).

3. Reference it in `viewers.config.yaml`:

```yaml
viewers:
- manifest_url: "https://example.com/manifests/my-viewer.yaml"
label: "Open in My Viewer"
```

4. Optionally, add a logo file at `frontend/src/assets/myviewer.png` (the normalized name, lowercase with non-alphanumeric characters removed).

## How Compatibility Works

The `@bioimagetools/capability-manifest` library handles all compatibility checking. When a user views an OME-Zarr dataset:

1. The application reads the dataset's metadata (OME-Zarr version, axes, codecs, etc.)
2. For each registered viewer, the library's `isCompatible()` function compares the dataset metadata against the manifest's declared capabilities
3. Only viewers whose capabilities match the dataset are shown to the user

This replaces the previous system where `valid_ome_zarr_versions` was a global config setting and custom viewers used simple version matching. Now all compatibility logic is driven by the detailed capabilities declared in each viewer's manifest.

## Adding Custom Viewer Logos

Logo resolution follows this order:

1. **Custom logo specified**: If you provide a `logo` field in the config entry, that filename is looked up in `frontend/src/assets/`
2. **Convention-based**: If no `logo` is specified, the system looks for `frontend/src/assets/{normalized_name}.png`, where the normalized name is the viewer's name lowercased with non-alphanumeric characters removed
3. **Fallback**: If neither is found, `frontend/src/assets/fallback_logo.png` is used

### Examples

**Using the naming convention (recommended):**

```yaml
viewers:
- manifest_url: "https://example.com/manifests/neuroglancer.yaml"
# Logo automatically resolves to @/assets/neuroglancer.png
```

Just add `frontend/src/assets/neuroglancer.png` -- no config needed.

**Using a custom logo filename:**

```yaml
viewers:
- manifest_url: "https://example.com/manifests/vizarr.yaml"
logo: "avivator.png" # Uses @/assets/avivator.png
```

## Development

When developing with custom configurations:

1. Edit `frontend/src/config/viewers.config.yaml`
2. Rebuild frontend: `pixi run node-build` or use watch mode: `pixi run dev-watch`
3. Check the browser console for viewer initialization messages

### Validation

The configuration is validated at build time using Zod schemas (see `frontend/src/config/viewersConfig.ts`). Validation enforces:

- The `viewers` array must contain at least one entry
- Each entry must have a valid `manifest_url` (a properly formed URL)
- Optional fields (`instance_template_url`, `label`, `logo`) must be strings if present

At runtime, manifests that fail to load are skipped with a warning. If a viewer has no `template_url` (neither from its manifest nor from `instance_template_url` in the config), it is also skipped.

## Copy URL Tool

The "Copy data URL" tool is always available when a data URL exists, regardless of viewer configuration.
Loading