@jax-data-science/sci-image-visualizer 0.6.0 → 0.7.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 (29) hide show
  1. package/esm2022/lib/contracts/display-types.mjs +28 -1
  2. package/esm2022/lib/contracts/ports/spatial-data.port.mjs +3 -1
  3. package/esm2022/lib/contracts/spatial-dataset.contract.mjs +3 -1
  4. package/esm2022/lib/contracts/visualizer.contract.mjs +1 -1
  5. package/esm2022/lib/implementations/napari-js/napari-navigator.mjs +195 -0
  6. package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +10 -2
  7. package/esm2022/lib/implementations/napari-js/napari-spatial-tiles.mjs +1098 -0
  8. package/esm2022/lib/implementations/napari-js/napari-spatial-tooltip.mjs +6 -3
  9. package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +185 -8
  10. package/esm2022/lib/implementations/spatial-data-http/spatial-data-http.service.mjs +120 -2
  11. package/esm2022/lib/implementations/spatial-data-http/spatial-wire.mjs +76 -1
  12. package/esm2022/lib/routing-visualizer.service.mjs +10 -1
  13. package/esm2022/lib/spatial/spatial-tiles.mjs +506 -0
  14. package/esm2022/lib/spatial-controls/spatial-controls.component.mjs +659 -19
  15. package/fesm2022/jax-data-science-sci-image-visualizer.mjs +2909 -62
  16. package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
  17. package/lib/contracts/display-types.d.ts +94 -1
  18. package/lib/contracts/ports/spatial-data.port.d.ts +59 -1
  19. package/lib/contracts/spatial-dataset.contract.d.ts +167 -0
  20. package/lib/contracts/visualizer.contract.d.ts +22 -1
  21. package/lib/implementations/napari-js/napari-navigator.d.ts +72 -0
  22. package/lib/implementations/napari-js/napari-region-overlay.d.ts +3 -0
  23. package/lib/implementations/napari-js/napari-spatial-tiles.d.ts +240 -0
  24. package/lib/implementations/napari-js/napari-visualizer.service.d.ts +42 -2
  25. package/lib/implementations/spatial-data-http/spatial-data-http.service.d.ts +26 -2
  26. package/lib/implementations/spatial-data-http/spatial-wire.d.ts +34 -1
  27. package/lib/spatial/spatial-tiles.d.ts +222 -0
  28. package/lib/spatial-controls/spatial-controls.component.d.ts +234 -3
  29. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
- import { EventEmitter, OnDestroy, OnInit } from '@angular/core';
1
+ import { EventEmitter, NgZone, OnDestroy, OnInit } from '@angular/core';
2
2
  import { IVisualizer, ISpatialControls } from '../contracts/visualizer.contract';
3
3
  import { SpatialDataset } from '../contracts/spatial-dataset.contract';
4
- import { ColormapNode, SpatialViewState } from '../contracts/display-types';
4
+ import { ColormapNode, SpatialViewState, TranscriptGlyphName } from '../contracts/display-types';
5
5
  import { SpatialSelectionMask } from '../spatial/spatial-selection';
6
6
  import * as i0 from "@angular/core";
7
7
  /** One legend row for a categorical colouring. */
@@ -9,6 +9,14 @@ export interface SpatialLegendEntry {
9
9
  label: string;
10
10
  color: string;
11
11
  }
12
+ /**
13
+ * `group,gene` rows (CSV or TSV, header optional) → gene groups, in file order.
14
+ * Exported for tests.
15
+ */
16
+ export declare function parseGeneGroups(text: string): {
17
+ name: string;
18
+ genes: string[];
19
+ }[];
12
20
  /**
13
21
  * Spatial-omics controls: a non-modal, resizable, draggable dialog for choosing
14
22
  * what the observation markers are coloured by, and how they are drawn.
@@ -25,6 +33,7 @@ export interface SpatialLegendEntry {
25
33
  */
26
34
  export declare class SpatialControlsComponent implements OnInit, OnDestroy {
27
35
  private readonly viz;
36
+ private readonly zone;
28
37
  /** The embedded chart, so a dialog resize can re-fit it. */
29
38
  private charts?;
30
39
  /**
@@ -130,7 +139,7 @@ export declare class SpatialControlsComponent implements OnInit, OnDestroy {
130
139
  private geneSearchToken;
131
140
  private keyToken;
132
141
  private readonly subs;
133
- constructor(viz: IVisualizer);
142
+ constructor(viz: IVisualizer, zone: NgZone);
134
143
  ngOnInit(): void;
135
144
  ngOnDestroy(): void;
136
145
  onVisibleChange(value: boolean): void;
@@ -145,6 +154,23 @@ export declare class SpatialControlsComponent implements OnInit, OnDestroy {
145
154
  * hop further away.
146
155
  */
147
156
  onGeneFilter(query: string): Promise<void>;
157
+ /**
158
+ * Opening either gene dropdown — "Colour by gene" or the transcript genes. Both share
159
+ * one list and one search, so each opens on the head of the full list rather than on
160
+ * whatever the other was last filtered to.
161
+ *
162
+ * A whole-transcriptome dataset does not inline its ~30k names; they are fetched here,
163
+ * once, on first open — the list is then resident and every keystroke filters locally,
164
+ * exactly as for a targeted panel. Until it arrives the dropdown falls back to asking
165
+ * the server per keystroke.
166
+ */
167
+ ensureGeneList(): Promise<void>;
168
+ /** Most names fetched for the lazy list — well above any panel, whole-transcriptome included. */
169
+ private static readonly GENE_LIST_MAX;
170
+ private geneListLoading;
171
+ /** Options for `names`, plus the genes already chosen (a multi-select shows a chip only
172
+ * for a value it can find among its options). */
173
+ private withSelected;
148
174
  /** A gene was picked; it supersedes any column selection. */
149
175
  onGene(name: string | null): void;
150
176
  /** Select every observation inside the drawn regions (their union). */
@@ -221,6 +247,211 @@ export declare class SpatialControlsComponent implements OnInit, OnDestroy {
221
247
  /** Mirrors the renderer's cap, for the note only. */
222
248
  private static readonly DENSITY_MAX_CLUSTERS;
223
249
  onClip(value: [number, number]): void;
250
+ readonly cellDrawOptions: {
251
+ label: string;
252
+ value: string;
253
+ }[];
254
+ readonly transcriptColorOptions: {
255
+ label: string;
256
+ value: string;
257
+ }[];
258
+ readonly glyphOptions: {
259
+ label: string;
260
+ value: TranscriptGlyphName;
261
+ points: string;
262
+ }[];
263
+ readonly budgetOptions: {
264
+ label: string;
265
+ value: number;
266
+ }[];
267
+ /** Boundaries on offer: tiled (level-of-detail) or whole-dataset rings. */
268
+ get hasCells(): boolean;
269
+ get hasTranscripts(): boolean;
270
+ /** Whether outlines are on — the explicit choice, or automatically for data that has them. */
271
+ get cellsOn(): boolean;
272
+ get activeCellSet(): string | null;
273
+ /**
274
+ * Option lists for the cell/transcript controls. Built once per dataset rather than in
275
+ * getters: a getter returns a fresh array on every change-detection pass, and PrimeNG
276
+ * re-renders its buttons whenever the array identity changes — which made them
277
+ * impossible to click.
278
+ */
279
+ cellSetOptions: {
280
+ label: string;
281
+ value: string;
282
+ }[];
283
+ transcriptModeOptions: {
284
+ label: string;
285
+ value: SpatialViewState['transcriptMode'];
286
+ }[];
287
+ private buildTileOptions;
288
+ /** Cell colour modes on offer (Xenium Explorer's "Cell Color"). */
289
+ cellColorOptions: {
290
+ label: string;
291
+ value: SpatialViewState['cellColorMode'];
292
+ }[];
293
+ /**
294
+ * The group picker: categorical columns under their section heading, a family of
295
+ * variants (k-means at k = 2…10) listed once. Values are a column name, or
296
+ * `family:<id>` for a family.
297
+ */
298
+ groupOptions: {
299
+ label: string;
300
+ items: {
301
+ label: string;
302
+ value: string;
303
+ }[];
304
+ }[];
305
+ /** Variants of the active family (k = 2…10), when a family is active. */
306
+ groupVariantOptions: {
307
+ label: string;
308
+ value: string;
309
+ }[];
310
+ /** The last variant chosen per family, so switching away and back keeps k. */
311
+ private familyChoice;
312
+ private buildGroupOptions;
313
+ /** The picker's value for the active group column. */
314
+ get activeGroupEntry(): string | null;
315
+ private refreshVariants;
316
+ onGroupEntry(value: string): void;
317
+ onGroupVariant(name: string): void;
318
+ /** The active grouping's categories with colours and cell counts, largest first. */
319
+ groupRows: {
320
+ label: string;
321
+ color: string;
322
+ count: number;
323
+ }[];
324
+ groupTotal: number;
325
+ groupsExpanded: boolean;
326
+ private groupRowsFor;
327
+ groupImportError: string | null;
328
+ groupImporting: boolean;
329
+ private refreshGroups;
330
+ isGroupShown(label: string): boolean;
331
+ get allGroupsShown(): boolean;
332
+ onGroupShown(label: string, on: boolean): void;
333
+ onAllGroupsShown(on: boolean): void;
334
+ get canImportGroups(): boolean;
335
+ /** '+': a CSV/TSV of `cell_id` and group, named after the file. */
336
+ onImportGroupsFile(input: HTMLInputElement): Promise<void>;
337
+ get activeCellTypeColumn(): string | null;
338
+ onShowCells(on: boolean): void;
339
+ onCellSet(set: string): void;
340
+ onCellDraw(draw: SpatialViewState['cellDraw']): void;
341
+ onCellOpacity(value: number | undefined): void;
342
+ onCellTypeColumn(name: string | null): void;
343
+ onCellColorMode(mode: SpatialViewState['cellColorMode']): void;
344
+ onCellColorGene(gene: string | null): void;
345
+ onCellSingleColor(hex: string): void;
346
+ /** Fill opacity as a 0–100 number, for the box beside the slider. */
347
+ onCellOpacityPercent(v: number | null): void;
348
+ onShowImage(on: boolean): void;
349
+ onShowAnnotations(on: boolean): void;
350
+ /** Which collapsible sections are open. Per dialog instance, not persisted. */
351
+ open: Record<'images' | 'cells' | 'transcripts' | 'annotations' | 'observations', boolean>;
352
+ toggleSection(name: keyof SpatialControlsComponent['open']): void;
353
+ /** The mode the section header's switch turns back on. */
354
+ private lastTranscriptMode;
355
+ onTranscriptsOn(on: boolean): void;
356
+ /** Estimated transcripts in view, against the budget — Explorer's points bar. */
357
+ estimate: {
358
+ points: number;
359
+ max: number;
360
+ } | null;
361
+ /** The density window in use and its densest bin, for the threshold control. */
362
+ densityStats: {
363
+ lo: number;
364
+ hi: number;
365
+ max: number;
366
+ } | null;
367
+ get estimatePercent(): number;
368
+ get estimateOverMax(): boolean;
369
+ editingMax: boolean;
370
+ /** The selected genes as Explorer's tree: named groups, then the ungrouped ones. */
371
+ /** Rebuilt when the view changes — never per change-detection pass (see buildTileOptions). */
372
+ geneTree: {
373
+ name: string | null;
374
+ genes: string[];
375
+ }[];
376
+ geneMenu: {
377
+ label: string;
378
+ icon: string;
379
+ command: () => void;
380
+ disabled?: boolean;
381
+ }[];
382
+ private buildGeneTree;
383
+ /** Real genes in the panel — the tree's denominator. */
384
+ get geneTotal(): number;
385
+ collapsedGeneGroups: Set<string>;
386
+ geneTreeOpen: boolean;
387
+ toggleGeneGroup(name: string): void;
388
+ isGeneShown(gene: string): boolean;
389
+ areGenesShown(genes: string[]): boolean;
390
+ /** The eye toggle: hide or show genes without removing them from the selection. */
391
+ onGenesShown(genes: string[], on: boolean): void;
392
+ onGeneColor(gene: string, hex: string): void;
393
+ /** '±': every gene, or back to the chosen list. */
394
+ onToggleAllGenes(): void;
395
+ /** '⋮' menu. */
396
+ private buildGeneMenu;
397
+ /** The hidden file input the menu's import opens. */
398
+ private geneGroupFileRef?;
399
+ private get geneGroupInput();
400
+ trackByLabel: (_i: number, row: {
401
+ label: string;
402
+ }) => string;
403
+ trackByNode: (_i: number, node: {
404
+ name: string | null;
405
+ }) => string;
406
+ trackByLabelString: (_i: number, s: string) => string;
407
+ get cellOpacityPercent(): number;
408
+ get densityOpacityPercent(): number;
409
+ geneGroupError: string | null;
410
+ onNewGeneGroup(): void;
411
+ /**
412
+ * Import gene groups: CSV/TSV with a group and a gene column (header optional), e.g.
413
+ * marker genes per cell type. The genes are added to the selection.
414
+ */
415
+ onImportGeneGroups(input: HTMLInputElement): Promise<void>;
416
+ readonly densityBins: number[];
417
+ get densityBinIndex(): number;
418
+ onDensityBinIndex(i: number | undefined): void;
419
+ onDensityOpacityPercent(v: number | null): void;
420
+ /**
421
+ * The threshold window shown: the one set, else the one derived. A stored array, not a
422
+ * getter: a range slider's `ngModel` given a fresh array every change-detection pass
423
+ * schedules another pass, forever — which hung the page the moment the density
424
+ * controls appeared.
425
+ */
426
+ densityWindow: [number, number];
427
+ densitySliderMax: number;
428
+ densityStep: number;
429
+ private refreshDensityWindow;
430
+ onDensityRange(range: [number, number] | number[] | undefined): void;
431
+ onDensityRangeEnd(which: 0 | 1, v: number | null): void;
432
+ onDensityAuto(): void;
433
+ selectedDensityColormapNode: ColormapNode | null;
434
+ onDensityColormap(node: ColormapNode | null): void;
435
+ get densityColorBarCss(): string;
436
+ onTranscriptMode(mode: SpatialViewState['transcriptMode']): void;
437
+ onTranscriptGenes(genes: string[] | null): void;
438
+ /** "All genes" needs the grouping pyramid, or at least tiles to draw individually. */
439
+ get canShowAllGenes(): boolean;
440
+ /** Why "All genes" is not offered yet, while the server builds its pyramid. */
441
+ get allGenesPreparing(): string | null;
442
+ get showingAllGenes(): boolean;
443
+ onTranscriptAllGenes(on: boolean): void;
444
+ onTranscriptBudget(n: number): void;
445
+ onTranscriptColorBy(by: SpatialViewState['transcriptColorBy']): void;
446
+ onTranscriptScale(value: number | undefined): void;
447
+ onTranscriptOpacity(value: number | undefined): void;
448
+ onTranscriptQuality(includeLow: boolean): void;
449
+ onDensityOpacity(value: number | undefined): void;
450
+ glyphOf(gene: string, slot: number): TranscriptGlyphName;
451
+ onGlyph(gene: string, glyph: TranscriptGlyphName): void;
452
+ /** The colour gene `slot` is drawn in when transcripts are coloured by gene. */
453
+ geneColor(slot: number): string;
454
+ geneColorOf(gene: string): string;
224
455
  reset(): void;
225
456
  /**
226
457
  * Description of the active column, when it has one. Surfaced because a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jax-data-science/sci-image-visualizer",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "engines": {
5
5
  "node": ">=20.9.0"
6
6
  },