@jax-data-science/sci-image-visualizer 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.md +215 -11
  2. package/esm2022/index.mjs +20 -1
  3. package/esm2022/lib/contracts/display-types.mjs +20 -2
  4. package/esm2022/lib/contracts/image.contract.mjs +1 -1
  5. package/esm2022/lib/contracts/plot-type.mjs +36 -1
  6. package/esm2022/lib/contracts/ports/spatial-data.port.mjs +3 -0
  7. package/esm2022/lib/contracts/spatial-dataset.contract.mjs +45 -0
  8. package/esm2022/lib/contracts/visualizer.contract.mjs +1 -1
  9. package/esm2022/lib/implementations/napari-js/napari-axes-labels.mjs +10 -19
  10. package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +71 -7
  11. package/esm2022/lib/implementations/napari-js/napari-scale-bar.mjs +1 -1
  12. package/esm2022/lib/implementations/napari-js/napari-spatial-tooltip.mjs +96 -0
  13. package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +1934 -55
  14. package/esm2022/lib/implementations/napari-js/napari-zoom.mjs +18 -0
  15. package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +5 -3
  16. package/esm2022/lib/implementations/osd/osd-region-overlay.mjs +3 -4
  17. package/esm2022/lib/implementations/osd/osd-zoom.mjs +20 -0
  18. package/esm2022/lib/implementations/plotly/omics-trace-builders.mjs +555 -0
  19. package/esm2022/lib/implementations/plotly/plotly.service.mjs +13 -7
  20. package/esm2022/lib/implementations/spatial-data-http/spatial-data-http.service.mjs +304 -0
  21. package/esm2022/lib/implementations/spatial-data-http/spatial-wire.mjs +179 -0
  22. package/esm2022/lib/routing-visualizer.service.mjs +223 -12
  23. package/esm2022/lib/spatial/embedding-compute.mjs +142 -0
  24. package/esm2022/lib/spatial/gene-search.mjs +77 -0
  25. package/esm2022/lib/spatial/spatial-density.mjs +224 -0
  26. package/esm2022/lib/spatial/spatial-encoding.mjs +232 -0
  27. package/esm2022/lib/spatial/spatial-expression.mjs +257 -0
  28. package/esm2022/lib/spatial/spatial-framing.mjs +60 -0
  29. package/esm2022/lib/spatial/spatial-heatmap.mjs +185 -0
  30. package/esm2022/lib/spatial/spatial-hover.mjs +79 -0
  31. package/esm2022/lib/spatial/spatial-sections.mjs +96 -0
  32. package/esm2022/lib/spatial/spatial-selection.mjs +264 -0
  33. package/esm2022/lib/spatial/spatial-volume-image.mjs +163 -0
  34. package/esm2022/lib/spatial/tsne-worker.mjs +10 -0
  35. package/esm2022/lib/spatial/world-grid.mjs +77 -0
  36. package/esm2022/lib/spatial-controls/spatial-charts/spatial-charts.component.mjs +1278 -0
  37. package/esm2022/lib/spatial-controls/spatial-controls.component.mjs +621 -0
  38. package/esm2022/lib/store/spatial-selection.service.mjs +47 -0
  39. package/esm2022/lib/store/visualizer-store.service.mjs +23 -1
  40. package/esm2022/lib/toolbar/segmentation/cellpose-segmenter.service.mjs +2 -2
  41. package/esm2022/lib/toolbar/segmentation/sam-model-registry.mjs +9 -9
  42. package/esm2022/lib/toolbar/toolbar.component.mjs +30 -7
  43. package/esm2022/lib/visualization.module.mjs +23 -5
  44. package/esm2022/lib/visualizer.component.mjs +283 -22
  45. package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs → jax-data-science-sci-image-visualizer-onnx-sam-session-BwP-A11m.mjs} +6 -4
  46. package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs.map → jax-data-science-sci-image-visualizer-onnx-sam-session-BwP-A11m.mjs.map} +1 -1
  47. package/fesm2022/jax-data-science-sci-image-visualizer-tsne-worker-B_nXDGf0.mjs +12 -0
  48. package/fesm2022/jax-data-science-sci-image-visualizer-tsne-worker-B_nXDGf0.mjs.map +1 -0
  49. package/fesm2022/jax-data-science-sci-image-visualizer.mjs +15282 -7829
  50. package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
  51. package/fesm2022/tsne.worker.js +19091 -0
  52. package/index.d.ts +15 -0
  53. package/lib/contracts/display-types.d.ts +131 -0
  54. package/lib/contracts/image.contract.d.ts +7 -0
  55. package/lib/contracts/plot-type.d.ts +42 -2
  56. package/lib/contracts/ports/spatial-data.port.d.ts +90 -0
  57. package/lib/contracts/spatial-dataset.contract.d.ts +281 -0
  58. package/lib/contracts/visualizer.contract.d.ts +99 -1
  59. package/lib/implementations/napari-js/napari-axes-labels.d.ts +9 -7
  60. package/lib/implementations/napari-js/napari-region-overlay.d.ts +38 -4
  61. package/lib/implementations/napari-js/napari-scale-bar.d.ts +7 -3
  62. package/lib/implementations/napari-js/napari-spatial-tooltip.d.ts +32 -0
  63. package/lib/implementations/napari-js/napari-visualizer.service.d.ts +497 -16
  64. package/lib/implementations/napari-js/napari-zoom.d.ts +15 -0
  65. package/lib/implementations/osd/osd-zoom.d.ts +19 -0
  66. package/lib/implementations/plotly/omics-trace-builders.d.ts +193 -0
  67. package/lib/implementations/plotly/plotly.service.d.ts +9 -0
  68. package/lib/implementations/spatial-data-http/spatial-data-http.service.d.ts +123 -0
  69. package/lib/implementations/spatial-data-http/spatial-wire.d.ts +115 -0
  70. package/lib/routing-visualizer.service.d.ts +45 -6
  71. package/lib/spatial/embedding-compute.d.ts +73 -0
  72. package/lib/spatial/gene-search.d.ts +48 -0
  73. package/lib/spatial/spatial-density.d.ts +73 -0
  74. package/lib/spatial/spatial-encoding.d.ts +155 -0
  75. package/lib/spatial/spatial-expression.d.ts +144 -0
  76. package/lib/spatial/spatial-framing.d.ts +37 -0
  77. package/lib/spatial/spatial-heatmap.d.ts +111 -0
  78. package/lib/spatial/spatial-hover.d.ts +49 -0
  79. package/lib/spatial/spatial-sections.d.ts +59 -0
  80. package/lib/spatial/spatial-selection.d.ts +113 -0
  81. package/lib/spatial/spatial-volume-image.d.ts +67 -0
  82. package/lib/spatial/tsne-worker.d.ts +7 -0
  83. package/lib/spatial/world-grid.d.ts +58 -0
  84. package/lib/spatial-controls/spatial-charts/spatial-charts.component.d.ts +439 -0
  85. package/lib/spatial-controls/spatial-controls.component.d.ts +257 -0
  86. package/lib/store/spatial-selection.service.d.ts +27 -0
  87. package/lib/store/visualizer-store.service.d.ts +13 -0
  88. package/lib/toolbar/toolbar.component.d.ts +19 -3
  89. package/lib/visualization.module.d.ts +28 -24
  90. package/lib/visualizer.component.d.ts +73 -2
  91. package/package.json +7 -3
  92. package/src/lib/assets/spatial-omics-3d.svg +31 -0
  93. package/src/lib/assets/spatial-omics.svg +27 -0
