diff --git a/.eslintrc.js b/.eslintrc.js index e7a581b33..170fd78ae 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -11,6 +11,7 @@ module.exports = { parser: "@typescript-eslint/parser", parserOptions: { project: "./tsconfig.json", + tsconfigRootDir: __dirname, }, plugins: ["@typescript-eslint", "react-hooks"], root: true, diff --git a/packages/web/custom.d.ts b/packages/web/custom.d.ts index 887f23f53..16fa1895c 100644 --- a/packages/web/custom.d.ts +++ b/packages/web/custom.d.ts @@ -5,4 +5,12 @@ declare module "*.svg" { export default src; } -declare module "*.png"; +declare module "*.png" { + const src: string; + export default src; +} + +declare module "*.css" { + const classes: { [key: string]: string }; + export default classes; +} diff --git a/packages/web/src/components/Footer/Footer.module.css b/packages/web/src/components/Footer/Footer.module.css index 4de7a43a7..2cbc3be5b 100644 --- a/packages/web/src/components/Footer/Footer.module.css +++ b/packages/web/src/components/Footer/Footer.module.css @@ -1,9 +1,14 @@ .footer { + position: fixed; + bottom: 0; + left: 0; + right: 0; height: fit-content; - margin: 0 auto; padding: var(--margin) 0; border-top: 1px solid var(--border-color); text-align: center; + background-color: var(--secondary-dark); + z-index: 100; } .footer-link { diff --git a/packages/web/src/components/Header/Menu.tsx b/packages/web/src/components/Header/Menu.tsx index dd2ae2cc8..5f2ea74d2 100644 --- a/packages/web/src/components/Header/Menu.tsx +++ b/packages/web/src/components/Header/Menu.tsx @@ -34,12 +34,12 @@ export default function Menu() { Open-source datasets - Learn + User guide p { - margin-top: 15px -} - -.icon-container { - align-items: center; - display: flex; - padding: 0 var(--margin); -} - -.icon-container i { - color: var(--aqua); - font-size: 24px; - font-weight: bold; -} - -.icon-stack { - margin-top: calc(var(--margin) * 2); -} - -.link, .link:active, .link:visited { - color: var(--aqua); -} - -.section { - border-bottom: 1px solid var(--border-color); - line-height: 1.5; - margin: 0 auto; - max-width: 1200px; - padding: 40px calc(2 * var(--margin)); -} - -.banner .section { - height: 280px; -} - -.section:last-of-type { - border-bottom: none; -} - -.section-footer { - margin-top: calc(var(--margin) * 2) !important; - text-align: center; -} - -.title-container { - align-items: center; - color: var(--highlight-text-color); - display: flex; - flex-direction: column; -} - -.title-container > h1 { - margin-top: auto; - width: 100%; -} - -.title-container > p { - margin-bottom: auto; - width: 100%; -} diff --git a/packages/web/src/components/Learn/index.tsx b/packages/web/src/components/Learn/index.tsx deleted file mode 100644 index 522c3d077..000000000 --- a/packages/web/src/components/Learn/index.tsx +++ /dev/null @@ -1,169 +0,0 @@ -import { Icon, Stack } from "@fluentui/react"; -import classNames from "classnames"; -import * as React from "react"; -import { Link } from "react-router-dom"; - -import LearnDiagramBFF from "../../../assets/learn-diagram-bff.png"; -import LearnDiagramCSV from "../../../assets/learn-diagram-csv.png"; -import LearnDiagramViewer from "../../../assets/learn-diagram-viewer.png"; -import Modal from "../../../../core/components/Modal"; - -import styles from "./Learn.module.css"; - -// Public-facing learning/about page -export default function Learn() { - return ( -
-
-
-
-

Curate your data your way

-

- BioFile Finder helps users effortlessly interact with, extrapolate, and - share data. -

-
-
-
-
-

Why BioFile Finder?

-

- Explore the data without any coding through the standardized metadata, using - filters and hierarchies of folders to search for the data needed to answer a - specific question. Example... -

- - -

Find.

-

- Find all images corresponding to a list of criteria provided by - annotation and tags. -

-
- -

View.

-

- Visualize each file before uploading them ensuring you extracted the - right data for your research. -

-
- -

Share.

-

- Provide the URL address to your collaborator in just one click for - analysis and further exploration of your exact query. -

-
- -

Publish.

-

- By storing the .csv file in a public cloud storage, you can include the - URL address in your publication for others to access your data in just - one click. -

-
-
-
-
-

How does it work?

-

- BioFile Finder is a web application that allows users to interact with their - data in a more efficient and effective way. Users upload a .csv file with - metadata and a link to their data. The .csv file is used to create a query that - can be shared with others. The URL contains the query that can be shared with - others to view the same data and metadata that the user is currently looking at. - The receiver of the URL will need access to the datasource (.csv). -

- - - Example CSV -

.csv File

-
- - - - - Example BioFile Finder Usage -

Biofile Finder

-
- - - - - Example Image Viewer -

Image Viewer

-
-
-
-
-

FAQ

-

Is my data a good fit?

-

- BioFile Finder is designed to be flexible and work with a wide range of data. - Currently, all you need to get started is a .csv file containing links to your - data. Add metadata to your .csv to make using BioFile Finder even more powerful! -

-

Does my data have to be public?

-

- No! You can use BioFile Finder with private data. Just make sure to provide a - link to your data that is accessible to BioFile Finder. Similarly, whoever you - share your query with will need to have access to the data you have linked to or - at the very least the .csv file you have used to create the query. -

-

Is BioFile Finder storing my image data or metadata?

-

- No! The data source file you upload is not stored by BioFile Finder, nor does - any of the data you query get sent to us. -

-

Can BioFile Finder store my data to make it public?

-

- Sort of. We are working on a way to allow users to store their .csv with us to - make it public. In the meantime, email us at - -  aics_software_support@alleninstitute.org - - to request your data be included with our own collection of open-source - datasets. Please note, your image data would need to be stored in a public - location like - -  Image Data Registry  - - or AWS. -

-

- Check out our{" "} - - Open-source datasets - {" "} - for inspiration and examples of datasets. -

-
-
-

Have questions, ideas, requests, or want help getting started?

-

- Please reach out to us at - -  aics_software_support@alleninstitute.org - - . -

-
- -
- ); -} diff --git a/packages/web/src/components/UserGuide/DocPage.module.css b/packages/web/src/components/UserGuide/DocPage.module.css new file mode 100644 index 000000000..a25d4d0f3 --- /dev/null +++ b/packages/web/src/components/UserGuide/DocPage.module.css @@ -0,0 +1,300 @@ +/* + * Styles here fall into two categories: + * + * 1. CSS Module classes (.root, .sectionBody, etc.) — used in DocPage.tsx via styles.* + * + * 2. Content classes (.ug-image, .ug-scroll-container, etc.) — applied as plain strings + * in the content/* files, which are data files that cannot import CSS modules. + * Scoped via `.sectionBody :global(.ug-*)` to prevent leaking outside the content area. + * When adding new content styles, add a ug-* class here instead of using inline styles. + */ + +.root { + max-width: 900px; +} + +/* + * TODO: The User Guide uses larger, lighter heading sizes than the global design system defaults + * (global: h2 24px, h3 18px/600). These are scoped here intentionally to avoid affecting the + * rest of the app. If heading sizes are ever updated globally to match, these overrides can be removed. + */ +.root h2 { + font-size: 28px; +} + +.root h3 { + font-size: 20px; + font-weight: 400; +} + +.notFound { + padding: 40px; + color: var(--light-grey); +} + +.breadcrumb { + display: flex; + align-items: center; + gap: 6px; + font-size: 14px; + color: var(--light-grey); + margin-bottom: 20px; +} + +.breadcrumbSeparator { + font-size: 10px; + color: var(--light-grey); +} + +.breadcrumbPage { + color: var(--primary-text-color); +} + +.title { + margin-bottom: 20px; + line-height: 1.2; +} + +.intro { + line-height: 1.6; + margin-bottom:34px; +} + +.sections { + display: flex; + flex-direction: column; + gap: 34px; +} + +.section { + scroll-margin-top: 80px; +} + +.sectionHeading { + margin-bottom: 20px; +} + +.sectionBody { + line-height: 1.6; + color: var(--primary-text-color); +} + +.sectionBody p { + margin: 0 0 12px; +} + +.sectionBody p:last-child { + margin-bottom: 0; +} + +.sectionBody ul, +.sectionBody ol { + margin: 10px 0 20px 20px; + padding: 0; +} + +.sectionBody li { + margin-bottom: 6px; + line-height: 1.6; + font-weight: 300; +} + +.sectionBody li span, +.sectionBody :global(.ug-feature-card) span { + font-weight: 500; +} + +.sectionBody a { + color: var(--aqua); + text-decoration: none; +} + +.sectionBody a:hover { + color: var(--bright-aqua); + text-decoration: underline; +} + +.sectionBody h3 { + margin: 20px 0 10px 0; +} + +/* Inline table styles for HTML tables in content */ +.sectionBody :global(.ug-scroll-container) { + position: relative; +} + +.sectionBody :global(.ug-scroll-container)::after { + content: ""; + position: absolute; + top: 0; + right: 0; + width: 60px; + height: 100%; + background-image: linear-gradient(to right, transparent, rgba(0, 0, 0, 0.9)); + pointer-events: none; + z-index: 1; +} + +.sectionBody :global(.ug-scroll-table) { + overflow-x: auto; + cursor: ew-resize; +} + +.sectionBody :global(.ug-scroll-table) table { + min-width: 1140px; +} + +.sectionBody table { + width: 100%; + border-collapse: collapse; + font-size: 12px; + margin-bottom:20px; +} + +.sectionBody th { + background-color: var(--aqua); + color: var(--white); + font-weight: 600; + padding: 10px 12px; + text-align: left; + border: 1px solid var(--border-color); +} + +.sectionBody td { + padding: 8px 12px; + border: 1px solid var(--border-color); + vertical-align: top; + line-height: 1.3; +} + +.sectionBody tr:nth-child(odd) td { + background-color: var(--primary-dark); +} + +.sectionBody tr:nth-child(even) td { + background-color: var(--secondary-dark); +} + +/* 2x2 feature grid */ +.sectionBody :global(.ug-feature-grid) { + display: grid; + grid-template-columns: 1fr 1fr; + gap:22px; + margin: 20px 0 30px 0; +} + +/* Asymmetric 1:2 grid layout — used when pairing a narrow panel with a wide screenshot */ +.sectionBody :global(.ug-feature-grid--wide) { + grid-template-columns: 1fr 2fr; +} + +.sectionBody :global(.ug-feature-card) { + font-weight: 300; + line-height: 1.6; +} + +@media (max-width: 769px) { + .sectionBody :global(.ug-feature-grid), + .sectionBody :global(.ug-feature-grid--wide) { + grid-template-columns: 1fr; + } +} + +/* Content images */ +.sectionBody :global(.ug-image) { + width: 100%; + border-radius: 4px; +} + +.sectionBody :global(.ug-image-mt) { + width: 100%; + border-radius: 4px; + margin-top: 8px; +} + +/* Inline icons within body text */ +.sectionBody :global(.ug-icon-sm) { + font-size: 11px; + vertical-align: middle; +} + +.sectionBody :global(.ug-icon-md) { + font-size: 16px; + vertical-align: middle; +} + +/* Placeholder box (for diagrams/future content) */ +.sectionBody :global(.ug-placeholder) { + background-color: var(--accent-dark); + border: 1px dashed var(--border-color); + border-radius: var(--small-border-radius); + color: var(--light-grey); + font-size: 14px; + font-style: italic; + padding: 24px; + text-align: center; + margin: 16px 0; +} + +/* Pagination row */ +.pagination { + display: flex; + justify-content: space-between; + gap: 16px; + margin-top: 60px; + padding-top: 24px; + border-top: 1px solid var(--border-color); +} + +.pageNav { + display: flex; + align-items: center; + gap: 10px; + padding: 12px 16px; + border: 1px solid var(--border-color); + border-radius: var(--small-border-radius); + color: var(--primary-text-color); + text-decoration: none; + flex: 1; + max-width: 300px; + transition: border-color 0.15s ease, background-color 0.15s ease; +} + +.pageNav:hover { + border-color: var(--aqua); + background-color: var(--aqua-secondary-hover); + color: var(--white); +} + +.pageNavPrev { + justify-content: flex-start; +} + +.pageNavNext { + justify-content: flex-end; + margin-left: auto; +} + +.pageNavIcon { + font-size: 12px; + color: var(--aqua); + flex-shrink: 0; +} + +.pageNavLabel { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--light-grey); + margin-bottom: 2px; +} + +.pageNavTitle { + font-size: 14px; + font-weight: 600; + color: var(--primary-text-color); +} + +.pageNav:hover .pageNavTitle { + color: var(--white); +} diff --git a/packages/web/src/components/UserGuide/DocPage.tsx b/packages/web/src/components/UserGuide/DocPage.tsx new file mode 100644 index 000000000..ec0fff31a --- /dev/null +++ b/packages/web/src/components/UserGuide/DocPage.tsx @@ -0,0 +1,85 @@ +import { Icon } from "@fluentui/react"; +import classNames from "classnames"; +import * as React from "react"; +import { Link } from "react-router-dom"; + +import { PAGE_CONTENT } from "./content/index"; +import { NAV, getAdjacentPages } from "./nav"; +import styles from "./DocPage.module.css"; + +interface DocPageProps { + sectionSlug: string; + pageSlug: string; +} + +export default function DocPage({ sectionSlug, pageSlug }: DocPageProps) { + const key = `${sectionSlug}/${pageSlug}`; + const content = PAGE_CONTENT[key]; + const section = NAV.find((s) => s.slug === sectionSlug); + const { prev, next } = getAdjacentPages(sectionSlug, pageSlug); + + if (!content) { + return ( +
+

Page not found.

+
+ ); + } + + return ( +
+ +

{content.title}

+ {content.intro &&

{content.intro}

} +
+ {content.sections.map((sec) => { + const HeadingTag = `h${sec.level ?? 2}` as "h2" | "h3" | "h4"; + return ( +
+ {sec.heading && ( + + {sec.heading} + + )} +
{sec.body}
+
+ ); + })} +
+
+ {prev ? ( + + +
+
Previous
+
{prev.page.title}
+
+ + ) : ( +
+ )} + {next ? ( + +
+
Next
+
{next.page.title}
+
+ + + ) : ( +
+ )} +
+
+ ); +} diff --git a/packages/web/src/components/UserGuide/Sidebar.module.css b/packages/web/src/components/UserGuide/Sidebar.module.css new file mode 100644 index 000000000..54587a8ab --- /dev/null +++ b/packages/web/src/components/UserGuide/Sidebar.module.css @@ -0,0 +1,83 @@ +.root { + padding: 16px 0; + background-color: var(--primary-dark); + height: 100%; +} + +.label { + color: var(--light-grey); + padding: 0 16px 12px; + border-bottom: 1px solid var(--border-color); + margin-bottom: 8px; +} + +.section { + margin-bottom: 2px; +} + +.sectionTitle { + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + background: none; + border: none; + cursor: pointer; + padding: 8px 16px; + font-size: 14px; + color: var(--primary-text-color); + text-align: left; + font-family: var(--font-family); + transition: background-color 0.15s ease; +} + +.sectionTitle:hover { + background-color: var(--accent-dark); +} + +.chevron { + font-size: 10px; + transition: transform 0.2s ease; + transform: rotate(-90deg); + color: var(--light-grey); + flex-shrink: 0; +} + +.chevronOpen { + transform: rotate(0deg); +} + +.pageList { + list-style: none; + margin: 0; + padding: 0; + padding-bottom: 4px; +} + +.pageLink { + display: block; + padding: 6px 16px 6px 24px; + font-size: 14px; + color: var(--primary-text-color); + text-decoration: none; + line-height: 1.4; + transition: background-color 0.15s ease; + border-left: 3px solid transparent; +} + +.pageLink:hover { + background-color: var(--accent-dark); + color: var(--white); +} + +.activePage { + background-color: var(--aqua-secondary-hover); + border-left-color: var(--aqua); + color: var(--bright-aqua); + padding-left: 21px; +} + +.activePage:hover { + background-color: var(--aqua-secondary-hover); + color: var(--bright-aqua); +} diff --git a/packages/web/src/components/UserGuide/Sidebar.tsx b/packages/web/src/components/UserGuide/Sidebar.tsx new file mode 100644 index 000000000..8d66454ad --- /dev/null +++ b/packages/web/src/components/UserGuide/Sidebar.tsx @@ -0,0 +1,85 @@ +import { Icon } from "@fluentui/react"; +import classNames from "classnames"; +import * as React from "react"; +import { Link } from "react-router-dom"; + +import { NAV } from "./nav"; +import styles from "./Sidebar.module.css"; + +interface SidebarProps { + activeSectionSlug: string; + activePageSlug: string; +} + +export default function Sidebar({ activeSectionSlug, activePageSlug }: SidebarProps) { + const [openSections, setOpenSections] = React.useState>( + () => new Set([activeSectionSlug]) + ); + + const toggleSection = (slug: string) => { + setOpenSections((prev) => { + const next = new Set(prev); + if (next.has(slug)) { + next.delete(slug); + } else { + next.add(slug); + } + return next; + }); + }; + + // When active section changes (navigating), ensure it opens + React.useEffect(() => { + setOpenSections((prev) => { + if (prev.has(activeSectionSlug)) return prev; + return new Set([...prev, activeSectionSlug]); + }); + }, [activeSectionSlug]); + + return ( + + ); +} diff --git a/packages/web/src/components/UserGuide/UserGuide.module.css b/packages/web/src/components/UserGuide/UserGuide.module.css new file mode 100644 index 000000000..6bbd98acb --- /dev/null +++ b/packages/web/src/components/UserGuide/UserGuide.module.css @@ -0,0 +1,89 @@ +.root { + display: flex; + background-color: var(--secondary-dark); + color: var(--primary-text-color); +} + +.sidebar { + width: 260px; + flex-shrink: 0; + border-right: 1px solid var(--border-color); + position: sticky; + top: 0; + height: calc(100vh - var(--header-bar-height)); +} + +.content { + flex: 1; + min-width: 0; + padding: 30px 40px 80px 40px; + height: calc(100vh - var(--header-bar-height)); + overflow-y: auto; +} + +.mobilNav { + display: none; + position: relative; + padding: 12px 16px; + border-bottom: 1px solid var(--border-color); + background-color: var(--secondary-dark); +} + +.mobileMenuTrigger { + width: 200px; + margin: 0 auto; +} + +.mobileMenuTrigger button { + width: auto; +} + + +.mobileMenuTrigger [class*="button-content"] { + flex-direction: row-reverse; +} + +.mobileMenuTrigger [class*="button-content"] i { + padding: 0 0 0 6px; +} + +.mobileMenuBackdrop { + position: absolute; + top: 100%; + left: 0; + right: 0; + height: 100vh; + z-index: 200; + background-color: rgba(0, 0, 0, 0.5); +} + +.mobileMenu { + position: absolute; + top: 100%; + left: 0; + right: 0; + z-index: 201; + background-color: var(--primary-dark); + border-bottom: 1px solid var(--border-color); + max-height: 60vh; + overflow-y: auto; +} + +@media (max-width: 768px) { + .root { + flex-direction: column; + } + + .sidebar { + display: none; + } + + .mobilNav { + display: flex; + justify-content: center; + } + + .content { + padding: 24px 20px 80px 20px; + } +} diff --git a/packages/web/src/components/UserGuide/assets/grouping-hierarchy.png b/packages/web/src/components/UserGuide/assets/grouping-hierarchy.png new file mode 100644 index 000000000..855e33fff Binary files /dev/null and b/packages/web/src/components/UserGuide/assets/grouping-hierarchy.png differ diff --git a/packages/web/src/components/UserGuide/assets/grouping-panel.png b/packages/web/src/components/UserGuide/assets/grouping-panel.png new file mode 100644 index 000000000..882b08671 Binary files /dev/null and b/packages/web/src/components/UserGuide/assets/grouping-panel.png differ diff --git a/packages/web/src/components/UserGuide/assets/querying-filters.png b/packages/web/src/components/UserGuide/assets/querying-filters.png new file mode 100644 index 000000000..bacd46c09 Binary files /dev/null and b/packages/web/src/components/UserGuide/assets/querying-filters.png differ diff --git a/packages/web/src/components/UserGuide/assets/querying-results.png b/packages/web/src/components/UserGuide/assets/querying-results.png new file mode 100644 index 000000000..5bbf5c785 Binary files /dev/null and b/packages/web/src/components/UserGuide/assets/querying-results.png differ diff --git a/packages/web/src/components/UserGuide/assets/sharing-url.png b/packages/web/src/components/UserGuide/assets/sharing-url.png new file mode 100644 index 000000000..b65cf5d1d Binary files /dev/null and b/packages/web/src/components/UserGuide/assets/sharing-url.png differ diff --git a/packages/web/src/components/UserGuide/assets/thumbnails.png b/packages/web/src/components/UserGuide/assets/thumbnails.png new file mode 100644 index 000000000..fc5803526 Binary files /dev/null and b/packages/web/src/components/UserGuide/assets/thumbnails.png differ diff --git a/packages/web/src/components/UserGuide/content/about.tsx b/packages/web/src/components/UserGuide/content/about.tsx new file mode 100644 index 000000000..3b593f68a --- /dev/null +++ b/packages/web/src/components/UserGuide/content/about.tsx @@ -0,0 +1,617 @@ +import * as React from "react"; + +import GroupingHierarchy from "../assets/grouping-hierarchy.png"; +import GroupingPanel from "../assets/grouping-panel.png"; +import QueryingFilters from "../assets/querying-filters.png"; +import QueryingResults from "../assets/querying-results.png"; +import SharingUrl from "../assets/sharing-url.png"; +import Thumbnails from "../assets/thumbnails.png"; + +import type { PageContent } from "./types"; + +export const ABOUT_CONTENT: Record = { + "about/overview": { + title: "Overview", + sections: [ + { + id: "what-is-bff", + heading: "", + body: ( + <> +

+ BioFile Finder (BFF) is a web-based application + designed to enable researchers to explore and manage large-scale + biological imaging datasets and associated files in a consistent and + streamlined way. It enables users to query structured metadata and + seamlessly connect results to associated image assets. +

+

+ Built to handle complex, high-volume data, BioFile Finder supports + advanced search, filtering, and sorting—making it easier to access, + curate, collaborate on, and share datasets. The intuitive interface + requires no coding, allowing users to quickly preview data through + thumbnails, open files in common industry tools, or visualize them in + the companion web-based 3D volume viewer, Vol-E. +

+ {/* TODO: Add publication link once paper is available in Nature Methods */} + + ), + }, + { + id: "who-is-bff-for", + heading: "Who is BFF for?", + body: ( + <> +

+ BFF is designed for anyone who needs to explore and manage large + collections of biological files, especially those associated with + imaging datasets. It is particularly useful for: +

+
    +
  • + Wet-lab scientists +
  • +
  • + Computational biologists +
  • +
  • + Data Engineers & Platform Teams +
  • +
  • + Academic Facility Managers & PIs +
  • +
  • + GLAM & Museum Professionals +
  • +
+

+ + Read detailed scenarios and use cases + +

+ + ), + }, + { + id: "why-use-bff", + heading: "What makes BFF unique?", + body: ( + <> +

+ A number of thoughful features set BFF apart from other similar tools. + Key differentiators include: +

+
+
+ No infrastructure +
BFF works entirely without a server, enabling users to + explore and share datasets instantly without setup, deployment, or + IT support. +
+
+ Querying power +
BFF's in-browser query system gives full SQL control + over arbitrary user metadata. No other tool in this space does that + client-side, without a backend. +
+
+ Format agnostic +
BFF treats metadata as data (Parquet/CSV), not tied to any + specific image format. By contrast, OMERO is deeply tied to + Bio-Formats, and tools like SSBD and Zarrcade are tied to specific + formats like OME-Zarr. +
+
+ Sharing +
BFF's URL-encoded query state is unique. Most tools + either require server access or only share static links to datasets + — BFF shares the exact filtered and sorted view as a URL anyone can + open instantly. +
+
+ + ), + }, + { + id: "bff-comparison-table", + heading: "How BFF compares", + body: ( + <> +

+ The following table highlights how BFF compares to similar tools in the + bioimaging data management ecosystem. +

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureBioFile Finder (BFF)OMEROIDRSSBDZarrcadeBioImage Archive (BIA)QuiltCytomineBisQue
+ Type + Desktop/web file browserClient-server image managementPublic image repositoryPublic dynamics databaseOME-Zarr discovery toolPublic image archiveData versioning platformWeb collaborative analysisWeb image management
+ Cost + Free/open-source + Free / open-source (self-hosted infrastructure + costs) + Free (public resource)Free (public resource)Free / open-sourceFree (public resource)Free tier; paid plans for teamsFree / open-source (self-hosted)Free / open-source (self-hosted)
+ Deployment + Local app or static web pageRequires server + PostgreSQL + storageHosted by EMBL-EBIHosted by RIKENStatic web pageHosted by EMBL-EBISaaS or self-hostedRequires server + DBRequires server + DB
+ File Format Support + Any file type; Parquet, CSV, JSON metadata150+ microscopy formats via Bio-FormatsSame as OMEROOME-Zarr, BD5/HDF5OME-Zarr onlyAny bioimaging formatAny file typeTIFF, whole-slide imagesMany image formats
+ Metadata Source + User-supplied files (Parquet/CSV) or URLsStored in PostgreSQL; key-value annotationsCurated study-level metadataCurated per-studyDerived from Zarr metadataStudy-level submissionsPackage-level metadataProject/annotation-basedTag/key-value on images
+ Dynamic Querying / Filtering + + Yes — in-browser SQL via DuckDB; filter, sort, group + by any column + Yes — HQL/API queries; filter by tag/key-valueLimited — browse by study, screen, geneLimited — browse by organism/studyNo — browse/list onlyLimited — search by study/accessionLimited — search by package nameYes — ontology-based spatial queriesYes — tag-based queries
+ Annotation Hierarchy / Grouping + + Yes — user-defined nested grouping by any annotation + Partial — tag groups, datasets, projectsNoNoNoNoNoPartial — project/folder hierarchyPartial — tag hierarchy
+ Shareable URLs / Copy-Paste Sharing + Yes — query state encoded in URL + Partial — links to images/datasets, requires server + access + Yes — public stable URLs per studyYes — public DOI-based URLsYes — public URLs to Zarr storesYes — accession-based URLsYes — versioned package URLsPartial — project links, requires loginPartial — resource links, requires server
+ Works Without a Server + + Yes — runs entirely in-browser or as desktop app + No — requires OMERO.serverN/A (hosted service)N/A (hosted service)Yes — static siteN/A (hosted service)No (SaaS)No — requires serverNo — requires server
+ Cloud / Remote Data + Yes — S3, HTTP/HTTPS URLsYes — via OMERO.server with S3 backendN/AN/AYes — any HTTP-hosted ZarrN/AYes — S3-backedLimitedLimited
+ Data Scale + Tested to 10M+ rows; limited by browser memoryMillions of images (server-dependent)~50 TB across studiesModerate (curated datasets)Unlimited (just a catalog)Petabyte-scale archivePackage-size dependentLarge histopathology imagesModerate
+ Image Viewing + Thumbnails; delegates to external viewers + Built-in multi-dimensional viewer (OMERO.web, + OMERO.figure) + Built-in viewer (idr.openmicroscopy.org)Built-in 3D/4D viewerLinks to external Zarr viewers (e.g. Vizarr)Links to BioStudies viewerBuilt-in preview for some typesBuilt-in annotation/viewerBuilt-in multi-dim viewer
+ User Annotations / Editing + Yes — add/edit metadata columns in-browserYes — key-value pairs, tags, ratings, ROIsNo (read-only)No (read-only)No (read-only)No (submission-based)Yes — package metadataYes — spatial annotations, ontology termsYes — tags, gobjects
+ Programmatic API + DuckDB SQL in-browser; no REST API needed + Full REST + JSON API; Python (omero-py), Java, CLI + REST API (same as OMERO)REST APINone (static JSON)REST API (BioStudies)Python SDK, REST APIREST API, Python clientREST API, Python/MATLAB
+ Multi-User / Auth + No — single-user local toolYes — LDAP, groups, permissionsPublic (no auth)Public (no auth)Public (no auth)Submission requires loginYes — teams, RBACYes — LDAP, project rolesYes — user/group permissions
+ Primary Use Case + + Explore & filter large tabular file metadata; + share queries via URL + + Manage, view & annotate microscopy data for a + lab/institute + Publish & browse reference image datasetsShare quantitative bio-dynamics dataDiscover & link to OME-Zarr datasetsArchive & publish bioimaging dataVersion & share data packages + Collaborative image annotation (pathology, etc.) + Manage & analyze diverse bio-images
+ License + MITAGPL v3N/A (hosted)N/A (hosted)MITN/A (hosted)Apache 2.0Apache 2.0BSD
+
+
+ + ), + }, + ], + }, + + "about/features": { + title: "Feature highlights", + intro: + "BioFile Finder (BFF) packs a lot of capability into a serverless, browser-based tool. Here is an overview of its key features.", + sections: [ + { + id: "in-browser-querying", + heading: "Powerful in-browser querying", + body: ( + <> +

+ BFF uses DuckDB — a high-performance analytical SQL + engine — to run queries entirely in your browser. No server, no backend, + no credentials required. Filter, sort, and search across millions of + rows of metadata instantly. +

+
    +
  • Filter by one or more metadata columns simultaneously
  • +
  • Sort by any column, ascending or descending
  • +
  • Full-text search across all metadata
  • +
  • Scales to 10M+ rows, limited only by browser memory
  • +
+
+ Filter panel showing active filters: Drug Label equals Staurosporine, Structure equals Microtubules, Treatment Group equals Drug, with Treatment Group sorted ascending + File list showing Microtubules folder with two files visible, Treatment Group column sorted ascending +
+ + ), + }, + { + id: "dynamic-grouping", + heading: "Dynamic grouping & hierarchy", + body: ( + <> +

+ Group files by any combination of metadata columns to create a navigable + folder-like hierarchy — without moving or reorganizing your actual + files. Switch grouping strategies instantly to explore different + dimensions of your dataset. +

+
    +
  • + Nest grouping across multiple levels (e.g., Experiment → Plate + → Well) +
  • +
  • Each group shows a count of the files it contains
  • +
  • Combine grouping with filters for focused exploration
  • +
  • Switch views without affecting the underlying data
  • +
+
+ Group by panel showing four nested grouping levels: Structure, Drug Label, Drug Concentration, and Timepoint + File list showing a multi-level folder hierarchy expanded under Microtubules > Staurosporine > 0.5 > 2, revealing individual files +
+ + ), + }, + { + id: "sharing", + heading: "Sharing", + body: ( + <> +

+ Sharing is one of BFF's most powerful and distinctive features. + Every filter, sort, grouping, and column layout you configure is encoded + directly into the URL. Copy the link and share it — anyone who opens it + sees exactly the same view of the data, without re-running any queries, + sending files, or setting anything up. +

+

+ This makes BFF uniquely suited for collaborative research and open + science: +

+
    +
  • + Collaborate instantly — share your exact filtered + view with a colleague in one click; they see the same subset without + any setup +
  • +
  • + Link publications to live data — tie a figure + directly to the specific filtered dataset view that produced it, so + readers can explore the full underlying data themselves +
  • +
  • + No accounts or infrastructure — shared links work + for anyone with a browser, no login required +
  • +
  • + Persistent and archivable — links are stable and + can be preserved alongside publications or in data repositories +
  • +
+

+ Most tools in this space either require server access to share data or + only link to a static dataset. BFF shares the exact filtered, sorted, + grouped view — making it a powerful tool for transparent and + reproducible science. +

+ Browser URL bar showing a BFF link with filters, groupings, and query state encoded as URL parameters + + ), + }, + { + id: "thumbnails", + heading: "Thumbnail previews", + body: ( + <> +

+ BFF renders thumbnail previews for files in your dataset so you can + visually scan your data without opening each file individually. + Thumbnails appear inline in the file list and update dynamically as you + filter and group. +

+
    +
  • + Automatically generated for supported file types and accessible URLs +
  • +
  • + Supply your own preview images via a Thumbnail column + in your dataset — useful for large or complex files where + auto-generation isn't possible +
  • +
  • + Navigate the filtered file list with arrow keys for rapid visual + review +
  • +
+ Thumbnail grid view showing a Golgi folder with 72 files, each displaying a rendered microscopy image thumbnail + + ), + }, + { + id: "code-generation", + heading: "Code generation", + body: ( +

+ Content coming soon. +

+ ), + }, + { + id: "viewer-integrations", + heading: "Viewer integrations", + body: ( + <> +

+ BFF connects directly to a variety of image viewers — web-based and + desktop. Select any file and open it in the viewer best suited for its + format and your workflow. +

+
    +
  • + Supported viewers: Vol-E, AGAVE, FIJI, Neuroglancer, Simularium, + VolView, and more +
  • +
  • + Open files with a single click from the file list or detail panel +
  • +
  • + Use a metadata descriptor file to define custom viewer links per + file +
  • +
+

+ + See the viewer comparison table + +

+ + ), + }, + ], + }, +}; diff --git a/packages/web/src/components/UserGuide/content/app-information.tsx b/packages/web/src/components/UserGuide/content/app-information.tsx new file mode 100644 index 000000000..460154f68 --- /dev/null +++ b/packages/web/src/components/UserGuide/content/app-information.tsx @@ -0,0 +1,399 @@ +import { Icon } from "@fluentui/react"; +import * as React from "react"; + +import type { PageContent } from "./types"; + +export const APP_INFORMATION_CONTENT: Record = { + "app-information/specifications": { + title: "Specifications", + intro: "Technical specifications for BioFile Finder (BFF).", + sections: [ + { + id: "file-size-limitations", + heading: "File size and format compatibility", + body: ( + <> +

+ BFF ingests metadata about biological files (a dataset), not the files + themselves. This metadata is intended to be tabular and can be stored as + the following formats: +

+
    +
  • + CSV — most human readable +
  • +
  • + Parquet — most performant +
  • +
  • + JSON +
  • +
+

+ Information on file size limitations coming soon. +

+

Files referenced by dataset

+

+ Limitations around the files tracked within BFF are imposed by the + applications BFF links to for that given file. For example, FIJI will + only work with the files that it supports. BFF itself is agnostic to the + file types and sizes referenced in a dataset. +

+ + ), + }, + { + id: "preferred-browsers", + heading: "Browser and device compatibility", + body: ( + <> +

+ For best performance and compatibility, we recommend using the latest + versions of the following browsers: +

+
    +
  • Chrome
  • +
  • Firefox
  • +
  • Opera
  • +
+

+ BFF is optimized for desktop use and is not currently designed for + mobile devices. +

+ + ), + }, + { + id: "open-source", + heading: "Open source", + body: ( +

+ BioFile Finder is open-source and free to use. You can find the code, report + issues, and contribute on{" "} + + GitHub + + . +

+ ), + }, + ], + }, + + "app-information/supported-viewers": { + title: "Supported viewers", + intro: + "BioFile Finder (BFF) links out to a variety of image viewers. Use the information below to choose the right one for your work.", + sections: [ + { + id: "decision-guide", + heading: "Decision guide", + body: ( + <> +

+ File format will heavily limit viewer options, but when multiple options + are feasible, the following information may help guide your decision. +

+
    +
  • + “I need to measure/analyze my images” →{" "} + FIJI +
  • +
  • + “I need a beautiful 3D render” →{" "} + AGAVE +
  • +
  • + + “I need to explore a huge dataset in the cloud” + {" "} + → Neuroglancer or Vol-E +
  • +
  • + “I need to view a simulation over time”{" "} + → Simularium +
  • +
  • + “I need to check my OME-Zarr is valid”{" "} + → OME NGFF Validator +
  • +
  • + “I have DICOM/medical volumes” →{" "} + VolView +
  • +
  • + + “I just want to glance at a simple file quickly” + {" "} + → Browser / OS preview +
  • +
+ + ), + }, + { + id: "viewer-table", + heading: "Image viewer comparison table", + body: ( + <> +

+ The following table offers comparisons between various supported + viewers. +

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureVol-EAGAVEFIJI / ImageJNeuroglancerOME NGFF ValidatorBrowser (web)SimulariumVolView
+ Type + Web-based 3D volume viewerDesktop GPU-accelerated volume rendererDesktop image analysis suiteWeb-based volumetric viewerWeb-based validation toolNative file previewWeb-based simulation viewerWeb-based 3D volume viewer
+ Platform + Web app (browser)Desktop (Windows, macOS, Linux)Desktop (Windows, macOS, Linux)Web app (browser)Web app (browser)Desktop (OS-native)Web app (browser)Web app (browser)
+ Installation required + NoYes (standalone app)Yes (Java-based)NoNoNo (built into OS)NoNo
+ Cost + Free / open-sourceFree / open-sourceFree / open-sourceFree / open-sourceFree / open-sourceFree (bundled with OS)Free / open-sourceFree / open-source
+ Primary use case + + Interactive 3D volume rendering of microscopy data + + High-quality cinematic 3D rendering and path tracing + + General-purpose image analysis, measurement, and + processing + + Explore large-scale connectomics / volumetric neuro + datasets + + Validate OME-Zarr/NGFF file structure and metadata + compliance + Quick preview of standard image/video files + Visualize agent-based biological simulations over + time + + Clinical and research DICOM/volume visualization +
+ Supported formats + OME-Zarr, TIFF, OME-TIFF + OME-TIFF, TIFF, CZI, LIF, and other microscopy + formats + 100+ formats via Bio-FormatsPrecomputed, N5, Zarr, NIFTIOME-Zarr (NGFF) onlyJPEG, PNG, TIFF, MP4, PDF (OS-dependent)Simularium, CytoSim, ReaDDy, SmoldynDICOM, NIFTI, MHA, VTI, NRRD, Zarr
+ 3D volume rendering + Yes — real-time ray marchingYes — GPU path tracing, cinematic qualityLimited — 3D Viewer pluginYes — multi-scale, GPU-acceleratedNoNoYes — 3D agent trajectories and meshesYes — GPU-accelerated ray casting
+ Multi-channel support + YesYesYesYesValidates channel metadataNoN/AYes
+ Time series / 4D + YesYesYesLimitedValidates time dimension metadataNoYes — primary featureLimited
+ Large data / streaming + Yes — streams OME-Zarr from cloud/HTTPNo — loads full volume into GPU memoryLimitedYes — designed for petascaleValidates metadata onlyNoStreams from URLYes — progressive loading
+ Cloud / remote data + Yes — HTTP/S3 URLsNo — local files onlyLimitedYes — GCS, S3, HTTPYes — validates remote URLsNoYesYes
+ Collaborative / sharing + Shareable URL with view stateNoNoYes — URL encodes full view stateShareable validation URLNoShareable URLShareable URL via hosted instance
+ Best for + + Quick interactive exploration of cloud-hosted + OME-Zarr volumes + + High-quality figures and movies of 3D microscopy + data + Comprehensive image analysis and scripting + Browsing terabyte+ volumetric datasets in the cloud + Checking OME-Zarr files before sharingQuickly previewing a standard image file + Viewing and sharing spatiotemporal biological + simulations + + Medical/research volumes with clinical-style tools +
+ Limitations + No analysis tools; limited format supportRequires dedicated GPU; local files only + Basic 3D rendering; struggles with very large + datasets + + Steep learning curve; specific pre-tiled formats + only + Validation only; OME-Zarr onlyNo scientific image capabilitiesSimulation data onlyLimited microscopy format support
+
+
+ + ), + }, + ], + }, +}; diff --git a/packages/web/src/components/UserGuide/content/getting-started.tsx b/packages/web/src/components/UserGuide/content/getting-started.tsx new file mode 100644 index 000000000..5ac873568 --- /dev/null +++ b/packages/web/src/components/UserGuide/content/getting-started.tsx @@ -0,0 +1,769 @@ +import { Icon } from "@fluentui/react"; +import * as React from "react"; + +import type { PageContent } from "./types"; + +export const GETTING_STARTED_CONTENT: Record = { + "getting-started/setup-overview": { + title: "Setup overview", + intro: + "BioFile Finder (BFF) works by connecting a metadata table to the files you want to explore. Rather than ingesting image data directly, BFF reads a dataset file (CSV, Parquet, or JSON) containing metadata and file references. Once loaded, BFF turns that dataset into an interactive interface for filtering, grouping, searching, previewing, and sharing files.", + sections: [ + { + id: "basic-setup", + heading: "Basic setup", + body: ( + <> +

1. Create a dataset file

+

+ Prepare a metadata table describing your files. Each row typically + represents a file, while columns contain metadata such as: +

+
    +
  • File path
  • +
  • Experimental condition
  • +
  • Cell line
  • +
  • Plate / well
  • +
  • Imaging modality
  • +
  • Date acquired
  • +
  • Any other annotations relevant to your workflow
  • +
+

+ See:{" "} + + Creating a dataset + + ,{" "} + + Metadata guidance + +

+ +

2. Reference your files

+

+ Your dataset must include paths or URLs pointing to the files you want + BFF to access. Files can live: +

+
    +
  • Locally on your computer
  • +
  • On network-attached storage
  • +
  • In cloud storage
  • +
  • In public repositories
  • +
+

+ BFF is storage agnostic and does not require files to be moved into a + proprietary system. +

+

+ See:{" "} + + Storage options + + ,{" "} + + Viewer compatibility + +

+ +

3. Load the dataset into BFF

+

Open BFF and either:

+
    +
  • Drag and drop a dataset file
  • +
  • Paste a dataset URL
  • +
  • Open a shared BFF link
  • +
+

+ Once loaded, BFF allows you to filter and search metadata, group files + dynamically, preview and open files in compatible viewers, and share + exact dataset views via URL. +

+ + ), + }, + { + id: "minimum-requirements", + heading: "Minimum requirements", + body: ( + <> +

To use BFF, you only need:

+
    +
  • Metadata file (CSV, Parquet, or JSON).
  • +
  • Files to reference in the dataset.
  • +
  • File paths linking metadata to images.
  • +
+

No backend, database, or server infrastructure is required.

+ + ), + }, + { + id: "common-workflows", + heading: "Common workflows", + body: ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
GoalTypical setup
Personal / local explorationLocal dataset + local files
Shared lab datasetHosted dataset + shared storage
Public publication companionHosted dataset + public cloud storage
Large-scale datasetsParquet + cloud storage
Metadata validation / QCDataset + metadata descriptor file
File lineage / relationship trackingDataset + provenance file
+ ), + }, + { + id: "recommended-setup", + heading: "Recommended setup", + body: ( + <> +

Sharing data publicly

+

+ If data is intended to be publicly shared — like in a publication — + store the dataset and files referenced in the dataset in{" "} + cloud storage{" "} + to enable readers to explore the dataset and its files via a sharable + BFF link (URL). +

+

+ Note: You can use BFF as a way to circumvent having to publish all files + by publishing only the dataset file and instructing readers to request + files directly. This allows viewers to see metadata about every file in + the dataset without you paying for full cloud storage of each file. + Building on this approach, you can host thumbnails of each file so + readers can get a preview without you paying for full-resolution images + to live in the cloud. +

+ +

Dataset best practices

+
    +
  • + Use consistent metadata conventions —{" "} + + see Metadata guidance + {" "} + for detailed best practices. +
  • +
  • + If the dataset is over 100,000 files, use Parquet (a columnar + storage format designed for high-performance data analytics) for + faster performance. BFF can convert your dataset for you, or use any + pyarrow-compatible tool like{" "} + + pandas{" "} + + + . +
  • +
  • + See{" "} + + Dataset examples + {" "} + for examples to follow when creating a dataset. +
  • +
+ + ), + }, + ], + }, + + "getting-started/creating-a-dataset": { + title: "Creating a dataset", + intro: + "BioFile Finder (BFF) works by referencing a spreadsheet you provide, populated by key-value pairs that are the metadata associated with your image files.", + sections: [ + { + id: "creating-spreadsheet", + heading: "What is a dataset?", + body: ( + <> +

+ A BFF dataset is a tabular file where each row represents a file and + each column is a piece of metadata about that file. The format is + flexible — any columns beyond the required ones are yours to define + based on what matters to your workflow. +

+

+ + See App information + {" "} + for accepted file types and size limitations. +

+ + ), + }, + { + id: "rows-columns", + heading: "Rows and columns", + body: ( + <> +

+ Rows: Each row in the table should correspond with a + file — either on the cloud, a hard drive, or network attached storage. + However, you can have a row corresponding to multiple files, or + different rows corresponding to the same file. +

+

+ Columns: Columns can be anything, but there is one + required column and a few special optional columns described below. +

+ + ), + }, + { + id: "required-columns", + heading: "Required columns", + body: ( +

+ File Path — A reference to the file that BFF will attempt + to open with relevant applications. This column does not have to be unique.{" "} + + Information about file storage options + + . +

+ ), + }, + { + id: "optional-columns", + heading: "Optional special columns", + body: ( + <> +

+ These columns are optional but enable specific features in BFF when + provided. +

+
    +
  • + Thumbnail — If provided, should contain the URL to + a cloud-hosted image (see{" "} + + cloud storage options + + ) and will override any thumbnail BFF will automatically try to + generate. Note: BFF cannot automatically generate a thumbnail for + all file types and storage locations. +
  • +
  • + File Name — BFF will automatically parse the file + name from the path if this is not provided. +
  • +
  • + File Size — Should contain the size of the file in + bytes. The display automatically converts to a human-readable format + and is used during file downloads to provide feedback about download + size. If not provided, the size will be approximated during + downloads. This is especially important for correct OME-Zarr + downloads, as the size for these is difficult to calculate with + certainty. +
  • +
+ + ), + }, + { + id: "spreadsheet-examples", + heading: "Spreadsheet (dataset) examples", + body: ( + <> +

Basic example

+

+ Each row is a file. Columns can be anything meaningful to your workflow + — here a well position, gene target, and color channel. +

+ + + + + + + + + + + + + + + + + + + + + + + +
File PathWellGeneColor
Abc123.txtB3CDH2Blue
Def456.txtG9VIMGreen
+

+ + Download this example as CSV{" "} + {" "} + + +

+

+ Browse open-source datasets +

+ + ), + }, + ], + }, + + "getting-started/metadata-guidance": { + title: "Metadata guidance", + intro: + "Clear, consistent metadata is what turns microscopy data from a static file into something others can actually find, interpret, and reuse. This section outlines recommended metadata practices that support sharing datasets in a way that is both accessible and meaningful to a broad audience — from collaborators to future researchers. Rather than prescribing a rigid standard, the guidance focuses on capturing the essential context needed to understand how the data was generated, how it is structured, and how it can be used. Our hope is that by following these suggestions, you can make your data easier to explore, visualize, and integrate into downstream analyses, while reducing ambiguity and the need for follow-up clarification.", + sections: [ + { + id: "recommendations", + heading: "Recommendations", + body: ( + <> +

FoundingGIDE

+

+ The following interpretation of the{" "} + + FoundingGIDE{" "} + + {" "} + metadata guidelines is a CSV template created to operationalize a + minimal, harmonized metadata schema that enables interoperability across + bioimaging data resources. Because imaging datasets are generated and + stored using diverse, often incompatible metadata models, they are + difficult to search, integrate, and reuse across repositories. + FoundingGIDE addresses this by defining a shared set of metadata fields, + grounded in common ontologies, that can be consistently applied across + studies. This template translates those recommendations into a simple, + spreadsheet-based format supporting cross-repository discovery, FAIR + data principles, and integration into a broader global image data + ecosystem. +

+

+ Fields included: Metadata Field, Study Description, Authors, + Organization, Publication, License, Release Date, Imaging Method, Cell + Line, Organism, Gene, Compound, Antibody, Channel — Content, Channel — + Biological Entity, Instrument, Dimension, Pixel/Voxel Size / Time + resolution, Study Unique ID, Dataset Unique ID, Pathology/Disease, + Phenotype, Organ, Analyzed Data. +

+

+ + Download FoundingGIDE template CSV{" "} + {" "} + + +

+

Example descriptions for these fields are provided below.

+ + ), + }, + { + id: "column-descriptions", + heading: "Providing column descriptions", + body: ( + <> +

+ BFF can display tooltips that describe the columns in your dataset if + provided an additional file (referenced as a “metadata descriptor + file” in the app). This file must contain three columns: +

+
    +
  • + Column Name — references a column in the dataset + you want to describe +
  • +
  • + Description — the description for that column +
  • +
  • + Type — the data type. BFF automatically detects + most types; the only value you may need to supply is{" "} + Open file link, which tells BFF the column represents a + link that can be opened with the “Open with…” + button. This is useful for pointing to alternative viewers or + related resources — for example, a column containing a direct link + to open a file in a specific tool. +
  • +
+

Example

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Column NameDescriptionType
Metadata FieldName of the metadata attribute being described
Study DescriptionSummary of the study's purpose, design, and scope
AuthorsList of contributors to the dataset or study
OrganizationInstitution or organization responsible for the dataset
PublicationAssociated publication or DOI describing the datasetOpen file link
LicenseUsage license governing the dataset (e.g., CC-BY)
Release DateDate the dataset was made publicly available
Imaging Method + Microscopy or imaging modality used (e.g., confocal, + light-sheet) +
Cell LineCell line used in the experiment
OrganismSpecies from which the sample was derived
GeneGene(s) of interest or manipulated in the experiment
CompoundChemical compound or treatment applied
AntibodyAntibody used for staining or detection
Channel — Content + Imaging channel identifier or label (e.g., Channel 1, GFP) +
Channel — Biological Entity + Biological structure or molecule represented in the channel +
InstrumentMicroscope or imaging instrument used
Dimension + Dimensionality of the dataset (e.g., 2D, 3D, time series) +
Pixel/Voxel Size / Time resolutionSpatial or temporal resolution of the imaging data
Study Unique IDUnique identifier for the overall study
Dataset Unique ID + Unique identifier for a specific dataset within the study +
Pathology/DiseaseDisease or pathological condition represented
PhenotypeObserved or computed phenotype from analysis
OrganOrgan or tissue source of the sample
Analyzed Data + Link to derived or processed data (e.g., segmentation, + features) + Open file link
+

+ + Download this example as CSV{" "} + {" "} + + +

+ + ), + }, + { + id: "provenance", + heading: "File & metadata provenance", + body: ( +

+ BFF supports describing relationships between files and metadata via a + provenance file.{" "} + + See the full provenance guide + + . +

+ ), + }, + ], + }, + + "getting-started/provenance": { + title: "File & metadata provenance", + intro: + 'Information about how files relate to each other or to different pieces of metadata can be provided via an additional file called a "Provenance file". Provenance in BioFile Finder (BFF) can describe relationships between files, between a file and a piece of metadata, and between two pieces of metadata.', + sections: [ + { + id: "provenance-where", + heading: "Where to provide the provenance file", + body: ( +

+ In BFF, open the data source panel by clicking the dataset name at the top + of the app. At the bottom of that panel you will find an optional field + labeled Provenance file. Paste the URL or drag in the file + there to load it alongside your dataset. +

+ ), + }, + { + id: "provenance-format", + heading: "Provenance file format", + body: ( + <> +

The provenance file should contain 6 columns:

+
    +
  • + Child — The column name in the dataset representing + the child entity in the relationship. +
  • +
  • + Relationship — Defines the relationship between + parent and child. If Relationship Type is empty, this should be a + human-readable description. If Relationship Type is{" "} + pointer, this should be the name of a dataset column + that encodes the relationship. +
  • +
  • + Parent — The column name in the dataset + representing the parent entity. +
  • +
  • + Child Typefile if the child is a + file in the dataset; entity if it is metadata. +
  • +
  • + Parent Typefile if the parent is a + file; entity if it is metadata. +
  • +
  • + Relationship Type — Empty for a static relationship + described in the Relationship field; pointer if the + relationship is defined via a dataset column. +
  • +
+

Simple example

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ChildRelationshipParentChild TypeParent TypeRelationship Type
WellIDis well inPlateIDentityentity
ColonyImageis image acquired fromWellIDfileentity
SegmentationImagesegmentation_algorithm_v1ColonyImagefilefilepointer
+
    +
  • + Plate → Well: Wells belong to a plate (simple + descriptive relationship). +
  • +
  • + Well → Colony Image: Colony images are acquired + from wells (image files linked to metadata). +
  • +
  • + Colony Image → Segmentation Image: Segmentation + images are derived from colony images; the relationship is specified + via a pointer to the segmentation algorithm version used. +
  • +
+

+ + Download this example{" "} + {" "} + + +

+ + ), + }, + { + id: "provenance-workflows", + heading: "Why provenance matters", + body: ( + <> +

+ Provenance is especially important in microscopy workflows that span + multiple levels of biological organization — such as plates, wells, and + individual image files. Without clear provenance linking each + segmentation file back to its original image, well, and plate context, + it becomes difficult to trace results back to the experimental setup. + Capturing these relationships ensures that derived data products remain + connected to their biological source, enabling validation, + troubleshooting, and reproducibility. +

+

+ In BFF, once a provenance file is loaded, each file row in the file list + will show a relationship indicator. Expanding a row reveals its linked + parent or child entities — for example, clicking a segmentation image + will show the colony image it was derived from and the well it + originated in. +

+

+ + Download example {" "} + + +

+

+ Provenance is also critical when a single publication draws on images + from multiple datasets. If the origin of each image is not clearly + documented — which dataset it came from, how it was selected, whether it + was processed consistently — readers and collaborators may struggle to + interpret how comparable those images truly are. By maintaining + provenance across datasets, researchers can clearly communicate how + figures were constructed and allow others to navigate back to the full + underlying data for verification or reuse. +

+

+ When provenance spans multiple datasets, BFF displays the dataset origin + of each file alongside its metadata. Filtering by dataset source allows + you to isolate images from a specific experiment, verify that processing + was applied consistently, and trace any figure back to its full source + dataset. +

+

+ + Download example {" "} + + +

+ + ), + }, + ], + }, +}; diff --git a/packages/web/src/components/UserGuide/content/index.ts b/packages/web/src/components/UserGuide/content/index.ts new file mode 100644 index 000000000..6c5b7a96a --- /dev/null +++ b/packages/web/src/components/UserGuide/content/index.ts @@ -0,0 +1,19 @@ +// Content is split into per-section files to keep each file focused and reviewable. +// To add a new page: add its entry to the relevant section file and add it to nav.ts. +// Note: PAGE_CONTENT keys must match the sectionSlug/pageSlug pattern used in nav.ts. + +export type { PageContent, PageSection } from "./types"; + +import { ABOUT_CONTENT } from "./about"; +import { APP_INFORMATION_CONTENT } from "./app-information"; +import { GETTING_STARTED_CONTENT } from "./getting-started"; +import { OTHER_RESOURCES_CONTENT } from "./other-resources"; +import { REAL_WORLD_USE_CASES_CONTENT } from "./real-world-use-cases"; + +export const PAGE_CONTENT = { + ...ABOUT_CONTENT, + ...APP_INFORMATION_CONTENT, + ...REAL_WORLD_USE_CASES_CONTENT, + ...GETTING_STARTED_CONTENT, + ...OTHER_RESOURCES_CONTENT, +}; diff --git a/packages/web/src/components/UserGuide/content/other-resources.tsx b/packages/web/src/components/UserGuide/content/other-resources.tsx new file mode 100644 index 000000000..1c8d9d0f3 --- /dev/null +++ b/packages/web/src/components/UserGuide/content/other-resources.tsx @@ -0,0 +1,204 @@ +import { Icon } from "@fluentui/react"; +import * as React from "react"; + +import type { PageContent } from "./types"; + +export const OTHER_RESOURCES_CONTENT: Record = { + "other-resources/storage-options": { + title: "Storage options", + intro: + "Whether cloud, local, or network storage, BioFile Finder (BFF) is agnostic of where your files live. The only requirement is that the metadata file is accessible to the browser running BFF.", + sections: [ + { + id: "cloud-storage", + heading: "Cloud storage", + body: ( + <> +

