Add annotation overlay for the OncoTree (+ migrate frontend to pnpm)#342
Open
inodb wants to merge 26 commits into
Open
Add annotation overlay for the OncoTree (+ migrate frontend to pnpm)#342inodb wants to merge 26 commits into
inodb wants to merge 26 commits into
Conversation
Replace package-lock.json with pnpm-lock.yaml (versions preserved via pnpm import), pin pnpm@10.33.0 via the packageManager field, and whitelist esbuild's build script. Switch CI workflows, Dockerfile, the husky pre-commit hook, and the READMEs to pnpm. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
Let users overlay custom annotations on the tree by mapping OncoTree codes to values. Annotations can be pasted (JSON or CSV), uploaded as a file, or shared via a ?annotations=<base64> URL. - annotations.ts: parse/normalize JSON and CSV/TSV into values, labels, or gene lists; validate codes against the tree; build a color scale; encode and decode the URL parameter. - AnnotationOverlay: decorate each rendered SVG node with a color-coded halo and a value/gene badge plus a hover tooltip. Re-applied on every tree re-render via a debounced MutationObserver since the tree library restyles its nodes on expand/collapse. - AnnotationPanel: paste/upload/sample/clear controls, share-link button, validation feedback, and a legend. - Wire annotation state and URL loading into App and Home. - vite: make the dev API proxy target overridable via ONCOTREE_API_PROXY. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
ts-jest defaults to an ES2015 target whose lib lacks Object.values; this surfaced as a TS2550 compile error under the pnpm node_modules layout (the npm layout happened to resolve a higher lib). Set an explicit ES2020 target/lib for the test transform, matching tsconfig.app.json. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
Use a neutral amber ring to mark annotated nodes instead of a data-colored halo, so each node keeps its own OncoTree color (e.g. HCC stays MediumSeaGreen). Numeric magnitude moves to the badge fill (value color scale); gene/text badges use a neutral slate. Legend updated to match. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
decodeAnnotations now accepts either raw (URL-encoded) JSON
(e.g. ?annotations={"LUAD":1204}) or base64/base64url-encoded JSON, and
normalizes every value shape so bare numbers, strings, and gene arrays all
render. Share both behind a shared normalizeAnnotationMap helper reused by
parseAnnotations.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
A collapsed node now aggregates its hidden subtree: numeric values are summed (badge shows "Σ <total>") and gene lists are unioned, with the tooltip reporting how many descendants contributed. Expanding a node splits the data back to each child, so every annotated code is counted exactly once at any expansion state. Clamp the color scale so summed values stay in range. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
A collapsed node already implies its badge is a rollup, so show the plain total. The tooltip still labels it "Sum". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
When annotations arrive from the ?annotations= URL parameter, populate the panel textarea with their JSON so they are visible and editable. Skips the sync when the user is already typing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
When embedded in an iframe, the app announces { type: "oncotree-ready" } to
its parent and listens for { type: "oncotree-annotations", annotations } to
overlay data programmatically (object, JSON string, or null to clear). Lets
tools push large payloads without a backend or URL-length limits. Document
all annotation input methods in the README.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
Add a script that builds the frontend and publishes it to a fork's gh-pages branch, served from a subpath against a public OncoTree API. Supporting config, all no-ops at their defaults: - vite `base` from DEPLOY_BASE (default "/") - router basename from import.meta.env.BASE_URL (so routing works on a subpath) - ONCOTREE_BASE_URL from VITE_ONCOTREE_BASE_URL (default window.location.origin) - fetch the tree from the canonical no-slash route (avoids a 301 redirect; the Gin route is registered without a trailing slash) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
An embedding page can post { type: "oncotree-search", query } to filter the
tree to nodes matching a code, name, or annotation (gene/label/value); the
tree collapses and expands the path to the matches and focuses the first.
An empty query (or { clear: true }) resets. The app posts back
{ type: "oncotree-search-result", query, count }.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
The demo fetched the tree from oncotree.mskcc.org at runtime, which fails CORS / Private Network Access when the page is embedded (the host can resolve to a private IP). Snapshot the API at build time and serve it from the deploy's own subpath so the running page only makes same-origin requests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
For iframe embedding, `?embed` (or ?embed=1) renders only the tree (with the expand/collapse and annotation controls) at full height, hiding the site header and footer. The host drives annotations and search via postMessage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
In ?embed mode the host supplies annotations via postMessage, so the input panel/toggle is unnecessary; hide it (the data overlays on the tree still render). Add a hideAnnotationPanel prop on Home, set from the embed flag. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
Rework the overlay for legibility: - Fold annotation detail (value, gene union) into the tree library's own node tooltip, shown on hovering the node name, instead of a separate tooltip on the hard-to-hit marker. - Drop the marker ring; the badge itself marks an annotated node. - Show the value/gene-count in a white pill with a dark bold number (fixes the low-contrast white-on-light-blue badges) and place it on the node's own row. - Remove the value color scale (de-emphasized) for a consistent neutral badge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
An expanded node's badge shows its own value only (no double-counting), which is confusing when a child's value exceeds the parent's. The tooltip now adds a "Subtree total: N across M nodes (collapse to combine)" line on expanded nodes that have annotated descendants, so the relationship is explicit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
Replace the faint default expand/collapse icons with labelled, high-contrast buttons (white pill, tooltip) and add a full-screen toggle that expands the tree area. Expand/collapse now call the OncoTree public API directly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
- Replace the chevron icons with custom glyphs: expand shows a node forking into two children, collapse a node with its subtree tucked into one line. - Give the content area its own light background so fullscreen shows the gray grid instead of the browser's black fullscreen backdrop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
requestFullscreen rejects when embedded without allow="fullscreen"; catch it and show a hint instead of an unhandled rejection. Document the iframe allow="fullscreen" requirement. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
- Node labels show a pointer cursor and underline on hover, and clicking a label now toggles the node (the library only wired the circle). - The annotation badge is interactive: pointer cursor, a hover state (border darkens), it shows the node tooltip on hover (previously only the label did), and clicking it toggles the node. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
When embedded, clicking a leaf node's annotation badge now posts
{ type: "oncotree-node-click", code, label } to the parent window, so a host
page can react (e.g. filter to that cancer type). Parent badges keep toggling
expand/collapse.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Add an "oncotree-selection" embed message: the host posts the set of selected codes, and their annotation badges render with a checkmark and a blue highlight so it's clear which cancer types are selected. Two-way with oncotree-node-click. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Clicking a badge selects the cancer type(s) it represents (leaf = that code;
collapsed parent = its whole subtree) and never expands/collapses.
- Clicking a node name/dot expands/collapses; expanding a collapsed parent also
selects every annotated cancer type in its subtree (mode:"add").
- Badge shows a 3-way selection state: none, partial (some of a collapsed
parent's subtree, "–"), or all ("✓").
🤖 Generated with [Claude Code](https://claude.com/claude-code)
A badge now represents its node's full annotated subtree regardless of expand state, so clicking an expanded parent's badge deselects (or selects) the whole branch and keeps it expanded, instead of only toggling the parent's own code. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The library's caret is positioned at left:10% of the tooltip width (via a stray-space selector that also applies it to every child), so it rarely points at the node. Hide it; the tooltip already appears next to the cursor. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sjAMWhXEZQt1Mqxdb4EBE
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an annotation overlay to the OncoTree frontend: users can map OncoTree codes to custom values and see them visualized on the tree. Useful for overlaying sample counts per cancer type, gene lists from tools like Oncotree-to-Genes-LLM, or any code → value data.
This branch contains two independent commits:
package-lock.json→pnpm-lock.yaml(versions preserved viapnpm import),packageManagerpinned topnpm@10.33.0, esbuild's build script whitelisted. CI workflows, Dockerfile, the husky hook, and the READMEs are switched to pnpm.Feature details
Input (top-right Annotations panel):
.json/.csv/.tsvfile, or load built-in sample data.?annotations=<base64-json>in the URL.Accepted value shapes per code: a number, a string label, a gene list, or
{ "value": n, "label": "…", "genes": [...] }. Example:{ "LUAD": {"label": "1204 samples", "value": 1204}, "GB": {"genes": ["EGFR", "PTEN", "TP53"]} }Visualization:
Implementation note: the
@oncokb/oncotreerenderer is a compiled D3 black box that re-styles its node circles on every expand/collapse, so overlays are appended as extra SVG children of each node and re-applied via a debouncedMutationObserver. No changes to the tree library or the Go backend.Testing
pnpm build,pnpm lint, andtsc -bpass.?annotations=URL loading. No console errors.🤖 Generated with Claude Code