@@ -0,0 +1,257 @@
1
+ import { EventEmitter, OnDestroy, OnInit } from '@angular/core';
2
+ import { IVisualizer, ISpatialControls } from '../contracts/visualizer.contract';
3
+ import { SpatialDataset } from '../contracts/spatial-dataset.contract';
4
+ import { ColormapNode, SpatialViewState } from '../contracts/display-types';
5
+ import { SpatialSelectionMask } from '../spatial/spatial-selection';
6
+ import * as i0 from "@angular/core";
7
+ /** One legend row for a categorical colouring. */
8
+ export interface SpatialLegendEntry {
9
+ label: string;
10
+ color: string;
11
+ }
12
+ /**
13
+ * Spatial-omics controls: a non-modal, resizable, draggable dialog for choosing
14
+ * what the observation markers are coloured by, and how they are drawn.
15
+ *
16
+ * Depends only on {@link ISpatialControls}, reached through the `VISUALIZER`
17
+ * contract token — never a concrete backend — mirroring how
18
+ * `ChannelHistogramComponent` depends only on `CHANNEL_HISTOGRAM_API`. When no
19
+ * host has bound `SPATIAL_DATA_PORT`, `getSpatialControls()` returns null and
20
+ * the dialog renders an explanatory empty state rather than dead controls.
21
+ *
22
+ * The legend swatches and the continuous colour bar are built with the SAME
23
+ * functions the renderer uses (`categoryColors` → `resolveCategoryColors`,
24
+ * `spatialContinuousLut`), so the key cannot drift from what is on screen.
25
+ */
26
+ export declare class SpatialControlsComponent implements OnInit, OnDestroy {
27
+ private readonly viz;
28
+ /** The embedded chart, so a dialog resize can re-fit it. */
29
+ private charts?;
30
+ /**
31
+ * Re-fit the chart after the dialog is resized.
32
+ *
33
+ * Plotly does not follow a container resize on its own — its `responsive`
34
+ * option listens for WINDOW resizes only, so dragging the dialog's handle
35
+ * reaches it through nothing. PrimeNG tells us when the drag ends, which is a
36
+ * far more direct signal than watching boxes, and firing once on release means
37
+ * no relayout churn during the drag.
38
+ */
39
+ onResizeEnd(): void;
40
+ /** Dialog visibility, two-way bound so a toolbar button can open it. */
41
+ visible: boolean;
42
+ /** Set while the 3D cloud is the active mode. Changes what the ROI selection
43
+ * MEANS — a screen-space lasso cutting through the cloud's full depth rather
44
+ * than a shape in tissue coordinates — so the hint below it says so. */
45
+ is3d: boolean;
46
+ visibleChange: EventEmitter<boolean>;
47
+ readonly clipOptions: {
48
+ label: string;
49
+ value: [number, number];
50
+ }[];
51
+ /** Null when the host bound no `SPATIAL_DATA_PORT`. */
52
+ controls: ISpatialControls | null;
53
+ dataset: SpatialDataset | null;
54
+ view: SpatialViewState;
55
+ /** Colour-by column choices — "None" plus every column the dataset declares. */
56
+ columnOptions: {
57
+ label: string;
58
+ value: string | null;
59
+ }[];
60
+ selectedColumn: string | null;
61
+ /** Virtual-scrolled only past this many options: the scroller earns its
62
+ * complexity for a few thousand names, and for eight it adds only overhead —
63
+ * a virtual viewport that short swallows the clicks it is meant to forward. */
64
+ readonly geneVirtualScrollFrom = 200;
65
+ /**
66
+ * Gene picker: a filterable dropdown rather than a free-text typeahead, so the
67
+ * options are visible before anything is typed and each keystroke narrows a list
68
+ * the user can see.
69
+ *
70
+ * `geneOptions` is the full name list when the dataset inlines it (a targeted
71
+ * panel: 8 for the ABC demo, 300–5,000 for Xenium/CosMx). A whole-transcriptome
72
+ * dataset does not ship its ~31k names, so there the list is what the port's last
73
+ * search returned and filtering is server-side — same control either way.
74
+ */
75
+ geneOptions: {
76
+ label: string;
77
+ value: string;
78
+ }[];
79
+ /**
80
+ * Every gene the dataset inlined, whether or not it is currently an option.
81
+ *
82
+ * Held as plain strings and searched per keystroke. A whole-transcriptome dataset
83
+ * inlines ~18k names — cheap to keep, ruinous to hand a dropdown all at once — so
84
+ * {@link geneOptions} is only ever the best few hundred of these.
85
+ */
86
+ private geneNames;
87
+ selectedGene: string | null;
88
+ /** True when the options come from the port per keystroke rather than a resident
89
+ * list, which changes what an empty list means (nothing matched *yet*). */
90
+ genesAreRemote: boolean;
91
+ geneSearchFailed: boolean;
92
+ /** Categorical key, or null when the active colouring is continuous. */
93
+ legend: SpatialLegendEntry[] | null;
94
+ /** CSS gradient for a continuous colouring, or null when categorical. */
95
+ colorBarCss: string | null;
96
+ /** Current selection — drives the count, the Clear button and the muting. */
97
+ selection: SpatialSelectionMask;
98
+ /** The legend row whose category is currently selected, for highlighting. */
99
+ selectedCategory: number | null;
100
+ /** An ROI selection that matched nothing, so the UI can say so rather than
101
+ * looking like the button did nothing. */
102
+ selectionMissed: boolean;
103
+ /**
104
+ * Whether the Distribution section is expanded. Collapsed by default: the
105
+ * panel's primary job is the colour controls, and the chart roughly doubles
106
+ * its height.
107
+ */
108
+ chartsOpen: boolean;
109
+ /** Per-instance id for the collapsible body, so `aria-controls` points at one
110
+ * element and expanding the second panel cannot scroll the first one's chart. */
111
+ readonly chartsBodyId: string;
112
+ /**
113
+ * The dataset's imaged section positions, or null when its z is continuous
114
+ * rather than sectioned. Read once per dataset — the scan walks the z of every
115
+ * observation, so it must not sit in a template getter.
116
+ */
117
+ sections: Float32Array | null;
118
+ /**
119
+ * Colormap tree for the continuous colour scale, and the node currently picked
120
+ * from it. The library's own `COLORMAP_OPTIONS`, shown with the same swatches
121
+ * the image's colormap picker uses — choosing a gradient is a visual decision,
122
+ * so a list of names would be the wrong control.
123
+ */
124
+ colormapOptions: ColormapNode[];
125
+ selectedColormapNode: ColormapNode | null;
126
+ private colormap;
127
+ private reverse;
128
+ /** Guards the gene typeahead and the legend/colour-bar rebuild: both are async
129
+ * and both are driven by input the user changes faster than they resolve. */
130
+ private geneSearchToken;
131
+ private keyToken;
132
+ private readonly subs;
133
+ constructor(viz: IVisualizer);
134
+ ngOnInit(): void;
135
+ ngOnDestroy(): void;
136
+ onVisibleChange(value: boolean): void;
137
+ /** Column dropdown. Choosing a column supersedes any gene selection. */
138
+ onColumn(name: string | null): void;
139
+ /**
140
+ * A keystroke in the gene dropdown's filter box.
141
+ *
142
+ * With the names resident the dropdown filters them itself and this only clears a
143
+ * stale failure. Without them there is nothing to filter, so the query goes to the
144
+ * port and its answer BECOMES the option list — the same control, filtering one
145
+ * hop further away.
146
+ */
147
+ onGeneFilter(query: string): Promise<void>;
148
+ /** A gene was picked; it supersedes any column selection. */
149
+ onGene(name: string | null): void;
150
+ /** Select every observation inside the drawn regions (their union). */
151
+ selectFromRegions(): void;
152
+ /** Legend click: select one category. Clicking the active row clears it, so a
153
+ * second click is an undo rather than a no-op. */
154
+ selectCategory(index: number): Promise<void>;
155
+ /**
156
+ * Expand or collapse the distribution section.
157
+ *
158
+ * Expanding SCROLLS it into view, because the panel is taller than the viewport
159
+ * once this section is open and the chart is its last row: expanding it drew a
160
+ * chart ~300 px below the fold, which reads as the section being empty. The
161
+ * scroll is deferred a task so the chart's own deferred first draw has given the
162
+ * body its height.
163
+ */
164
+ toggleCharts(): void;
165
+ clearSelection(): void;
166
+ /** True while a selection is active — everything else renders muted. */
167
+ get hasSelection(): boolean;
168
+ onPointScale(value: number | undefined): void;
169
+ onOpacity(value: number | undefined): void;
170
+ onLogScale(on: boolean): void;
171
+ /**
172
+ * Set when the active categorical colouring has more categories than the 3D
173
+ * cloud can keep apart, so the points are drawing FLAT.
174
+ *
175
+ * Said in the panel rather than left to a console warning: `subclass` (338) is
176
+ * served precisely because the density volumes and the 2D view can render it, and
177
+ * a user who picks it in the cloud and sees one colour deserves to know both why
178
+ * and what to do instead.
179
+ */
180
+ get exceedsCloudPalette(): boolean;
181
+ /** The ceiling itself, for the message. */
182
+ readonly cloudPaletteLimit = 95;
183
+ onGeneMap(on: boolean): void;
184
+ /** 3D: smooth the per-section sheets along z into a continuous volume. */
185
+ onGeneMapVolume(on: boolean): void;
186
+ /** 3D: restrict the sheets to one imaged section. */
187
+ onGeneMapOneSection(on: boolean): void;
188
+ onGeneMapSection(value: number | undefined): void;
189
+ get geneMapOneSection(): boolean;
190
+ /** "12 of 53" for the gene map's own section, 1-based like the cloud's. */
191
+ get geneMapSectionLabel(): string;
192
+ onGeneMapSmoothing(value: number | undefined): void;
193
+ onGeneMapOpacity(value: number | undefined): void;
194
+ /**
195
+ * What the 3D gene map is currently showing, said plainly — the sheets are a
196
+ * measurement and the volume is an estimate, and the panel has to be the place
197
+ * that says which one is on screen.
198
+ */
199
+ get geneMapVolumeNote(): string;
200
+ /** True while a gene is the colour source — the only thing a gene map can map. */
201
+ get canMapGene(): boolean;
202
+ onShowVolume(on: boolean): void;
203
+ onVolumeOpacity(value: number | undefined): void;
204
+ onShowPoints(on: boolean): void;
205
+ /** The "one section at a time" switch: null restores the whole stack. */
206
+ onOneSection(on: boolean): void;
207
+ onPointSection(value: number | undefined): void;
208
+ /** True while the cloud is restricted to a single section. */
209
+ get oneSection(): boolean;
210
+ /** Highest section index the slider can reach. */
211
+ get lastSection(): number;
212
+ /** "12 of 53" — 1-based, because the sections are slides, not array slots. */
213
+ get sectionLabel(): string;
214
+ /** Whether this dataset has sections to pick from at all. */
215
+ get isSectioned(): boolean;
216
+ onDensityVolume(on: boolean): void;
217
+ onDensitySmoothing(value: number | undefined): void;
218
+ /** What the density volumes are actually showing, said plainly — an estimate is
219
+ * only honest if the reader knows it is one, and which clusters are in view. */
220
+ get densityNote(): string;
221
+ /** Mirrors the renderer's cap, for the note only. */
222
+ private static readonly DENSITY_MAX_CLUSTERS;
223
+ onClip(value: [number, number]): void;
224
+ reset(): void;
225
+ /**
226
+ * Description of the active column, when it has one. Surfaced because a
227
+ * DERIVED column (k-means clusters, QC totals computed at conversion) must not
228
+ * read as though it came with the data.
229
+ */
230
+ get activeDescription(): string | null;
231
+ /** Label for the current colouring, for the key's heading. */
232
+ get colorByLabel(): string;
233
+ /** True when the active colouring is a categorical column (drives the key). */
234
+ get isCategorical(): boolean;
235
+ /** Whether log/clip apply — they are continuous-only knobs. */
236
+ get isContinuous(): boolean;
237
+ private columnLabel;
238
+ /** Rebuild the legend or colour bar for the current colouring. */
239
+ private refreshKey;
240
+ /**
241
+ * The continuous colour scale's colormap. Clearing it goes back to following
242
+ * the image's, which is the default.
243
+ */
244
+ onContinuousColormap(node: ColormapNode | null): void;
245
+ /** The tree node holding a colormap value, so the picker shows what is in use. */
246
+ private colormapNodeFor;
247
+ /**
248
+ * `linear-gradient(...)` sampling the active colormap at 16 stops.
249
+ *
250
+ * Resolved exactly the way the renderer resolves it — the same override, the
251
+ * same grey fallback. Built from `lutFor` alone, this bar showed a black-to-white
252
+ * ramp while the canvas drew Viridis, which makes the key worse than no key.
253
+ */
254
+ private buildColorBar;
255
+ static ɵfac: i0.ɵɵFactoryDeclaration<SpatialControlsComponent, never>;
256
+ static ɵcmp: i0.ɵɵComponentDeclaration<SpatialControlsComponent, "spatial-controls", never, { "visible": { "alias": "visible"; "required": false; }; "is3d": { "alias": "is3d"; "required": false; }; }, { "visibleChange": "visibleChange"; }, never, never, false, never>;
257
+ }
@@ -0,0 +1,27 @@
1
+ import { Observable } from 'rxjs';
2
+ import { SpatialSelectionMask } from '../spatial/spatial-selection';
3
+ import * as i0 from "@angular/core";
4
+ /**
5
+ * The set of currently-selected spatial observations, shared the way
6
+ * `RegionStore` shares regions: one store, many writers (an ROI selection, a
7
+ * legend click, a future chart brush), one reader per consumer.
8
+ *
9
+ * Deliberately thin — every rule about WHICH observations are selected lives in
10
+ * the pure `spatial-selection.ts`; this only holds the answer and announces it.
11
+ */
12
+ export declare class SpatialSelectionStore {
13
+ private readonly selection$;
14
+ /** Emits on every change, including a clear. */
15
+ getSelection$(): Observable<SpatialSelectionMask>;
16
+ /** Synchronous read, for a renderer building its first frame. */
17
+ current(): SpatialSelectionMask;
18
+ /** True when nothing is selected — the state in which nothing is muted. */
19
+ isEmpty(): boolean;
20
+ /** Replace the selection. An empty result is published like any other, so a
21
+ * "selected nothing" ROI visibly clears rather than silently keeping the
22
+ * previous selection on screen. */
23
+ set(selection: SpatialSelectionMask): void;
24
+ clear(): void;
25
+ static ɵfac: i0.ɵɵFactoryDeclaration<SpatialSelectionStore, never>;
26
+ static ɵprov: i0.ɵɵInjectableDeclaration<SpatialSelectionStore>;
27
+ }
@@ -4,6 +4,7 @@ import { ClassPreset, PresetSet } from '../models/class-preset';
4
4
  import { PreferencesPort } from '../contracts/ports/preferences.port';