Public vs private

+

+ BFF works with both private and public cloud storage. The only + requirement is that{" "} + CORS permissions are + configured on the bucket so the browser can access the files. +

+ + ), + }, + { + id: "hard-drive", + heading: "Local and network storage", + body: ( + <> +

+ BFF can load a dataset file from a local hard drive or network-attached + storage. However, because BFF runs in the browser, local paths are not + persisted — if you refresh the page or share the link, you will be + prompted to reload the dataset file. +

+

+ Files referenced in the dataset can also live locally or on a network + drive, but they can only be opened in desktop applications (e.g. FIJI). + Web-based viewers do not have access to the local file system. If the + drive is disconnected, BFF will still display the metadata but the + viewers will be unable to open the files. +

+ + ), + }, + { + id: "cloud-examples", + heading: "Cloud storage examples", + body: ( + <> +

Open source image archives (IDR, BIA, SSBD)

+

+ A useful way to think about integrating the Image Data Resource (IDR), + BioImage Archive (BIA), and SSBD with BFF is that they occupy different + layers of the bioimaging data stack, and BFF can sit above them as a + unified discovery and navigation interface. +

+

How each resource differs

+
    +
  • + IDR (Image Data Resource) — Acts as a curated, + analysis-rich repository. Contains re-annotated datasets with strong + experimental structure (plates, wells, phenotypes, publications). + Best suited for exploration of complete biological studies. +
  • +
  • + BioImage Archive (BIA) — Acts as a raw data + deposition archive. Stores original imaging data from experiments, + often before heavy curation. Best suited for long-term storage and + reproducibility of raw microscopy data. +
  • +
  • + SSBD (Systems Science of Biological Dynamics) — + Acts as a modeling and dynamics-oriented repository. Focuses on + time-series, quantitative measurements, and computational models. + Best suited for quantitative and dynamic systems biology data rather + than raw image browsing. +
  • +
+

How BFF can use them together

+

+ BFF can sit above all three as a unified metadata and exploration layer: +

+
    +
  • + From IDR, BFF can link directly to structured + experimental datasets with rich biological context (e.g., plate → + well → image relationships already well-defined). +
  • +
  • + From BioImage Archive, BFF can expose raw datasets + by indexing deposited image files and attaching lightweight + metadata. +
  • +
  • + From SSBD, BFF can surface derived quantitative + datasets alongside images, enabling links between raw imaging and + downstream measurements or models. +
  • +
+

+ By sitting on top of these resources, BFF provides a single search and + discovery interface across raw (BIA), curated (IDR), and + quantitative/model-based (SSBD) datasets — allowing cross-repository + linking and normalized navigation from raw images through to derived + analysis. +

+

Google Sheets

+

You can use Google Sheets to publish your dataset publicly as a CSV:

+
    +
  1. + Click File in the toolbar +
  2. +
  3. + Choose Share then Publish to web +
  4. +
  5. + Select the sheet you want to publish and select{" "} + Comma-separated values +
  6. +
  7. + Click Publish and copy/paste the link into BFF +
  8. +
+

GitHub

+

+ In GitHub you can link to the Raw version of a file in + a repository to share the dataset with anyone that has access to that + repository. This also provides implicit dataset versioning, which can be + very useful for collaboration. +

+
    +
  1. + In GitHub, navigate to the file in the repository you want to + publish +
  2. +
  3. + Click the Raw button +
  4. +
  5. Copy and paste the URL from the browser into BFF
  6. +
+

+ New to GitHub?{" "} + + See GitHub documentation{" "} + + +

+

AWS S3

+

+ Your organization may provide support for choosing the best option, but + AWS S3 is a commonly used cloud storage service compatible with BFF that + you may consider.{" "} + + See AWS S3 documentation{" "} + + + . +

+ + ), + }, + ], + }, + + "other-resources/cors": { + title: "Avoiding CORS errors", + intro: + "You may need to set up CORS permissions on your cloud bucket to enable bff.allencell.org to access your files. The same will be true for any web-based image viewers — for example vole.allencell.org — that you want to use to view your data.", + sections: [ + { + id: "cors-setup", + heading: "Setting up CORS permissions", + body: ( + <> +

+ CORS (Cross-Origin Resource Sharing) permissions tell your cloud storage + bucket which web origins are allowed to read its files. Without this, + browsers block BFF and web-based viewers from accessing your data. +

+

+ Example for AWS S3:{" "} + + AWS CORS documentation{" "} + + + . +

+ + ), + }, + ], + }, +}; diff --git a/packages/web/src/components/UserGuide/content/real-world-use-cases.tsx b/packages/web/src/components/UserGuide/content/real-world-use-cases.tsx new file mode 100644 index 000000000..54ba5e6d6 --- /dev/null +++ b/packages/web/src/components/UserGuide/content/real-world-use-cases.tsx @@ -0,0 +1,539 @@ +import { Icon } from "@fluentui/react"; +import * as React from "react"; + +import type { PageContent } from "./types"; + +// Note: "real-world-use-cases/other-examples" exists in this file but has no +// corresponding nav entry. It is unreachable unless added to nav.ts. +export const REAL_WORLD_USE_CASES_CONTENT: Record = { + "real-world-use-cases/use-cases-overview": { + title: "Use cases & scenarios", + intro: + "BioFile Finder (BFF) is flexible enough to fit many different workflows and contexts. This page highlights common use cases observed across research labs, core facilities, and data teams — along with real-world scenarios showing how different types of users leverage BFF in their work.", + sections: [ + { + id: "use-case-table", + heading: "How people use BFF", + body: ( + <> +

