diff --git a/.github/images/dashboard.png b/.github/images/dashboard.png deleted file mode 100644 index 9f0a1b86..00000000 Binary files a/.github/images/dashboard.png and /dev/null differ diff --git a/.github/images/run-detail.png b/.github/images/run-detail.png deleted file mode 100644 index 125f9bd7..00000000 Binary files a/.github/images/run-detail.png and /dev/null differ diff --git a/.github/images/runs-table.png b/.github/images/runs-table.png deleted file mode 100644 index 928e7b60..00000000 Binary files a/.github/images/runs-table.png and /dev/null differ diff --git a/.github/workflows/docs-screenshots.yml b/.github/workflows/docs-screenshots.yml new file mode 100644 index 00000000..e66fa6ff --- /dev/null +++ b/.github/workflows/docs-screenshots.yml @@ -0,0 +1,117 @@ +name: Docs screenshots + +# Retakes the docs site screenshots and opens a pull request into staging. +# Run it by hand from the Actions tab for an extra refresh. Pull requests made +# with the built-in token don't start other workflows, which is fine because +# staging requires a review and no checks. + +on: + schedule: + # 17:00 UTC on the 1st, which is morning in US Pacific time. + - cron: "0 17 1 * *" + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +# A second run would force-push over the first one's branch. +concurrency: + group: docs-screenshots + cancel-in-progress: false + +env: + BRANCH: automation/docs-screenshots + +jobs: + docs-screenshots: + runs-on: ubuntu-latest + timeout-minutes: 60 + + services: + postgres: + image: postgres:17 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - uses: actions/checkout@v6 + with: + ref: staging + + - uses: actions/setup-node@v6 + with: + node-version: 24 + + - uses: astral-sh/setup-uv@v8.1.0 + + - name: Install Node.js packages + run: npm ci + working-directory: web + + # The sample files go through the Python processors, as in local + # development. + - name: Install Python packages + run: uv sync --all-packages + + - name: Install the browser + run: npx playwright install --with-deps chromium + working-directory: web + + # A screen that fails keeps its old image and is listed in the pull + # request. The run exits non-zero for it, so the next steps must not + # depend on this one passing. + - name: Take the screenshots + id: shots + run: make docs-screenshots + continue-on-error: true + + - name: Check for changes + id: changes + run: | + git add docs/site/public/images docs/site/src/lib/screenshots.generated.ts + if git diff --cached --quiet; then + echo "changed=false" >> "$GITHUB_OUTPUT" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Open or update the pull request + if: steps.changes.outputs.changed == 'true' + env: + GH_TOKEN: ${{ github.token }} + run: | + summary=web/.docs-screenshots/summary.md + if [ ! -f "$summary" ]; then + echo "Automated refresh of the docs screenshots. The run did not write a summary, so check the workflow log." > "$summary" + fi + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git switch -C "$BRANCH" + git commit -m "Refresh docs screenshots" + git push --force origin "$BRANCH" + + open=$(gh pr list --head "$BRANCH" --base staging --state open --json number --jq 'length') + if [ "$open" = "0" ]; then + gh pr create --base staging --head "$BRANCH" \ + --title "Refresh docs screenshots" --body-file "$summary" + else + gh pr edit "$BRANCH" --body-file "$summary" + fi + + # If nothing changed and the run failed, it never got as far as taking + # screenshots. Failing here makes that visible. + - name: Fail when nothing was captured + if: steps.changes.outputs.changed == 'false' && steps.shots.outcome == 'failure' + run: | + echo "::error::The screenshot run failed before it changed any image. Check the log above." + exit 1 diff --git a/Makefile b/Makefile index 58885531..0f74acde 100644 --- a/Makefile +++ b/Makefile @@ -137,6 +137,14 @@ docs-catalogs: make py-watcher-cli-catalog make docs-bundle-generate +# Retake the docs site screenshots from a throwaway local database and rewrite +# docs/site/public/images. Needs Postgres, uv, and Playwright's Chromium. +# Usage: make docs-screenshots [ONLY=home,my-runs] [SKIP_SEED=1]. See +# docs/developer/local-development.md. +.PHONY: docs-screenshots +docs-screenshots: + cd web && npm run docs:screenshots -- $(if $(ONLY),--only $(ONLY)) $(if $(SKIP_SEED),--skip-seed) + .PHONY: docs-format docs-format: cd docs/site && npm run lint:fix diff --git a/README.md b/README.md index 1971c6a9..077182cb 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,21 @@ Nobody signs up for Data Hub. Your team deploys it, so raw instrument data and e [Documentation](https://datahub.arcadiascience.com/docs) · [Set up an instrument](https://datahub.arcadiascience.com/docs/set-up-an-instrument) · [Deploy it yourself](docs/developer/first-time-deployment.md) · [Architecture](docs/developer/architecture.md) -![The Data Hub dashboard, showing run counts, instrument status, and a table of recent runs](.github/images/dashboard.png) - -![The runs table for a SpectraMax iD5 plate reader, filtered by measurement mode, with wavelength and measurement type columns](.github/images/runs-table.png) - -![A plate reader run detail page showing measurement metadata, a file list, and a color-coded 96-well plate map](.github/images/run-detail.png) + + + + The Data Hub dashboard, showing run counts, instrument status, and a table of recent runs + + + + + The runs table for a SpectraMax iD5 plate reader, with wavelength, measurement mode, and measurement type columns + + + + + A plate reader run detail page showing measurement metadata, a file list, and a color-coded plate map + ## Web app, REST API, and MCP diff --git a/docs/developer/local-development.md b/docs/developer/local-development.md index ad9983b3..0d61c1ce 100644 --- a/docs/developer/local-development.md +++ b/docs/developer/local-development.md @@ -227,9 +227,47 @@ A few details worth knowing: - The route is gated on `NODE_ENV !== "production"` AND `LOCAL_S3_MIRROR` set; either condition unmet returns 404 unconditionally, so a production build can never expose the filesystem. - The MCP tool at `/mcp/v1` returns a relative `/api/local-s3/...` URL when the mirror is active — browsers resolve it against the current origin, but non-browser MCP clients on localhost may need to prefix with `http://localhost:3000`. +## Docs screenshots + +The screenshots on the docs site are generated. `make docs-screenshots` seeds a separate database, starts the web app on its own port, opens each screen in a headless Chromium, and writes a light and a dark WebP file for it into `docs/site/public/images/`. The workflow [.github/workflows/docs-screenshots.yml](../../.github/workflows/docs-screenshots.yml) runs it on the first of every month and opens a pull request into `staging`. You can also start it from the Actions tab with **Run workflow**. + +You need Postgres running with the same `postgres` login as above, `uv` (the sample files go through the Python processors), and the browser, which you install once: + +```sh +cd web && npx playwright install chromium +``` + +```sh +make docs-screenshots # every screenshot +make docs-screenshots ONLY=home,my-runs # only these +make docs-screenshots SKIP_SEED=1 # reuse the data from the last run +cd web && npm run docs:screenshots -- --serve # seed and start the app, then wait +``` + +How it works: + +- It uses its own database, `data_hub_screenshots`, and its own file mirror under `web/.docs-screenshots/`, so your local data is untouched and `make dev` can keep running. +- It runs the app with `next dev`, because the local file mirror that serves the gel images, PDFs, and plate maps is turned off in production builds. It restores `web/tsconfig.json`, `web/AGENTS.md`, and `web/next-env.d.ts`, which `next dev` edits when it starts. +- The data is the same as `make db-reseed` ([web/scripts/seed-workspace.ts](../../web/scripts/seed-workspace.ts)), plus a few things only the screenshots need ([web/scripts/docs-screenshots/setup.ts](../../web/scripts/docs-screenshots/setup.ts)): realistic access tokens, a fake Linear workspace from the feedback package's test server, sample feedback reports, and a sample AI client for the sign-in approval screen. Shots that change data, such as the manual-upload run and the deleted run, undo their change when they finish. +- It signs in by writing a session row for Alice, so the dev-only sign-in form isn't needed. +- Dates are real, and every run rewrites every image. Before each shot it moves the seeded watcher heartbeats forward, because a watcher shows as offline after five minutes without one. +- With `--serve`, it writes `web/.docs-screenshots/serve.json` with the app's address and a session cookie, so you can drive the app yourself while you work out the steps for a new shot. + +If a screen fails, its old images stay. The run prints the failure, saves what the browser showed to `web/.docs-screenshots/failures/`, and writes `web/.docs-screenshots/summary.md`, which the workflow uses as the pull request description. + +To add a screenshot: + +1. Add an entry to [web/scripts/docs-screenshots/shots.ts](../../web/scripts/docs-screenshots/shots.ts). It names the page to open, the steps to reach the right state, and whether it is a full window or a close-up of one element. +2. Run `make docs-screenshots ONLY=`. +3. Reference `![Alt text](/images/.webp)` from a docs page. The docs site draws a browser frame around full windows and shows the light or dark image to match the reader's theme. +4. Commit the new images and `docs/site/src/lib/screenshots.generated.ts`, which the script writes. + +A screenshot the script can't take, such as the Claude desktop app in `mcp.png`, is listed under `MANUAL_SHOTS` in the same file with the date it was taken. The pull request lists how old each one is. + ## Where the seed lives - [web/lib/db/seed.ts](../../web/lib/db/seed.ts) — shared builder functions (`seedDevUser`, `seedInstruments`, `seedRuns`, etc.) plus a schema-driven `clearAll()`. +- [web/scripts/seed-workspace.ts](../../web/scripts/seed-workspace.ts) — the full seeded workspace, shared by `npm run db:seed` and the docs screenshots. - [web/scripts/seed-database.ts](../../web/scripts/seed-database.ts) — the entry point that `npm run db:seed` runs. - [web/scripts/process-fixtures.ts](../../web/scripts/process-fixtures.ts) — shared probe/spawn logic for the post-seed handler step. - [web/scripts/process-seeded-fixtures.ts](../../web/scripts/process-seeded-fixtures.ts) — `npm run db:process-fixtures` entry point for re-running the handler step on demand. diff --git a/docs/site/biome.json b/docs/site/biome.json index f6b16aeb..77febf0f 100644 --- a/docs/site/biome.json +++ b/docs/site/biome.json @@ -13,7 +13,8 @@ "!dist", "!build", "!.source", - "!src/lib/*.snapshot.json" + "!src/lib/*.snapshot.json", + "!src/lib/screenshots.generated.ts" ] }, "formatter": { diff --git a/docs/site/content/docs/browse-runs.mdx b/docs/site/content/docs/browse-runs.mdx index c9689c68..a3c7d88a 100644 --- a/docs/site/content/docs/browse-runs.mdx +++ b/docs/site/content/docs/browse-runs.mdx @@ -14,18 +14,20 @@ The sidebar and the home page are the two starting points for daily work. **Home** lists recent runs across instruments, most recent first, with counts for today and this week at the top. Filter by date range, who ran it, and instrument-specific metadata when it’s available, then page through results. Below the runs, Home shows up to four of today’s comments; use the date picker to see comments from a different range. -![Home dashboard with instrument status and recent runs](/images/home.png) +![Home dashboard with instrument status and recent runs](/images/home.webp) **Instruments** in the sidebar lists five instruments, and **View all** opens the full list. Each instrument’s page shows the folder its watcher reads from and the file patterns it uploads, then a runs table with column filters that match the instrument type (for example wavelengths on a plate reader). -![Plate reader instrument page with the runs table and column filters](/images/plate-reader-runs-table.png) +![Plate reader instrument page with the runs table and column filters](/images/plate-reader-runs-table.webp) **My runs** lists runs you’ve claimed, so you can return to your own work without re-filtering the full dashboard. -![My runs page listing runs attributed to the signed-in user](/images/my-runs.png) +![My runs page listing runs attributed to the signed-in user](/images/my-runs.webp) **Comments** lists every run comment by day. Open it from **View all comments** on Home. Each person’s profile page also shows the comments they wrote and the comments on runs they ran. +![Comments page listing each run comment by day, with filters for instrument and for comments on your runs](/images/comments-page.webp) + ### Narrow a long run list [#narrow-a-long-run-list] Home and the instrument pages share the same four controls: @@ -57,7 +59,7 @@ A run with files in several states shows the first status in this table that app Press **⌘ K** (macOS) or **Ctrl K** (Windows/Linux) to open global search from any page. Results are grouped into runs, files, instruments, users, and comments. That means you can also look up a colleague’s work or the text of a lab note. A file hit opens the parent run with the files table already filtered to that file. -![Global search modal with results for runs and files](/images/search-modal.png) +![Global search modal with results for runs and files](/images/search-modal.webp) ## Open a run and inspect results [#open-run-detail] @@ -75,11 +77,11 @@ Reports with many images, PDFs, or spectra show one item at a time. Search by fi Per-type inputs and outputs are in [Instrument data preprocessing](/docs/instrument-preprocessing). -![Gel doc run detail with metadata badges, files table, and report preview](/images/gel-doc-run-details.png) +![Gel doc run detail with metadata badges, files table, and report preview](/images/gel-doc-run-details.webp) -![Plate reader run detail with plate map visualization](/images/plate-reader-run-details-1.png) +![Plate reader run detail with plate map visualization](/images/plate-reader-run-details-1.webp) -![Plate reader run detail with kinetic traces and comments](/images/plate-reader-run-details-2.png) +![Plate reader run detail with kinetic traces and comments](/images/plate-reader-run-details-2.webp) ### Filter the files table [#filter-files] @@ -101,6 +103,8 @@ From the files table you can: - Select files and choose **Download** in the bar that appears above the files table. One file downloads directly. Two or more come as a single zip, which builds in the background like **Download all** - Choose **Download all** to build a zip of active files in the run, with any active file filters applied +![File preview window showing the first rows of a processed CSV, with a Download button and buttons for the next and previous file](/images/csv-preview.webp) + Large zips build in the background. When one isn’t ready yet, Data Hub tells you how long to wait before trying again. ## Act on several runs at once [#bulk-actions] @@ -120,7 +124,7 @@ The selection stays when you move to another page of the same results. It clears The files table on a run page works the same way: select files to get **Upload**, **Dismiss**, **Reprocess**, and **Download** for just those files. Its selection also clears when you change the files search, a filter, or the sort. -![Run list with several runs selected and the bulk action bar open](/images/bulk-actions.png) +![Run list with several runs selected and the bulk action bar open](/images/bulk-actions.webp) ## Comment on a run [#comment] @@ -128,7 +132,7 @@ The comments thread on run details is for lab notes and follow-ups. Comment acti Only the author of a comment can edit it. Authors can delete their own comments, and workspace admins can delete any comment. Comments posted by a script through an API token show the token’s name with a key icon instead of a person. -![Gel doc processed image with a lab comment on the run](/images/gel-doc-run-comments.png) +![Gel doc processed image with a lab comment on the run](/images/gel-doc-run-comments.webp) ## Reprocess or delete a run [#reprocess-and-delete] @@ -142,12 +146,16 @@ Reprocessing sends only the original instrument files. Files Data Hub generated, **Delete** marks the run deleted without removing stored files. To find a deleted run again, turn on **Show deleted runs** under **Add filter**, then open it and choose **Restore**. Anyone signed in can delete or restore a run. +![Deleted run page with a red banner naming who deleted it and when, and a Restore button](/images/deleted-run.webp) + ### Request an upload [#request-upload] A file that the watcher found but hasn’t uploaded shows **Pending**. Choose **Upload** on that file, or select runs and choose **Upload**, to ask the watcher to send it. The watcher picks up the request within a minute. On instruments that upload automatically, this needs watcher 1.2.1 or later; see [Roll out watcher releases](/docs/watcher-releases#what-each-version-adds). For instruments in [manual upload mode](/docs/concepts#upload-mode), every file waits for this approval. **Dismiss** tells the watcher not to upload a file. See [Manage the watcher fleet](/docs/manage-watchers#approving-manual-uploads). +![Run page for a manual-upload instrument with its pending files selected and a bar offering Upload and Dismiss](/images/manual-upload.webp) + ## Use the same data outside the UI [#programmatic-access] You can also list and fetch the same runs and files over the [REST API](/docs/api) or the [Model Context Protocol (MCP) endpoint](/docs/mcp). REST needs a [personal access token](/docs/concepts#personal-access-token); scopes and creation are covered in [Issue and revoke tokens](/docs/manage-tokens). MCP clients sign in through the browser instead, and clients that support MCP Apps show the same interactive run report you see here. diff --git a/docs/site/content/docs/configure-notifications.mdx b/docs/site/content/docs/configure-notifications.mdx index 1413b862..3bb7d540 100644 --- a/docs/site/content/docs/configure-notifications.mdx +++ b/docs/site/content/docs/configure-notifications.mdx @@ -8,7 +8,7 @@ contentType: How-to Data Hub can notify you in the app and in Slack when new runs arrive, when someone comments on a run you care about, when a script sends you a message, and when feedback you sent is closed. Open **Settings → Notifications** while signed in. Everything on that page is a personal choice that affects only you. -![Settings Notifications page with in-app preferences and the notification bell](/images/notifications.png) +![Settings Notifications page with in-app preferences and the notification bell](/images/notifications.webp) Connections that the whole workspace shares, such as the Slack channel for new runs and the Slack app behind direct messages, are under **Settings → Integrations** and only admins can change them. See [Set up integrations](/docs/integrations). @@ -37,6 +37,8 @@ The bell icon in the app lists recent notifications: - Clicking a comment notification opens the run and scrolls to that comment. - Messages sent by a script through an API token show the token’s name with a key icon. +![Notification list opened from the bell, with new runs grouped by day and instrument](/images/notification-bell.webp) + ## Slack direct messages [#slack-direct-messages] Connect your Slack account on the same settings page to receive matching events as personal direct messages. After connecting, choose which types also go to Slack: diff --git a/docs/site/content/docs/instrument-preprocessing.mdx b/docs/site/content/docs/instrument-preprocessing.mdx index 8a32f138..8d22cc0b 100644 --- a/docs/site/content/docs/instrument-preprocessing.mdx +++ b/docs/site/content/docs/instrument-preprocessing.mdx @@ -35,6 +35,8 @@ Processing is chosen by the instrument’s **type**, not by its instrument ID. T To set it, open the instrument page as an admin, choose **Edit details** from the three-dot menu, and pick an **Instrument type**. +![Edit instrument window with the display name and instrument type fields](/images/instrument-edit-dialog.webp) + One type means one vendor’s output format. Names like `qpcr` and `fplc` read as generic, but each is bound to a single vendor’s parser: `fplc` expects ÄKTA PDFs, `qpcr` expects Azure Cielo exports. Typing a different vendor’s instrument into an existing type feeds files to a parser that can’t read them. Supporting a second vendor means splitting the type, which is a code change. @@ -103,6 +105,8 @@ Reads dye channel names, melting curves, and the instrument’s PDF report. The **Melting Curves** section shows the plate for one dye channel at a time. Click a well to see its melting curve and derivative, with a dashed line at the melting temperature. A run with more than one PDF gets previous and next buttons under **Report Data**. +![qPCR run page with dye-channel badges, the files table, and a Melting Curves plate showing a derivative curve in each well](/images/qpcr-run-detail.webp) + The other CSV files a qPCR run writes upload normally and show as completed, but contribute no metadata. ### `.AZE` project files [#aze-project-files] @@ -182,6 +186,8 @@ Reads Aunty exports, which measure protein melting temperature and particle size - **Processed files**: `{run_id}_aunty_curves.csv`, with every curve point for every well, and `{run_id}_aunty_plate.json`, which drives the plate view - **On the run page**: a plate with a small chart in each well. Click a well to open its full chart. +![Aunty run page with the experiment type and temperature badges, the files table, and a plate with a small curve in each well](/images/aunty-run-detail.webp) + On the instrument page, filter Aunty runs by experiment type, analysis mode, temperature, and ramp rate. ## Types with no processor [#instruments-without-a-matching-processor] diff --git a/docs/site/content/docs/integrations.mdx b/docs/site/content/docs/integrations.mdx index 828d8c3e..80a02697 100644 --- a/docs/site/content/docs/integrations.mdx +++ b/docs/site/content/docs/integrations.mdx @@ -14,6 +14,8 @@ contentType: How-to Each person’s own notification choices are separate, under **Settings → Notifications**. See [Configure notifications](/docs/configure-notifications). +![Settings Integrations page before anything is connected, showing the first step of the Linear card with fields for the client ID and client secret](/images/integrations.webp) + ## How secrets are stored [#integration-secrets-key] Data Hub encrypts the secrets you paste on this page before saving them: the Linear client secret and signing secret, and the Slack bot token and client secret. It never shows a saved secret again. To change one, paste a new value and save. diff --git a/docs/site/content/docs/manage-instruments.mdx b/docs/site/content/docs/manage-instruments.mdx index 9cb6f423..4fb4e7cb 100644 --- a/docs/site/content/docs/manage-instruments.mdx +++ b/docs/site/content/docs/manage-instruments.mdx @@ -18,7 +18,7 @@ The **Instruments** page has tabs for active, pending, and retired instruments. | `active` | Active | Confirmed and accepting uploads. | | `inactive` | Retired | Deactivated; no new uploads. Historical runs stay browsable. | -![Instruments page with Active, Pending, and Retired tabs](/images/instruments-table.png) +![Instruments page with Active, Pending, and Retired tabs](/images/instruments-table.webp) ## Confirm a pending instrument [#confirm-a-pending-instrument] @@ -38,6 +38,8 @@ Click **Confirm**. Status becomes `active` and the operator’s watcher can uplo +![Instruments page on the Pending tab, with a pending instrument showing a yellow Pending badge and a Confirm button](/images/instruments-pending.webp) + Confirming is admin-only for signed-in users. Under the hood it is `PATCH /api/v1/instruments/:id`. Token automation uses the `instruments:write` scope instead. See the [generated API docs](/docs/api). ## Read an instrument’s page [#view-instrument-health] @@ -60,4 +62,6 @@ The three-dot menu on an instrument page, and the row menu on the instruments ta - **View watcher**: open the watcher registered for this instrument - **Retire** or **Reactivate**: see [Retire an instrument](/docs/retire-an-instrument) +![Edit instrument window with the display name and instrument type fields](/images/instrument-edit-dialog.webp) + The instrument type decides which processor reads the instrument’s files, including DishCam and Aunty; see [Instrument data preprocessing](/docs/instrument-preprocessing). The instrument ID never changes, because it is the storage key prefix. diff --git a/docs/site/content/docs/manage-members.mdx b/docs/site/content/docs/manage-members.mdx index 7eeb7599..afcbe35a 100644 --- a/docs/site/content/docs/manage-members.mdx +++ b/docs/site/content/docs/manage-members.mdx @@ -14,7 +14,7 @@ Go to **Settings → Members**. Non-admins who open the URL see an “Admins onl The table lists every signed-in user: name, email, role badge, and an **Admin** toggle. -![Settings Members page with the workspace members table and Admin toggles](/images/workspace-members.png) +![Settings Members page with the workspace members table and Admin toggles](/images/workspace-members.webp) ## Promote or demote diff --git a/docs/site/content/docs/manage-tokens.mdx b/docs/site/content/docs/manage-tokens.mdx index a5903125..5fbfee54 100644 --- a/docs/site/content/docs/manage-tokens.mdx +++ b/docs/site/content/docs/manage-tokens.mdx @@ -56,8 +56,12 @@ Click **Create token**. +![Create access token window with the name filled in and the Watcher scope preset selected](/images/token-create-dialog.webp) + The plaintext token is shown once: copy it immediately. It starts with `dhub_` followed by a 64-character hex string. +![Token created window showing the new token once, with a copy button and a warning that it can’t be shown again](/images/token-created.webp) + ### Scope presets [#scope-presets] A preset selects the minimum scopes a common client needs: @@ -123,6 +127,8 @@ Tokens don’t authenticate MCP clients, which sign in with OAuth instead. See [ - **Expires** - **Created** +![Access Tokens page listing tokens with their creator, token prefix, scopes, last used time, and expiry](/images/tokens-list.webp) + ## Revoke a token [#revoke-a-token] diff --git a/docs/site/content/docs/manage-watchers.mdx b/docs/site/content/docs/manage-watchers.mdx index eb01b419..5de2929a 100644 --- a/docs/site/content/docs/manage-watchers.mdx +++ b/docs/site/content/docs/manage-watchers.mdx @@ -17,8 +17,14 @@ Each watcher sends a [heartbeat](/docs/concepts#heartbeat) every 60 seconds with - **Hostname and instrument assignment** - **Events**: per-watcher log (`watcher_started`, `run_reported`, `file_uploaded`, `update_*`, and `error` with a `details.kind` discriminator) +![Watchers page listing each watcher with its instrument, hostname, version, status badge, and last heartbeat](/images/watchers-list.webp) + Events are the primary remote-diagnosis tool. For operator-side symptoms, see [Troubleshoot a watcher](/docs/troubleshoot-a-watcher). +Open a watcher to see its event log, a status chart with a connectivity strip of recent heartbeats, and its configuration. + +![Watcher page on the Status tab, with an Online badge, the version, a chart of files, runs, and errors, and a connectivity strip](/images/watcher-detail.webp) + ## Deregister a watcher [#deregister-a-watcher] Each instrument can have at most one active watcher. Deregister the old one when a PC is reimaged or replaced, or to clear “instrument already has an active watcher” on a new install. @@ -41,7 +47,9 @@ You don’t need to deregister a watcher just because its token was revoked. Re- ## Approving manual uploads [#approving-manual-uploads] -For instruments in [manual upload mode](/docs/concepts#upload-mode), the watcher reports runs but waits for server approval before uploading file bytes. Approve a file with **Upload**, or choose **Dismiss** to tell the watcher not to upload it. +For instruments in [manual upload mode](/docs/concepts#upload-mode), the watcher reports runs but waits for server approval before uploading file bytes. Approve a file with **Upload**, or choose **Dismiss** to tell the watcher not to upload it. To act on several files at once, select them in the files table and use the bar that appears. + +![Run page for a manual-upload instrument with its pending files selected and a bar offering Upload and Dismiss](/images/manual-upload.webp) Safeguards that keep the queue healthy: diff --git a/docs/site/content/docs/mcp/index.mdx b/docs/site/content/docs/mcp/index.mdx index dd9c7184..3700550e 100644 --- a/docs/site/content/docs/mcp/index.mdx +++ b/docs/site/content/docs/mcp/index.mdx @@ -32,6 +32,8 @@ Click **Allow**. +![The Authorize application screen, naming the client, the host it redirects to, and the access it asks for, with Deny and Allow buttons](/images/mcp-consent.webp) + Your client stores the access token it receives and renews it without asking again. **Allow** grants everything on the screen: there are no per-permission checkboxes, so click **Deny** if any line looks wrong. ### Scopes diff --git a/docs/site/content/docs/retire-an-instrument.mdx b/docs/site/content/docs/retire-an-instrument.mdx index f40ce036..a0f703a2 100644 --- a/docs/site/content/docs/retire-an-instrument.mdx +++ b/docs/site/content/docs/retire-an-instrument.mdx @@ -37,6 +37,8 @@ Confirm in the dialog. The dialog lists run count and how many watchers will be +![Retire instrument window explaining that the instrument’s runs and files are kept and that its watcher will be deregistered](/images/instrument-retire-dialog.webp) + The instrument moves to the **Retired** tab on **Instruments**. ## Reactivate diff --git a/docs/site/content/docs/send-feedback.mdx b/docs/site/content/docs/send-feedback.mdx index 3b04e3cf..909ecf30 100644 --- a/docs/site/content/docs/send-feedback.mdx +++ b/docs/site/content/docs/send-feedback.mdx @@ -36,6 +36,8 @@ Choose **Send feedback**. +![Send feedback window with a Bug type, a title, and a description filled in](/images/feedback-send-dialog.webp) + Data Hub also records the page you were on, so you don’t need to paste a link. If you send a report with the same title within 24 hours, Data Hub returns the report you already sent instead of creating a second one, unless that report has been closed. @@ -76,8 +78,12 @@ Admins review every report under **Settings → Feedback**: - Each row shows the Linear issue ID, status, label, priority, and assignee. - A report’s detail includes the Linear status changes and comments, and an **Open in Linear** link. +![Settings Feedback page with Open and Closed tabs and a table of reports showing issue ID, title, reporter, and date](/images/feedback-review.webp) + Status changes happen in Linear. Data Hub has no control for changing a report’s status. Admins can also turn on **New feedback** under **Settings → Notifications** to hear when someone sends a report. ## See what’s new in Data Hub [#changelog] Choose **Changelog** in the account menu to see what has changed in Data Hub, newest first. It opens on top of the page you’re on. A dot on your account button means there’s an entry you haven’t opened yet. Many entries link to the page on this site that covers the change. + +![Changelog window listing recent changes to Data Hub, newest first](/images/changelog-dialog.webp) diff --git a/docs/site/content/docs/set-up-an-instrument.mdx b/docs/site/content/docs/set-up-an-instrument.mdx index cb76664e..c8ea066c 100644 --- a/docs/site/content/docs/set-up-an-instrument.mdx +++ b/docs/site/content/docs/set-up-an-instrument.mdx @@ -50,6 +50,8 @@ Click **Create token**, then copy the token. It starts with `dhub_` and is shown +![Create access token window with the name filled in and the Watcher scope preset selected](/images/token-create-dialog.webp) + Keep the token handy: you paste it into the setup wizard in step 3. For rotation, revocation, and the full scope list, see [Issue and revoke tokens](/docs/manage-tokens). ## 2. Install the watcher @@ -140,6 +142,8 @@ Click **Confirm**. The instrument should move to the “Active” tab and appear +![Instruments page on the Pending tab, with a pending instrument showing a yellow Pending badge and a Confirm button](/images/instruments-pending.webp) + Open the instrument’s page and check the watched folder and file patterns shown under its name. They should match what you entered in the wizard; files outside that folder, or with names that don’t match a pattern, won’t reach Data Hub. Some instruments also get automated preprocessing (metadata extraction, image conversion, and related outputs). To choose a processor, an admin opens the three-dot menu, selects **Edit details**, and picks a supported instrument type. What each type produces is in [Instrument data preprocessing](/docs/instrument-preprocessing). diff --git a/docs/site/content/docs/watcher-releases.mdx b/docs/site/content/docs/watcher-releases.mdx index 9ac8eeb1..bec90988 100644 --- a/docs/site/content/docs/watcher-releases.mdx +++ b/docs/site/content/docs/watcher-releases.mdx @@ -45,7 +45,7 @@ Approve the `pypi` deployment under **Actions → Publish watcher** in GitHub. In **Settings → Watchers**, pick the new version under **Latest version**. The list comes straight from PyPI, so a release shows up as soon as PyPI publishes it. If PyPI can’t be reached, the field accepts a typed version instead. Each environment has its own database, so set `staging` and `production` separately to roll the fleet gradually. -![Settings Watchers page with Latest version and release controls](/images/watcher-settings.png) +![Settings Watchers page with Latest version and release controls](/images/watcher-settings.webp) diff --git a/docs/site/public/images/aunty-run-detail.dark.webp b/docs/site/public/images/aunty-run-detail.dark.webp new file mode 100644 index 00000000..a1d68b22 Binary files /dev/null and b/docs/site/public/images/aunty-run-detail.dark.webp differ diff --git a/docs/site/public/images/aunty-run-detail.webp b/docs/site/public/images/aunty-run-detail.webp new file mode 100644 index 00000000..204decf7 Binary files /dev/null and b/docs/site/public/images/aunty-run-detail.webp differ diff --git a/docs/site/public/images/bulk-actions.dark.webp b/docs/site/public/images/bulk-actions.dark.webp new file mode 100644 index 00000000..0a27387b Binary files /dev/null and b/docs/site/public/images/bulk-actions.dark.webp differ diff --git a/docs/site/public/images/bulk-actions.png b/docs/site/public/images/bulk-actions.png deleted file mode 100644 index 83bc774f..00000000 Binary files a/docs/site/public/images/bulk-actions.png and /dev/null differ diff --git a/docs/site/public/images/bulk-actions.webp b/docs/site/public/images/bulk-actions.webp new file mode 100644 index 00000000..8a686a90 Binary files /dev/null and b/docs/site/public/images/bulk-actions.webp differ diff --git a/docs/site/public/images/changelog-dialog.dark.webp b/docs/site/public/images/changelog-dialog.dark.webp new file mode 100644 index 00000000..d6aa66b2 Binary files /dev/null and b/docs/site/public/images/changelog-dialog.dark.webp differ diff --git a/docs/site/public/images/changelog-dialog.webp b/docs/site/public/images/changelog-dialog.webp new file mode 100644 index 00000000..d2c75df9 Binary files /dev/null and b/docs/site/public/images/changelog-dialog.webp differ diff --git a/docs/site/public/images/comments-page.dark.webp b/docs/site/public/images/comments-page.dark.webp new file mode 100644 index 00000000..dbac62aa Binary files /dev/null and b/docs/site/public/images/comments-page.dark.webp differ diff --git a/docs/site/public/images/comments-page.webp b/docs/site/public/images/comments-page.webp new file mode 100644 index 00000000..2fdfa4ff Binary files /dev/null and b/docs/site/public/images/comments-page.webp differ diff --git a/docs/site/public/images/csv-preview.dark.webp b/docs/site/public/images/csv-preview.dark.webp new file mode 100644 index 00000000..9da4cd07 Binary files /dev/null and b/docs/site/public/images/csv-preview.dark.webp differ diff --git a/docs/site/public/images/csv-preview.webp b/docs/site/public/images/csv-preview.webp new file mode 100644 index 00000000..6234d830 Binary files /dev/null and b/docs/site/public/images/csv-preview.webp differ diff --git a/docs/site/public/images/deleted-run.dark.webp b/docs/site/public/images/deleted-run.dark.webp new file mode 100644 index 00000000..2fbbfb8a Binary files /dev/null and b/docs/site/public/images/deleted-run.dark.webp differ diff --git a/docs/site/public/images/deleted-run.webp b/docs/site/public/images/deleted-run.webp new file mode 100644 index 00000000..fcd0ac11 Binary files /dev/null and b/docs/site/public/images/deleted-run.webp differ diff --git a/docs/site/public/images/feedback-review.dark.webp b/docs/site/public/images/feedback-review.dark.webp new file mode 100644 index 00000000..7977b876 Binary files /dev/null and b/docs/site/public/images/feedback-review.dark.webp differ diff --git a/docs/site/public/images/feedback-review.webp b/docs/site/public/images/feedback-review.webp new file mode 100644 index 00000000..9c04cdf6 Binary files /dev/null and b/docs/site/public/images/feedback-review.webp differ diff --git a/docs/site/public/images/feedback-send-dialog.dark.webp b/docs/site/public/images/feedback-send-dialog.dark.webp new file mode 100644 index 00000000..0775835e Binary files /dev/null and b/docs/site/public/images/feedback-send-dialog.dark.webp differ diff --git a/docs/site/public/images/feedback-send-dialog.webp b/docs/site/public/images/feedback-send-dialog.webp new file mode 100644 index 00000000..b01b0dcd Binary files /dev/null and b/docs/site/public/images/feedback-send-dialog.webp differ diff --git a/docs/site/public/images/gel-doc-run-comments.dark.webp b/docs/site/public/images/gel-doc-run-comments.dark.webp new file mode 100644 index 00000000..d10e3ee0 Binary files /dev/null and b/docs/site/public/images/gel-doc-run-comments.dark.webp differ diff --git a/docs/site/public/images/gel-doc-run-comments.png b/docs/site/public/images/gel-doc-run-comments.png deleted file mode 100644 index 1be044bf..00000000 Binary files a/docs/site/public/images/gel-doc-run-comments.png and /dev/null differ diff --git a/docs/site/public/images/gel-doc-run-comments.webp b/docs/site/public/images/gel-doc-run-comments.webp new file mode 100644 index 00000000..11f51eb7 Binary files /dev/null and b/docs/site/public/images/gel-doc-run-comments.webp differ diff --git a/docs/site/public/images/gel-doc-run-details.dark.webp b/docs/site/public/images/gel-doc-run-details.dark.webp new file mode 100644 index 00000000..e519241a Binary files /dev/null and b/docs/site/public/images/gel-doc-run-details.dark.webp differ diff --git a/docs/site/public/images/gel-doc-run-details.png b/docs/site/public/images/gel-doc-run-details.png deleted file mode 100644 index d48950ee..00000000 Binary files a/docs/site/public/images/gel-doc-run-details.png and /dev/null differ diff --git a/docs/site/public/images/gel-doc-run-details.webp b/docs/site/public/images/gel-doc-run-details.webp new file mode 100644 index 00000000..428e2cb7 Binary files /dev/null and b/docs/site/public/images/gel-doc-run-details.webp differ diff --git a/docs/site/public/images/home.dark.webp b/docs/site/public/images/home.dark.webp new file mode 100644 index 00000000..01a71ebe Binary files /dev/null and b/docs/site/public/images/home.dark.webp differ diff --git a/docs/site/public/images/home.png b/docs/site/public/images/home.png deleted file mode 100644 index 9f0a1b86..00000000 Binary files a/docs/site/public/images/home.png and /dev/null differ diff --git a/docs/site/public/images/home.webp b/docs/site/public/images/home.webp new file mode 100644 index 00000000..52f8bd08 Binary files /dev/null and b/docs/site/public/images/home.webp differ diff --git a/docs/site/public/images/instrument-edit-dialog.dark.webp b/docs/site/public/images/instrument-edit-dialog.dark.webp new file mode 100644 index 00000000..685453fc Binary files /dev/null and b/docs/site/public/images/instrument-edit-dialog.dark.webp differ diff --git a/docs/site/public/images/instrument-edit-dialog.webp b/docs/site/public/images/instrument-edit-dialog.webp new file mode 100644 index 00000000..ac4e5f97 Binary files /dev/null and b/docs/site/public/images/instrument-edit-dialog.webp differ diff --git a/docs/site/public/images/instrument-retire-dialog.dark.webp b/docs/site/public/images/instrument-retire-dialog.dark.webp new file mode 100644 index 00000000..d9acd8dd Binary files /dev/null and b/docs/site/public/images/instrument-retire-dialog.dark.webp differ diff --git a/docs/site/public/images/instrument-retire-dialog.webp b/docs/site/public/images/instrument-retire-dialog.webp new file mode 100644 index 00000000..bff2afa3 Binary files /dev/null and b/docs/site/public/images/instrument-retire-dialog.webp differ diff --git a/docs/site/public/images/instruments-pending.dark.webp b/docs/site/public/images/instruments-pending.dark.webp new file mode 100644 index 00000000..f8c71292 Binary files /dev/null and b/docs/site/public/images/instruments-pending.dark.webp differ diff --git a/docs/site/public/images/instruments-pending.webp b/docs/site/public/images/instruments-pending.webp new file mode 100644 index 00000000..890955d6 Binary files /dev/null and b/docs/site/public/images/instruments-pending.webp differ diff --git a/docs/site/public/images/instruments-table.dark.webp b/docs/site/public/images/instruments-table.dark.webp new file mode 100644 index 00000000..6c20a72f Binary files /dev/null and b/docs/site/public/images/instruments-table.dark.webp differ diff --git a/docs/site/public/images/instruments-table.png b/docs/site/public/images/instruments-table.png deleted file mode 100644 index 1f2a0d28..00000000 Binary files a/docs/site/public/images/instruments-table.png and /dev/null differ diff --git a/docs/site/public/images/instruments-table.webp b/docs/site/public/images/instruments-table.webp new file mode 100644 index 00000000..65f0bb9a Binary files /dev/null and b/docs/site/public/images/instruments-table.webp differ diff --git a/docs/site/public/images/integrations.dark.webp b/docs/site/public/images/integrations.dark.webp new file mode 100644 index 00000000..2e746355 Binary files /dev/null and b/docs/site/public/images/integrations.dark.webp differ diff --git a/docs/site/public/images/integrations.webp b/docs/site/public/images/integrations.webp new file mode 100644 index 00000000..fc63c181 Binary files /dev/null and b/docs/site/public/images/integrations.webp differ diff --git a/docs/site/public/images/manual-upload.dark.webp b/docs/site/public/images/manual-upload.dark.webp new file mode 100644 index 00000000..50930481 Binary files /dev/null and b/docs/site/public/images/manual-upload.dark.webp differ diff --git a/docs/site/public/images/manual-upload.webp b/docs/site/public/images/manual-upload.webp new file mode 100644 index 00000000..00b993ad Binary files /dev/null and b/docs/site/public/images/manual-upload.webp differ diff --git a/docs/site/public/images/mcp-consent.dark.webp b/docs/site/public/images/mcp-consent.dark.webp new file mode 100644 index 00000000..387d9aa1 Binary files /dev/null and b/docs/site/public/images/mcp-consent.dark.webp differ diff --git a/docs/site/public/images/mcp-consent.webp b/docs/site/public/images/mcp-consent.webp new file mode 100644 index 00000000..a815961a Binary files /dev/null and b/docs/site/public/images/mcp-consent.webp differ diff --git a/docs/site/public/images/my-runs.dark.webp b/docs/site/public/images/my-runs.dark.webp new file mode 100644 index 00000000..444fa8ee Binary files /dev/null and b/docs/site/public/images/my-runs.dark.webp differ diff --git a/docs/site/public/images/my-runs.png b/docs/site/public/images/my-runs.png deleted file mode 100644 index 02fb0391..00000000 Binary files a/docs/site/public/images/my-runs.png and /dev/null differ diff --git a/docs/site/public/images/my-runs.webp b/docs/site/public/images/my-runs.webp new file mode 100644 index 00000000..6a0ac5c6 Binary files /dev/null and b/docs/site/public/images/my-runs.webp differ diff --git a/docs/site/public/images/notification-bell.dark.webp b/docs/site/public/images/notification-bell.dark.webp new file mode 100644 index 00000000..4e19ec01 Binary files /dev/null and b/docs/site/public/images/notification-bell.dark.webp differ diff --git a/docs/site/public/images/notification-bell.webp b/docs/site/public/images/notification-bell.webp new file mode 100644 index 00000000..4b501b83 Binary files /dev/null and b/docs/site/public/images/notification-bell.webp differ diff --git a/docs/site/public/images/notifications.dark.webp b/docs/site/public/images/notifications.dark.webp new file mode 100644 index 00000000..5d4b767d Binary files /dev/null and b/docs/site/public/images/notifications.dark.webp differ diff --git a/docs/site/public/images/notifications.png b/docs/site/public/images/notifications.png deleted file mode 100644 index 128e0003..00000000 Binary files a/docs/site/public/images/notifications.png and /dev/null differ diff --git a/docs/site/public/images/notifications.webp b/docs/site/public/images/notifications.webp new file mode 100644 index 00000000..2b62f99c Binary files /dev/null and b/docs/site/public/images/notifications.webp differ diff --git a/docs/site/public/images/plate-reader-run-details-1.dark.webp b/docs/site/public/images/plate-reader-run-details-1.dark.webp new file mode 100644 index 00000000..f5d1795a Binary files /dev/null and b/docs/site/public/images/plate-reader-run-details-1.dark.webp differ diff --git a/docs/site/public/images/plate-reader-run-details-1.png b/docs/site/public/images/plate-reader-run-details-1.png deleted file mode 100644 index 125f9bd7..00000000 Binary files a/docs/site/public/images/plate-reader-run-details-1.png and /dev/null differ diff --git a/docs/site/public/images/plate-reader-run-details-1.webp b/docs/site/public/images/plate-reader-run-details-1.webp new file mode 100644 index 00000000..046ec6cf Binary files /dev/null and b/docs/site/public/images/plate-reader-run-details-1.webp differ diff --git a/docs/site/public/images/plate-reader-run-details-2.dark.webp b/docs/site/public/images/plate-reader-run-details-2.dark.webp new file mode 100644 index 00000000..5799fd03 Binary files /dev/null and b/docs/site/public/images/plate-reader-run-details-2.dark.webp differ diff --git a/docs/site/public/images/plate-reader-run-details-2.png b/docs/site/public/images/plate-reader-run-details-2.png deleted file mode 100644 index de234bdd..00000000 Binary files a/docs/site/public/images/plate-reader-run-details-2.png and /dev/null differ diff --git a/docs/site/public/images/plate-reader-run-details-2.webp b/docs/site/public/images/plate-reader-run-details-2.webp new file mode 100644 index 00000000..2616846e Binary files /dev/null and b/docs/site/public/images/plate-reader-run-details-2.webp differ diff --git a/docs/site/public/images/plate-reader-runs-table.dark.webp b/docs/site/public/images/plate-reader-runs-table.dark.webp new file mode 100644 index 00000000..ea23f603 Binary files /dev/null and b/docs/site/public/images/plate-reader-runs-table.dark.webp differ diff --git a/docs/site/public/images/plate-reader-runs-table.png b/docs/site/public/images/plate-reader-runs-table.png deleted file mode 100644 index 928e7b60..00000000 Binary files a/docs/site/public/images/plate-reader-runs-table.png and /dev/null differ diff --git a/docs/site/public/images/plate-reader-runs-table.webp b/docs/site/public/images/plate-reader-runs-table.webp new file mode 100644 index 00000000..98194414 Binary files /dev/null and b/docs/site/public/images/plate-reader-runs-table.webp differ diff --git a/docs/site/public/images/qpcr-run-detail.dark.webp b/docs/site/public/images/qpcr-run-detail.dark.webp new file mode 100644 index 00000000..b9a55957 Binary files /dev/null and b/docs/site/public/images/qpcr-run-detail.dark.webp differ diff --git a/docs/site/public/images/qpcr-run-detail.webp b/docs/site/public/images/qpcr-run-detail.webp new file mode 100644 index 00000000..539852f6 Binary files /dev/null and b/docs/site/public/images/qpcr-run-detail.webp differ diff --git a/docs/site/public/images/search-modal.dark.webp b/docs/site/public/images/search-modal.dark.webp new file mode 100644 index 00000000..5c56f41d Binary files /dev/null and b/docs/site/public/images/search-modal.dark.webp differ diff --git a/docs/site/public/images/search-modal.png b/docs/site/public/images/search-modal.png deleted file mode 100644 index 5e469a22..00000000 Binary files a/docs/site/public/images/search-modal.png and /dev/null differ diff --git a/docs/site/public/images/search-modal.webp b/docs/site/public/images/search-modal.webp new file mode 100644 index 00000000..d928b782 Binary files /dev/null and b/docs/site/public/images/search-modal.webp differ diff --git a/docs/site/public/images/token-create-dialog.dark.webp b/docs/site/public/images/token-create-dialog.dark.webp new file mode 100644 index 00000000..34812dfd Binary files /dev/null and b/docs/site/public/images/token-create-dialog.dark.webp differ diff --git a/docs/site/public/images/token-create-dialog.webp b/docs/site/public/images/token-create-dialog.webp new file mode 100644 index 00000000..b3c52338 Binary files /dev/null and b/docs/site/public/images/token-create-dialog.webp differ diff --git a/docs/site/public/images/token-created.dark.webp b/docs/site/public/images/token-created.dark.webp new file mode 100644 index 00000000..ea0c2edd Binary files /dev/null and b/docs/site/public/images/token-created.dark.webp differ diff --git a/docs/site/public/images/token-created.webp b/docs/site/public/images/token-created.webp new file mode 100644 index 00000000..0ca41ebc Binary files /dev/null and b/docs/site/public/images/token-created.webp differ diff --git a/docs/site/public/images/tokens-list.dark.webp b/docs/site/public/images/tokens-list.dark.webp new file mode 100644 index 00000000..b52486cb Binary files /dev/null and b/docs/site/public/images/tokens-list.dark.webp differ diff --git a/docs/site/public/images/tokens-list.webp b/docs/site/public/images/tokens-list.webp new file mode 100644 index 00000000..0c76e96d Binary files /dev/null and b/docs/site/public/images/tokens-list.webp differ diff --git a/docs/site/public/images/watcher-detail.dark.webp b/docs/site/public/images/watcher-detail.dark.webp new file mode 100644 index 00000000..ff7d0ea4 Binary files /dev/null and b/docs/site/public/images/watcher-detail.dark.webp differ diff --git a/docs/site/public/images/watcher-detail.webp b/docs/site/public/images/watcher-detail.webp new file mode 100644 index 00000000..fbea4571 Binary files /dev/null and b/docs/site/public/images/watcher-detail.webp differ diff --git a/docs/site/public/images/watcher-settings.dark.webp b/docs/site/public/images/watcher-settings.dark.webp new file mode 100644 index 00000000..7c5d7d82 Binary files /dev/null and b/docs/site/public/images/watcher-settings.dark.webp differ diff --git a/docs/site/public/images/watcher-settings.png b/docs/site/public/images/watcher-settings.png deleted file mode 100644 index 2db275ff..00000000 Binary files a/docs/site/public/images/watcher-settings.png and /dev/null differ diff --git a/docs/site/public/images/watcher-settings.webp b/docs/site/public/images/watcher-settings.webp new file mode 100644 index 00000000..395f6160 Binary files /dev/null and b/docs/site/public/images/watcher-settings.webp differ diff --git a/docs/site/public/images/watchers-list.dark.webp b/docs/site/public/images/watchers-list.dark.webp new file mode 100644 index 00000000..b94df73e Binary files /dev/null and b/docs/site/public/images/watchers-list.dark.webp differ diff --git a/docs/site/public/images/watchers-list.webp b/docs/site/public/images/watchers-list.webp new file mode 100644 index 00000000..f3a04e9d Binary files /dev/null and b/docs/site/public/images/watchers-list.webp differ diff --git a/docs/site/public/images/workspace-members.dark.webp b/docs/site/public/images/workspace-members.dark.webp new file mode 100644 index 00000000..3d984a79 Binary files /dev/null and b/docs/site/public/images/workspace-members.dark.webp differ diff --git a/docs/site/public/images/workspace-members.png b/docs/site/public/images/workspace-members.png deleted file mode 100644 index e1da7eae..00000000 Binary files a/docs/site/public/images/workspace-members.png and /dev/null differ diff --git a/docs/site/public/images/workspace-members.webp b/docs/site/public/images/workspace-members.webp new file mode 100644 index 00000000..85104b5c Binary files /dev/null and b/docs/site/public/images/workspace-members.webp differ diff --git a/docs/site/src/app/docs/(home)/page.tsx b/docs/site/src/app/docs/(home)/page.tsx index bd55d7e2..b15f311e 100644 --- a/docs/site/src/app/docs/(home)/page.tsx +++ b/docs/site/src/app/docs/(home)/page.tsx @@ -9,6 +9,7 @@ import { import Link from "next/link"; import { DataHubLogo } from "@/components/data-hub-logo"; import { DocsImage } from "@/components/docs-image"; +import { Screenshot } from "@/components/screenshot"; import { CodeBlock, CodeBlockActions, @@ -18,9 +19,8 @@ import { CodeBlockTitle, } from "@/components/ui/code"; import { cn } from "@/lib/cn"; -import homeImage from "../../../../public/images/home.png"; +import { screenshots } from "@/lib/screenshots.generated"; import mcpImage from "../../../../public/images/mcp.png"; -import runDetailImage from "../../../../public/images/plate-reader-run-details-1.png"; const features = [ { @@ -58,15 +58,17 @@ const features = [ ]; const homeShot = { - src: homeImage, + entry: screenshots.home, alt: "Data Hub home dashboard with instrument status and recent runs", } as const; const runDetailShot = { - src: runDetailImage, + entry: screenshots["plate-reader-run-details-1"], alt: "Data Hub run detail with plate map visualization and comments", } as const; +// Taken by hand in the Claude desktop app, so the script cannot retake it. +// It is also listed in `web/scripts/docs-screenshots/shots.ts`. const mcpShot = { src: mcpImage, alt: "Claude Desktop asking Data Hub about microscope runs over MCP", @@ -178,23 +180,23 @@ export default function HomePage() { -
- +
-
- +
diff --git a/docs/site/src/components/docs-image.tsx b/docs/site/src/components/docs-image.tsx index ff2b3c3c..1a4dc2d2 100644 --- a/docs/site/src/components/docs-image.tsx +++ b/docs/site/src/components/docs-image.tsx @@ -1,7 +1,5 @@ import type { ImageProps, StaticImageData } from "next/image"; import Image from "next/image"; -import type { ImgHTMLAttributes } from "react"; -import { cn } from "@/lib/cn"; type DocsImageProps = Omit; @@ -13,12 +11,9 @@ const responsiveStyle = { height: "auto", } as const; -const mdxSizes = - "(max-width: 768px) 100vw, (max-width: 1200px) 70vw, 900px" as const; - const PIXEL_DIMENSION = /^\d+$/; -function toPixel(value: unknown): number | undefined { +export function toPixel(value: unknown): number | undefined { if (typeof value === "number" && Number.isFinite(value)) { return value; } @@ -40,19 +35,6 @@ function getStaticImageData( return src; } -function resolveMdxSrc(src: unknown): ImageProps["src"] | undefined { - if (typeof src === "string" && src.length > 0) { - return src; - } - if (!src || typeof src !== "object" || src instanceof Blob) { - return; - } - if ("default" in src || "src" in src) { - return src as ImageProps["src"]; - } - return; -} - export function DocsImage({ alt, height, @@ -84,36 +66,3 @@ export function DocsImage({ /> ); } - -type MdxImageProps = ImgHTMLAttributes & - Pick; - -export function MdxImage({ - alt = "", - className, - height, - placeholder, - sizes, - src, - style: _style, - title, - width, -}: MdxImageProps) { - const imageSrc = resolveMdxSrc(src); - if (!imageSrc) { - return null; - } - - return ( - - ); -} diff --git a/docs/site/src/components/mdx-image.tsx b/docs/site/src/components/mdx-image.tsx new file mode 100644 index 00000000..21c6b9ee --- /dev/null +++ b/docs/site/src/components/mdx-image.tsx @@ -0,0 +1,69 @@ +import type { ImageProps } from "next/image"; +import type { ImgHTMLAttributes } from "react"; +import { cn } from "@/lib/cn"; +import { DocsImage, toPixel } from "./docs-image"; +import { findScreenshot, Screenshot } from "./screenshot"; + +const mdxSizes = + "(max-width: 768px) 100vw, (max-width: 1200px) 70vw, 900px" as const; + +function resolveMdxSrc(src: unknown): ImageProps["src"] | undefined { + if (typeof src === "string" && src.length > 0) { + return src; + } + if (!src || typeof src !== "object" || src instanceof Blob) { + return; + } + if ("default" in src || "src" in src) { + return src as ImageProps["src"]; + } + return; +} + +type MdxImageProps = ImgHTMLAttributes & + Pick; + +// A page references the light file. If it is a generated screenshot, this +// shows the light and dark pair, inside a browser frame for full windows. +// Any other image renders as it always has. +export function MdxImage({ + alt = "", + className, + height, + placeholder, + sizes, + src, + style: _style, + title, + width, +}: MdxImageProps) { + const imageSrc = resolveMdxSrc(src); + if (!imageSrc) { + return null; + } + + const screenshot = findScreenshot(imageSrc); + if (screenshot) { + return ( + + ); + } + + return ( + + ); +} diff --git a/docs/site/src/components/mdx.tsx b/docs/site/src/components/mdx.tsx index 819f0f0a..93fce2fe 100644 --- a/docs/site/src/components/mdx.tsx +++ b/docs/site/src/components/mdx.tsx @@ -3,10 +3,10 @@ import defaultMdxComponents from "fumadocs-ui/mdx"; import type { MDXComponents } from "mdx/types"; import { Audience } from "./audience"; import { WatcherCliCatalog } from "./cli/cli-catalog"; -import { MdxImage } from "./docs-image"; import { McpPromptCatalog } from "./mcp/prompt-catalog"; import { McpResourceCatalog } from "./mcp/resource-catalog"; import { McpToolCatalog } from "./mcp/tool-catalog"; +import { MdxImage } from "./mdx-image"; import { Mermaid } from "./mermaid"; export function getMDXComponents(components?: MDXComponents) { diff --git a/docs/site/src/components/screenshot.tsx b/docs/site/src/components/screenshot.tsx new file mode 100644 index 00000000..b369d12c --- /dev/null +++ b/docs/site/src/components/screenshot.tsx @@ -0,0 +1,134 @@ +import type { ReactNode } from "react"; +import { cn } from "@/lib/cn"; +import { + SCREENSHOT_SCALE, + type Screenshot as ScreenshotEntry, + screenshots, +} from "@/lib/screenshots.generated"; +import { DocsImage } from "./docs-image"; + +// MDX puts a standalone image inside a

, so everything here is a . +// A

would be invalid HTML there and fail hydration. + +const bySource = new Map( + Object.values(screenshots).map((entry) => [entry.light.src, entry]) +); + +/** Finds the entry for an image imported by an MDX page, by its built URL. */ +export function findScreenshot(src: unknown): ScreenshotEntry | undefined { + if (!src || typeof src !== "object") { + return; + } + const staticImage = "default" in src ? src.default : src; + if ( + !staticImage || + typeof staticImage !== "object" || + !("src" in staticImage) + ) { + return; + } + return bySource.get(String(staticImage.src)); +} + +export function BrowserFrame({ + address, + children, + className, +}: { + address: string; + children: ReactNode; + className?: string; +}) { + return ( + + +