5
5
  import { IImageMetadata } from '../contracts/image.contract';
6
6
  import { IChannelState } from '../contracts/channel-histogram-api.contract';
7
+ import { SpatialViewState } from '../contracts/display-types';
7
8
  import * as i0 from "@angular/core";
8
9
  /**
9
10
  * Backend-neutral store for the **visualization session** — the state that
@@ -43,6 +44,10 @@ export declare class VisualizerStore {
43
44
  private readonly savePresets$;
44
45
  private presetsLoaded;
45
46
  private readonly activeTool$;
47
+ /** Spatial-omics display state (colour source, point size, alpha, scaling).
48
+ * Held here, next to the colormap/channel state, so the active backend reacts
49
+ * to an edit exactly as it does for an image. */
50
+ private readonly spatialView$;
46
51
  constructor(http?: HttpClient | null, prefsPort?: PreferencesPort | null);
47
52
  /**
48
53
  * Resolve the colormap tree's LUT keys (e.g. "GREYS_LUT") to their colour
@@ -122,6 +127,14 @@ export declare class VisualizerStore {
122
127
  resetPresets(): void;
123
128
  /** Force a (debounced) write-back of the current preset set. */
124
129
  persistPresets(): void;
130
+ getSpatialView$(): Observable<SpatialViewState>;
131
+ /** Synchronous read, for a renderer building its first frame. */
132
+ currentSpatialView(): SpatialViewState;
133
+ /** Patch the spatial view state; emits once with the merged value. */
134
+ setSpatialView(partial: Partial<SpatialViewState>): void;
135
+ /** Reset to defaults — e.g. when a different dataset is selected, where the
136
+ * previous colour column almost certainly does not exist. */
137
+ resetSpatialView(): void;
125
138
  getActiveTool$(): Observable<string | null>;