This table is summary of the in-depth use cases described below.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Use caseKey BFF actionsTime saved vs. manual
+ Explore screening results + + Group by plate/treatment; filter by phenotype; share URL + Hours of scripting per query
+ Validate metadata + + Filter for blanks/duplicates; group to check counts; export + errors + Days of spreadsheet auditing
+ Inspect image subsets + + Multi-filter to exact subset; open in viewer; arrow-key + navigation + Hunting through folders by hand
+ Perform QC on datasets + + Aggregate counts per group; filter for anomalies; + cross-validate columns + Custom scripts per dataset
+ Manage image inventory + + Host metadata file; browse by any column; shareable filtered + URLs + Building and maintaining a web portal
+ Compare across experimental dimensions + + Pivot/group across multiple metadata axes (e.g., cell line × + staining × condition); rapidly switch views + Rewriting analysis scripts per comparison
+ Collaborative data exploration + + Share filtered views; maintain consistent dataset state + across users; parallel exploration + Back-and-forth file exchange and re-alignment
+ Publish interactive datasets + + Share public BFF links tied to figures; enable readers to + explore full datasets in-browser + Building custom portals or static supplements
+ + ), + }, + { + id: "explore-screening", + heading: "Explore screening results", + level: 3, + body: ( + <> +

+ A high-content screening run produces tens of thousands of images across + hundreds of wells, multiple plates, and several time points. The + pipeline outputs a Parquet or CSV manifest linking each image file to + its well position, compound treatment, concentration, cell line, and + measured phenotype scores. +

+

How BFF helps

+

+ Load the manifest into BFF and immediately group files by Plate > + Treatment > Concentration to see how many images exist at each + condition. Filter to a specific compound and sort by phenotype score to + surface the most interesting wells. Click into a well to see thumbnails + of every image at that position. Share the filtered view with a + colleague by copying the URL — they see exactly the same subset without + re-running any queries. +

+

Alternative use case

+

+ A genomics core runs CRISPR screens and outputs per-guide results as a + CSV. Researchers load it into BFF to filter by gene target, sort by + effect size, and quickly identify which guides to follow up on — without + writing R or Python code. +

+ + ), + }, + { + id: "validate-metadata", + heading: "Validate metadata", + level: 3, + body: ( + <> +

+ Before publishing a dataset or submitting to a repository, you need to + confirm that every file has complete, consistent metadata — no missing + cell lines, no mislabeled plates, no blank file paths. +

+

How BFF helps

+

+ Load your metadata file and use BFF's filters to find gaps. Group + by "Cell Line" and look for a blank or "(No value)" + group — those are your missing entries. Sort by "File Path" to + spot duplicates or malformed paths. Filter for rows where + "Treatment" is empty to find unlabeled conditions. Use the + aggregate count at each folder level to verify expected file counts per + condition (e.g., "I should have 96 images per plate — any plate + with fewer has missing data"). Export the problematic subset as a + CSV for correction. +

+

Alternative use case

+

+ A museum digitization team loads their specimen catalog CSV into BFF to + check for records missing accession numbers, blank taxonomic + classifications, or broken file paths to scans — catching errors before + ingesting into their collection management system. +

+ + ), + }, + { + id: "inspect-subsets", + heading: "Inspect subsets of images", + level: 3, + body: ( + <> +

+ You don't want to look at all 50,000 images. You want to look at a + very specific slice — maybe failed QC images, or images from a + particular experimental condition, or everything captured on a specific + date. +

+

How BFF helps

+

+ Apply filters to narrow down to exactly the subset you care about: + "Cell Line = iPSC" AND "Plate = 007" AND "QC + Status = Failed". The file list updates instantly to show only + matching files. Click any file to see its full metadata in the detail + panel. Open the image directly in your preferred viewer (FIJI, AGAVE, + Neuroglancer, or the browser) to visually inspect it. Navigate through + the filtered list with arrow keys to quickly scan through the subset + one-by-one. +

+

Alternative use case

+

+ A pathology lab filters their slide inventory to all H&E-stained + tissue sections from a specific patient cohort and date range, then + opens each in their whole-slide viewer to confirm stain quality before + analysis. +

+ + ), + }, + { + id: "perform-qc", + heading: "Perform QC on datasets", + level: 3, + body: ( + <> +

+ Quality control means systematically checking that your data meets + expectations — correct file counts, valid value ranges, no corrupted + entries, consistent naming. Doing this manually in Excel breaks down + past a few thousand rows. +

+

How BFF helps

+

+ Load a 2-million-row Parquet manifest and immediately see the total file + count in the aggregate info bar. Group by "Experiment > Plate + > Well" and check that each well has the expected number of + images — any well showing a lower count is a red flag. Filter for files + where "File Size" is 0 to find corrupt or empty files. Sort by + "Date Acquired" to verify temporal consistency. Group by + "Instrument" to check that all files came from the expected + microscope. Apply multiple filters simultaneously to cross-validate: + "If Plate = Control, then Treatment should be DMSO" — filter + for Control plates with non-DMSO treatments to find mislabeled rows. +

+

Alternative use case

+

+ A data engineer receives a new batch of sequencing metadata from a + collaborator and loads it into BFF to check for duplicate sample IDs, + verify that every file path resolves to an existing object in S3 (by + sorting/filtering paths), and confirm that all expected runs are + represented before ingesting into the pipeline. +

+ + ), + }, + { + id: "manage-inventory", + heading: "Manage image inventory", + level: 3, + body: ( + <> +

+ You or your team have accumulated a large collection of files over + months or years. They live across local drives, shared network storage, + or cloud buckets. You have metadata about them — maybe a database + export, maybe a painstakingly maintained spreadsheet — and you need an + easy way to browse, search, and share access to this inventory without + maintaining a server. +

+

How BFF helps

+

+ Export your inventory as a Parquet file (or maintain it as a CSV) with + columns for file path, file name, and any annotations that matter to + your team (project, investigator, organism, imaging modality, date, + etc.). Host the file on a web server, S3 bucket, or just keep it local. + Point BFF at it. Your entire team can now browse the inventory by any + column, search for specific files, and open them directly. Add a source + metadata file to provide human-readable descriptions for each column. + When someone asks "do we have any confocal images of iPSC-derived + cardiomyocytes from 2024?", the answer is three clicks away instead + of a Slack thread. +

+

Alternative use case

+

+ A natural history museum has 200,000 digitized specimen records in a CSV + exported from their collection database. They host a BFF instance on + their website so visiting researchers can browse specimens by taxonomy, + collection site, and date — filtering to exactly the subset relevant to + their study and downloading a manifest of matching file paths. +

+ + ), + }, + { + id: "real-world-scenarios", + heading: "Real-world scenarios", + body: ( + <> +

Wet-lab scientists

+

+ + I have thousands of images and I just want to find the right ones. + +

+

+ You ran a plate screen last week and now need to find every image from + Well A3 treated with Drug X. Your files are scattered across folders, + drives, or cloud storage, with no easy way to search by experimental + conditions. BFF lets you load a spreadsheet of your file metadata and + instantly filter, sort, and group by any column—cell line, treatment, + plate, date, or anything else you need. No coding, no databases, no IT + tickets. Just drag, drop, and find your files. +

+

Computational biologists

+

+ + I want to query millions of files without writing a pipeline to do + it. + +

+

+ You have a Parquet manifest with 10 million rows of imaging metadata. + You need to pull a specific subset for your next analysis run. BFF runs + full SQL queries in your browser via DuckDB—no server, no cluster, no + credentials. Filter by any combination of annotations, copy out the file + paths you need, and get back to your actual work. Share your exact query + with a collaborator by copying the URL. +

+

Data Engineers & Platform Teams

+

+ + I want to give users self-service data access without building a + portal. + +

+

+ Your team maintains the imaging pipeline. Scientists keep asking you to + "just pull all the files where..." and it turns into a JIRA + ticket every time. BFF is a zero-infrastructure frontend: point it at a + Parquet file on S3 or a CSV on a web server and your users can explore, + filter, and export on their own. No backend to deploy, no API to + maintain, no accounts to manage. Host a static web page and you're + done. +

+

Academic Facility Managers & PIs

+

+ I need to make my shared data actually usable. +

+

+ You run a core imaging facility or oversee a lab generating terabytes of + data. Your shared drive has 50,000 files and a naming convention that + made sense two years ago. BFF turns any metadata spreadsheet into a + searchable, filterable, shareable interface. Publish a dataset with a + BFF link and reviewers, collaborators, or new lab members can explore it + immediately—no software to install, no accounts to create. +

+

GLAM & Museum Professionals

+

+ I want to make my collection metadata interactive. +

+

+ You have a CSV with 200,000 digitized specimens, each with accession + numbers, taxonomic classifications, collection dates, and file paths to + high-resolution scans. BFF turns that spreadsheet into a browsable, + filterable, groupable interface—right in the browser. Let researchers + explore your collection by species, date range, or geographic origin. + Share a filtered view as a URL. No web developer needed. +

+ + ), + }, + ], + }, + + "real-world-use-cases/example-aics": { + title: "The cell science accelerator at Allen Institute", + intro: + "BioFile Finder (BFF) was used in publication by the cell science accelerator at Allen Institute.", + sections: [ + { + id: "publication", + heading: "", + body: ( + <> +

+ + Open publication{" "} + + +
+ + View dataset in BFF{" "} + + +

+

+ In this study on epithelial-to-mesenchymal transition (EMT), the authors + generated a large-scale microscopy dataset consisting of 3,538 3D + Z-stack datasets across 37 experimental conditions, 8 cell lines, and 9 + antibody stainings. BioFile Finder (BFF) was used to organize and + explore this complex dataset without relying on a fixed folder + hierarchy. Instead, BFF enabled dynamic filtering, grouping, and + navigation based on metadata, allowing users to analyze the data across + multiple dimensions (e.g., comparing stainings across cell lines) + without duplicating files. This approach improved collaboration between + experimental and computational researchers, supported parallel analysis + workflows, and reduced friction in large-scale data exploration. + Additionally, BFF was used to share the dataset publicly, enabling + readers to directly access figure-associated data, explore full 3D + timelapse datasets in the browser, and interact with the dataset using + the same flexible metadata-driven framework. +

+

Key takeaways

+
+

+ “Every organizational choice comes at the cost of another. In + other words, every choice is a bad choice.” — Antoine + Borensztejn, author +

+

+ “BioFile Finder (BFF) allowed us to break away from this + constraint entirely.” — Antoine Borensztejn,{" "} + author +

+

+ “We believe this approach sets a new standard for FAIR data + sharing, and will significantly improve the accessibility, + transparency, and reuse of complex biological datasets.” + — Antoine Borensztejn, author +

+
+ + ), + }, + ], + }, + + "real-world-use-cases/example-aibs": { + title: "The brain science accelerator at Allen Institute", + intro: + "BioFile Finder (BFF) was used in publication by the brain science accelerator at Allen Institute.", + sections: [ + { + id: "publication", + heading: "", + body: ( + <> +

+ + Open publication{" "} + + +
{" "} + + View dataset in BFF{" "} + + +

+

+ Yoav Ben-Simon from the Allen Institute for Brain Science describes + using BioFile Finder (BFF) as a flexible data management and sharing + platform for imaging datasets related to viral vector targeting in the + brain. BFF was used to organize datasets in a spreadsheet-like + interface, enabling intuitive querying, filtering, and restructuring of + data without requiring custom software development. The tool allowed + users to quickly create and curate datasets, organize them + hierarchically based on relevant features, and visualize grouped image + sets with thumbnails. This significantly lowered the barrier to entry + for data management and sharing, enabling non-engineers to deploy and + share datasets via simple links rather than building dedicated web + interfaces. Additionally, BFF facilitated collaboration by allowing + teams to interact with shared datasets dynamically and supported reuse + across different domains, extending from cell imaging to brain section + and genomic data visualization. +

+

Key takeaways

+
+

+ “BioFile Finder is a data management tool… like a fancy + spreadsheet so that you can interact with it in multiple different + ways.” — Yoav Ben-Simon, author +

+

+ “I can create and curate data sets in two or three clicks of a + button.” +

+

+ “It doesn't require exchanging of files—it just + requires exchanging of links.” — Yoav Ben-Simon,{" "} + author +

+

+ “It was really easy for us to repurpose it… from + looking at individual cells to looking at images of brain sections + and genomic data.” — Yoav Ben-Simon, author +

+
+ + ), + }, + { + id: "video", + heading: "", + body: <>{/* TODO: Add link to video once it is made public */}, + }, + ], + }, + + "real-world-use-cases/example-isas": { + title: "AMBIOM at ISAS", + sections: [ + { + id: "publication", + heading: "", + body: ( + <> + {/* TODO: Add publication link once available */} +

+ Content coming soon. +

+ + ), + }, + ], + }, + + "real-world-use-cases/other-examples": { + title: "Other examples", + sections: [], + }, +}; diff --git a/packages/web/src/components/UserGuide/content/types.ts b/packages/web/src/components/UserGuide/content/types.ts new file mode 100644 index 000000000..2c5d10852 --- /dev/null +++ b/packages/web/src/components/UserGuide/content/types.ts @@ -0,0 +1,18 @@ +import * as React from "react"; + +export interface PageSection { + id: string; + heading?: string; + /** + * Heading level rendered for this section (h2, h3, or h4). + * Defaults to h2 if omitted. Use h3/h4 for subsections within a page. + */ + level?: 2 | 3 | 4; + body: React.ReactNode; +} + +export interface PageContent { + title: string; + intro?: string; + sections: PageSection[]; +} diff --git a/packages/web/src/components/UserGuide/index.tsx b/packages/web/src/components/UserGuide/index.tsx new file mode 100644 index 000000000..d05f155ff --- /dev/null +++ b/packages/web/src/components/UserGuide/index.tsx @@ -0,0 +1,75 @@ +import * as React from "react"; +import { Navigate, useLocation, useParams } from "react-router-dom"; + +import { PrimaryButton } from "../../../../core/components/Buttons"; +import DocPage from "./DocPage"; +import Sidebar from "./Sidebar"; +import styles from "./UserGuide.module.css"; + +export default function UserGuide() { + const { sectionSlug, pageSlug } = useParams<{ + sectionSlug: string; + pageSlug: string; + }>(); + const location = useLocation(); + const [menuOpen, setMenuOpen] = React.useState(false); + const contentRef = React.useRef(null); + + React.useEffect(() => { + setMenuOpen(false); + if (contentRef.current) { + contentRef.current.scrollTop = 0; + } + }, [location.pathname]); + + // Close mobile menu on Escape — standard accessibility expectation for overlays + React.useEffect(() => { + if (!menuOpen) return; + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === "Escape") setMenuOpen(false); + }; + document.addEventListener("keydown", handleKeyDown); + return () => document.removeEventListener("keydown", handleKeyDown); + }, [menuOpen]); + + if (!sectionSlug || !pageSlug) { + return ; + } + + return ( +
+ {/* Mobile page picker */} +
+
+ setMenuOpen((prev) => !prev)} + /> +
+ {menuOpen && ( + <> +
setMenuOpen(false)} + /> +
+ +
+ + )} +
+ + {/* Desktop sidebar */} + + + {/* Main content */} +
+ +
+
+ ); +} diff --git a/packages/web/src/components/UserGuide/nav.ts b/packages/web/src/components/UserGuide/nav.ts new file mode 100644 index 000000000..f43465b5c --- /dev/null +++ b/packages/web/src/components/UserGuide/nav.ts @@ -0,0 +1,196 @@ +export type NavHeading = { + id: string; + text: string; +}; + +export type NavPage = { + title: string; + slug: string; + // headings: reserved for possible future in-page anchor navigation in the sidebar + headings?: NavHeading[]; +}; + +export type NavSection = { + title: string; + slug: string; + pages: NavPage[]; +}; + +export const NAV: NavSection[] = [ + { + title: "About", + slug: "about", + pages: [ + { + title: "Overview", + slug: "overview", + headings: [ + { id: "what-is-bff", text: "What is BioFile Finder?" }, + { id: "who-is-bff-for", text: "Who is BFF for?" }, + { id: "why-use-bff", text: "Why use BFF?" }, + { id: "bff-comparison-table", text: "How BFF compares" }, + ], + }, + { + title: "Feature highlights", + slug: "features", + headings: [ + { id: "in-browser-querying", text: "In-browser querying" }, + { id: "dynamic-grouping", text: "Dynamic grouping & hierarchy" }, + { id: "sharing", text: "Sharing" }, + { id: "thumbnails", text: "Thumbnail previews" }, + { id: "code-generation", text: "Code generation" }, + { id: "viewer-integrations", text: "Viewer integrations" }, + ], + }, + ], + }, + { + title: "Real world use", + slug: "real-world-use-cases", + pages: [ + { + title: "Use cases & scenarios", + slug: "use-cases-overview", + headings: [ + { id: "use-case-table", text: "How people use BFF" }, + { id: "explore-screening", text: "Explore screening results" }, + { id: "validate-metadata", text: "Validate metadata" }, + { id: "inspect-subsets", text: "Inspect subsets of images" }, + { id: "perform-qc", text: "Perform QC on datasets" }, + { id: "manage-inventory", text: "Manage image inventory" }, + { id: "real-world-scenarios", text: "Real-world scenarios" }, + ], + }, + { + title: "The cell science accelerator at Allen Institute", + slug: "example-aics", + headings: [ + { id: "publication", text: "Publication" }, + { id: "dataset", text: "Dataset" }, + ], + }, + { + title: "The brain science accelerator at Allen Institute", + slug: "example-aibs", + headings: [ + { id: "publication", text: "Publication" }, + { id: "dataset", text: "Dataset" }, + { id: "video", text: "Video" }, + ], + }, + { + title: "AMBIOM at ISAS", + slug: "example-isas", + headings: [], + }, + ], + }, + { + title: "App information", + slug: "app-information", + pages: [ + { + title: "Specifications", + slug: "specifications", + headings: [ + { id: "file-size-limitations", text: "File size limitations" }, + { id: "compatible-file-formats", text: "Compatible file formats" }, + { id: "preferred-browsers", text: "Preferred browsers" }, + { id: "open-source", text: "Open source" }, + ], + }, + { + title: "Supported viewers", + slug: "supported-viewers", + headings: [ + { id: "decision-guide", text: "Decision guide" }, + { id: "viewer-table", text: "Viewer comparison table" }, + ], + }, + ], + }, + { + title: "Getting started", + slug: "getting-started", + pages: [ + { + title: "Setup overview", + slug: "setup-overview", + headings: [ + { id: "basic-setup", text: "Basic setup" }, + { id: "minimum-requirements", text: "Minimum requirements" }, + { id: "common-workflows", text: "Common workflows" }, + { id: "recommended-setup", text: "Recommended setup" }, + ], + }, + { + title: "Creating a dataset", + slug: "creating-a-dataset", + headings: [ + { id: "creating-spreadsheet", text: "What is a dataset?" }, + { id: "rows-columns", text: "Rows and columns" }, + { id: "required-columns", text: "Required columns" }, + { id: "optional-columns", text: "Optional special columns" }, + { id: "spreadsheet-examples", text: "Spreadsheet examples" }, + ], + }, + { + title: "Metadata guidance", + slug: "metadata-guidance", + headings: [ + { id: "recommendations", text: "Recommendations" }, + { id: "column-descriptions", text: "Providing column descriptions" }, + { id: "provenance", text: "File & metadata provenance" }, + ], + }, + { + title: "Provenance", + slug: "provenance", + headings: [ + { id: "provenance-where", text: "Where to add the file" }, + { id: "provenance-format", text: "File format" }, + { id: "provenance-workflows", text: "Why provenance matters" }, + ], + }, + ], + }, + { + title: "Other resources", + slug: "other-resources", + pages: [ + { + title: "Storage options", + slug: "storage-options", + headings: [ + { id: "cloud-storage", text: "Cloud storage" }, + { id: "cloud-examples", text: "Cloud storage examples" }, + { id: "hard-drive", text: "Local and network storage" }, + ], + }, + { + title: "Avoiding CORS errors", + slug: "cors", + headings: [{ id: "cors-setup", text: "Setting up CORS permissions" }], + }, + ], + }, +]; + +export function getAdjacentPages( + sectionSlug: string, + pageSlug: string +): { + prev: { section: NavSection; page: NavPage } | null; + next: { section: NavSection; page: NavPage } | null; +} { + const allPages: { section: NavSection; page: NavPage }[] = []; + NAV.forEach((section) => section.pages.forEach((page) => allPages.push({ section, page }))); + const idx = allPages.findIndex( + (p) => p.section.slug === sectionSlug && p.page.slug === pageSlug + ); + return { + prev: idx > 0 ? allPages[idx - 1] : null, + next: idx < allPages.length - 1 ? allPages[idx + 1] : null, + }; +} diff --git a/packages/web/src/index.tsx b/packages/web/src/index.tsx index 1c4e8a778..5223cf18b 100644 --- a/packages/web/src/index.tsx +++ b/packages/web/src/index.tsx @@ -4,7 +4,7 @@ import { memoize } from "lodash"; import * as React from "react"; import { render } from "react-dom"; import { Provider } from "react-redux"; -import { createBrowserRouter, RouterProvider } from "react-router-dom"; +import { createBrowserRouter, Navigate, RouterProvider } from "react-router-dom"; import NotificationServiceWeb from "./services/NotificationServiceWeb"; import ApplicationInfoServiceWeb from "./services/ApplicationInfoServiceWeb"; @@ -14,10 +14,10 @@ import FileViewerServiceWeb from "./services/FileViewerServiceWeb"; import FileDownloadServiceWeb from "./services/FileDownloadServiceWeb"; import PersistentConfigServiceWeb from "./services/PersistentConfigServiceWeb"; import ErrorPage from "./components/ErrorPage"; -import Learn from "./components/Learn"; import Home from "./components/Home"; import Layout from "./components/Layout"; import OpenSourceDatasets from "./components/OpenSourceDatasets"; +import UserGuide from "./components/UserGuide"; import SiteLogo from "../assets/site-logo.png"; import FmsFileExplorer from "../../core/App"; import { createReduxStore } from "../../core/state"; @@ -38,10 +38,6 @@ const router = createBrowserRouter( path: "/", element: , // Splash page }, - { - path: "learn", - element: , - }, { path: "app", element: , @@ -50,6 +46,14 @@ const router = createBrowserRouter( path: "datasets", element: , }, + { + path: "user-guide", + element: , + }, + { + path: "user-guide/:sectionSlug/:pageSlug", + element: , + }, ], }, ], diff --git a/packages/web/tsconfig.json b/packages/web/tsconfig.json new file mode 100644 index 000000000..d0a87e682 --- /dev/null +++ b/packages/web/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "include": [ + "src/**/*", + "custom.d.ts" + ] +} diff --git a/packages/web/webpack/webpack.config.js b/packages/web/webpack/webpack.config.js index 0782e8957..53976a64d 100644 --- a/packages/web/webpack/webpack.config.js +++ b/packages/web/webpack/webpack.config.js @@ -95,6 +95,7 @@ module.exports = ({ analyze, production } = {}) => ({ output: { path: path.resolve(__dirname, "../", "dist"), filename: "[name].[chunkhash].js", + publicPath: "/", }, plugins: getPluginsByEnv(production, analyze), resolve: {