Version packages - #70
Merged
Merged
Conversation
github-actions
Bot
force-pushed
the
changeset-release/main
branch
10 times, most recently
from
July 6, 2026 10:36
6956a6c to
5e3f46f
Compare
github-actions
Bot
force-pushed
the
changeset-release/main
branch
2 times, most recently
from
July 6, 2026 16:13
938f668 to
b148add
Compare
github-actions
Bot
force-pushed
the
changeset-release/main
branch
8 times, most recently
from
July 20, 2026 07:15
a339278 to
e408285
Compare
github-actions
Bot
force-pushed
the
changeset-release/main
branch
from
July 27, 2026 16:05
e408285 to
a3a44d7
Compare
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.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or setup this action to publish automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@spatialdata/core@0.3.0
Minor Changes
#88
6e153a6Thanks @xinaesthete! - Non-blocking shapes loading + a vertex-pullingFlatPolygonLayer.Shapes no longer gate first paint. The geometry column is decoded (WKB → flat buffers)
and tessellated into render topology inside the geometry worker and transferred back
zero-copy;
ShapesResolver.blockingResourcesis now[], and a main-thread tessellationfallback covers the no-worker path. This removes the full-element main-thread WKB decode
that previously blocked behind the "Loading layer data…" overlay, and it lets Visium HD
square_002um(~2.7M polygons) load without running out of memory.Polygon shapes now render through a new hand-rolled
FlatPolygonLayer(
@spatialdata/layers) instead of deck'sSolidPolygonLayer+ aPathLayeroutline:vertex's position and a boundary edge-distance from two shared geometry textures via
gl_VertexID, and imputes an anti-aliased outline withfwidthin the fragment shader(no separate outline layer). Per-frame cost ≈ the fill; geometry memory ≈ the stock
indexed fill. Works on arbitrary polygons (cell segmentation, not just grids).
buffer" primitive): colour-by-column, hide, and fade re-upload only a small texture,
never the geometry buffers. Picking colours are computed in-shader from the feature
index.
shape's on-screen size and faded out for sub-pixel shapes — clear when zoomed in,
non-dominating (no moiré) when zoomed out.
New/changed public surface:
@spatialdata/coreexportstessellateFlatPolygons/TessellatedPolygonsand carries the tessellated topology onShapesRenderData;@spatialdata/layersexportsFlatPolygonLayer.@spatialdata/visdecouples theone-shot auto-fit from the shapes-blocking transition so a shapes-only view still frames
correctly.
Also fixes a fill-colour "one column behind" bug (the feature-state runtime cache is now
keyed on the fill-colour entry identity, not just its column signature); the hover/pan
buffer-thrash from unstable deck
updateTriggerarrays; and a per-feature colour-bufferthrash where two shapes layers sharing the default feature-state runtime rebuilt each
other's (million-element) colour buffer on every frame — the
FlatPolygonLayercolourcache is now keyed per layer.
Known follow-ups: the main-thread GPU texture upload (~seconds on the largest elements)
is not yet off the main thread — a WGSL/WebGPU variant (storage buffers instead of
texture-packing) is the intended fix; explicit per-feature stroke override on the polygon
path; non-blocking associated-table load.
Patch Changes
#89
e94ba97Thanks @xinaesthete! - Points: feature selection now works on large elements, and persists by name.Selections persist as feature names.
PointsLayerConfig.featureNamesis thedurable, serializable form and what the UI writes. Codes are app-assigned for a
dictionary-only element (a Xenium
transcriptshasfeature_nameand no codecolumn), so a stored code could silently come back meaning a different feature.
featureCodesstill works and still takes effect at runtime, but names win whenboth are present.
resolveFeatureSelectionCodes/featureNamesForCodesareexported from
@spatialdata/corefor converting between the two.The feature scan now completes on large elements. Previously, selecting a
feature on a multi-million-row element frequently never resolved — the scan
plateaued part-way through and the layer sat there. It now reads through
ParquetFile.stream({ columns, rowGroups }), which fetches per column chunk, sothe projection reaches the network instead of pulling whole row groups — all 12
columns of a Xenium
transcriptsto use three. The scan runs in the pointsworker, keeping the parquet decode off the main thread. Selecting one gene from a 12.1M-row element now
settles in ~1.0s, with main-thread time roughly a third of what the pre-streaming
path cost.
Also fixed along the way: a full-dataset catalog scan being silently cancelled by
the resident preview settling underneath it (leaving counts stuck and colours
mismatched); row-group chunks handing out the cached footer buffer, which the
worker transfer detached (
DataCloneError, dropping the element onto whole-filereads); parquet part layout being re-probed on every call; a server that answers
a directory path with 500 rather than 404 wedging part traversal; and point size
not accounting for an element's transform scale.
Known limitation: for a dictionary-only element the fallback catalog path cannot
tally per-feature counts, and it settles successfully without them — so the
retry path does not repair it and counts stay absent for the session. Names and
selection are unaffected. Treat a missing count as unknown, not zero, and do not
read the presence of counts as a signal that the scan completed.
#80
ab1b809Thanks @xinaesthete! - Points/transcript rendering: composite layer, loading engine, and size controls.Points elements render through the
@spatialdata/layersPointsLayercomposite(ADR 0003) via a store-agnostic
resolvePointsRenderResourceboundary, backed bya new React-free
PointsDataEnginethat owns points loading, caching, andrender-resource resolution.
@spatialdata/coregains the points I/O foundation(bounded/capped loading, Morton tiling metadata, feature catalog, an opt-in
worker, and vendored parquet-wasm with row-group range reads). SpatialCanvas adds
a point-size control; preloaded points are sized in world units so they scale
with zoom, clamped to a pixel range.
Updated dependencies [
e343a72]:@spatialdata/layers@0.3.0
Minor Changes
#88
6e153a6Thanks @xinaesthete! - Non-blocking shapes loading + a vertex-pullingFlatPolygonLayer.Shapes no longer gate first paint. The geometry column is decoded (WKB → flat buffers)
and tessellated into render topology inside the geometry worker and transferred back
zero-copy;
ShapesResolver.blockingResourcesis now[], and a main-thread tessellationfallback covers the no-worker path. This removes the full-element main-thread WKB decode
that previously blocked behind the "Loading layer data…" overlay, and it lets Visium HD
square_002um(~2.7M polygons) load without running out of memory.Polygon shapes now render through a new hand-rolled
FlatPolygonLayer(
@spatialdata/layers) instead of deck'sSolidPolygonLayer+ aPathLayeroutline:vertex's position and a boundary edge-distance from two shared geometry textures via
gl_VertexID, and imputes an anti-aliased outline withfwidthin the fragment shader(no separate outline layer). Per-frame cost ≈ the fill; geometry memory ≈ the stock
indexed fill. Works on arbitrary polygons (cell segmentation, not just grids).
buffer" primitive): colour-by-column, hide, and fade re-upload only a small texture,
never the geometry buffers. Picking colours are computed in-shader from the feature
index.
shape's on-screen size and faded out for sub-pixel shapes — clear when zoomed in,
non-dominating (no moiré) when zoomed out.
New/changed public surface:
@spatialdata/coreexportstessellateFlatPolygons/TessellatedPolygonsand carries the tessellated topology onShapesRenderData;@spatialdata/layersexportsFlatPolygonLayer.@spatialdata/visdecouples theone-shot auto-fit from the shapes-blocking transition so a shapes-only view still frames
correctly.
Also fixes a fill-colour "one column behind" bug (the feature-state runtime cache is now
keyed on the fill-colour entry identity, not just its column signature); the hover/pan
buffer-thrash from unstable deck
updateTriggerarrays; and a per-feature colour-bufferthrash where two shapes layers sharing the default feature-state runtime rebuilt each
other's (million-element) colour buffer on every frame — the
FlatPolygonLayercolourcache is now keyed per layer.
Known follow-ups: the main-thread GPU texture upload (~seconds on the largest elements)
is not yet off the main thread — a WGSL/WebGPU variant (storage buffers instead of
texture-packing) is the intended fix; explicit per-feature stroke override on the polygon
path; non-blocking associated-table load.
Patch Changes
#71
bd594e2Thanks @xinaesthete! - Fix multiscale labels rendering with an obviously wrong (vertically stretched, mis-placed) transformation when zoomed out past the coarsest resolution level.The
MultiscaleLabelsTileLayerwas configured withminZoom: -20, so deck.gl kept subdividing the tile grid below the deepest available resolution level. Past that levelgetTileDataclamps to the deepest loader and returns the same data, but the tile bbox keeps doubling — so the bounds formula stretched that fixed data across an ever-larger world rect, far beyond the image extent.minZoomis now capped at-(loader.length - 1), matching Viv'sMultiscaleImageLayer, so the coarsest real tiles stay correctly placed at any zoom-out.Also adds the bbox-culling guards Viv's
renderSubLayersapplies (skip tiles with negative bbox edges or zero-sized data) for defense in depth. This is the underlying cause that #44 only masked by making sublayer ids unique.#89
e94ba97Thanks @xinaesthete! - Points: feature selection now works on large elements, and persists by name.Selections persist as feature names.
PointsLayerConfig.featureNamesis thedurable, serializable form and what the UI writes. Codes are app-assigned for a
dictionary-only element (a Xenium
transcriptshasfeature_nameand no codecolumn), so a stored code could silently come back meaning a different feature.
featureCodesstill works and still takes effect at runtime, but names win whenboth are present.
resolveFeatureSelectionCodes/featureNamesForCodesareexported from
@spatialdata/corefor converting between the two.The feature scan now completes on large elements. Previously, selecting a
feature on a multi-million-row element frequently never resolved — the scan
plateaued part-way through and the layer sat there. It now reads through
ParquetFile.stream({ columns, rowGroups }), which fetches per column chunk, sothe projection reaches the network instead of pulling whole row groups — all 12
columns of a Xenium
transcriptsto use three. The scan runs in the pointsworker, keeping the parquet decode off the main thread. Selecting one gene from a 12.1M-row element now
settles in ~1.0s, with main-thread time roughly a third of what the pre-streaming
path cost.
Also fixed along the way: a full-dataset catalog scan being silently cancelled by
the resident preview settling underneath it (leaving counts stuck and colours
mismatched); row-group chunks handing out the cached footer buffer, which the
worker transfer detached (
DataCloneError, dropping the element onto whole-filereads); parquet part layout being re-probed on every call; a server that answers
a directory path with 500 rather than 404 wedging part traversal; and point size
not accounting for an element's transform scale.
Known limitation: for a dictionary-only element the fallback catalog path cannot
tally per-feature counts, and it settles successfully without them — so the
retry path does not repair it and counts stay absent for the session. Names and
selection are unaffected. Treat a missing count as unknown, not zero, and do not
read the presence of counts as a signal that the scan completed.
#80
ab1b809Thanks @xinaesthete! - Points/transcript rendering: composite layer, loading engine, and size controls.Points elements render through the
@spatialdata/layersPointsLayercomposite(ADR 0003) via a store-agnostic
resolvePointsRenderResourceboundary, backed bya new React-free
PointsDataEnginethat owns points loading, caching, andrender-resource resolution.
@spatialdata/coregains the points I/O foundation(bounded/capped loading, Morton tiling metadata, feature catalog, an opt-in
worker, and vendored parquet-wasm with row-group range reads). SpatialCanvas adds
a point-size control; preloaded points are sized in world units so they scale
with zoom, clamped to a pixel range.
#79
8607083Thanks @xinaesthete! - SpatialCanvas hover/picking performance and Rules-of-React cleanup.Picking/tooltip performance:
hoverTooltipModeprop ('off' | 'simple' | 'aggregate', default'aggregate') onSpatialCanvasandSpatialCanvasViewer, with a matchingselector in the
SpatialCanvasUI.'aggregate'reports every feature underthe cursor across layers (
pickMultipleObjectsGPU passes);'simple'resolves the single top-most pick deck.gl already does for hover/highlight;
'off'makes shape layers non-pickable entirely (no autoHighlight, nopicking-buffer render) — the cheapest mode. Replaces the earlier boolean
aggregateHoverTooltips.pickingEnabledoption (
@spatialdata/layers) that'off'mode uses to drop picking, but itis no longer toggled by camera gestures — the
FlatPolygonLayerpick pass is asingle cheap vertex-pulled draw, so no gesture gate is needed.
and the per-missing-layer supplemental aggregation pick is collapsed into a
single batched pick. The hover-tooltip machinery (pick → tooltip → portal) is a
single
useHoverFeatureTooltiphook shared by both canvas surfaces.Rules-of-React fixes (eslint-plugin-react-hooks,
pnpm lint:reactnow clean andthe
react-lintCI job is required): removed ref reads/writes during render andreplaced setState-in-effect patterns with derived state in
@spatialdata/reactuseSpatialDataand the visTransforms,Table,Shapes,ImageView, andSpatialCanvascomponents.Updated dependencies [
e94ba97,ab1b809,6e153a6]:@spatialdata/vis@0.3.0
Minor Changes
#88
6e153a6Thanks @xinaesthete! - Non-blocking shapes loading + a vertex-pullingFlatPolygonLayer.Shapes no longer gate first paint. The geometry column is decoded (WKB → flat buffers)
and tessellated into render topology inside the geometry worker and transferred back
zero-copy;
ShapesResolver.blockingResourcesis now[], and a main-thread tessellationfallback covers the no-worker path. This removes the full-element main-thread WKB decode
that previously blocked behind the "Loading layer data…" overlay, and it lets Visium HD
square_002um(~2.7M polygons) load without running out of memory.Polygon shapes now render through a new hand-rolled
FlatPolygonLayer(
@spatialdata/layers) instead of deck'sSolidPolygonLayer+ aPathLayeroutline:vertex's position and a boundary edge-distance from two shared geometry textures via
gl_VertexID, and imputes an anti-aliased outline withfwidthin the fragment shader(no separate outline layer). Per-frame cost ≈ the fill; geometry memory ≈ the stock
indexed fill. Works on arbitrary polygons (cell segmentation, not just grids).
buffer" primitive): colour-by-column, hide, and fade re-upload only a small texture,
never the geometry buffers. Picking colours are computed in-shader from the feature
index.
shape's on-screen size and faded out for sub-pixel shapes — clear when zoomed in,
non-dominating (no moiré) when zoomed out.
New/changed public surface:
@spatialdata/coreexportstessellateFlatPolygons/TessellatedPolygonsand carries the tessellated topology onShapesRenderData;@spatialdata/layersexportsFlatPolygonLayer.@spatialdata/visdecouples theone-shot auto-fit from the shapes-blocking transition so a shapes-only view still frames
correctly.
Also fixes a fill-colour "one column behind" bug (the feature-state runtime cache is now
keyed on the fill-colour entry identity, not just its column signature); the hover/pan
buffer-thrash from unstable deck
updateTriggerarrays; and a per-feature colour-bufferthrash where two shapes layers sharing the default feature-state runtime rebuilt each
other's (million-element) colour buffer on every frame — the
FlatPolygonLayercolourcache is now keyed per layer.
Known follow-ups: the main-thread GPU texture upload (~seconds on the largest elements)
is not yet off the main thread — a WGSL/WebGPU variant (storage buffers instead of
texture-packing) is the intended fix; explicit per-feature stroke override on the polygon
path; non-blocking associated-table load.
#79
8607083Thanks @xinaesthete! - SpatialCanvas hover/picking performance and Rules-of-React cleanup.Picking/tooltip performance:
hoverTooltipModeprop ('off' | 'simple' | 'aggregate', default'aggregate') onSpatialCanvasandSpatialCanvasViewer, with a matchingselector in the
SpatialCanvasUI.'aggregate'reports every feature underthe cursor across layers (
pickMultipleObjectsGPU passes);'simple'resolves the single top-most pick deck.gl already does for hover/highlight;
'off'makes shape layers non-pickable entirely (no autoHighlight, nopicking-buffer render) — the cheapest mode. Replaces the earlier boolean
aggregateHoverTooltips.pickingEnabledoption (
@spatialdata/layers) that'off'mode uses to drop picking, but itis no longer toggled by camera gestures — the
FlatPolygonLayerpick pass is asingle cheap vertex-pulled draw, so no gesture gate is needed.
and the per-missing-layer supplemental aggregation pick is collapsed into a
single batched pick. The hover-tooltip machinery (pick → tooltip → portal) is a
single
useHoverFeatureTooltiphook shared by both canvas surfaces.Rules-of-React fixes (eslint-plugin-react-hooks,
pnpm lint:reactnow clean andthe
react-lintCI job is required): removed ref reads/writes during render andreplaced setState-in-effect patterns with derived state in
@spatialdata/reactuseSpatialDataand the visTransforms,Table,Shapes,ImageView, andSpatialCanvascomponents.Patch Changes
#68
25124c5Thanks @xinaesthete! - Bump viv to 0.22.0 and deck.gl/luma.gl ecosystem to 9.3.5#86
716bc44Thanks @xinaesthete! - useLayerData consumes the Resource Resolvers via a single reconcile loop.useLayerDatanow drives layer loading through@spatialdata/core'sSpatialEntryStore.reconcile()over per-kindResourceResolvers —PointsResolver/
ShapesResolverfromcore,ImagesResolver/LabelsResolverfromvis—instead of the previous per-kind
Promise.allload switch. Shapes geometry/tooltip/fill-colour rows, image and labels channel defaults, and points preload are all read
from their resolvers; points continue to run through the stable
PointsDataEngine,which the store borrows via a non-owning proxy so a dataset swap does not dispose it.
Purely an internal restructuring behind ADR 0004 (Step 1 consumption): the 17-member
public surface is unchanged and guarded by
useLayerData.spec.tsx.#75
f109b95Thanks @xinaesthete! - Auto-select the coordinate system when a SpatialData object has exactly one. Previously the picker started unselected (showing "Select a coordinate system") even when there was only one choice, and a separate effect would eagerly pick the first of several. Now selection defaults only in the unambiguous single-coordinate-system case; multi-system datasets still require an explicit choice.#89
e94ba97Thanks @xinaesthete! - Points: feature selection now works on large elements, and persists by name.Selections persist as feature names.
PointsLayerConfig.featureNamesis thedurable, serializable form and what the UI writes. Codes are app-assigned for a
dictionary-only element (a Xenium
transcriptshasfeature_nameand no codecolumn), so a stored code could silently come back meaning a different feature.
featureCodesstill works and still takes effect at runtime, but names win whenboth are present.
resolveFeatureSelectionCodes/featureNamesForCodesareexported from
@spatialdata/corefor converting between the two.The feature scan now completes on large elements. Previously, selecting a
feature on a multi-million-row element frequently never resolved — the scan
plateaued part-way through and the layer sat there. It now reads through
ParquetFile.stream({ columns, rowGroups }), which fetches per column chunk, sothe projection reaches the network instead of pulling whole row groups — all 12
columns of a Xenium
transcriptsto use three. The scan runs in the pointsworker, keeping the parquet decode off the main thread. Selecting one gene from a 12.1M-row element now
settles in ~1.0s, with main-thread time roughly a third of what the pre-streaming
path cost.
Also fixed along the way: a full-dataset catalog scan being silently cancelled by
the resident preview settling underneath it (leaving counts stuck and colours
mismatched); row-group chunks handing out the cached footer buffer, which the
worker transfer detached (
DataCloneError, dropping the element onto whole-filereads); parquet part layout being re-probed on every call; a server that answers
a directory path with 500 rather than 404 wedging part traversal; and point size
not accounting for an element's transform scale.
Known limitation: for a dictionary-only element the fallback catalog path cannot
tally per-feature counts, and it settles successfully without them — so the
retry path does not repair it and counts stay absent for the session. Names and
selection are unaffected. Treat a missing count as unknown, not zero, and do not
read the presence of counts as a signal that the scan completed.
#80
ab1b809Thanks @xinaesthete! - Points/transcript rendering: composite layer, loading engine, and size controls.Points elements render through the
@spatialdata/layersPointsLayercomposite(ADR 0003) via a store-agnostic
resolvePointsRenderResourceboundary, backed bya new React-free
PointsDataEnginethat owns points loading, caching, andrender-resource resolution.
@spatialdata/coregains the points I/O foundation(bounded/capped loading, Morton tiling metadata, feature catalog, an opt-in
worker, and vendored parquet-wasm with row-group range reads). SpatialCanvas adds
a point-size control; preloaded points are sized in world units so they scale
with zoom, clamped to a pixel range.
#69
e343a72Thanks @xinaesthete! - Switch HTJ2K codec from@cornerstonejs/codec-openjphtoopenjph-wasm, which correctly round-trips multi-component (volumetric) HTJ2K data. The cornerstone build silently dropped components 2..N on decode;openjph-wasmhandles arbitrary component counts losslessly.Also adds true z>1 multi-component chunk support: z-planes are now encoded as components of a single codestream rather than one plane per chunk. Exports
Htj2kPlanefrom the package index.Updated dependencies [
bd594e2,e94ba97,ab1b809,6e153a6,8607083,e343a72]:zarrextra@0.3.0
Minor Changes
#69
e343a72Thanks @xinaesthete! - Switch HTJ2K codec from@cornerstonejs/codec-openjphtoopenjph-wasm, which correctly round-trips multi-component (volumetric) HTJ2K data. The cornerstone build silently dropped components 2..N on decode;openjph-wasmhandles arbitrary component counts losslessly.Also adds true z>1 multi-component chunk support: z-planes are now encoded as components of a single codestream rather than one plane per chunk. Exports
Htj2kPlanefrom the package index.@spatialdata/avivatorish@0.3.0
Patch Changes
e343a72]:@spatialdata/react@0.3.0
Patch Changes
#79
8607083Thanks @xinaesthete! - SpatialCanvas hover/picking performance and Rules-of-React cleanup.Picking/tooltip performance:
hoverTooltipModeprop ('off' | 'simple' | 'aggregate', default'aggregate') onSpatialCanvasandSpatialCanvasViewer, with a matchingselector in the
SpatialCanvasUI.'aggregate'reports every feature underthe cursor across layers (
pickMultipleObjectsGPU passes);'simple'resolves the single top-most pick deck.gl already does for hover/highlight;
'off'makes shape layers non-pickable entirely (no autoHighlight, nopicking-buffer render) — the cheapest mode. Replaces the earlier boolean
aggregateHoverTooltips.pickingEnabledoption (
@spatialdata/layers) that'off'mode uses to drop picking, but itis no longer toggled by camera gestures — the
FlatPolygonLayerpick pass is asingle cheap vertex-pulled draw, so no gesture gate is needed.
and the per-missing-layer supplemental aggregation pick is collapsed into a
single batched pick. The hover-tooltip machinery (pick → tooltip → portal) is a
single
useHoverFeatureTooltiphook shared by both canvas surfaces.Rules-of-React fixes (eslint-plugin-react-hooks,
pnpm lint:reactnow clean andthe
react-lintCI job is required): removed ref reads/writes during render andreplaced setState-in-effect patterns with derived state in
@spatialdata/reactuseSpatialDataand the visTransforms,Table,Shapes,ImageView, andSpatialCanvascomponents.Updated dependencies [
e94ba97,ab1b809,6e153a6]: