Work Done
Issue virdx/mono#313, PR
virdx/mono#314 (draft, branch
fix/diffsimscan-single-viewer).
- Problem:
DiffsimScanCardrendered oneVolumeBrowserperDiffsimScan. An Essen01 patient has hundreds ofHistoScans and roughly oneDiffsimScaneach, so the patient page became an endless widget list and mounted hundreds of Niivue WebGL contexts. - Change:
VolumeBrowsergained an optionalgroups?: VolumeGroup[]prop (VolumeGroup={ id, label, volumes }inimaging-types.ts). When set, the rail renders collapsible groups instead of a flat list; the flatvolumesprop stays the source of truth for selection, sorting, type filtering, search and prefetch. Rail order in grouped mode follows on-screen order so arrow keys and prefetch match the list.DiffsimScanCardnow renders exactly oneVolumeBrowser.ImagingStudySection’s flat usage is unchanged. - Found and fixed a pre-existing race in
VolumeViewer: the volume-load effect readnvRefat effect time, but Niivue is attached in acanvas-keyed effect that only runs after the element mounts. A viewer whose volume is known on first render fired the effect withnvRef === nulland never retried, so the canvas stayed on Niivue’s own “loading …” screen until the user clicked another volume. Addingcanvasto the effect’s dependency list fixes it. This became the default state once the diffsim rail preselects a volume in a single viewer. - Validation (as the issue demanded, all local): native
vxdata-apistack viajust deploy(Postgres + MinIO + API on 12300) +just inject-mock, then 60 syntheticDiffsimScans x 3DiffsimVolumes posted to/resources/create, with small synthetic NIfTI phantoms uploaded through the API’s own/s3/presignupload flow. Frontend viavite devwith a gitignored.env.local(VITE_PLATFORM_PROXY_TARGET=http://127.0.0.1:12300,VITE_S3_PROXY_TARGET=http://127.0.0.1:12304,VITE_PLATFORM_BEARER_TOKEN=vxd_dev_admin) against the already-runningdashboard-apion 8080. Result: one card reading “180 volumes in 61 scans”, a two-level rail, 3<canvas>elements on the whole page, and volumes rendering in the shared viewer. Screenshot kept locally on the Desktop. - Static checks:
bunx nx run dashboard-frontend:{typecheck,build,format}all pass;biome checkon the touched files shows the same 11 pre-existing diagnostics asmain.
Lessons Learned: Pitfalls
wiki/workstreams/has noproduct/directory (and most workstreams have only anindex.md, nosops/), yet thebegin-workskill instructs the agent to readwiki/workstreams/<workstream>/index.mdand.../sops/index.md. For frontend / dashboard work there is nothing to read, so the step is a dead end. Either add aproduct/index.mdstub or makebegin-worksay “read whichever of these exist”.apps/vxdata-api’sjust deployfails in two ways that are not documented:process-compose’s own admin HTTP server hardcodes port 8080, which collides with a locally runningdashboard-api. Workaround: runprocess-compose ... -p 18080 upinstead ofjust deploy(or stop dashboard-api first).- Postgres refuses to start when the checkout path is long: the process-compose recipe
puts the Unix socket in
$(pwd)/.pg, and paths over ~103 bytes (e.g. any worktree under~/work/tries/<long-dated-slug>/mono) exceed the socket-path limit. Symlinking to a short path does not help (cwd is resolved physically); the fix is a shorter checkout path, or pointingunix_socket_directoriesat/tmp.
src/vxdata/api/mock/mock_data.pystill emits the diffsim volumes asVolumepayloads parented to theDiffsimScan, but the frontend (and the current schema) expectDiffsimVolumewith aurlfield. Sojust inject-mockalone produces a diffsim scan with zero visible volumes – easy to mistake for a frontend bug. Worth fixing in the mock data.bunx nx run dashboard-frontend:formatreformats an unrelated--font-sansdeclaration insrc/styles/globals.cssevery time. Revert that file before committing, or fix it once.apps/dashboard-frontendhas ~173 pre-existingbiome checkerrors and no wiredlintortesttarget inproject.json, so “run lint before finishing” cannot mean the whole app here; compare per-file diagnostics againstmaininstead.
Lessons Learned: Improvements
- A short SOP for “run the dashboard frontend against a fully local platform stack” would
have saved the most time. The pieces are scattered:
apps/dashboard-frontend/AGENTS.mddocumentsVITE_PLATFORM_BEARER_TOKENbut not that MinIO listens on 12304 whileVITE_S3_PROXY_TARGETdefaults to 9000, andapps/vxdata-api/AGENTS.mddocumentsjust deploybut not the 8080 collision or the socket-path limit. - Useful trick worth recording: to get a viewer-testable volume into the local stack, POST
/s3/presignwithdirection: "upload", PUT the bytes to the returned URL, and use the returneds3://path as the resource’surl/path_nii. The upload presign chooses the key itself, so read it back rather than assuming a layout. - Headless Chromium (
chrome-headless-shell) renders Niivue canvases unreliably; use a headed browser for any WebGL screenshot validation.