Skip to content

feat: Add data sidebar in the embedded viewer - #464

Merged
allison-truhlar merged 14 commits into
ngviews-13-edit-savefrom
ngviews-14-viewer-sidebar
Oct 16, 2026
Merged

allison-truhlar merged 14 commits into
ngviews-13-edit-savefrom
ngviews-14-viewer-sidebar

Conversation

@allison-truhlar

@allison-truhlar allison-truhlar commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

A View's owner can add data to the View they have open. This builds on #463 (edit and save). An "Add data" sidebar in the embedded viewer (/view/<read_key>) shows the Layer Cart, and "Add to this View" adds the cart's datasets to the live View. Adding doesn't save: the View has unsaved changes and the owner keeps the change with the Save bar from #463.

What changed

  • "Add data" sidebar: the toolbar button "Add data" opens a 320px panel to the left of Neuroglancer with the Layer Cart and its per-dataset warnings. The button shows only to the owner, and only when the viewer bridge is ready. Whether the sidebar is open is local state, with no new preference. Opening or closing the sidebar never remounts the iframe, so unsaved edits survive.
  • "Add to this View": CartList takes an action render prop, so the sidebar shows "Add to this View" where /ngviews and the Browse drawer show "Create View". The button stays disabled until every dataset is probed, because the mismatch check isn't final before then.
  • Same gating as checkout: useCartCheckout splits out resolveCartDatasets, which finds or creates each dataset's Data Link. useCreateViewFlow gains startAddToView, so adding shows the same consent and dimension-mismatch dialogs as Create View. The dialogs are titled "Add to this View", and adding skips the dialog when there is nothing to ask.
  • Dimension check against the View: beside the viewer, the cart's mismatch check uses the View's first source with OME dimensions as the reference, because a View's dimensions come from its first dataset at checkout. Every cart dataset that differs gets the warning triangle, and the Add to this View dialog asks for the acknowledgement. Without a View (on /ngviews and in the Browse drawer), the first cart dataset stays the reference.
  • Merging layers: useAddToView builds layers with the existing buildViewState, then merges them into the viewer's current state with the pure helper appendLayers. The View keeps its dimensions, layout, and selected layer. Added layers from index 4 on start archived. Neuroglancer requires unique layer names, so a repeated name gets (2), then (3). Neuroglancer splits a multichannel OME-Zarr layer X into X <channel> layers, so a name also counts as taken when one of its split layers exists. Without that check, Neuroglancer renames the collision itself to X <channel>1.
  • Edits from outside the viewer: useViewEditState gains markEdited. Branch 13 treats state changes before the first in-viewer input as Neuroglancer's own and absorbs them into the baseline. An add from the sidebar happens outside the iframe, so without markEdited the View would never show as unsaved.
  • Unsupported datasets: a dataset that can't load as a Neuroglancer layer, such as a plain directory, is still added as a source. Its Data Link waits in useViewEditState.pendingSources, which counts as an unsaved change, and Discard drops it. On Save, PUT /api/neuroglancer/views/{short_key} sends it in the new unsupported_sharing_keys field. The server appends an unsupported view_layers row for each key the View doesn't already list, and rejects an unknown key with a 400. The Views table then lists the dataset in Sources with the warning triangle, as it does after checkout.
  • Re-adding a dataset whose Data Link was deleted: adding the dataset creates a new link, and creating a link relinks the owner's broken Views on that dataset server-side. The add rewrites the same broken sources to the new link in the viewer's local state, using a snapshot of the View's rows taken before the link exists. Without this, the next Save would send the dead key back and re-break the old layer. dataLinkSegment and rewriteDataLinkSegment port the server's data_link_segment and rewrite_layer_segment.
  • Cart cleanup: every added dataset leaves the cart, including a base entry that a channel entry for the same dataset shadows. The toast counts datasets, and names how many will be listed only as sources.
  • Reload during an add: if Neuroglancer reloads while an add is still probing, the sidebar unmounts and the add stops without changing the viewer state or the cart.

Testing

The backend suite (1127) and frontend suite (550) pass, ESLint and Prettier are clean, and node-check stays at the known 5-error baseline.

I tested in headless Chromium against the dev passthrough:

  • Adding an OME-Zarr that the View already contains names the new layers 5025551.zarr (2) <channel>.
  • Adding an OME-Zarr together with a plain directory, then saving, stores the directory as an unsupported view_layers row.
  • In a View whose only layer's Data Link was deleted, adding that dataset again (through the consent dialog) and then saving leaves every layer on the new link, with no broken rows.
  • With a View on 5025551.zarr and a cart of fused-multichannel.zarr then 5025552.zarr, the sidebar flags fused-multichannel.zarr and not 5025552.zarr.

@krokicki

@allison-truhlar
allison-truhlar added this pull request to stack #429 October 7, 2026 19:18
@allison-truhlar allison-truhlar changed the title Neuroglancer Views 14: Add data sidebar in the embedded viewer feat: Add data sidebar in the embedded viewer Oct 7, 2026
@allison-truhlar
allison-truhlar marked this pull request as ready for review October 7, 2026 19:27
@allison-truhlar
allison-truhlar force-pushed the ngviews-14-viewer-sidebar branch 2 times, most recently from a785e52 to c9f1cf2 Compare October 15, 2026 15:34
Neuroglancer splits a multichannel OME-Zarr layer into one layer per
channel, named "<layer> <channel>". appendLayers compared only exact
names, so re-adding a dataset collided after the split and NG appended
its own digit ("…CD3-170Er1"). A name now also counts as taken when a
split layer of it exists, so the copy becomes "<layer> (2) <channel>".
…ources

Add to this View no longer leaves datasets that can't load as layers in
the cart. Their Data Links wait in the edit state, make the View dirty,
and go to the server on Save as unsupported_sharing_keys, which appends
unsupported view_layers rows once each. The Views table then lists them
in Sources with the warning triangle, as checkout does.
… View

Adding a dataset whose Data Link was deleted creates a new link, and
creating a link relinks the owner's broken Views on that dataset
server-side. The viewer's local state still pointed the old layer at
the dead key, so the next Save overwrote the relink and re-broke the
layer. The add now rewrites those broken sources to the new link in the
local state too, from a snapshot of the View's rows taken before the
link exists.
The cart's mismatch check compared datasets only with the first cart
dataset. Beside the viewer, the reference is now the View's first source
with OME dimensions, since a View's dimensions come from its first
dataset at checkout. Every cart dataset that differs gets the warning
triangle, and the Add to this View dialog asks for the acknowledgement.
@allison-truhlar
allison-truhlar force-pushed the ngviews-14-viewer-sidebar branch from c9f1cf2 to a88f582 Compare October 15, 2026 17:12
@allison-truhlar
allison-truhlar merged commit 1c6baac into main Oct 16, 2026
5 of 8 checks passed
@allison-truhlar
allison-truhlar deleted the ngviews-14-viewer-sidebar branch October 16, 2026 17:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant