From 86bbcfe8143ddca41284b4e6c019e740275e29c6 Mon Sep 17 00:00:00 2001 From: Daniel Rice Date: Thu, 13 Aug 2026 21:18:33 +0100 Subject: [PATCH 1/3] Playground: add CDN-based extend-uniprot preset so the extends: demo works on the hosted site --- docs/pages-site.md | 3 ++- src/playground/__spec__/presets.spec.ts | 27 ++++++++++++++++++- src/playground/presets.ts | 36 ++++++++++++++++++++++--- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/docs/pages-site.md b/docs/pages-site.md index 9131745..6a98c4d 100644 --- a/docs/pages-site.md +++ b/docs/pages-site.md @@ -53,7 +53,8 @@ Which examples are surfaced is curated for a single hosted page (the rationale i - `basic` and `inline-data` render fully standalone. - `csv` (a single standalone track — one row, no group) and `json` (a live UniProt API track next to the BYO file) are bring-your-own-file. The examples reference `data: ./hotspots.*`, which the loader resolves against the *page*, not the config's directory (see the path-resolution caveat in `examples/README.md`). So `presets.ts` repoints them at the site-absolute `/protvista/sample-data/hotspots.*`, served from `docs/public/sample-data/` (copies of `examples/csv|json/hotspots.*`). That is what makes the file-backed presets render on the playground page. -- `extend-default` is omitted: it `extends: /src/default-config.yaml`, which the built `site/` bundle does not serve, so it can only load under the dev server. `tsv` (same shape as `csv`) and `bed` (niche) are omitted for brevity — add them to `PRESETS` if wanted. +- `extend-uniprot` (from `starter-kit/recipes/`) is surfaced: it layers a custom track on the full default viewer via `extends:`, pinned to the jsDelivr `dist/default-config.yaml` URL, which the element fetches over the network at render time — so it works on the hosted page. `presets.spec.ts` loads it offline by substituting `src/default-config.yaml` (the `starter-kit.spec.ts` pattern). Its `./data/` sample path is repointed at the served `hotspots.csv`. +- `extend-default` is omitted: it `extends: /src/default-config.yaml`, which the built `site/` bundle does not serve, so it can only load under the dev server (`extend-uniprot` is the hosted-page-friendly variant). `tsv` (same shape as `csv`) and `bed` (niche) are omitted for brevity — add them to `PRESETS` if wanted. ## Q2 "Documentation and training" deliverables (done) diff --git a/src/playground/__spec__/presets.spec.ts b/src/playground/__spec__/presets.spec.ts index 6a861e8..7fa3c00 100644 --- a/src/playground/__spec__/presets.spec.ts +++ b/src/playground/__spec__/presets.spec.ts @@ -4,6 +4,9 @@ * broken seed can never reach the playground UI. */ import { describe, it, expect } from 'vitest'; +import { readFile } from 'node:fs/promises'; +import { resolve, dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; import { loadConfig } from '../../schema/load.js'; import { createRegistry } from '../../schema/registry.js'; import { @@ -13,6 +16,27 @@ import { isDevPreset, } from '../presets.js'; +// This spec lives at src/playground/__spec__/ → three levels up is the repo root. +const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '../../..'); + +/** + * The `extend-uniprot` preset's `extends:` is a version-pinned jsDelivr URL + * whose release is not published in CI. Mirror `starter-kit.spec.ts`: + * substitute this repo's `src/default-config.yaml` (which the build copies + * verbatim to the `dist/` file jsDelivr serves) so the seed loads offline. + * Presets without an `extends:` never invoke this fetcher. + */ +const extendsFetcher = async (ref: string): Promise => { + if (/^https?:\/\//i.test(ref)) { + expect( + ref.endsWith('/dist/default-config.yaml'), + `unexpected remote extends target: ${ref}` + ).toBe(true); + return readFile(join(REPO_ROOT, 'src/default-config.yaml'), 'utf8'); + } + throw new Error(`unexpected local extends target: ${ref}`); +}; + describe('presets', () => { it('exposes the default preset', () => { expect(getPreset(DEFAULT_PRESET_ID)).toBeDefined(); @@ -31,13 +55,14 @@ describe('presets', () => { loadConfig(preset.config, { accession: preset.accession, registry: createRegistry(), + extendsFetcher, }) ).resolves.toBeDefined(); } ); it('file-backed presets point at the served sample data, not a bare page-relative file', () => { - for (const id of ['csv', 'json']) { + for (const id of ['csv', 'json', 'extend-uniprot']) { const preset = getPreset(id); expect(preset).toBeDefined(); // Repointed to the served /protvista/sample-data/ path so it loads. diff --git a/src/playground/presets.ts b/src/playground/presets.ts index 58a5602..ae6d7bb 100644 --- a/src/playground/presets.ts +++ b/src/playground/presets.ts @@ -21,11 +21,16 @@ * Those files are served from `docs/public/sample-data/` (copies of the * canonical `examples/csv|json/hotspots.*`). That is the only edit from * verbatim, and it makes the presets render on the native Astro page. + * - `extend-uniprot` (from `starter-kit/recipes/`) layers a custom track + * on the full default viewer via `extends:`. It pins the jsDelivr + * `dist/default-config.yaml` URL — fetched over the network at render + * time — so it resolves on the hosted page, unlike `examples/extend-default` + * whose `extends: /src/default-config.yaml` only loads under the dev + * server (the built `site/` bundle does not serve `/src/`). Its `./data/` + * sample is repointed at the served `hotspots.csv` like the presets above. * - `extend-default` / `tsv` / `bed` are intentionally omitted: - * `extend-default` extends `/src/default-config.yaml`, which the - * built `site/` bundle does not serve (see the note in that example - * and examples/README.md); `tsv` duplicates `csv`'s shape and `bed` - * is niche. + * `extend-default` is the dev-only `/src/`-extends variant just described; + * `tsv` duplicates `csv`'s shape and `bed` is niche. * * `__spec__/presets.spec.ts` loads every preset through `loadConfig`, so * a broken seed can never ship. @@ -35,6 +40,11 @@ import basicConfig from '../../examples/basic/config.yaml?raw'; import inlineDataConfig from '../../examples/inline-data/config.yaml?raw'; import csvConfig from '../../examples/csv/config.yaml?raw'; import jsonConfig from '../../examples/json/config.yaml?raw'; +// The CDN-`extends:` recipe from the Starter Kit. Its base config is a +// version-pinned jsDelivr URL, so — unlike examples/extend-default, which +// extends the dev-only `/src/` path — it resolves on the hosted playground +// (the element fetches it over the network at render time). +import extendUniprotConfig from '../../starter-kit/recipes/extend-uniprot.yaml?raw'; import { DEFAULT_ACCESSION } from './url-state.js'; // Repoint a file-backed example's page-relative data path at the sample data @@ -47,6 +57,15 @@ const withServedData = (config: string): string => 'data: /protvista/sample-data/$1' ); +// The extend-uniprot recipe ships its sample under `./data/`; repoint it at the +// same served hotspots.csv the csv/json presets use so it renders on the page. +// (Its `extends:` jsDelivr URL is fetched over the network at render time.) +const withServedExtendsData = (config: string): string => + config.replace( + 'data: ./data/hotspots-extends.csv', + 'data: /protvista/sample-data/hotspots.csv' + ); + export interface Preset { /** Stable id used in shareable links (`#preset=`). */ id: string; @@ -97,6 +116,15 @@ export const PRESETS: readonly Preset[] = [ config: withServedData(jsonConfig), accession: DEFAULT_ACCESSION, }, + { + id: 'extend-uniprot', + label: 'Extend UniProt (your track + the full default viewer)', + description: + 'Your own track layered on the entire default UniProt viewer via ' + + 'extends: — fetches the version-pinned base config over the network.', + config: withServedExtendsData(extendUniprotConfig), + accession: DEFAULT_ACCESSION, + }, ]; /** From 72933aec2d8cb52177463065acf71dacfdcb86ac Mon Sep 17 00:00:00 2001 From: Daniel Rice Date: Thu, 13 Aug 2026 22:40:06 +0100 Subject: [PATCH 2/3] Playground: deep-link tutorial to extend-uniprot preset; broaden preset data-path assertion --- docs/src/content/docs/tutorial.md | 7 ++++--- src/playground/__spec__/presets.spec.ts | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/src/content/docs/tutorial.md b/docs/src/content/docs/tutorial.md index d0dbb5b..cc5cef1 100644 --- a/docs/src/content/docs/tutorial.md +++ b/docs/src/content/docs/tutorial.md @@ -174,9 +174,10 @@ merge rules and the full caveat. :::tip[Try it live] Open the [full default viewer](/protvista/playground/#preset=uniprot-default) to -see the base you're extending. To see it combined with your track, switch -`extends:` to the CDN URL above — that form resolves from any page, once 5.0.0 -ships. +see the base you're extending, then open the +[extended viewer](/protvista/playground/#preset=extend-uniprot) to see your own +track layered on top of it — that preset uses the CDN `extends:` URL above, so it +resolves right on the hosted page. ::: ![The full default UniProt viewer for P05067 with an additional group labelled My lab at the bottom, containing the lab's own Hotspots track.](../../assets/screenshots/tutorial-extended.png) diff --git a/src/playground/__spec__/presets.spec.ts b/src/playground/__spec__/presets.spec.ts index 7fa3c00..3a3c5f0 100644 --- a/src/playground/__spec__/presets.spec.ts +++ b/src/playground/__spec__/presets.spec.ts @@ -67,7 +67,9 @@ describe('presets', () => { expect(preset).toBeDefined(); // Repointed to the served /protvista/sample-data/ path so it loads. expect(preset!.config).toContain('/protvista/sample-data/hotspots.'); - expect(preset!.config).not.toMatch(/data:\s*\.\/hotspots\./); + // No bare relative `data:` path survives (covers both `./hotspots.*` + // and extend-uniprot's `./data/hotspots-extends.csv`). + expect(preset!.config).not.toMatch(/data:\s*\.\//); } }); }); From 60c49f6197bb99b28510a54835c4ad40c24683f0 Mon Sep 17 00:00:00 2001 From: Daniel Rice Date: Thu, 13 Aug 2026 23:30:53 +0100 Subject: [PATCH 3/3] Blog: clarify Starter Kit paragraph and correct the config-check claim --- docs/src/content/docs/blog/protvista-5.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/src/content/docs/blog/protvista-5.md b/docs/src/content/docs/blog/protvista-5.md index 5517220..dd5b461 100644 --- a/docs/src/content/docs/blog/protvista-5.md +++ b/docs/src/content/docs/blog/protvista-5.md @@ -82,14 +82,14 @@ nothing to install. Type an accession, edit the configuration, watch the visualisation change. Every view has its own web address, so you can send a colleague exactly what you are looking at. -When you want to keep it, the +When you want to keep a view you've built, the [Starter Kit](https://github.com/ebi-webcomponents/protvista-starter-kit) is a -page and a configuration already wired together: select **Use this template**, -put your data file in the `data` folder, edit `config.yaml`, and switch on -GitHub Pages. That -gives you a web page you can share, with nothing installed. Your configuration -is checked each time you save a change, so a mistyped field is flagged there and -then rather than leaving you with an empty track and no explanation. +page and a configuration already wired together. Select **Use this template**, +drop your data file into the `data` folder, edit `config.yaml`, and switch on +GitHub Pages — that gives you a shareable web page with nothing installed. Each +time you save a change, an automatic check validates `config.yaml` against +ProtVista's schema, so a mistyped or unknown field is caught and named for you +rather than slipping through unnoticed. ## Getting it