Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@ The website of [TissUUmaps](https://github.com/TissUUmaps/TissUUmaps), a GPU-acc

The website hosts the TissUUmaps application and its documentation, both versioned by MAJOR.MINOR line. The documentation is written in the TissUUmaps repository; changes to it go there. This repository holds the rest of the website.

## Editing the website

Most pages are Markdown files that can be edited on GitHub in the browser, through their "Edit this page" link.

| Page | File |
| --------------- | -------------------------------------------------------------------------------- |
| Gallery | one folder per project in [`gallery/`](gallery/README.md) |
| Getting started | [`src/pages/getting-started.mdx`](src/pages/getting-started.mdx) |
| About | [`src/pages/about.mdx`](src/pages/about.mdx) |
| Documentation | `docs/` in the [TissUUmaps](https://github.com/TissUUmaps/TissUUmaps) repository |

The Markdown pages can use a few components for their layout, such as `<Steps>`, `<Box>`, `<Publication>` or `<Logo>`, without an import (see [`src/components/markdown`](src/components/markdown/index.ts)). Image paths start with `/`, from `static/`. The landing page is [`src/pages/index.tsx`](src/pages/index.tsx).

## Releases

On every application release, the TissUUmaps release workflow pushes the released docs to `releases/<line>/`:
Expand Down
127 changes: 73 additions & 54 deletions docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,10 @@ import type { Config } from "@docusaurus/types";
import { themes as prismThemes } from "prism-react-renderer";
import semver from "semver";

import { docsPath, loadVersions } from "./tissuumaps-docs";
import { docsPath, loadTypedocSidebar, loadVersions } from "./tissuumaps-docs";

// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...)

// The versioned TissUUmaps app is served next to the website as static files,
// so link to it by pathname instead of as a Docusaurus route
const appUrl = "pathname:///app/";

const versions = loadVersions();
if (versions.length === 0 && !docsPath) {
throw new Error(
Expand All @@ -26,6 +22,11 @@ const config: Config = {
tagline: "Spatial Biology Visualization",
favicon: "img/favicon.ico",

// Lato, the SciLifeLab typeface
stylesheets: [
"https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,300;0,400;0,700;1,400&display=swap",
],

// Future flags, see https://docusaurus.io/docs/api/docusaurus-config#future
future: {
v4: true, // Improve compatibility with the upcoming Docusaurus v4
Expand Down Expand Up @@ -59,14 +60,59 @@ const config: Config = {
docs: {
path: docsPath,
sidebarPath: "./sidebars.ts",
sidebarItemsGenerator: async ({
defaultSidebarItemsGenerator,
...args
}) => {
const items = await defaultSidebarItemsGenerator(args);
return items.map((item) =>
item.type === "category" &&
item.link?.type === "doc" &&
item.link.id === "api/index"
? {
...item,
label: "API",
link: { type: "generated-index", slug: "/category/api" },
items: loadTypedocSidebar(
args.version.contentPath,
) as typeof items,
}
: item,
);
},
includeCurrentVersion: docsPath !== undefined,
lastVersion: docsPath ? "current" : lastVersion?.line,
editUrl: ({ docPath }) =>
docPath.startsWith("api/")
? undefined
: `https://github.com/TissUUmaps/TissUUmaps/tree/main/docs/${docPath}`,
},
blog: false,
// Markdown pages in src/pages get an "Edit this page" link
pages: {
editUrl:
"https://github.com/TissUUmaps/tissuumaps.github.io/edit/main/",
},
// The gallery: one Markdown file per project in gallery/, shown as
// cards by our own list page (see gallery/README.md)
blog: {
path: "gallery",
routeBasePath: "gallery",
blogTitle: "Gallery",
blogDescription:
"Example projects. Each one opens directly in TissUUmaps, in your browser.",
blogListComponent: "@site/src/components/pages/GalleryListPage",
blogTagsPostsComponent: "@site/src/components/pages/GalleryListPage",
blogPostComponent: "@site/src/components/pages/GalleryProjectPage",
exclude: ["**/_*/**", "**/_*.md", "README.md"],
postsPerPage: "ALL",
blogSidebarCount: 0,
showReadingTime: false,
archiveBasePath: null,
feedOptions: { type: null },
onUntruncatedBlogPosts: "ignore",
editUrl:
"https://github.com/TissUUmaps/tissuumaps.github.io/edit/main/",
},
theme: {
customCss: "./src/css/custom.css",
},
Expand All @@ -78,81 +124,54 @@ const config: Config = {
colorMode: {
respectPrefersColorScheme: true,
},
// Points TissUUmaps 3 users to the legacy website while 4 is in beta
announcementBar: {
id: "tissuumaps3-legacy",
content:
'TissUUmaps 4 is in beta. Looking for TissUUmaps 3? <a href="https://tissuumaps.github.io/TissUUmaps3">Go to the TissUUmaps 3 website</a>',
backgroundColor: "var(--scilifelab-lime)",
textColor: "var(--scilifelab-dark-gray)",
},
navbar: {
title: "TissUUmaps",
logo: {
alt: "TissUUmaps Logo",
src: "img/logo.svg",
},
items: [
{
to: "/getting-started",
label: "Getting started",
position: "left",
},
{
type: "docSidebar",
sidebarId: "docsSidebar",
position: "left",
label: "Documentation",
},
{
type: "docSidebar",
sidebarId: "apiSidebar",
to: "/gallery",
label: "Gallery",
position: "left",
label: "Packages",
},
{
type: "docsVersionDropdown",
position: "right",
to: "/about",
label: "About",
position: "left",
},
{
href: "https://github.com/TissUUmaps/TissUUmaps",
label: "GitHub",
type: "docsVersionDropdown",
position: "right",
},
{
href: appUrl,
label: "App",
type: "custom-partnerLogos",
position: "right",
},
],
},
footer: {
style: "dark",
links: [
{
title: "Docs",
items: [
{
label: "Documentation",
to: "/docs/",
},
{
label: "Packages",
to: "/docs/api",
},
],
},
{
title: "Community",
items: [
{
label: "SciLifeLab BioImage Informatics Unit",
href: "https://www.scilifelab.se/units/bioimage-informatics/",
},
],
},
{
title: "More",
items: [
{
label: "GitHub",
href: "https://github.com/TissUUmaps/TissUUmaps",
},
{
label: "App",
href: appUrl,
},
],
},
],
copyright: `Copyright © ${new Date().getFullYear()} TissUUmaps Core Team. Built with Docusaurus.`,
copyright: `Copyright © ${new Date().getFullYear()} TissUUmaps Core Team.`,
},
prism: {
theme: prismThemes.github,
Expand Down
12 changes: 12 additions & 0 deletions gallery/2026-10-09-luca-7color/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: LuCa 7-color multiplex scan
description: Multiplex immunofluorescence whole-slide scan (DAPI, FITC, CY3, Texas Red, CY5), opened directly from the 2 GB QPTIFF file.
image: ./thumbnail.jpg
project: https://tissuumaps4.serve.scilifelab.se/examples/luca-7color_qptiff.tm4
tags: [Multiplex IF, QPTIFF]
data_provider: PerkinElmer (now Akoya), via the OME sample images
data_url: https://downloads.openmicroscopy.org/images/Vectra-QPTIFF/perkinelmer/PKI_scans/LuCa-7color_Scan1.qptiff
data_license: CC BY 4.0, © PerkinElmer
---

A whole-slide scan from a PerkinElmer Vectra multiplex imaging system, read directly from the QPTIFF file without conversion.
Binary file added gallery/2026-10-09-luca-7color/thumbnail.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions gallery/2026-10-10-xenium-lung/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: Human lung cancer (Xenium)
description: H&E and morphology images, cell and nucleus segmentation, 12.2M transcripts and cell boundaries, read from a SpatialData store.
image: ./thumbnail.jpg
project: https://tissuumaps4.serve.scilifelab.se/examples/xenium-lung_ome-zarr_table-per-element.tm4
tags: [Xenium, SpatialData, OME-Zarr, Parquet]
data_provider: 10x Genomics
data_url: https://www.10xgenomics.com/datasets/preview-data-ffpe-human-lung-cancer-with-xenium-multimodal-cell-segmentation-1-standard
data_license: CC BY 4.0
---

A Xenium 2.0 section of human lung cancer, stored as a SpatialData store with one table per element.
Binary file added gallery/2026-10-10-xenium-lung/thumbnail.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions gallery/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Gallery

Every folder here is one project of the Gallery page of the website. Folders starting with `_` are not shown.

## Add a project

1. Make a screenshot of the project in TissUUmaps, cropped to 16:10 (for example 800 × 500 pixels), without the side panel.
2. On GitHub, open this folder and choose **Add file > Create new file**. Name it `YYYY-MM-DD-my-project/index.md`: today's date, then a short name with lowercase letters and dashes, for example `2026-10-12-mouse-brain/index.md`. The newest projects are shown first.
3. Copy the contents of [`_template/index.md`](_template/index.md) into it and fill in the fields (see below).
4. Upload the screenshot into the new folder as `thumbnail.jpg` (**Add file > Upload files**).
5. Commit to a new branch and open a pull request. Its checks build the website; a missing field fails them with a message that names the file and the field. Once the pull request is merged, the website is updated within a few minutes.

## Fields

| Field | What to write |
| --------------- | ------------------------------------------------------------------ |
| `title` | Short name of the project |
| `description` | One or two sentences for the card |
| `image` | `./thumbnail.jpg` |
| `project` | Link to the project file (`.tm4`); the card opens it in TissUUmaps |
| `tags` | Technologies and formats, in square brackets, separated by commas |
| `data_provider` | Who made the data available |
| `data_url` | Where the data comes from |
| `data_license` | The license of the data, for example `CC BY 4.0` |

The text below the fields is optional. It appears on the project's own page and can use [Markdown](https://www.markdownguide.org/basic-syntax/): paragraphs, **bold**, links, lists and images.
12 changes: 12 additions & 0 deletions gallery/_template/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: Short name of the project
description: One or two sentences shown on the gallery card.
image: ./thumbnail.jpg
project: https://example.org/path/to/project.tm4
tags: [Technology, File format]
data_provider: Who made the data available
data_url: https://example.org/where/the/data/comes/from
data_license: CC BY 4.0
---

Optional longer text, shown on the project's own page. Remove this paragraph if there is nothing more to say.
6 changes: 5 additions & 1 deletion scripts/prepare.mts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ export function readReleases(releasesDir: string): string[] {
*
* @param options.siteDir - The site directory
* @param options.versions - The versions to prepare
* @param options.latestVersion - The version `app/` redirects to
* @param options.latestVersion - The version `app/` redirects to, keeping the
* query (e.g. `?project=…`) and the fragment
* @param options.customHtml - The HTML to insert into every app page
* @param options.assetsDir - A directory holding the app assets
* (`tissuumaps-<version>.zip`); downloaded from the releases if omitted
Expand Down Expand Up @@ -177,6 +178,9 @@ export function prepareApps(options: {
<head>
<meta charset="utf-8" />
<meta name="robots" content="noindex" />
<script>
location.replace("${latestLine}/" + location.search + location.hash);
</script>
<meta http-equiv="refresh" content="0; url=${latestLine}/" />
<title>TissUUmaps</title>
</head>
Expand Down
21 changes: 14 additions & 7 deletions scripts/prepare.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,16 @@ void test("prepares the selected versions' docs and apps", () => {
assert.ok(!existsSync(join(siteDir, "versioned_docs/version-4.0")));
const sidebars = JSON.parse(
read("versioned_sidebars/version-4.1-sidebars.json"),
) as Record<string, [{ items?: unknown }]>;
assert.deepEqual(sidebars.docsSidebar, [
{ type: "autogenerated", dirName: "." },
]);
assert.deepEqual(sidebars.apiSidebar?.[0].items, [
{ type: "doc", id: "api/core" },
]);
) as unknown;
assert.deepEqual(sidebars, {
docsSidebar: [{ type: "autogenerated", dirName: "." }],
});
// The version's typedoc sidebar, read when building its docs sidebar
assert.ok(
existsSync(
join(siteDir, "versioned_docs/version-4.1/api/typedoc-sidebar.cjs"),
),
);
// Apps with the custom HTML, and app/ redirecting to the latest stable line
assert.equal(
read("static/app/4.1/index.html"),
Expand All @@ -153,6 +156,10 @@ void test("prepares the selected versions' docs and apps", () => {
assert.ok(existsSync(join(siteDir, "static/app/4.3/index.html")));
assert.ok(!existsSync(join(siteDir, "static/app/4.0")));
assert.match(read("static/app/index.html"), /url=4\.1\/"/);
assert.match(
read("static/app/index.html"),
/location\.replace\("4\.1\/" \+ location\.search \+ location\.hash\)/,
);
} finally {
rmSync(dir, { recursive: true, force: true });
}
Expand Down
46 changes: 8 additions & 38 deletions sidebars.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,16 @@
import type { SidebarsConfig } from "@docusaurus/plugin-content-docs";
import { existsSync } from "node:fs";
import { createRequire } from "node:module";
import { join } from "node:path";

import { docsPath } from "./tissuumaps-docs";

// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...)

/**
* Creates the sidebars of the current version, which `docusaurus docs:version`
* also saves for every released version (see scripts/prepare.mts)
*
* @param docsPath - The docs folder, with the API docs generated by typedoc
* @returns The sidebars
* The sidebars of the current version, which `docusaurus docs:version` also
* saves for every released version (see scripts/prepare.mts); the API docs
* generated by typedoc are a category of the docs sidebar (see
* `sidebarItemsGenerator` in docusaurus.config.ts)
*/
function createSidebars(docsPath: string): SidebarsConfig {
const typedocSidebarFile = join(docsPath, "api", "typedoc-sidebar.cjs");
if (!existsSync(typedocSidebarFile)) {
throw new Error(
`${typedocSidebarFile} not found, generate the API docs in TissUUmaps first`,
);
}
const require = createRequire(import.meta.url);
return {
// By default, Docusaurus generates a sidebar from the docs folder structure
docsSidebar: [{ type: "autogenerated", dirName: "." }],

// The API reference has its own sidebar, generated by typedoc
apiSidebar: [
{
type: "category",
label: "API",
link: { type: "doc", id: "api/index" },
items: require(typedocSidebarFile) as Extract<
SidebarsConfig[string],
unknown[]
>,
},
],
};
}

const sidebars: SidebarsConfig = docsPath ? createSidebars(docsPath) : {};
const sidebars: SidebarsConfig = {
// By default, Docusaurus generates a sidebar from the docs folder structure
docsSidebar: [{ type: "autogenerated", dirName: "." }],
};

export default sidebars;
Loading
Loading