Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
edd824f
feat(ui): replace Ant Design with the Slim v2 design system
igoroctaviano Sep 30, 2026
e427c18
refactor(ui): move business logic out of components and fix review fi…
igoroctaviano Oct 1, 2026
3f1b496
docs: describe the Tailwind/Radix UI stack and server button
igoroctaviano Oct 1, 2026
f6d5beb
fix(viewer): apply annotation preferences and add loading states
igoroctaviano Oct 1, 2026
d0acb2b
feat(header): refresh preferences, server pill and branding
igoroctaviano Oct 1, 2026
6a00f70
style(branding): use a muted slate background for the favicon
igoroctaviano Oct 1, 2026
8effcee
style(branding): match the logo tile to the muted favicon color
igoroctaviano Oct 1, 2026
2b7b095
Merge origin/master into feat/slim-modern-redesign
igoroctaviano Oct 1, 2026
9c0c4a5
style(header): shrink logo tile to 24px
igoroctaviano Oct 1, 2026
a7f7590
build: move to TypeScript 6 and clean up tooling
igoroctaviano Oct 1, 2026
9d9b4af
fix(app): restore auth rationale comments and legacy server selection
igoroctaviano Oct 1, 2026
dac015e
refactor: give preferences their own feature and drop re-export shims
igoroctaviano Oct 1, 2026
ba8791f
feat(ui): lucide icons, shared primitives and feature fixes
igoroctaviano Oct 1, 2026
fd58b36
refactor(panels): function components and a shared layer kit
igoroctaviano Oct 1, 2026
d3a7c3a
fix(viewer): restore toasts and ROI styles, split SlideViewer
igoroctaviano Oct 1, 2026
b18c33b
refactor(utils): consolidate DICOM helpers and fix value bugs
igoroctaviano Oct 1, 2026
13c1c46
refactor(viewer): batch style updates and single-source shortcuts
igoroctaviano Oct 1, 2026
e62e0fa
docs: describe the redesign tooling, toasts and server selection
igoroctaviano Oct 1, 2026
4cae1c3
refactor(app): function components and oidc-client-ts
igoroctaviano Oct 1, 2026
183392f
refactor(viewer): SlideViewer as a function component with hooks
igoroctaviano Oct 1, 2026
a41d88f
build: move to Vite 8, Vitest 5, React 19, React Compiler and TS 7
igoroctaviano Oct 1, 2026
3c47a9d
build: move to Tailwind CSS 4
igoroctaviano Oct 1, 2026
3bd4652
chore: drop the Ant Design profiling script
igoroctaviano Oct 1, 2026
60a1772
fix: address SonarCloud and DeepSource findings
igoroctaviano Oct 1, 2026
9013d80
refactor: move stateless class methods to pure functions
igoroctaviano Oct 1, 2026
c0d8f57
fix: explain configuration errors instead of crashing
igoroctaviano Oct 1, 2026
33f6410
fix: use a Map for config-name lookups
igoroctaviano Oct 1, 2026
e5e45cf
fix: release viewers, timers and caches that outlived their owners
igoroctaviano Oct 1, 2026
a8fb810
fix(header): show the deployment's custom logo again
igoroctaviano Oct 1, 2026
02b839c
fix(notifications): always show error notifications
igoroctaviano Oct 1, 2026
a291d7c
fix(config): accept URL-less servers on Google Cloud store routes
igoroctaviano Oct 1, 2026
5664430
fix(header): accept server URLs without a path
igoroctaviano Oct 1, 2026
4ecc751
feat(auth): allow OIDC sign-in over plain HTTP
igoroctaviano Oct 1, 2026
c1d818f
feat(ui): compact worklist and toolbar, viewport overlay preferences
igoroctaviano Oct 1, 2026
fe8b05d
feat(header): restore default/custom server choice in server dialog
igoroctaviano Oct 1, 2026
2b2235f
feat(worklist): add a "Contains" filter for derived data
igoroctaviano Oct 1, 2026
7a6ce20
feat(theme): make dark mode the default
igoroctaviano Oct 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
11 changes: 10 additions & 1 deletion .deepsource.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
version = 1

test_patterns = [
"src/**/__tests__/**",
"src/**/*.test.ts",
"src/**/*.test.tsx",
"src/**/testing/**",
"src/__mocks__/**",
"src/setupTests.tsx",
]

[[analyzers]]
name = "javascript"
enabled = true
Expand All @@ -8,4 +17,4 @@ enabled = true
environment = ["browser"]
plugins = ["react"]
style_guide = "standard"
dialect = "typescript"
dialect = "typescript"
41 changes: 0 additions & 41 deletions .eslintrc.strict.cjs

This file was deleted.

16 changes: 16 additions & 0 deletions .github/workflows/container-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,22 @@ jobs:
docker compose logs --tail=80 app || true
exit 1

- name: Test compiled styles
run: |
# The build must run Tailwind (@tailwindcss/vite); raw directives
# mean src/styles/globals.css was not compiled. Vite emits the
# stylesheet as assets/index-<hash>.css.
css_path=$(curl -s http://localhost:8008/ | grep -o 'assets/[^"]*\.css' | head -1)
test -n "$css_path"
css=$(curl -s "http://localhost:8008/$css_path")
if echo "$css" | grep -qE '@tailwind|@apply|@theme|@custom-variant|@plugin|@utility|@import ?["'\'']tailwindcss'; then
echo "Unprocessed Tailwind directives found in $css_path"
exit 1
fi
echo "$css" | grep -q '\.flex{display:flex}'
# Theme tokens from the @theme block
echo "$css" | grep -q '\.text-11\\\.5{font-size:var(--text-11_5)}'

- name: Test DICOMweb service
run: |
# Poll instead of a fixed sleep: the archive can take a while to boot.
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/deploy-to-firebase.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ jobs:
else
pnpm install --frozen-lockfile --ignore-scripts
fi
# allowBuilds in pnpm-workspace.yaml: core-js(+pure) and dicom-microscopy-viewer
pnpm rebuild dicom-microscopy-viewer core-js core-js-pure
# allowBuilds in pnpm-workspace.yaml: core-js-pure and dicom-microscopy-viewer
pnpm rebuild dicom-microscopy-viewer core-js-pure
# Build DMV if dist files are missing (git dependency in package.json)
if [ ! -f "node_modules/dicom-microscopy-viewer/dist/dynamic-import/dicomMicroscopyViewer.min.js" ]; then
echo "DMV dist files missing - building from git dependency"
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ jobs:
- name: Typecheck
run: pnpm run typecheck

- name: React Compiler check
run: pnpm run check:compiler

- name: Build
run: pnpm run build

Expand Down
4 changes: 3 additions & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#!/usr/bin/env sh
# Fast checks on every commit: lint + format (biome check runs both)
# Fast checks on every commit: lint + format (biome check runs both), then
# React Compiler bailouts (a few seconds)
pnpm run lint
pnpm run check:compiler
5 changes: 0 additions & 5 deletions .npmrc

This file was deleted.

28 changes: 22 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,36 @@ The `SlideViewer` further provides annotation tools, which enable the user to dr

## Implementation details

The app is implemented in [TypeScript](https://www.typescriptlang.org/) using the [React](https://reactjs.org/) framework.
The [antd](https://ant.design/https://ant.design/) React UI component library is used with a [customized theme](https://ant.design/docs/react/customize-theme).
The app is implemented in [TypeScript](https://www.typescriptlang.org/) 7 using [React](https://react.dev/) 19 with the [React Compiler](https://react.dev/learn/react-compiler) enabled, so components and hooks are memoized automatically and new code rarely needs `useMemo`, `useCallback` or `React.memo`.
The UI is styled with [Tailwind CSS](https://tailwindcss.com/) 4 on top of [Radix UI](https://www.radix-ui.com/) primitives (`src/components/ui`).
Icons are [lucide](https://lucide.dev/) SVGs rendered through `Icon` (`src/components/ui/icon.tsx`); add new glyphs to its `ICONS` map so `IconName` stays a closed union.

The app is built using [craco](https://github.com/gsoft-inc/craco) (with the [craco-less plugin](https://github.com/DocSpring/craco-less)), which serves as a configuration layer around [create-react-app](https://github.com/facebook/create-react-app/).
Tailwind is configured in CSS, without a `tailwind.config.js` or PostCSS config, and compiled by the `@tailwindcss/vite` plugin. `src/styles/globals.css` is the entry point:

Tests are written and run using the [jest](https://jestjs.io/) framework.
- Light and dark color tokens are RGB-triplet CSS variables on `:root` and `.dark` (the theme toggle sets the `dark` class on `<html>`). The `@theme` block maps them to utilities (`--color-panel: rgb(var(--panel))` gives `bg-panel`, `text-panel`, `border-panel`, …), so opacity modifiers such as `bg-primary/10` keep working.
- The same `@theme` block holds the custom font sizes (`--text-11_5` gives `text-11.5`; `_` stands for the dot), radii (`rounded-card`, `rounded-tile`), shadows (`shadow-menu`, …), the `control` spacing step (`h-control`, `size-control`) and the layout sizes (`w-sidebar`, `h-header`, …).
- `dark:` utilities use `@custom-variant dark (&:where(.dark, .dark *))`.
- Radix enter/exit animations (`data-[state=open]:animate-in`, `fade-in-0`, `zoom-in-95`, `slide-in-from-top-2`, …) come from [tw-animate-css](https://github.com/Wombosvideo/tw-animate-css).

Register every new custom font-size, radius, shadow, spacing or size key in `src/lib/utils.ts` as well, so `cn()` merges it correctly. Otherwise tailwind-merge treats an unknown `text-*` or `shadow-*` as a color.

Components render props and call callbacks; DICOM parsing, formatting, filtering and other rules live in pure functions under `src/utils` and `src/features/*/utils`, with unit tests in sibling `__tests__` folders.
Shared sidebar layer controls (visibility toggle, settings popover, opacity row, style hooks) live in `src/components/panel`.
Transient notifications go through `publishToast` (`src/features/viewer/services/toast.ts`) and are rendered by `ToastHost`, which applies `config.messages`.

The app is built and served with [Vite](https://vite.dev/) (`vite.config.ts`). The compiler runs through Babel (`@rolldown/plugin-babel` with `reactCompilerPreset`) in dev, build and tests. Only `VITE_*` and `REACT_APP_*` variables reach the client, as `import.meta.env.*`.

Tests are written and run with [Vitest](https://vitest.dev/) in a jsdom environment, using [Testing Library](https://testing-library.com/). Mocks are reset before each test (`mockReset: true`); for Vitest, that means a `vi.fn(impl)` goes back to `impl`, not to returning `undefined`.

`pnpm run check:compiler` fails when the React Compiler bails out of a component or hook, and lists each file, line and reason. It runs in the pre-commit hook and in CI. Fix the code rather than opting out. Add `'use no memo'` only for a known compiler limitation (for example a library the compiler marks incompatible, such as TanStack Table's `useReactTable`), and explain why in a `/** */` comment next to the directive.

The [pnpm](https://pnpm.io/) package manager is used to manage dependencies and run scripts specified in `package.json` (`build`, `lint`, `test`, etc.).

## Coding style

Source code is linted and formatted using [Biome](https://biomejs.dev/). TypeScript is used with [strict type checking compiler options](https://www.typescriptlang.org/tsconfig#Strict_Type_Checking_Options_6173) enabled. Semicolons are not used at the end of statements (Biome uses `asNeeded`).
Source code is linted and formatted using [Biome](https://biomejs.dev/), the only linter in the project (typescript-eslint does not support TypeScript 7). TypeScript is used with [strict type checking compiler options](https://www.typescriptlang.org/tsconfig#Strict_Type_Checking_Options_6173) enabled. Semicolons are not used at the end of statements (Biome uses `asNeeded`).

Explanatory comments use JSDoc-style block comments (`/** … */`), not `//` line comments. Keep `//` only for tooling directives (`eslint-disable`, `@ts-expect-error`, `biome-ignore`), temporarily commented-out code, and shebang lines.
Explanatory comments use JSDoc-style block comments (`/** … */`), not `//` line comments. Keep `//` only for tooling directives (`biome-ignore`, `@ts-expect-error`, triple-slash references), temporarily commented-out code, and shebang lines.

Use the following commands to check and fix style:

Expand Down
5 changes: 2 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,11 @@ RUN corepack enable && corepack prepare pnpm@11.9.0 --activate
WORKDIR /usr/local/share/mghcomputationalpathology/slim

# Install dependencies first and then include code for efficient caching
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./

RUN pnpm install --frozen-lockfile

COPY craco.config.js .
COPY tsconfig.json .
COPY vite.config.ts index.html tsconfig.json ./
COPY types ./types
COPY public ./public
COPY scripts ./scripts
Expand Down
35 changes: 22 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ This feature was implemented in response to [issue #159](https://github.com/Imag

### Messages/popups configuration

Configure message popup notifications that appear at the top of the screen. By default, all message popups are enabled.
Configure the transient message popups (toasts). By default, all message popups are enabled and stack at the bottom centre of the window, at most three at a time.

```js
window.config = {
Expand All @@ -236,9 +236,9 @@ window.config = {

**Options:**

- `disabled`: Disable specific message types or all messages
- `duration`: How long messages are shown (in seconds)
- `top`: Distance from top of screen (in pixels)
- `disabled`: Disable specific message types or all messages. Error notifications (sign-in failures, rejected or failed server requests, viewer errors) are always shown.
- `duration`: How long messages are shown (in seconds); `0` keeps them until dismissed. Error notifications always close after 3 seconds.
- `top`: Anchor the stack this many pixels from the top of the window instead of the bottom

**Available message types:**

Expand Down Expand Up @@ -268,7 +268,7 @@ messages: {
**Defaults** (if not specified):

- `duration`: 5 seconds
- `top`: 100 pixels
- `top`: not set (bottom centre)

### Memory monitoring configuration

Expand Down Expand Up @@ -304,7 +304,7 @@ The following topics are documented in [docs/CONFIGURATION.md](docs/CONFIGURATIO

### Prerequisites

- [Node.js](https://nodejs.org/) (LTS recommended)
- [Node.js](https://nodejs.org/) 22.22 or newer (24 LTS recommended)
- [pnpm](https://pnpm.io/) `11.9.0` (see `packageManager` in `package.json`)

Download the latest release from [github.com/ImagingDataCommons/slim/releases](https://github.com/ImagingDataCommons/slim/releases), then install dependencies and build the app:
Expand Down Expand Up @@ -444,11 +444,13 @@ Existing configs continue to work without changes:

Deep links are restored after login through the OIDC `state` parameter (not `localStorage`). Silent token renewal reuses the same registered redirect URI (no additional IdP redirect URI is required).

Sign-in hashes the PKCE challenge in the code flow and checks the ID token signature against the provider's published keys in the implicit flow. Browsers only offer the Web Crypto API over HTTPS and on `localhost`, so on plain-HTTP deployments Slim does both in JavaScript instead. Serving Slim over HTTPS is still recommended because it keeps tokens from being exposed on the network.

## Development

### Prerequisites

- [Node.js](https://nodejs.org/) (LTS recommended)
- [Node.js](https://nodejs.org/) 22.22 or newer (24 LTS recommended)
- [pnpm](https://pnpm.io/) `11.9.0` (see `packageManager` in `package.json`)

Install dependencies and run the app for local development:
Expand Down Expand Up @@ -481,10 +483,13 @@ Useful scripts:

| Command | Description |
| ------- | ----------- |
| `pnpm run start` | Start the development server |
| `pnpm run build` | Create a production build |
| `pnpm run test` | Run lint checks and tests |
| `pnpm run lint` | Check for lint issues |
| `pnpm run start` (or `dev`) | Start the Vite development server on port 3000 |
| `pnpm run build` | Create a production build in `build/` |
| `pnpm run test` | Run the unit tests once with Vitest |
| `pnpm run test:watch` | Run Vitest in watch mode |
| `pnpm run typecheck` | Type-check with `tsc` (TypeScript 7) |
| `pnpm run check:compiler` | Fail if the React Compiler bails out on any component or hook |
| `pnpm run lint` | Check for lint and format issues with Biome |
| `pnpm run lint:fix` | Auto-fix lint issues |
| `pnpm run fmt` | Format source code |

Expand Down Expand Up @@ -537,11 +542,12 @@ If neither applies, the preview uses the version in `package.json`. Editing the
pnpm run start
```

When linked, `craco.config.js` registers the DMV `dist/` folder as a webpack watch dependency so Slim rebuilds after DMV watch emits a new bundle. Restart Slim after linking or after changing `craco.config.js`.
When linked, Vite pre-bundles the DMV `dist/dynamic-import` bundle on every start and watches `dicomMicroscopyViewer.min.js`. When DMV watch emits a new bundle, the dev server re-runs the dependency optimizer and restarts itself. The worker and WebAssembly decoders under `/static/js/` are served straight from the linked `dist/` folder. Restart Slim after linking or after changing `vite.config.ts`.

### Notes

- Running `pnpm install` in Slim removes the link — re-run step 3 afterward.
- Running `pnpm install` in Slim removes the link — re-run step 2 afterward.
- If a tab that stayed open across a DMV rebuild logs `Invalid hook call`, hard-reload it: it still holds modules from the previous optimizer run.
- Do not add `link:` overrides to `package.json`; the commands above are sufficient.
- Slim imports OpenLayers CSS directly (`ol/ol.css`), so `ol` is listed as a direct dependency. This keeps linked dev working when DMV's transitive dependencies are not hoisted into Slim's `node_modules`.
- If Slim still serves a stale DMV bundle, confirm step 3 (realpath must not contain `.pnpm`) and that DMV watch logged `[emitted] dicomMicroscopyViewer.min.js` for your change.
Expand All @@ -550,8 +556,11 @@ If neither applies, the preview uses the version in `package.json`. Editing the
```bash
pnpm unlink dicom-microscopy-viewer
pnpm install
pnpm run start --force
```

`--force` makes Vite discard the dependency cache that still holds the linked bundle.

## Related projects

- [dicom-microscopy-viewer](https://github.com/ImagingDataCommons/dicom-microscopy-viewer) — JavaScript library used by Slim for web-based visualization of DICOM VL Whole Slide Microscopy Image datasets
Expand Down
7 changes: 0 additions & 7 deletions babel.config.js

This file was deleted.

7 changes: 1 addition & 6 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,7 @@
"useIgnoreFile": true
},
"files": {
"includes": [
"src/**/*.{ts,tsx,js,jsx}",
"!src/__mocks__",
"!src/**/*.test.{ts,tsx}",
"!src/**/__tests__"
]
"includes": ["src/**/*.{ts,tsx,js,jsx}", "!src/__mocks__"]
},
"formatter": {
"enabled": true,
Expand Down
Loading
Loading