126
139
  getActiveTool(): string | null;
127
140
  setActiveTool(tool: string | null): void;
@@ -30,6 +30,13 @@ export declare class ToolbarComponent implements OnChanges {
30
30
  zIndex: number;
31
31
  /** True for any 2D (non Surface-3D) plot type. */
32
32
  isHeatmap: boolean;
33
+ /**
34
+ * Region tools apply even though this is a 3D view, because they draw a
35
+ * SCREEN-SPACE lasso (the 3D spatial cloud). Separate from {@link isHeatmap},
36
+ * which means "2D view" and also drives the zoom tools and the 3D camera
37
+ * controls — flipping that would show 2D pan/zoom and hide the orbit controls.
38
+ */
39
+ is3dRegions: boolean;
33
40
  activeDragMode: string | null;
34
41
  activeSurface3dMode: string;
35
42
  /** Whether the napari 3D axes/scale gizmo is currently shown (drives the toggle's look). */
@@ -83,6 +90,8 @@ export declare class ToolbarComponent implements OnChanges {
83
90
  isoRangeChange: EventEmitter<number[] | undefined>;
84
91
  /** Open the Channels & Histogram dialog (brightness/contrast, colormap, …). */
85
92
  openChannelHistogram: EventEmitter<void>;
93
+ /** Open the spatial-omics controls (colour-by, legend, point display). */
94
+ openSpatialControls: EventEmitter<void>;
86
95
  openRegionEditor: EventEmitter<void>;
87
96
  selectStackOption: EventEmitter<{
88
97
  name: string;
@@ -190,6 +199,11 @@ export declare class ToolbarComponent implements OnChanges {
190
199
  /** The Image plot type renders as a natively pan/zoom-able raster, so the
191
200
  * generic zoom/pan tools are hidden. */
192
201
  get isImageView(): boolean;
202
+ /** Either spatial-omics mode is active, so its controls are worth offering.
203
+ * The 3D cloud needs the panel MORE than the 2D view does, not less: colouring,
204
+ * the legend and category show/hide are the only way to make sense of a
205
+ * million overlapping points. */
206
+ get isSpatialMode(): boolean;
193
207
  /** A plot-type icon is a PrimeNG font glyph (e.g. `pi pi-image`) rather than an
194
208
  * SVG asset path — drives which element the selector item template renders. */
195
209
  isPiIcon(icon: string | undefined): boolean;
@@ -197,8 +211,10 @@ export declare class ToolbarComponent implements OnChanges {
197
211
  * (jit-ui#102). Gates the polygon / add-vertex / delete-vertex / Bézier-convert tools. */
198
212
  get supportsRegionVertexTools(): boolean;
199
213
  /** Plot types that scrub a z-stack live (the renderer swaps the slice in place): the OSD Image
200
- * view, the napari-js WebGPU image, and the napari-js surface — which rebuilds the height field
201
- * from the picked slice (jit-ui#102). Drives the per-slice slider. */
214
+ * view, the napari-js WebGPU image, the napari-js surface — which rebuilds the height field
215
+ * from the picked slice (jit-ui#102) — and the 2D spatial-omics view, where a 3D dataset's
216
+ * registered volume is the image and the slider picks the section whose observations are
217
+ * drawn. Drives the per-slice slider. */
202
218
  get showsLiveSliceScrubber(): boolean;
203
219
  /** Any napari-js WebGPU plot type. The single-image/stack toggle + slice-number field are
204
220
  * Plotly-only stack controls, so they're hidden for napari (jit-ui#102). */
@@ -214,5 +230,5 @@ export declare class ToolbarComponent implements OnChanges {
214
230
  get isIntensityCapable(): boolean;
215
231
  showHelp(): void;
216
232
  static ɵfac: i0.ɵɵFactoryDeclaration<ToolbarComponent, never>;
217
- static ɵcmp: i0.ɵɵComponentDeclaration<ToolbarComponent, "plotting-toolbar", never, { "imageInfo": { "alias": "imageInfo"; "required": false; }; "plotTypeOptions": { "alias": "plotTypeOptions"; "required": false; }; "selectedPlotType": { "alias": "selectedPlotType"; "required": false; }; "isoRange": { "alias": "isoRange"; "required": false; }; "maxIndex": { "alias": "maxIndex"; "required": false; }; "zIndex": { "alias": "zIndex"; "required": false; }; "isHeatmap": { "alias": "isHeatmap"; "required": false; }; "activeDragMode": { "alias": "activeDragMode"; "required": false; }; "activeSurface3dMode": { "alias": "activeSurface3dMode"; "required": false; }; "axesVisible": { "alias": "axesVisible"; "required": false; }; "wireframeActive": { "alias": "wireframeActive"; "required": false; }; "resolutionScale": { "alias": "resolutionScale"; "required": false; }; "wandSensitivity": { "alias": "wandSensitivity"; "required": false; }; "brushSize": { "alias": "brushSize"; "required": false; }; "vertexEraserRadius": { "alias": "vertexEraserRadius"; "required": false; }; "canUndo": { "alias": "canUndo"; "required": false; }; "canRedo": { "alias": "canRedo"; "required": false; }; "stackOptions": { "alias": "stackOptions"; "required": false; }; "tools": { "alias": "tools"; "required": false; }; "imageSmoothingEnabled": { "alias": "imageSmoothingEnabled"; "required": false; }; "samModels": { "alias": "samModels"; "required": false; }; "samModelId": { "alias": "samModelId"; "required": false; }; "contributedTools": { "alias": "contributedTools"; "required": false; }; "toolModelIds": { "alias": "toolModelIds"; "required": false; }; }, { "selectPlotType": "selectPlotType"; "addProfileLine": "addProfileLine"; "toggleImageSmoothing": "toggleImageSmoothing"; "isoRangeChange": "isoRangeChange"; "openChannelHistogram": "openChannelHistogram"; "openRegionEditor": "openRegionEditor"; "selectStackOption": "selectStackOption"; "zScrub": "zScrub"; "zSlide": "zSlide"; "zIndexInput": "zIndexInput"; "reloadAndPlot": "reloadAndPlot"; "downloadImage": "downloadImage"; "autoscaleImage": "autoscaleImage"; "toggleDragMode": "toggleDragMode"; "zoomIn": "zoomIn"; "zoomOut": "zoomOut"; "toggleSurface3dMode": "toggleSurface3dMode"; "resetSurfaceCamera": "resetSurfaceCamera"; "toggleAxes": "toggleAxes"; "toggleWireframe": "toggleWireframe"; "selectResolution": "selectResolution"; "deleteRegion": "deleteRegion"; "undoRegion": "undoRegion"; "redoRegion": "redoRegion"; "segmentRegions": "segmentRegions"; "segmentCellpose": "segmentCellpose"; "runTool": "runTool"; "toolModelChange": "toolModelChange"; "openToolParams": "openToolParams"; "samModelChange": "samModelChange"; "wandSensitivityChange": "wandSensitivityChange"; "brushSizeChange": "brushSizeChange"; "vertexEraserRadiusChange": "vertexEraserRadiusChange"; }, never, ["*"], false, never>;
233
+ static ɵcmp: i0.ɵɵComponentDeclaration<ToolbarComponent, "plotting-toolbar", never, { "imageInfo": { "alias": "imageInfo"; "required": false; }; "plotTypeOptions": { "alias": "plotTypeOptions"; "required": false; }; "selectedPlotType": { "alias": "selectedPlotType"; "required": false; }; "isoRange": { "alias": "isoRange"; "required": false; }; "maxIndex": { "alias": "maxIndex"; "required": false; }; "zIndex": { "alias": "zIndex"; "required": false; }; "isHeatmap": { "alias": "isHeatmap"; "required": false; }; "is3dRegions": { "alias": "is3dRegions"; "required": false; }; "activeDragMode": { "alias": "activeDragMode"; "required": false; }; "activeSurface3dMode": { "alias": "activeSurface3dMode"; "required": false; }; "axesVisible": { "alias": "axesVisible"; "required": false; }; "wireframeActive": { "alias": "wireframeActive"; "required": false; }; "resolutionScale": { "alias": "resolutionScale"; "required": false; }; "wandSensitivity": { "alias": "wandSensitivity"; "required": false; }; "brushSize": { "alias": "brushSize"; "required": false; }; "vertexEraserRadius": { "alias": "vertexEraserRadius"; "required": false; }; "canUndo": { "alias": "canUndo"; "required": false; }; "canRedo": { "alias": "canRedo"; "required": false; }; "stackOptions": { "alias": "stackOptions"; "required": false; }; "tools": { "alias": "tools"; "required": false; }; "imageSmoothingEnabled": { "alias": "imageSmoothingEnabled"; "required": false; }; "samModels": { "alias": "samModels"; "required": false; }; "samModelId": { "alias": "samModelId"; "required": false; }; "contributedTools": { "alias": "contributedTools"; "required": false; }; "toolModelIds": { "alias": "toolModelIds"; "required": false; }; }, { "selectPlotType": "selectPlotType"; "addProfileLine": "addProfileLine"; "toggleImageSmoothing": "toggleImageSmoothing"; "isoRangeChange": "isoRangeChange"; "openChannelHistogram": "openChannelHistogram"; "openSpatialControls": "openSpatialControls"; "openRegionEditor": "openRegionEditor"; "selectStackOption": "selectStackOption"; "zScrub": "zScrub"; "zSlide": "zSlide"; "zIndexInput": "zIndexInput"; "reloadAndPlot": "reloadAndPlot"; "downloadImage": "downloadImage"; "autoscaleImage": "autoscaleImage"; "toggleDragMode": "toggleDragMode"; "zoomIn": "zoomIn"; "zoomOut": "zoomOut"; "toggleSurface3dMode": "toggleSurface3dMode"; "resetSurfaceCamera": "resetSurfaceCamera"; "toggleAxes": "toggleAxes"; "toggleWireframe": "toggleWireframe"; "selectResolution": "selectResolution"; "deleteRegion": "deleteRegion"; "undoRegion": "undoRegion"; "redoRegion": "redoRegion"; "segmentRegions": "segmentRegions"; "segmentCellpose": "segmentCellpose"; "runTool": "runTool"; "toolModelChange": "toolModelChange"; "openToolParams": "openToolParams"; "samModelChange": "samModelChange"; "wandSensitivityChange": "wandSensitivityChange"; "brushSizeChange": "brushSizeChange"; "vertexEraserRadiusChange": "vertexEraserRadiusChange"; }, never, ["*"], false, never>;
218
234
  }
@@ -4,29 +4,33 @@ import * as i2 from "./toolbar/toolbar.component";
4
4
  import * as i3 from "./region-editor/region-editor.component";
5
5
  import * as i4 from "./hex-color-picker/hex-color-picker.component";
6
6
  import * as i5 from "./channel-histogram/channel-histogram.component";
7
- import * as i6 from "@angular/common";
8
- import * as i7 from "@angular/forms";
9
- import * as i8 from "primeng/toolbar";
10
- import * as i9 from "primeng/button";
11
- import * as i10 from "primeng/menu";
12
- import * as i11 from "primeng/dropdown";
13
- import * as i12 from "primeng/slider";
14
- import * as i13 from "primeng/treeselect";
15
- import * as i14 from "primeng/inputnumber";
16
- import * as i15 from "primeng/dialog";
17
- import * as i16 from "primeng/contextmenu";
18
- import * as i17 from "primeng/progressbar";
19
- import * as i18 from "primeng/toast";
20
- import * as i19 from "primeng/tooltip";
21
- import * as i20 from "primeng/ripple";
22
- import * as i21 from "primeng/table";
23
- import * as i22 from "primeng/paginator";
24
- import * as i23 from "primeng/overlaypanel";
25
- import * as i24 from "primeng/confirmdialog";
26
- import * as i25 from "primeng/inputtext";
27
- import * as i26 from "primeng/checkbox";
28
- import * as i27 from "primeng/radiobutton";
29
- import * as i28 from "primeng/selectbutton";
7
+ import * as i6 from "./spatial-controls/spatial-controls.component";
8
+ import * as i7 from "./spatial-controls/spatial-charts/spatial-charts.component";
9
+ import * as i8 from "@angular/common";
10
+ import * as i9 from "@angular/forms";
11
+ import * as i10 from "primeng/toolbar";
12
+ import * as i11 from "primeng/button";
13
+ import * as i12 from "primeng/menu";
14
+ import * as i13 from "primeng/dropdown";
15
+ import * as i14 from "primeng/slider";
16
+ import * as i15 from "primeng/treeselect";
17
+ import * as i16 from "primeng/inputnumber";
18
+ import * as i17 from "primeng/dialog";
19
+ import * as i18 from "primeng/contextmenu";
20
+ import * as i19 from "primeng/progressbar";
21
+ import * as i20 from "primeng/toast";
22
+ import * as i21 from "primeng/tooltip";
23
+ import * as i22 from "primeng/ripple";
24
+ import * as i23 from "primeng/table";
25
+ import * as i24 from "primeng/paginator";
26
+ import * as i25 from "primeng/overlaypanel";
27
+ import * as i26 from "primeng/confirmdialog";
28
+ import * as i27 from "primeng/inputtext";
29
+ import * as i28 from "primeng/checkbox";
30
+ import * as i29 from "primeng/multiselect";
31
+ import * as i30 from "primeng/radiobutton";
32
+ import * as i31 from "primeng/selectbutton";
33
+ import * as i32 from "primeng/autocomplete";
30
34
  /**
31
35
  * Self-contained plotting UI: the {@link VisualizerComponent} (plot surface
32
36
  * + render orchestration), its {@link ToolbarComponent}, and the
@@ -40,6 +44,6 @@ import * as i28 from "primeng/selectbutton";
40
44
  */
41
45
  export declare class VisualizationModule {
42
46
  static ɵfac: i0.ɵɵFactoryDeclaration<VisualizationModule, never>;
43
- static ɵmod: i0.ɵɵNgModuleDeclaration<VisualizationModule, [typeof i1.VisualizerComponent, typeof i2.ToolbarComponent, typeof i3.RegionEditorComponent, typeof i4.HexColorPickerComponent, typeof i5.ChannelHistogramComponent], [typeof i6.CommonModule, typeof i7.FormsModule, typeof i8.ToolbarModule, typeof i9.ButtonModule, typeof i10.MenuModule, typeof i11.DropdownModule, typeof i12.SliderModule, typeof i13.TreeSelectModule, typeof i14.InputNumberModule, typeof i15.DialogModule, typeof i16.ContextMenuModule, typeof i17.ProgressBarModule, typeof i18.ToastModule, typeof i19.TooltipModule, typeof i20.RippleModule, typeof i21.TableModule, typeof i22.PaginatorModule, typeof i23.OverlayPanelModule, typeof i24.ConfirmDialogModule, typeof i25.InputTextModule, typeof i26.CheckboxModule, typeof i27.RadioButtonModule, typeof i28.SelectButtonModule], [typeof i1.VisualizerComponent, typeof i3.RegionEditorComponent, typeof i4.HexColorPickerComponent, typeof i5.ChannelHistogramComponent]>;
47
+ static ɵmod: i0.ɵɵNgModuleDeclaration<VisualizationModule, [typeof i1.VisualizerComponent, typeof i2.ToolbarComponent, typeof i3.RegionEditorComponent, typeof i4.HexColorPickerComponent, typeof i5.ChannelHistogramComponent, typeof i6.SpatialControlsComponent, typeof i7.SpatialChartsComponent], [typeof i8.CommonModule, typeof i9.FormsModule, typeof i10.ToolbarModule, typeof i11.ButtonModule, typeof i12.MenuModule, typeof i13.DropdownModule, typeof i14.SliderModule, typeof i15.TreeSelectModule, typeof i16.InputNumberModule, typeof i17.DialogModule, typeof i18.ContextMenuModule, typeof i19.ProgressBarModule, typeof i20.ToastModule, typeof i21.TooltipModule, typeof i22.RippleModule, typeof i23.TableModule, typeof i24.PaginatorModule, typeof i25.OverlayPanelModule, typeof i26.ConfirmDialogModule, typeof i27.InputTextModule, typeof i28.CheckboxModule, typeof i29.MultiSelectModule, typeof i30.RadioButtonModule, typeof i31.SelectButtonModule, typeof i32.AutoCompleteModule], [typeof i1.VisualizerComponent, typeof i3.RegionEditorComponent, typeof i4.HexColorPickerComponent, typeof i5.ChannelHistogramComponent, typeof i6.SpatialControlsComponent, typeof i7.SpatialChartsComponent]>;
44
48
  static ɵinj: i0.ɵɵInjectorDeclaration<VisualizationModule>;
45
49
  }
@@ -15,6 +15,7 @@ import { CellSegmentToolService } from './toolbar/segmentation/cell-segment-tool
15
15
  import { NumberParamSpec, SelectParamSpec, ToolParamSpec, ToolbarToolContribution } from './contracts/toolbar-tool.contract';
16
16
  import { ToolbarToolVisibility } from './contracts/toolbar-config';
17
17
  import { VizConfig } from './contracts/viz-config';
18
+ import { SpatialDataPort } from './contracts/ports/spatial-data.port';
18
19
  import * as i0 from "@angular/core";
19
20
  export declare class VisualizerComponent implements OnInit, OnChanges, AfterViewInit, OnDestroy {
20
21
  private state;
@@ -28,6 +29,7 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
28
29
  private samPointTool;
29
30
  private regionOps;
30
31
  private vizConfig?;
32
+ private spatialData?;
31
33
  /** Per-instance toast key. MessageService is a global singleton, so two live
32
34
  * `<visualizer>` instances (main viewer + pipeline-dialog preview) sharing
33
35
  * one key would each render the same message — a duplicate toast. A unique
@@ -177,6 +179,8 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
177
179
  selectedColormap: any;
178
180
  /** Channels & Histogram dialog visibility (opened from the toolbar). */
179
181
  showChannelHistogram: boolean;
182
+ /** Spatial-omics controls dialog visibility (toolbar button). */
183
+ showSpatialControls: boolean;
180
184
  /** Region Editor dialog visibility (opened from the toolbar). */
181
185
  showRegionEditor: boolean;
182
186
  /** Region Editor dialog width. On open it is set to the configured host
@@ -230,6 +234,10 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
230
234
  private profileDragMoveListener?;
231
235
  private profileDragUpListener?;
232
236
  private profileResizeListener?;
237
+ /** True while the 3D spatial cloud is the active mode. The controls drop the ROI
238
+ * selection there: region tools are screen-space, and against an orbiting
239
+ * camera a drawn rectangle has no fixed meaning in the data. */
240
+ get isSpatial3dMode(): boolean;
233
241
  /** LINE plot type shows the image + draggable line ROI + intensity inset. */
234
242
  get isProfileMode(): boolean;
235
243
  /** True for the Image plot type, which renders as a natively pan/zoom-able
@@ -239,6 +247,24 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
239
247
  /** Isosurface band as a 0–255 slider position, mapped onto the volume's real
240
248
  * intensity range by the renderer. Defaults to the full range. */
241
249
  isoRange: number[];
250
+ /** Whether a spatial-omics dataset is currently published on
251
+ * `SPATIAL_DATA_PORT` — gates the spatial plot types in the selector. */
252
+ private hasSpatialDataset;
253
+ /** Whether that dataset's observations carry a z, gating the 3D spatial mode. */
254
+ private hasSpatial3dDataset;
255
+ /** Whether it carries a registered volume. Change detection only: a volume
256
+ * appearing is what publishes it as the image, and the plot-type gates then
257
+ * see an ordinary grayscale stack. */
258
+ private hasSpatialVolume;
259
+ /** Dataset identity + capability shape the last emission was handled at, so a
260
+ * switch between two datasets of the same shape is not mistaken for a repeat. */
261
+ private spatialDatasetKey;
262
+ /** Dataset + geometry the currently published volume image was built from, so a
263
+ * re-emitted dataset doesn't re-fetch megabytes or reset the user's scrub. */
264
+ private volumeImageKey;
265
+ /** Blob URLs backing that image — ours to revoke. */
266
+ private volumeImageUrls;
267
+ private spatialDatasetSubscription?;
242
268
  plotWidthSubscription?: Subscription;
243
269
  imageLoadingSubscription?: Subscription;
244
270
  imgLoadingMessageSubscription?: Subscription;
@@ -250,7 +276,7 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
250
276
  private wheelListener?;
251
277
  /** Debounced z-slice scrubbing (see SliceScrubber — refactoring plan Step 7). */
252
278
  private readonly scrubber;
253
- constructor(state: ImageStatePort, plotService: IVisualizer, messageService: MessageService, ngZone: NgZone, cdr: ChangeDetectorRef, session: VisualizerStore, samTool: SamToolService, cellSegmentTool: CellSegmentToolService, samPointTool: SamPointToolService, regionOps: RegionOpsService, vizConfig?: VizConfig | undefined, toolContributions?: readonly ToolbarToolContribution[]);
279
+ constructor(state: ImageStatePort, plotService: IVisualizer, messageService: MessageService, ngZone: NgZone, cdr: ChangeDetectorRef, session: VisualizerStore, samTool: SamToolService, cellSegmentTool: CellSegmentToolService, samPointTool: SamPointToolService, regionOps: RegionOpsService, vizConfig?: VizConfig | undefined, toolContributions?: readonly ToolbarToolContribution[], spatialData?: SpatialDataPort | undefined);
254
280
  /**
255
281
  * Plot types offered in the selector for the current image:
256
282
  * - Outside **test mode**, only the curated default set (descriptors with a
@@ -263,12 +289,55 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
263
289
  * - scalar-intensity types (contour, surface, isosurface) hidden for RGB
264
290
  * images — they map a single intensity per pixel. Image and Heatmap render
265
291
  * any image.
292
+ * - spatial-omics types hidden until a `SpatialDataset` is published on
293
+ * `SPATIAL_DATA_PORT` — the mode has nothing to draw without observations,
294
+ * exactly as a volume has nothing to draw without a stack.
266
295
  */
296
+ /**
297
+ * Track whether a spatial-omics dataset is available. A dataset appearing (or
298
+ * being cleared) changes which plot types make sense, so it drives the same
299
+ * recompute + reconcile that the image stream and the test-mode toggle do.
300
+ *
301
+ * No-op when the host provides no `SPATIAL_DATA_PORT` — the spatial types then
302
+ * stay hidden for the life of the component.
303
+ */
304
+ private watchSpatialDataset;
305
+ /**
306
+ * Publish a dataset's reference volume AS the image, and open the 2D Image view
307
+ * on it.
308
+ *
309
+ * The volume is a 3D image delivered in one file, so for a dataset that has no
310
+ * section image it is the honest thing to put on screen: the slice bar scrubs z,
311
+ * and the contrast window, colormaps and region tools all work because the
312
+ * volume genuinely is the image now. The 3D cloud stays one menu pick away.
313
+ *
314
+ * Keyed by dataset + geometry: the dataset stream re-emits on things like a
315
+ * colour-column change, and rebuilding then would re-fetch the voxels and throw
316
+ * the user back to the middle slice.
317
+ */
318
+ private showVolumeAsImage;
319
+ /** Forget the published volume image so re-selecting the dataset rebuilds it.
320
+ * The URLs deliberately stay alive: the host may still be displaying that image
321
+ * at this instant, and revoking under it would break every later tile read. They
322
+ * are freed when the next volume replaces them, or on destroy. */
323
+ private dropVolumeImage;
324
+ private revokeVolumeImageUrls;
267
325
  private computePlotTypeOptions;
268
326
  ngOnChanges(changes: SimpleChanges): void;
269
327
  /** If the active plot type is no longer in the offered options (e.g. test mode
270
328
  * turned off while a test-only type was active, or a scalar type carried onto
271
329
  * an RGB image), fall back to the default 2D Image view. */
330
+ /** Whether the live spatial dataset brings pixels of its own — a tissue image it
331
+ * registers onto, or a volume that is published as a z-stack image. */
332
+ private spatialDatasetHasPixels;
333
+ /**
334
+ * The host published NO image — a spatial dataset that brings none does this.
335
+ *
336
+ * Without handling the empty emission, `imageInfo` keeps the LAST image's metadata: the
337
+ * selector goes on offering pixel modes for pixels that are gone, and every
338
+ * `isStack`/`isGrayscale` decision is made against a file that is no longer loaded.
339
+ */
340
+ private onImageCleared;
272
341
  private reconcileSelectedPlotType;
273
342
  ngOnInit(): void;
274
343
  ngAfterViewInit(): void;
@@ -302,6 +371,8 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
302
371
  selectColormap(colormapNode: TreeNode): void;
303
372
  /** Open the Channels & Histogram dialog (toolbar button). */
304
373
  openChannelHistogram(): void;
374
+ /** Toolbar → open the spatial-omics controls. */
375
+ openSpatialControls(): void;
305
376
  /** Open the Region Editor dialog (toolbar button). The editor stays linked to
306
377
  * this plotting instance through the root RegionStore singleton, so it works
307
378
  * the same as the former right-panel Regions tab. */
@@ -476,6 +547,6 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
476
547
  onSelectPlotType(type: PlotType): void;
477
548
  /** Deactivate whatever tool is armed and clear every tool mode. */
478
549
  private deactivateActiveTool;
479
- static ɵfac: i0.ɵɵFactoryDeclaration<VisualizerComponent, [null, null, null, null, null, null, null, null, null, null, { optional: true; }, { optional: true; }]>;
550
+ static ɵfac: i0.ɵɵFactoryDeclaration<VisualizerComponent, [null, null, null, null, null, null, null, null, null, null, { optional: true; }, { optional: true; }, { optional: true; }]>;
480
551
  static ɵcmp: i0.ɵɵComponentDeclaration<VisualizerComponent, "visualizer", never, { "toolbarTools": { "alias": "toolbarTools"; "required": false; }; "testMode": { "alias": "testMode"; "required": false; }; }, { "isStackEvent": "isStackEvent"; "isGrayscaleEvent": "isGrayscaleEvent"; }, never, ["*"], false, never>;
481
552
  }
package/package.json CHANGED
@@ -1,6 +1,9 @@
1
1
  {
2
2
  "name": "@jax-data-science/sci-image-visualizer",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
+ "engines": {
5
+ "node": ">=20.9.0"
6
+ },
4
7
  "description": "Browser-based scientific image visualization for Angular — gigapixel whole-slide (OpenSeadragon), 2D/3D (Plotly) and WebGPU (napari-js) rendering, with region/annotation editing and in-browser SAM segmentation.",
5
8
  "keywords": [
6
9
  "angular",
@@ -34,9 +37,10 @@
34
37
  "access": "public"
35
38
  },
36
39
  "dependencies": {
37
- "cellpose-js": "^0.5.0",
40
+ "@jax-js/jax": "^0.1.24",
41
+ "cellpose-js": "^0.6.0",
38
42
  "fast-png": "^6.4.0",
39
- "napari-js": "^0.11.0",
43
+ "napari-js": "^0.14.0",
40
44
  "openseadragon": "^6.0.2",
41
45
  "plotly.js-dist-min": "^3.0.1",
42
46
  "tslib": "^2.3.0"