@vitessce/all 4.0.1 → 4.0.2

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.
@@ -5440,12 +5440,12 @@ function baseIsEqual$3(value2, other, bitmask, customizer, stack2) {
5440
5440
  var now$4 = function() {
5441
5441
  return root$d.Date.now();
5442
5442
  };
5443
- var FUNC_ERROR_TEXT$2 = "Expected a function";
5443
+ var FUNC_ERROR_TEXT$3 = "Expected a function";
5444
5444
  var nativeMax$3 = Math.max, nativeMin$1 = Math.min;
5445
5445
  function debounce$5(func, wait2, options) {
5446
5446
  var lastArgs, lastThis, maxWait, result, timerId, lastCallTime, lastInvokeTime = 0, leading = false, maxing = false, trailing = true;
5447
5447
  if (typeof func != "function") {
5448
- throw new TypeError(FUNC_ERROR_TEXT$2);
5448
+ throw new TypeError(FUNC_ERROR_TEXT$3);
5449
5449
  }
5450
5450
  wait2 = toNumber$3(wait2) || 0;
5451
5451
  if (isObject$f(options)) {
@@ -5603,6 +5603,22 @@ var range$e = createRange();
5603
5603
  function sum$6(array2) {
5604
5604
  return array2 && array2.length ? baseSum(array2, identity$h) : 0;
5605
5605
  }
5606
+ var FUNC_ERROR_TEXT$2 = "Expected a function";
5607
+ function throttle$1(func, wait2, options) {
5608
+ var leading = true, trailing = true;
5609
+ if (typeof func != "function") {
5610
+ throw new TypeError(FUNC_ERROR_TEXT$2);
5611
+ }
5612
+ if (isObject$f(options)) {
5613
+ leading = "leading" in options ? !!options.leading : leading;
5614
+ trailing = "trailing" in options ? !!options.trailing : trailing;
5615
+ }
5616
+ return debounce$5(func, wait2, {
5617
+ "leading": leading,
5618
+ "maxWait": wait2,
5619
+ "trailing": trailing
5620
+ });
5621
+ }
5606
5622
  var INFINITY = 1 / 0;
5607
5623
  var createSet = !(Set$3 && 1 / setToArray$2(new Set$3([, -0]))[1] == INFINITY) ? noop$h : function(values3) {
5608
5624
  return new Set$3(values3);
@@ -10936,7 +10952,18 @@ const ngPrecomputedMeshSchema = z.object({
10936
10952
  const ngPointAnnotationSchema = z.object({
10937
10953
  projectionAnnotationSpacing: z.number(),
10938
10954
  featureIndexProp: z.string().optional().describe("The name of the Neuroglancer AnnotationProperty containing feature IDs. For example, specify 'gene' to use prop_gene() in the Neuroglancer shader code."),
10939
- pointIndexProp: z.string().optional().describe("The name of the Neuroglancer AnnotationProperty containing point IDs. For example, specify 'point_id' to use prop_point_id() in the Neuroglancer shader code.")
10955
+ pointIndexProp: z.string().optional().describe("The name of the Neuroglancer AnnotationProperty containing point IDs. For example, specify 'point_id' to use prop_point_id() in the Neuroglancer shader code."),
10956
+ transform: z.object({
10957
+ matrix: z.array(z.array(z.number())),
10958
+ outputDimensions: z.record(z.tuple([z.number(), z.string()]))
10959
+ }).optional().describe("A coordinate transformation matrix to apply to the annotation layer source to map annotation coordinates to the global coordinate space."),
10960
+ // Note: See comments in neuroglancer-sorger.js
10961
+ // We may remove these in the future, pending clarification
10962
+ // to questions in the tissue-map-tools repo
10963
+ // - https://github.com/hms-dbmi/tissue-map-tools/issues/20
10964
+ // - https://github.com/hms-dbmi/tissue-map-tools/issues/38
10965
+ quantitativeColorProp: z.string().optional(),
10966
+ quantitativeColorMax: z.number().optional()
10940
10967
  }).partial().nullable();
10941
10968
  const obsEmbeddingAnndataSchema = annDataObsEmbedding;
10942
10969
  const obsSpotsAnndataSchema = annDataObsLocations;
@@ -18349,7 +18376,7 @@ const { tss } = createTss({
18349
18376
  },
18350
18377
  "usePlugin": useMuiThemeStyleOverridesPlugin
18351
18378
  });
18352
- const useStyles$x = tss.create({});
18379
+ const useStyles$y = tss.create({});
18353
18380
  function createChainedFunction$1(...funcs) {
18354
18381
  return funcs.reduce((acc, func) => {
18355
18382
  if (func == null) {
@@ -72637,7 +72664,7 @@ const Popper = React.forwardRef((props, ref2) => {
72637
72664
  }, [handlePopperRef]);
72638
72665
  React.useImperativeHandle(popperRefProp, () => popperRef.current, []);
72639
72666
  const [exited, setExited] = React.useState(true);
72640
- const { theme } = useStyles$x();
72667
+ const { theme } = useStyles$y();
72641
72668
  const rtlPlacement = flipPlacement(initialPlacement, theme);
72642
72669
  const [placement, setPlacement] = React.useState(rtlPlacement);
72643
72670
  React.useEffect(() => {
@@ -73371,7 +73398,7 @@ var VariableSizeList$1 = /* @__PURE__ */ createListComponent({
73371
73398
  });
73372
73399
  const VariableSizeList = VariableSizeList$1;
73373
73400
  const LIST_ROW_HEIGHT$1 = 48;
73374
- const useStyles$w = makeStyles()(() => ({
73401
+ const useStyles$x = makeStyles()(() => ({
73375
73402
  searchInput: {
73376
73403
  lineHeight: "initial",
73377
73404
  height: "auto !important"
@@ -73411,7 +73438,7 @@ const ListboxComponent$1 = React__default.forwardRef((props, ref2) => {
73411
73438
  });
73412
73439
  function SimpleAutocomplete(props) {
73413
73440
  const { getMatches, onChange, textInputLabel = "Enter a gene", getItemLabel = (option) => option.label } = props;
73414
- const { classes: classes2 } = useStyles$w();
73441
+ const { classes: classes2 } = useStyles$x();
73415
73442
  const [potentialItems, setPotentialItems] = useState([]);
73416
73443
  const handleInputChange = async (_event, value2) => {
73417
73444
  if (!value2) {
@@ -82961,7 +82988,7 @@ function defaultUrlTransform(value2) {
82961
82988
  }
82962
82989
  return "";
82963
82990
  }
82964
- const useStyles$v = makeStyles()((theme) => ({
82991
+ const useStyles$w = makeStyles()((theme) => ({
82965
82992
  description: {
82966
82993
  "& p, details, table": {
82967
82994
  fontSize: "80%",
@@ -82998,7 +83025,7 @@ const useStyles$v = makeStyles()((theme) => ({
82998
83025
  }));
82999
83026
  function Description(props) {
83000
83027
  const { description: description2, metadata: metadata2, descriptionType } = props;
83001
- const { classes: classes2 } = useStyles$v();
83028
+ const { classes: classes2 } = useStyles$w();
83002
83029
  return jsxs("div", { className: classes2.description, children: [descriptionType && descriptionType === DescriptionType.MARKDOWN ? jsx$1(Markdown, { children: description2 }) : jsx$1("p", { children: description2 }), metadata2 && Array.from(metadata2.entries()).map(([layerIndex, { name: layerName, metadata: metadataRecord }]) => metadataRecord && Object.entries(metadataRecord).length > 0 ? jsxs("details", { children: [jsx$1("summary", { children: layerName }), jsx$1("div", { className: classes2.metadataContainer, children: jsx$1("table", { children: jsx$1("tbody", { children: Object.entries(metadataRecord).map(([key2, value2]) => jsxs("tr", { children: [jsx$1("th", { title: key2, children: key2 }), jsx$1("td", { title: value2, children: value2 })] }, key2)) }) }) })] }, layerIndex) : null)] });
83003
83030
  }
83004
83031
  function DescriptionSubscriber(props) {
@@ -91250,6 +91277,37 @@ function dataToCellSetsTree(data2, options) {
91250
91277
  return cellSetsTree;
91251
91278
  }
91252
91279
  const schemeRdBu = [[103, 0, 31], [178, 24, 43], [214, 96, 77], [244, 165, 130], [253, 219, 199], [247, 247, 247], [209, 229, 240], [146, 197, 222], [67, 147, 195], [33, 102, 172], [5, 48, 97]];
91280
+ const schemePlasma = [[13, 8, 135], [16, 7, 136], [19, 7, 137], [22, 7, 138], [25, 6, 140], [27, 6, 141], [29, 6, 142], [32, 6, 143], [34, 6, 144], [36, 6, 145], [38, 5, 145], [40, 5, 146], [42, 5, 147], [44, 5, 148], [46, 5, 149], [47, 5, 150], [49, 5, 151], [51, 5, 151], [53, 4, 152], [55, 4, 153], [56, 4, 154], [58, 4, 154], [60, 4, 155], [62, 4, 156], [63, 4, 156], [65, 4, 157], [67, 3, 158], [68, 3, 158], [70, 3, 159], [72, 3, 159], [73, 3, 160], [75, 3, 161], [76, 2, 161], [78, 2, 162], [80, 2, 162], [81, 2, 163], [83, 2, 163], [85, 2, 164], [86, 1, 164], [88, 1, 164], [89, 1, 165], [91, 1, 165], [92, 1, 166], [94, 1, 166], [96, 1, 166], [97, 0, 167], [99, 0, 167], [100, 0, 167], [102, 0, 167], [103, 0, 168], [105, 0, 168], [106, 0, 168], [108, 0, 168], [110, 0, 168], [111, 0, 168], [113, 0, 168], [114, 1, 168], [116, 1, 168], [117, 1, 168], [119, 1, 168], [120, 1, 168], [122, 2, 168], [123, 2, 168], [125, 3, 168], [126, 3, 168], [128, 4, 168], [129, 4, 167], [131, 5, 167], [132, 5, 167], [134, 6, 166], [135, 7, 166], [136, 8, 166], [138, 9, 165], [139, 10, 165], [141, 11, 165], [142, 12, 164], [143, 13, 164], [145, 14, 163], [146, 15, 163], [148, 16, 162], [149, 17, 161], [150, 19, 161], [152, 20, 160], [153, 21, 159], [154, 22, 159], [156, 23, 158], [157, 24, 157], [158, 25, 157], [160, 26, 156], [161, 27, 155], [162, 29, 154], [163, 30, 154], [165, 31, 153], [166, 32, 152], [167, 33, 151], [168, 34, 150], [170, 35, 149], [171, 36, 148], [172, 38, 148], [173, 39, 147], [174, 40, 146], [176, 41, 145], [177, 42, 144], [178, 43, 143], [179, 44, 142], [180, 46, 141], [181, 47, 140], [182, 48, 139], [183, 49, 138], [184, 50, 137], [186, 51, 136], [187, 52, 136], [188, 53, 135], [189, 55, 134], [190, 56, 133], [191, 57, 132], [192, 58, 131], [193, 59, 130], [194, 60, 129], [195, 61, 128], [196, 62, 127], [197, 64, 126], [198, 65, 125], [199, 66, 124], [200, 67, 123], [201, 68, 122], [202, 69, 122], [203, 70, 121], [204, 71, 120], [204, 73, 119], [205, 74, 118], [206, 75, 117], [207, 76, 116], [208, 77, 115], [209, 78, 114], [210, 79, 113], [211, 81, 113], [212, 82, 112], [213, 83, 111], [213, 84, 110], [214, 85, 109], [215, 86, 108], [216, 87, 107], [217, 88, 106], [218, 90, 106], [218, 91, 105], [219, 92, 104], [220, 93, 103], [221, 94, 102], [222, 95, 101], [222, 97, 100], [223, 98, 99], [224, 99, 99], [225, 100, 98], [226, 101, 97], [226, 102, 96], [227, 104, 95], [228, 105, 94], [229, 106, 93], [229, 107, 93], [230, 108, 92], [231, 110, 91], [231, 111, 90], [232, 112, 89], [233, 113, 88], [233, 114, 87], [234, 116, 87], [235, 117, 86], [235, 118, 85], [236, 119, 84], [237, 121, 83], [237, 122, 82], [238, 123, 81], [239, 124, 81], [239, 126, 80], [240, 127, 79], [240, 128, 78], [241, 129, 77], [241, 131, 76], [242, 132, 75], [243, 133, 75], [243, 135, 74], [244, 136, 73], [244, 137, 72], [245, 139, 71], [245, 140, 70], [246, 141, 69], [246, 143, 68], [247, 144, 68], [247, 145, 67], [247, 147, 66], [248, 148, 65], [248, 149, 64], [249, 151, 63], [249, 152, 62], [249, 154, 62], [250, 155, 61], [250, 156, 60], [250, 158, 59], [251, 159, 58], [251, 161, 57], [251, 162, 56], [252, 163, 56], [252, 165, 55], [252, 166, 54], [252, 168, 53], [252, 169, 52], [253, 171, 51], [253, 172, 51], [253, 174, 50], [253, 175, 49], [253, 177, 48], [253, 178, 47], [253, 180, 47], [253, 181, 46], [254, 183, 45], [254, 184, 44], [254, 186, 44], [254, 187, 43], [254, 189, 42], [254, 190, 42], [254, 192, 41], [253, 194, 41], [253, 195, 40], [253, 197, 39], [253, 198, 39], [253, 200, 39], [253, 202, 38], [253, 203, 38], [252, 205, 37], [252, 206, 37], [252, 208, 37], [252, 210, 37], [251, 211, 36], [251, 213, 36], [251, 215, 36], [250, 216, 36], [250, 218, 36], [249, 220, 36], [249, 221, 37], [248, 223, 37], [248, 225, 37], [247, 226, 37], [247, 228, 37], [246, 230, 38], [246, 232, 38], [245, 233, 38], [245, 235, 39], [244, 237, 39], [243, 238, 39], [243, 240, 39], [242, 242, 39], [241, 244, 38], [241, 245, 37], [240, 247, 36], [240, 249, 33]];
91281
+ const schemeViridis = [[68, 1, 84], [68, 2, 86], [69, 4, 87], [69, 5, 89], [70, 7, 90], [70, 8, 92], [70, 10, 93], [70, 11, 94], [71, 13, 96], [71, 14, 97], [71, 16, 99], [71, 17, 100], [71, 19, 101], [72, 20, 103], [72, 22, 104], [72, 23, 105], [72, 24, 106], [72, 26, 108], [72, 27, 109], [72, 28, 110], [72, 30, 111], [72, 31, 112], [72, 32, 113], [72, 34, 115], [72, 35, 116], [72, 36, 117], [72, 38, 118], [72, 39, 119], [72, 40, 120], [72, 42, 121], [72, 43, 122], [72, 44, 123], [72, 46, 124], [72, 47, 125], [72, 48, 126], [72, 50, 127], [72, 51, 128], [72, 52, 129], [72, 53, 130], [72, 55, 131], [72, 56, 132], [72, 57, 133], [72, 59, 134], [72, 60, 135], [72, 61, 136], [72, 63, 137], [72, 64, 137], [72, 65, 138], [72, 67, 139], [72, 68, 140], [72, 69, 141], [72, 71, 142], [72, 72, 143], [72, 73, 144], [72, 75, 145], [72, 76, 146], [72, 77, 147], [72, 79, 148], [72, 80, 149], [72, 82, 150], [72, 83, 150], [72, 84, 151], [72, 86, 152], [72, 87, 153], [72, 88, 154], [72, 90, 155], [71, 91, 156], [71, 92, 157], [71, 94, 158], [71, 95, 158], [71, 97, 159], [71, 98, 160], [71, 99, 161], [71, 101, 162], [71, 102, 163], [71, 103, 164], [71, 105, 164], [71, 106, 165], [71, 108, 166], [71, 109, 167], [71, 110, 168], [71, 112, 169], [71, 113, 169], [71, 114, 170], [71, 116, 171], [71, 117, 172], [70, 119, 173], [70, 120, 174], [70, 121, 174], [70, 123, 175], [70, 124, 176], [70, 125, 177], [70, 127, 178], [70, 128, 178], [70, 130, 179], [70, 131, 180], [70, 132, 181], [70, 134, 182], [70, 135, 182], [70, 136, 183], [70, 138, 184], [70, 139, 185], [70, 141, 186], [70, 142, 186], [70, 143, 187], [70, 145, 188], [70, 146, 189], [70, 148, 189], [70, 149, 190], [70, 150, 191], [70, 152, 192], [70, 153, 192], [70, 155, 193], [70, 156, 194], [70, 157, 195], [70, 159, 195], [70, 160, 196], [70, 162, 197], [70, 163, 198], [70, 164, 198], [70, 166, 199], [70, 167, 200], [71, 169, 200], [71, 170, 201], [71, 171, 202], [71, 173, 203], [71, 174, 203], [71, 176, 204], [71, 177, 205], [71, 178, 205], [72, 180, 206], [72, 181, 207], [72, 183, 207], [72, 184, 208], [73, 185, 209], [73, 187, 209], [73, 188, 210], [74, 190, 211], [74, 191, 211], [74, 192, 212], [75, 194, 213], [75, 195, 213], [76, 197, 214], [76, 198, 214], [77, 199, 215], [77, 201, 216], [78, 202, 216], [78, 204, 217], [79, 205, 217], [79, 207, 218], [80, 208, 218], [81, 209, 219], [81, 211, 220], [82, 212, 220], [83, 214, 221], [83, 215, 221], [84, 216, 222], [85, 218, 222], [85, 219, 223], [86, 221, 223], [87, 222, 224], [88, 224, 224], [88, 225, 225], [89, 226, 225], [90, 228, 226], [91, 229, 226], [92, 231, 227], [92, 232, 227], [93, 234, 228], [94, 235, 228], [95, 236, 229], [96, 238, 229], [97, 239, 230], [97, 241, 230], [98, 242, 231], [99, 244, 231], [100, 245, 232], [101, 246, 232], [102, 248, 233], [103, 249, 233], [104, 251, 234], [105, 252, 234]];
91282
+ const schemeJet = Array.from({ length: 256 }, (_, i2) => {
91283
+ const t4 = i2 / 255;
91284
+ let r3;
91285
+ let g2;
91286
+ let b2;
91287
+ if (t4 < 0.125) {
91288
+ r3 = 0;
91289
+ g2 = 0;
91290
+ b2 = 0.5 + t4 * 4;
91291
+ } else if (t4 < 0.375) {
91292
+ r3 = 0;
91293
+ g2 = (t4 - 0.125) * 4;
91294
+ b2 = 1;
91295
+ } else if (t4 < 0.625) {
91296
+ r3 = (t4 - 0.375) * 4;
91297
+ g2 = 1;
91298
+ b2 = 1 - (t4 - 0.375) * 4;
91299
+ } else if (t4 < 0.875) {
91300
+ r3 = 1;
91301
+ g2 = 1 - (t4 - 0.625) * 4;
91302
+ b2 = 0;
91303
+ } else {
91304
+ r3 = 1 - (t4 - 0.875) * 4;
91305
+ g2 = 0;
91306
+ b2 = 0;
91307
+ }
91308
+ return [Math.round(r3 * 255), Math.round(g2 * 255), Math.round(b2 * 255)];
91309
+ });
91310
+ const schemeGreys = Array.from({ length: 256 }, (_, i2) => [i2, i2, i2]);
91253
91311
  function rgbSpline$1(spline) {
91254
91312
  return (colors2) => {
91255
91313
  const n3 = colors2.length;
@@ -91287,7 +91345,15 @@ function basis$2(values3) {
91287
91345
  };
91288
91346
  }
91289
91347
  const interpolateRgbBasis = rgbSpline$1(basis$2);
91348
+ function interpolateSequentialMulti(range2) {
91349
+ const n3 = range2.length;
91350
+ return (t4) => range2[Math.max(0, Math.min(n3 - 1, Math.floor(t4 * n3)))];
91351
+ }
91290
91352
  interpolateRgbBasis(schemeRdBu);
91353
+ const interpolatePlasma = interpolateSequentialMulti(schemePlasma);
91354
+ const interpolateViridis = interpolateSequentialMulti(schemeViridis);
91355
+ const interpolateJet = interpolateSequentialMulti(schemeJet);
91356
+ const interpolateGreys = interpolateSequentialMulti(schemeGreys);
91291
91357
  function getCellColors(params2) {
91292
91358
  const { cellSets, cellSetSelection, cellSetColor, obsIndex, theme } = params2;
91293
91359
  if (cellSetSelection && cellSets) {
@@ -98069,7 +98135,7 @@ const Tooltip$2 = /* @__PURE__ */ React.forwardRef((props, ref2) => {
98069
98135
  }, extraProps), getChildren());
98070
98136
  });
98071
98137
  const nodeHeight = 32;
98072
- const useStyles$u = makeStyles()((theme) => ({
98138
+ const useStyles$v = makeStyles()((theme) => ({
98073
98139
  setsManager: {
98074
98140
  position: "relative",
98075
98141
  width: "100%",
@@ -98236,7 +98302,7 @@ const useStyles$u = makeStyles()((theme) => ({
98236
98302
  }));
98237
98303
  function SetsManagerTreeGlobalStyles(props) {
98238
98304
  const { classes: classes2 } = props;
98239
- const { theme } = useStyles$x();
98305
+ const { theme } = useStyles$y();
98240
98306
  return jsx$1(ScopedGlobalStyles, { parentClassName: classes2.setsManagerTree, styles: {
98241
98307
  ".rc-tree": {
98242
98308
  paddingLeft: "0",
@@ -98577,7 +98643,7 @@ function HelpTooltipGlobalStyles(props) {
98577
98643
  }
98578
98644
  function PopoverGlobalStyles(props) {
98579
98645
  const { classes: classes2 } = props;
98580
- const { theme } = useStyles$x();
98646
+ const { theme } = useStyles$y();
98581
98647
  return jsx$1(ScopedGlobalStyles, { parentClassName: classes2.popover, styles: {
98582
98648
  ".rc-tooltip-inner": {
98583
98649
  boxSizing: "border-box",
@@ -103261,7 +103327,7 @@ function NamedSetNodeStatic(props) {
103261
103327
  const onClick = level === 0 && !expanded ? () => onCheckLevel(nodeKey, nextLevelToCheck) : () => onNodeView(path2);
103262
103328
  const tooltipProps = disableTooltip ? { visible: false } : {};
103263
103329
  const popoverMenuConfig = makeNodeViewMenuConfig(props);
103264
- const { classes: classes2 } = useStyles$u();
103330
+ const { classes: classes2 } = useStyles$v();
103265
103331
  return jsxs("span", { children: [jsx$1(HelpTooltip, { title: tooltipText, ...tooltipProps, children: jsx$1("button", { type: "button", onClick, onKeyPress: (e3) => callbackOnKeyPress(e3, "v", () => onNodeView(path2)), className: classes2.titleButton, children: title2 }) }), popoverMenuConfig.length > 0 ? jsx$1(PopoverMenu, { menuConfig: makeNodeViewMenuConfig(props), color: level > 0 && editable ? color2 || getDefaultColor(theme) : null, setColor: (c2) => onNodeSetColor(path2, c2), children: jsx$1("span", { children: jsx$1(SvgMenu, { className: classes2.nodeMenuIcon, "aria-label": "Open Node View Menu" }) }) }) : null, level > 0 && isChecking ? checkbox2 : null, level > 0 && jsx$1("span", { className: classes2.nodeSizeLabel, children: niceSize })] });
103266
103332
  }
103267
103333
  function NamedSetNodeEditing(props) {
@@ -103273,7 +103339,7 @@ function NamedSetNodeEditing(props) {
103273
103339
  onNodeSetName(path2, currentTitle, true);
103274
103340
  }
103275
103341
  }
103276
- const { classes: classes2 } = useStyles$u();
103342
+ const { classes: classes2 } = useStyles$v();
103277
103343
  return jsxs("span", { className: classes2.titleButtonWithInput, children: [jsx$1("input", {
103278
103344
  // eslint-disable-next-line jsx-a11y/no-autofocus
103279
103345
  autoFocus: true,
@@ -103299,7 +103365,7 @@ function LevelsButtons(props) {
103299
103365
  onCheckLevel(nodeKey, newLevel);
103300
103366
  }
103301
103367
  }
103302
- const { classes: classes2 } = useStyles$u();
103368
+ const { classes: classes2 } = useStyles$v();
103303
103369
  return jsx$1("div", { className: classes2.levelButtonsContainer, children: range$e(1, height2 + 1).map((i2) => {
103304
103370
  const isChecked = isEqual$3(path2, checkedLevelPath) && i2 === checkedLevelIndex;
103305
103371
  return jsx$1("div", { children: jsx$1(HelpTooltip, { title: getLevelTooltipText(i2), children: jsx$1("input", { className: clsx(classes2.levelRadioButton, { [classes2.levelRadioButtonChecked]: isChecked && !hasColorEncoding }), type: "checkbox", value: i2, checked: isChecked && hasColorEncoding, onChange: onCheck }) }) }, i2);
@@ -103383,7 +103449,7 @@ let TreeNode$1 = class TreeNode extends TreeNode$2 {
103383
103449
  };
103384
103450
  function PlusButton(props) {
103385
103451
  const { datatype, onError, onImportTree, onCreateLevelZeroNode, importable, editable } = props;
103386
- const { classes: classes2 } = useStyles$u();
103452
+ const { classes: classes2 } = useStyles$v();
103387
103453
  const onImport = useCallback((importHandler, mimeType) => () => {
103388
103454
  const uploadInputNode = document.createElement("input");
103389
103455
  uploadInputNode.setAttribute("type", "file");
@@ -103500,7 +103566,7 @@ function SetsManager(props) {
103500
103566
  }, onNodeCheckNewName, onNodeSetIsEditing: setIsEditingNodeName, onNodeRemove, onExportLevelZeroNodeJSON, onExportLevelZeroNodeTabular, onExportSetJSON, disableTooltip: isDragging, onDragStart: () => setIsDragging(true), onDragEnd: () => setIsDragging(false), children: renderTreeNodes(node2.children, readOnly, newPath) }, pathToKey(newPath));
103501
103567
  });
103502
103568
  }
103503
- const { classes: classes2 } = useStyles$u();
103569
+ const { classes: classes2 } = useStyles$v();
103504
103570
  return jsxs("div", { className: classes2.setsManager, children: [jsx$1(SetsManagerTreeGlobalStyles, { classes: classes2 }), jsxs("div", { className: classes2.setsManagerTree, children: [jsx$1(Tree$1, { draggable: false, checkable, checkedKeys: setSelectionKeys, expandedKeys: setExpansionKeys, autoExpandParent, onCheck: (checkedKeys, info2) => onCheckNode(info2.node.props.nodeKey, info2.checked), onExpand: (expandedKeys, info2) => onExpandNode(expandedKeys, info2.node.props.nodeKey, info2.expanded), children: renderTreeNodes(processedSets.tree, true, []) }), jsx$1(Tree$1, { draggable: true, checkable, checkedKeys: additionalSetSelectionKeys, expandedKeys: additionalSetExpansionKeys, autoExpandParent, onCheck: (checkedKeys, info2) => onCheckNode(info2.node.props.nodeKey, info2.checked), onExpand: (expandedKeys, info2) => onExpandNode(expandedKeys, info2.node.props.nodeKey, info2.expanded), onDrop: (info2) => {
103505
103571
  const { eventKey: dropKey } = info2.node.props;
103506
103572
  const { eventKey: dragKey } = info2.dragNode.props;
@@ -209465,22 +209531,22 @@ async function getDecoder(fileDirectory) {
209465
209531
  const Decoder = await importFn();
209466
209532
  return new Decoder(fileDirectory);
209467
209533
  }
209468
- addDecoder([void 0, 1], () => import("./raw-BLQKlp_4.js").then((m2) => m2.default));
209469
- addDecoder(5, () => import("./lzw-hla-j59h.js").then((m2) => m2.default));
209534
+ addDecoder([void 0, 1], () => import("./raw-UWcztCkw.js").then((m2) => m2.default));
209535
+ addDecoder(5, () => import("./lzw-xVcb3-tK.js").then((m2) => m2.default));
209470
209536
  addDecoder(6, () => {
209471
209537
  throw new Error("old style JPEG compression is not supported.");
209472
209538
  });
209473
- addDecoder(7, () => import("./jpeg-_87xUwY6.js").then((m2) => m2.default));
209474
- addDecoder([8, 32946], () => import("./deflate-E8TfyvM7.js").then((m2) => m2.default));
209475
- addDecoder(32773, () => import("./packbits-C8umOnfG.js").then((m2) => m2.default));
209539
+ addDecoder(7, () => import("./jpeg-9VJS3-Bw.js").then((m2) => m2.default));
209540
+ addDecoder([8, 32946], () => import("./deflate-BnG3V84V.js").then((m2) => m2.default));
209541
+ addDecoder(32773, () => import("./packbits-Dm4fdK8S.js").then((m2) => m2.default));
209476
209542
  addDecoder(
209477
209543
  34887,
209478
- () => import("./lerc-DFl9HcRa.js").then(async (m2) => {
209544
+ () => import("./lerc-D1J26SPX.js").then(async (m2) => {
209479
209545
  await m2.zstd.init();
209480
209546
  return m2;
209481
209547
  }).then((m2) => m2.default)
209482
209548
  );
209483
- addDecoder(50001, () => import("./webimage-nk5eMN68.js").then((m2) => m2.default));
209549
+ addDecoder(50001, () => import("./webimage-C-QWVV-f.js").then((m2) => m2.default));
209484
209550
  function copyNewSize(array2, width2, height2, samplesPerPixel = 1) {
209485
209551
  return new (Object.getPrototypeOf(array2)).constructor(width2 * height2 * samplesPerPixel);
209486
209552
  }
@@ -226254,7 +226320,7 @@ class ContourLayerWithText extends ContourLayer {
226254
226320
  }
226255
226321
  ContourLayerWithText.layerName = "ContourLayerWithText";
226256
226322
  ContourLayerWithText.defaultProps = defaultProps;
226257
- const useStyles$t = makeStyles()(() => ({
226323
+ const useStyles$u = makeStyles()(() => ({
226258
226324
  toolButton: {
226259
226325
  display: "inline-flex",
226260
226326
  "&:active": {
@@ -226316,12 +226382,12 @@ const useStyles$t = makeStyles()(() => ({
226316
226382
  }));
226317
226383
  function IconTool(props) {
226318
226384
  const { alt, onClick, isActive: isActive2, children: children3 } = props;
226319
- const { classes: classes2 } = useStyles$t();
226385
+ const { classes: classes2 } = useStyles$u();
226320
226386
  return jsx$1("button", { className: clsx(classes2.toolIcon, { [classes2.toolActive]: isActive2 }), onClick, type: "button", title: alt, children: children3 });
226321
226387
  }
226322
226388
  function IconButton(props) {
226323
226389
  const { alt, onClick, children: children3 } = props;
226324
- const { classes: classes2 } = useStyles$t();
226390
+ const { classes: classes2 } = useStyles$u();
226325
226391
  return jsx$1("button", { className: clsx(classes2.toolIcon, classes2.toolButton), onClick, type: "button", title: alt, children: children3 });
226326
226392
  }
226327
226393
  function ToolMenu(props) {
@@ -226329,7 +226395,7 @@ function ToolMenu(props) {
226329
226395
  const lassoIconAltText = "Select lasso";
226330
226396
  const { setActiveTool, activeTool, visibleTools = { pan: true, selectLasso: true, recenter: true }, recenterOnClick = () => {
226331
226397
  } } = props;
226332
- const { classes: classes2 } = useStyles$t();
226398
+ const { classes: classes2 } = useStyles$u();
226333
226399
  const onRecenterButtonCLick = () => {
226334
226400
  recenterOnClick();
226335
226401
  };
@@ -227486,7 +227552,7 @@ function ScatterplotOptions(props) {
227486
227552
  id: `feature-aggregation-strategy-${scatterplotOptionsId}`
227487
227553
  }, children: FEATURE_AGGREGATION_STRATEGIES$2.map((opt) => jsx$1("option", { value: opt, children: capitalize$3(opt) }, opt)) }) })] }) : null] });
227488
227554
  }
227489
- const useStyles$s = makeStyles()((theme) => ({
227555
+ const useStyles$t = makeStyles()((theme) => ({
227490
227556
  tooltipAnchor: {
227491
227557
  position: "relative",
227492
227558
  width: "0px",
@@ -227517,7 +227583,7 @@ const useStyles$s = makeStyles()((theme) => ({
227517
227583
  function Tooltip3(props) {
227518
227584
  const { x: x2, y: y2, parentWidth, parentHeight, children: children3 } = props;
227519
227585
  const ref2 = useRef();
227520
- const { classes: classes2 } = useStyles$s();
227586
+ const { classes: classes2 } = useStyles$t();
227521
227587
  const [placementX, setPlacementX] = useState("start");
227522
227588
  const [placementY, setPlacementY] = useState("bottom");
227523
227589
  const getTooltipContainer = useVitessceContainer(ref2);
@@ -227533,7 +227599,7 @@ function Tooltip3(props) {
227533
227599
  }, [x2, y2, parentWidth, parentHeight]);
227534
227600
  return jsx$1("div", { ref: ref2, className: classes2.tooltipAnchor, children: ref2 && ref2.current ? jsx$1(Popper, { open: true, anchorEl: ref2.current, container: getTooltipContainer, transition: true, placement: `${placementY}-${placementX}`, children: jsx$1(Paper, { elevation: 8, className: classes2.tooltipContent, children: children3 }) }) : null });
227535
227601
  }
227536
- const useStyles$r = makeStyles()((theme) => ({
227602
+ const useStyles$s = makeStyles()((theme) => ({
227537
227603
  cellEmphasisCrosshair: {
227538
227604
  zIndex: 50,
227539
227605
  position: "absolute",
@@ -227544,7 +227610,7 @@ const useStyles$r = makeStyles()((theme) => ({
227544
227610
  }));
227545
227611
  function Tooltip2D(props) {
227546
227612
  const { parentUuid, sourceUuid, x: x2, y: y2, parentWidth, parentHeight, children: children3 } = props;
227547
- const { classes: classes2 } = useStyles$r();
227613
+ const { classes: classes2 } = useStyles$s();
227548
227614
  if (x2 < 0 || x2 > parentWidth || y2 < 0 || y2 > parentHeight) {
227549
227615
  return null;
227550
227616
  }
@@ -231456,7 +231522,7 @@ function getXlinkHref(cmap) {
231456
231522
  const xlinkHref = ramp(color2.copy().domain(quantize$3(interpolate$1$1(0, 1), n3))).toDataURL();
231457
231523
  return xlinkHref;
231458
231524
  }
231459
- const useStyles$q = makeStyles()(() => ({
231525
+ const useStyles$r = makeStyles()(() => ({
231460
231526
  legend: {
231461
231527
  position: "relative",
231462
231528
  // Needed for absolute positioning of slider overlay
@@ -231653,7 +231719,7 @@ function Legend(props) {
231653
231719
  contourThresholds
231654
231720
  } = props;
231655
231721
  const svgRef = useRef(null);
231656
- const { classes: classes2 } = useStyles$q();
231722
+ const { classes: classes2 } = useStyles$r();
231657
231723
  const [localRange, setLocalRange] = useState(featureValueColormapRange);
231658
231724
  useEffect(() => {
231659
231725
  setLocalRange(featureValueColormapRange);
@@ -231987,7 +232053,7 @@ function Legend(props) {
231987
232053
  }
231988
232054
  } })] })] });
231989
232055
  }
231990
- const useStyles$p = makeStyles()(() => ({
232056
+ const useStyles$q = makeStyles()(() => ({
231991
232057
  multiLegend: {
231992
232058
  position: "absolute",
231993
232059
  top: "0px",
@@ -232014,7 +232080,7 @@ function MultiLegend(props) {
232014
232080
  pointLayerCoordination,
232015
232081
  pointMultiIndicesData
232016
232082
  } = props;
232017
- const { classes: classes2 } = useStyles$p();
232083
+ const { classes: classes2 } = useStyles$q();
232018
232084
  const reversedSegmentationLayerScopes = useMemo$1(() => [...segmentationLayerScopes || []].reverse(), [segmentationLayerScopes]);
232019
232085
  const reversedSpotLayerScopes = useMemo$1(() => [...spotLayerScopes || []].reverse(), [spotLayerScopes]);
232020
232086
  const reversedPointLayerScopes = useMemo$1(() => [...pointLayerScopes || []].reverse(), [pointLayerScopes]);
@@ -232107,7 +232173,7 @@ function MultiLegend(props) {
232107
232173
  }) : null;
232108
232174
  }) : null] });
232109
232175
  }
232110
- const useStyles$o = makeStyles()(() => ({
232176
+ const useStyles$p = makeStyles()(() => ({
232111
232177
  channelNamesLegendContainer: {
232112
232178
  position: "absolute",
232113
232179
  bottom: "0px",
@@ -232130,7 +232196,7 @@ const useStyles$o = makeStyles()(() => ({
232130
232196
  }));
232131
232197
  function ChannelNamesLegend(props) {
232132
232198
  const { images, imageLayerScopes, imageLayerCoordination, imageChannelScopesByLayer, imageChannelCoordination } = props;
232133
- const { classes: classes2 } = useStyles$o();
232199
+ const { classes: classes2 } = useStyles$p();
232134
232200
  const reversedImageLayerScopes = useMemo$1(() => [...imageLayerScopes || []].reverse(), [imageLayerScopes]);
232135
232201
  return jsx$1("div", { className: classes2.channelNamesLegendContainer, children: imageLayerScopes ? reversedImageLayerScopes.map((layerScope) => {
232136
232202
  const layerCoordination = imageLayerCoordination[0][layerScope];
@@ -253674,7 +253740,7 @@ class Spatial2 extends AbstractSpatialOrScatterplot {
253674
253740
  this.viewInfoDidUpdate();
253675
253741
  const shallowDiff2 = (propName) => prevProps[propName] !== this.props[propName];
253676
253742
  const shallowDiffByLayer2 = (propName, scopeName) => prevProps?.[propName]?.[scopeName] !== this.props?.[propName]?.[scopeName];
253677
- const shallowDiffByChannel = (propName, firstName, secondName) => prevProps?.[propName]?.[firstName]?.[secondName] !== this.props?.[propName]?.[firstName]?.[secondName];
253743
+ const shallowDiffByChannel2 = (propName, firstName, secondName) => prevProps?.[propName]?.[firstName]?.[secondName] !== this.props?.[propName]?.[firstName]?.[secondName];
253678
253744
  const shallowDiffByLayerCoordination = (propName, layerScope) => prevProps?.[propName]?.[0]?.[layerScope] !== this.props?.[propName]?.[0]?.[layerScope];
253679
253745
  const shallowDiffByChannelCoordination = (propName, layerScope, channelScope) => prevProps?.[propName]?.[0]?.[layerScope]?.[channelScope] !== this.props?.[propName]?.[0]?.[layerScope]?.[channelScope];
253680
253746
  let forceUpdate = false;
@@ -253695,7 +253761,7 @@ class Spatial2 extends AbstractSpatialOrScatterplot {
253695
253761
  } else {
253696
253762
  this.props.segmentationLayerScopes?.forEach((layerScope) => {
253697
253763
  this.props.segmentationChannelScopesByLayer?.[layerScope]?.forEach((channelScope) => {
253698
- if (shallowDiffByChannel("obsSegmentationsLocations", layerScope, channelScope)) {
253764
+ if (shallowDiffByChannel2("obsSegmentationsLocations", layerScope, channelScope)) {
253699
253765
  this.onUpdateSegmentationsLocationsData(layerScope, channelScope);
253700
253766
  forceUpdate = true;
253701
253767
  }
@@ -253708,7 +253774,7 @@ class Spatial2 extends AbstractSpatialOrScatterplot {
253708
253774
  } else {
253709
253775
  this.props.segmentationLayerScopes?.forEach((layerScope) => {
253710
253776
  this.props.segmentationChannelScopesByLayer?.[layerScope]?.forEach((channelScope) => {
253711
- if (shallowDiffByChannel("obsSegmentationsSets", layerScope, channelScope) || shallowDiffByChannelCoordination("segmentationChannelCoordination", layerScope, channelScope)) {
253777
+ if (shallowDiffByChannel2("obsSegmentationsSets", layerScope, channelScope) || shallowDiffByChannelCoordination("segmentationChannelCoordination", layerScope, channelScope)) {
253712
253778
  this.onUpdateSegmentationsSetsData(layerScope, channelScope);
253713
253779
  forceUpdate = true;
253714
253780
  }
@@ -253721,7 +253787,7 @@ class Spatial2 extends AbstractSpatialOrScatterplot {
253721
253787
  } else {
253722
253788
  this.props.segmentationLayerScopes?.forEach((layerScope) => {
253723
253789
  this.props.segmentationChannelScopesByLayer?.[layerScope]?.forEach((channelScope) => {
253724
- if (shallowDiffByChannel("segmentationMatrixIndices", layerScope, channelScope)) {
253790
+ if (shallowDiffByChannel2("segmentationMatrixIndices", layerScope, channelScope)) {
253725
253791
  this.onUpdateSegmentationsIndexData(layerScope, channelScope);
253726
253792
  forceUpdate = true;
253727
253793
  }
@@ -253734,7 +253800,7 @@ class Spatial2 extends AbstractSpatialOrScatterplot {
253734
253800
  } else {
253735
253801
  this.props.segmentationLayerScopes.forEach((layerScope) => {
253736
253802
  this.props.segmentationChannelScopesByLayer?.[layerScope]?.forEach((channelScope) => {
253737
- if (shallowDiffByChannel("segmentationMatrixIndices", layerScope, channelScope) || shallowDiffByChannel("segmentationMultiExpressionData", layerScope, channelScope)) {
253803
+ if (shallowDiffByChannel2("segmentationMatrixIndices", layerScope, channelScope) || shallowDiffByChannel2("segmentationMultiExpressionData", layerScope, channelScope)) {
253738
253804
  this.onUpdateSegmentationsExpressionData(layerScope, channelScope);
253739
253805
  forceUpdate = true;
253740
253806
  }
@@ -253988,12 +254054,12 @@ class ErrorBoundary extends React__default.Component {
253988
254054
  }
253989
254055
  }
253990
254056
  const LazySpatialThree = React__default.lazy(async () => {
253991
- const { SpatialWrapper: SpatialWrapper2 } = await import("./index-0nuepTVk.js").then((n3) => n3.a);
254057
+ const { SpatialWrapper: SpatialWrapper2 } = await import("./index-CwGBUQIk.js").then((n3) => n3.a);
253992
254058
  return { default: SpatialWrapper2 };
253993
254059
  });
253994
254060
  const SpatialThreeAdapter = React__default.forwardRef((props, ref2) => jsx$1("div", { ref: ref2, style: { width: "100%", height: "100%" }, children: jsx$1(ErrorBoundary, { fallback: jsx$1("div", { children: "3D spatial view is not available in this environment." }), children: jsx$1(Suspense, { fallback: jsx$1("div", { children: "Loading..." }), children: jsx$1(LazySpatialThree, { ...props }) }) }) }));
253995
254061
  const LazySpatialAccelerated = React__default.lazy(async () => {
253996
- const { SpatialWrapper: SpatialWrapper2 } = await import("./index-BHE3zASA.js");
254062
+ const { SpatialWrapper: SpatialWrapper2 } = await import("./index-B9h9IQaz.js");
253997
254063
  return { default: SpatialWrapper2 };
253998
254064
  });
253999
254065
  const SpatialAcceleratedAdapter = React__default.forwardRef((props, ref2) => jsx$1("div", { ref: ref2, style: { width: "100%", height: "100%" }, children: jsx$1(ErrorBoundary, { fallback: jsx$1("div", { children: "3D spatial view is not available in this environment." }), children: jsx$1(Suspense, { fallback: jsx$1("div", { children: "Loading..." }), children: jsx$1(LazySpatialAccelerated, { ...props }) }) }) }));
@@ -262066,7 +262132,7 @@ _defineProperty$3(WindowScroller, "defaultProps", {
262066
262132
  serverHeight: 0,
262067
262133
  serverWidth: 0
262068
262134
  });
262069
- const useStyles$n = makeStyles()((theme) => ({
262135
+ const useStyles$o = makeStyles()((theme) => ({
262070
262136
  selectableTable: {
262071
262137
  flex: "1 1 auto",
262072
262138
  outline: "none"
@@ -262215,7 +262281,7 @@ function SelectableTable(props) {
262215
262281
  onChange(null);
262216
262282
  }
262217
262283
  }, [selectedRows, allowMultiple]);
262218
- const { classes: classes2 } = useStyles$n();
262284
+ const { classes: classes2 } = useStyles$o();
262219
262285
  const inputUuid = v4$1();
262220
262286
  const rowRenderer = ({ index: index2, style: style2 }) => (
262221
262287
  // eslint-disable-next-line jsx-a11y/interactive-supports-focus
@@ -262240,7 +262306,7 @@ const FEATURELIST_SORT_OPTIONS = [
262240
262306
  "original"
262241
262307
  ];
262242
262308
  const ALT_COLNAME = "Alternate ID";
262243
- const useStyles$m = makeStyles()(() => ({
262309
+ const useStyles$n = makeStyles()(() => ({
262244
262310
  searchBar: {
262245
262311
  marginTop: "10px",
262246
262312
  marginLeft: "10px",
@@ -262253,7 +262319,7 @@ const useStyles$m = makeStyles()(() => ({
262253
262319
  }));
262254
262320
  function FeatureList(props) {
262255
262321
  const { width: width2, height: height2, hasColorEncoding, geneList = [], featureLabelsMap, geneSelection = [], geneFilter = null, setGeneSelection, enableMultiSelect, showFeatureTable, featureListSort, featureListSortKey, hasFeatureLabels, primaryColumnName } = props;
262256
- const { classes: classes2 } = useStyles$m();
262322
+ const { classes: classes2 } = useStyles$n();
262257
262323
  const [searchTerm, setSearchTerm] = useState("");
262258
262324
  const selectableTableSortKey = featureListSortKey === "featureIndex" ? "key" : "name";
262259
262325
  const searchResults = useMemo$1(() => geneList.filter((gene) => gene.toLowerCase().includes(searchTerm.toLowerCase()) || featureLabelsMap?.get(gene)?.toLowerCase().includes(searchTerm.toLowerCase()) || featureLabelsMap?.get(cleanFeatureId(gene))?.toLowerCase().includes(searchTerm.toLowerCase())), [geneList, searchTerm, featureLabelsMap]);
@@ -262326,7 +262392,7 @@ function FeatureListOptions(props) {
262326
262392
  id: `feature-list-show-alternative-ids-${featureListId}`
262327
262393
  } } }) })] })] }) : null] });
262328
262394
  }
262329
- const useStyles$l = makeStyles()((theme) => ({
262395
+ const useStyles$m = makeStyles()((theme) => ({
262330
262396
  featureListContainer: {
262331
262397
  width: "100%",
262332
262398
  height: "100%",
@@ -262341,7 +262407,7 @@ function FeatureListSubscriber(props) {
262341
262407
  const loaders = useLoaders();
262342
262408
  const coordinationScopes = useCoordinationScopes(coordinationScopesRaw);
262343
262409
  const [width2, height2, containerRef] = useGridItemSize();
262344
- const { classes: classes2 } = useStyles$l();
262410
+ const { classes: classes2 } = useStyles$m();
262345
262411
  const [{ dataset, obsType, featureType, featureSelection: geneSelection, featureFilter: geneFilter, obsColorEncoding: cellColorEncoding }, { setFeatureSelection: setGeneSelection, setFeatureFilter: setGeneFilter, setFeatureHighlight: setGeneHighlight, setObsColorEncoding: setCellColorEncoding }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType$1.FEATURE_LIST], coordinationScopes);
262346
262412
  const variablesLabel = variablesLabelOverride || featureType;
262347
262413
  const title2 = titleOverride || `${capitalize$3(variablesLabel)} List`;
@@ -262476,7 +262542,7 @@ const useSelectionSliderStyles = makeStyles()(() => ({
262476
262542
  backgroundColor: "rgba(128, 128, 128, 0.7)"
262477
262543
  }
262478
262544
  }));
262479
- const useStyles$k = makeStyles()((theme) => ({
262545
+ const useStyles$l = makeStyles()((theme) => ({
262480
262546
  paletteContainer: {
262481
262547
  width: "70px",
262482
262548
  height: "40px",
@@ -262497,10 +262563,10 @@ const useStyles$k = makeStyles()((theme) => ({
262497
262563
  }
262498
262564
  }));
262499
262565
  const ColorPalette = ({ handleChange }) => {
262500
- const { classes: classes2 } = useStyles$k();
262566
+ const { classes: classes2 } = useStyles$l();
262501
262567
  return jsx$1("div", { className: classes2.paletteContainer, "aria-label": "Color swatch", children: VIEWER_PALETTE.map((color2) => jsx$1(IconButton$1, { className: classes2.button, onClick: () => handleChange(color2), "aria-label": `Change color to ${color2}`, children: jsx$1(LensIcon, { fontSize: "small", style: { color: `rgb(${color2})` }, className: classes2.icon }) }, color2)) });
262502
262568
  };
262503
- const useStyles$j = makeStyles()(() => ({
262569
+ const useStyles$k = makeStyles()(() => ({
262504
262570
  menuButton: {
262505
262571
  backgroundColor: "transparent"
262506
262572
  },
@@ -262519,7 +262585,7 @@ function MuiSpan(props) {
262519
262585
  }
262520
262586
  function ChannelOptions$1({ handlePropertyChange, handleChannelRemove, handleIQRUpdate }) {
262521
262587
  const [open2, setOpen] = useState(false);
262522
- const { classes: classes2 } = useStyles$j();
262588
+ const { classes: classes2 } = useStyles$k();
262523
262589
  const handleColorSelect = (color2) => {
262524
262590
  handlePropertyChange("color", color2);
262525
262591
  };
@@ -263136,7 +263202,7 @@ function LayerController$1(props) {
263136
263202
  minWidth: "50%"
263137
263203
  } })] }), jsx$1(TabPanel, { value: tab2, index: 0, children: FullController }), jsx$1(TabPanel, { value: tab2, index: 1, style: { marginTop: 4 }, children: jsx$1(VolumeOptions, { loader: loader2, handleSlicerSetting, handleRenderingModeChange: setRenderingMode, renderingMode, xSlice, ySlice, zSlice, use3d, setViewState, spatialHeight, spatialWidth, modelMatrix: modelMatrix2 }) })] }) : FullController, shouldShowRemoveLayerButton ? jsx$1(Button, { onClick: handleLayerRemove, fullWidth: true, variant: "outlined", style: buttonStyles, size: "small", children: "Remove Image Layer" }) : null] })] });
263138
263204
  }
263139
- const useStyles$i = makeStyles()(() => ({
263205
+ const useStyles$j = makeStyles()(() => ({
263140
263206
  addButton: {
263141
263207
  marginTop: "10px",
263142
263208
  marginBottom: "10px",
@@ -263148,7 +263214,7 @@ function ImageAddIcon() {
263148
263214
  }
263149
263215
  function ImageAddButton({ imageOptions, handleImageAdd }) {
263150
263216
  const [open2, setOpen] = useState(false);
263151
- const { classes: classes2 } = useStyles$i();
263217
+ const { classes: classes2 } = useStyles$j();
263152
263218
  const handleAdd = (imgData) => {
263153
263219
  setOpen((prev2) => !prev2);
263154
263220
  handleImageAdd(imgData);
@@ -263480,7 +263546,7 @@ const useEllipsisMenuStyles = makeStyles()(() => ({
263480
263546
  padding: "4px"
263481
263547
  }
263482
263548
  }));
263483
- const useStyles$h = makeStyles()(() => ({
263549
+ const useStyles$i = makeStyles()(() => ({
263484
263550
  colorPickerPaper: {
263485
263551
  overflow: "hidden",
263486
263552
  "& > ul": {
@@ -263538,7 +263604,7 @@ function ChannelColorPickerMenu(props) {
263538
263604
  setColor2([rgb2.r, rgb2.g, rgb2.b]);
263539
263605
  }
263540
263606
  }
263541
- const { classes: classes2 } = useStyles$h();
263607
+ const { classes: classes2 } = useStyles$i();
263542
263608
  const currentColor = color2 ? colorArrayToString(color2) : colorArrayToString(getDefaultColor(theme));
263543
263609
  const isWritable = typeof setColor2 === "function";
263544
263610
  const derivedOpen = isWritable ? isStaticColor && visible ? open2 : false : false;
@@ -263546,7 +263612,7 @@ function ChannelColorPickerMenu(props) {
263546
263612
  [classes2.colorIconOutline]: theme !== "dark" && isEqual$3(color2, [255, 255, 255])
263547
263613
  }), style: { backgroundColor: currentColor } }) : isColormap && visible && featureValueColormap ? jsx$1("div", { className: classes2.colorIcon, children: jsx$1("svg", { width: "18", height: "18", children: jsx$1("image", { x: 0, y: 0, width: 18, height: 18, preserveAspectRatio: "none", href: getXlinkHref(featureValueColormap), clipPath: "inset(0% round 4px)" }) }) }) : jsx$1("div", { className: classes2.colorIcon }), buttonClassName: classes2.colorIconButton, withPaper: true, paperClassName: classes2.colorPickerPaper, "aria-label": "Open color picker menu", children: jsx$1(Twitter$1, { className: classes2.colorPicker, disableAlpha: true, width: 108, triangle: "hide", colors: defaultPalette, color: currentColor, onChangeComplete: handleColorChange }) });
263548
263614
  }
263549
- const useStyles$g = makeStyles()(() => ({
263615
+ const useStyles$h = makeStyles()(() => ({
263550
263616
  layerTypeSpotIcon: {
263551
263617
  height: "100%",
263552
263618
  marginLeft: "1px",
@@ -263559,7 +263625,7 @@ const useStyles$g = makeStyles()(() => ({
263559
263625
  function SpotLayerEllipsisMenu(props) {
263560
263626
  const { strokeWidth, setStrokeWidth, filled, setFilled, featureSelection, obsColorEncoding, setObsColorEncoding, featureValueColormap, setFeatureValueColormap, featureValueColormapRange, setFeatureValueColormapRange, tooltipsVisible, setTooltipsVisible, tooltipCrosshairsVisible, setTooltipCrosshairsVisible, legendVisible, setLegendVisible } = props;
263561
263627
  const [open2, setOpen] = useState(false);
263562
- const { classes: classes2 } = useStyles$g();
263628
+ const { classes: classes2 } = useStyles$h();
263563
263629
  const { classes: selectClasses2 } = useSelectStyles();
263564
263630
  const { classes: menuClasses } = useEllipsisMenuStyles();
263565
263631
  const filledId = $bdb11010cef70236$export$f680877a34711e37();
@@ -263581,7 +263647,7 @@ function SpotLayerController(props) {
263581
263647
  const Visibility = useMemo$1(() => visibleSetting ? VisibilityIcon : VisibilityOffIcon, [visibleSetting]);
263582
263648
  const isStaticColor = obsColorEncoding === "spatialLayerColor";
263583
263649
  const isColormap = obsColorEncoding === "geneSelection";
263584
- const { classes: classes2 } = useStyles$g();
263650
+ const { classes: classes2 } = useStyles$h();
263585
263651
  const { classes: lcClasses } = useControllerSectionStyles();
263586
263652
  const { classes: menuClasses } = useEllipsisMenuStyles();
263587
263653
  const handleVisibleChange = useCallback(() => {
@@ -263591,7 +263657,7 @@ function SpotLayerController(props) {
263591
263657
  const handleOpacityChange = useCallback((e3, v) => setOpacity2(v), [setOpacity2]);
263592
263658
  return jsx$1(Grid$1, { className: lcClasses.layerControllerGrid, children: jsx$1(Paper, { elevation: 4, className: lcClasses.layerControllerRoot, children: jsxs(Grid$1, { container: true, direction: "row", justifyContent: "space-between", children: [jsx$1(Grid$1, { size: 1, children: jsx$1(Button, { onClick: handleVisibleChange, className: menuClasses.imageLayerVisibleButton, "aria-label": "Toggle spot layer visibility", children: jsx$1(Visibility, {}) }) }), jsx$1(Grid$1, { size: 1, children: jsx$1(ChannelColorPickerMenu, { theme, color: color2, setColor: setColor2, palette, isStaticColor, isColormap, featureValueColormap, visible }) }), jsx$1(Grid$1, { size: 6, children: jsx$1(Typography, { className: menuClasses.imageLayerName, children: label2 }) }), jsx$1(Grid$1, { size: 2, children: jsx$1(Slider, { value: opacity2, min: 0, max: 1, step: 1e-3, onChange: handleOpacityChange, className: menuClasses.imageLayerOpacitySlider, orientation: "horizontal", "aria-label": `Adjust opacity for layer ${label2}` }) }), jsx$1(Grid$1, { size: 1, children: jsx$1(SpotLayerEllipsisMenu, { filled, setFilled, strokeWidth, setStrokeWidth, featureSelection, obsColorEncoding, setObsColorEncoding, featureValueColormap, setFeatureValueColormap, featureValueColormapRange, setFeatureValueColormapRange, tooltipsVisible, setTooltipsVisible, tooltipCrosshairsVisible, setTooltipCrosshairsVisible, legendVisible, setLegendVisible }) }), jsx$1(Grid$1, { size: 1, children: jsx$1(SvgSpots, { className: classes2.layerTypeSpotIcon }) })] }) }) });
263593
263659
  }
263594
- const useStyles$f = makeStyles()(() => ({
263660
+ const useStyles$g = makeStyles()(() => ({
263595
263661
  layerFeatureControllerGrid: {
263596
263662
  padding: "0",
263597
263663
  flexWrap: "nowrap"
@@ -263636,14 +263702,15 @@ function LayerPerFeatureController(props) {
263636
263702
  ]);
263637
263703
  }
263638
263704
  }, [featureName, featureColor, setFeatureColor, featureColorIndex]);
263639
- const { classes: classes2 } = useStyles$f();
263705
+ const { classes: classes2 } = useStyles$g();
263640
263706
  const { classes: lcClasses } = useControllerSectionStyles();
263641
263707
  const { classes: menuClasses } = useEllipsisMenuStyles();
263642
263708
  const [open2, setOpen] = useState(false);
263643
263709
  const { colorPickerColor, colorPickerReadable, colorPickerWritable, colorPickerTooltip } = useMemo$1(() => {
263644
263710
  if (obsColorEncoding === "geneSelection") {
263711
+ const storedColor = featureColor?.find((fc) => fc.name === featureName)?.color;
263645
263712
  return {
263646
- colorPickerColor: featureColor?.[featureColorIndex]?.color ?? spatialLayerColor,
263713
+ colorPickerColor: storedColor ?? randomByFeatureColor ?? spatialLayerColor,
263647
263714
  colorPickerReadable: true,
263648
263715
  colorPickerWritable: true,
263649
263716
  colorPickerTooltip: null
@@ -263689,7 +263756,7 @@ function LayerPerFeatureController(props) {
263689
263756
  ]);
263690
263757
  return jsx$1(Grid$1, { className: lcClasses.layerControllerGrid, children: jsxs(Paper, { elevation: 2, className: lcClasses.layerControllerSubRow, children: [jsxs(Grid$1, { container: true, direction: "row", justifyContent: "space-between", children: [jsx$1(Grid$1, { size: 1, children: jsx$1(Button, { onClick: handleRemoveFeature, className: menuClasses.imageLayerVisibleButton, "aria-label": "Remove feature", children: jsx$1(ClearIcon, {}) }) }), jsx$1(Grid$1, { size: 1, children: jsx$1(Tooltip$3, { title: colorPickerTooltip ?? "", disableHoverListener: !colorPickerTooltip, placement: "top", children: jsx$1("span", { children: jsx$1(ChannelColorPickerMenu, { theme, color: colorPickerColor, setColor: colorPickerWritable ? handleColorChange : null, palette, isStaticColor: colorPickerReadable, isColormap: false, featureValueColormap, visible: true }) }) }) }), jsx$1(Grid$1, { size: 6, children: jsx$1(Typography, { className: menuClasses.imageLayerName, children: featureName }) }), jsx$1(Grid$1, { size: 2, sx: { paddingRight: "12px", overflow: "visible" }, children: jsx$1(Slider, { value: opacity2, min: 0, max: 1, step: 1e-3, onChange: handleOpacityChange, className: menuClasses.imageLayerOpacitySlider, orientation: "horizontal", "aria-label": `Adjust opacity for layer ${featureName}` }) }), jsxs(Grid$1, { size: 1, container: true, direction: "row", children: [jsx$1(SvgPoints, { className: classes2.layerTypeFeatureIcon }), null] })] }), loadingDoneFraction < 1 ? jsx$1(Grid$1, { size: 12, container: true, direction: "column", justifyContent: "space-between", className: classes2.layerFeatureControllerGrid, children: jsx$1(LinearProgress, { variant: loadingDoneFraction === 0 ? "indeterminate" : "determinate", value: loadingDoneFraction * 100 }) }) : null] }) }, featureName);
263691
263758
  }
263692
- const useStyles$e = makeStyles()(() => ({
263759
+ const useStyles$f = makeStyles()(() => ({
263693
263760
  pointLayerButton: {
263694
263761
  borderStyle: "dashed",
263695
263762
  marginTop: "10px",
@@ -263718,7 +263785,7 @@ const useStyles$e = makeStyles()(() => ({
263718
263785
  }
263719
263786
  }));
263720
263787
  function PointLayerEllipsisMenu(props) {
263721
- const { featureSelection, featureFilterMode, setFeatureFilterMode, obsColorEncoding, setObsColorEncoding, featureValueColormapRange, setFeatureValueColormapRange, tooltipsVisible, setTooltipsVisible, tooltipCrosshairsVisible, setTooltipCrosshairsVisible, legendVisible, setLegendVisible } = props;
263788
+ const { featureSelection, featureFilterMode, setFeatureFilterMode, obsColorEncoding, setObsColorEncoding, featureValueColormap, setFeatureValueColormap, featureValueColormapRange, setFeatureValueColormapRange, tooltipsVisible, setTooltipsVisible, tooltipCrosshairsVisible, setTooltipCrosshairsVisible, legendVisible, setLegendVisible } = props;
263722
263789
  const [open2, setOpen] = useState(false);
263723
263790
  const { classes: selectClasses2 } = useSelectStyles();
263724
263791
  const { classes: menuClasses } = useEllipsisMenuStyles();
@@ -263728,7 +263795,8 @@ function PointLayerEllipsisMenu(props) {
263728
263795
  const crosshairsVisibleId = $bdb11010cef70236$export$f680877a34711e37();
263729
263796
  const legendVisibleId = $bdb11010cef70236$export$f680877a34711e37();
263730
263797
  const featureFilterModeId = $bdb11010cef70236$export$f680877a34711e37();
263731
- return jsxs(PopperMenu, { open: open2, setOpen, buttonIcon: jsx$1(MoreVertIcon, {}), buttonClassName: menuClasses.imageLayerMenuButton, containerClassName: menuClasses.imageLayerPopperContainer, withPaper: true, "aria-label": "Open point layer options menu", children: [jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: quantitativeColormapId, children: "Color Encoding: " }), jsxs(NativeSelect, { onChange: (e3) => setObsColorEncoding(e3.target.value), value: obsColorEncoding, inputProps: { id: quantitativeColormapId, "aria-label": "Color encoding selector" }, classes: { root: selectClasses2.selectRoot }, children: [jsx$1("option", { value: "spatialLayerColor", children: "Static Color" }), jsx$1("option", { value: "geneSelection", children: "Feature Color" }), jsx$1("option", { value: "randomByFeature", children: "Random Color per Feature" }), jsx$1("option", { value: "random", children: "Random Color per Point" })] })] }), jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: featureFilterModeId, children: "Filter to Feature Selection: " }), jsx$1(Checkbox$1, { color: "primary", className: menuClasses.menuItemCheckbox, checked: featureFilterMode === "featureSelection", onChange: (e3, v) => setFeatureFilterMode(v ? "featureSelection" : null), slotProps: { input: { id: featureFilterModeId, "aria-label": "Toggle feature filter mode" } } })] }), jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: colormapRangeId, children: "Colormap Range: " }), jsx$1(Slider, { disabled: obsColorEncoding !== "geneSelection", value: featureValueColormapRange, min: 0, max: 1, step: 0.01, onChange: (e3, v) => setFeatureValueColormapRange(v), className: menuClasses.menuItemSlider, orientation: "horizontal", id: colormapRangeId, getAriaLabel: (index2) => index2 === 0 ? "Low value colormap range slider" : "High value colormap range slider" })] }), jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: tooltipsVisibleId, children: "Tooltips Visible: " }), jsx$1(Checkbox$1, { color: "primary", className: menuClasses.menuItemCheckbox, checked: tooltipsVisible, onChange: (e3, v) => setTooltipsVisible(v), slotProps: { input: { id: tooltipsVisibleId, "aria-label": "Toggle tooltip visibility" } } })] }), jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: crosshairsVisibleId, children: "Tooltip Crosshairs Visible: " }), jsx$1(Checkbox$1, { color: "primary", className: menuClasses.menuItemCheckbox, checked: tooltipCrosshairsVisible, onChange: (e3, v) => setTooltipCrosshairsVisible(v), slotProps: { input: { id: crosshairsVisibleId, "aria-label": "Toggle tooltip crosshair visibility" } } })] }), jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: legendVisibleId, children: "Legend Visible: " }), jsx$1(Checkbox$1, { color: "primary", className: menuClasses.menuItemCheckbox, checked: legendVisible, onChange: (e3, v) => setLegendVisible(v), slotProps: { input: { id: legendVisibleId, "aria-label": "Toggle legend visibility" } } })] })] });
263798
+ const colormapSelectorId = $bdb11010cef70236$export$f680877a34711e37();
263799
+ return jsxs(PopperMenu, { open: open2, setOpen, buttonIcon: jsx$1(MoreVertIcon, {}), buttonClassName: menuClasses.imageLayerMenuButton, containerClassName: menuClasses.imageLayerPopperContainer, withPaper: true, getAriaLabel: () => "Open point layer options menu", children: [jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: colormapSelectorId, children: "Colormap: " }), jsx$1(NativeSelect, { onChange: (e3) => setFeatureValueColormap(e3.target.value), value: featureValueColormap, inputProps: { id: colormapSelectorId, "aria-label": "Colormap selector" }, classes: { root: selectClasses2.selectRoot }, children: GLSL_COLORMAPS.map((cmap) => jsx$1("option", { value: cmap, children: cmap }, cmap)) })] }), jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: quantitativeColormapId, children: "Color Encoding: " }), jsxs(NativeSelect, { onChange: (e3) => setObsColorEncoding(e3.target.value), value: obsColorEncoding, inputProps: { id: quantitativeColormapId, "aria-label": "Color encoding selector" }, classes: { root: selectClasses2.selectRoot }, children: [jsx$1("option", { value: "spatialLayerColor", children: "Static Color" }), jsx$1("option", { value: "geneSelection", children: "Feature Color" }), jsx$1("option", { value: "randomByFeature", children: "Random Color per Feature" }), jsx$1("option", { value: "random", children: "Random Color per Point" })] })] }), jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: featureFilterModeId, children: "Filter to Feature Selection: " }), jsx$1(Checkbox$1, { color: "primary", className: menuClasses.menuItemCheckbox, checked: featureFilterMode === "featureSelection", onChange: (e3, v) => setFeatureFilterMode(v ? "featureSelection" : null), slotProps: { input: { id: featureFilterModeId, "aria-label": "Toggle feature filter mode" } } })] }), jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: colormapRangeId, children: "Colormap Range: " }), jsx$1(Slider, { disabled: obsColorEncoding !== "geneSelection", value: featureValueColormapRange, min: 0, max: 1, step: 0.01, onChange: (e3, v) => setFeatureValueColormapRange(v), className: menuClasses.menuItemSlider, orientation: "horizontal", id: colormapRangeId, getAriaLabel: (index2) => index2 === 0 ? "Low value colormap range slider" : "High value colormap range slider" })] }), jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: tooltipsVisibleId, children: "Tooltips Visible: " }), jsx$1(Checkbox$1, { color: "primary", className: menuClasses.menuItemCheckbox, checked: tooltipsVisible, onChange: (e3, v) => setTooltipsVisible(v), slotProps: { input: { id: tooltipsVisibleId, "aria-label": "Toggle tooltip visibility" } } })] }), jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: crosshairsVisibleId, children: "Tooltip Crosshairs Visible: " }), jsx$1(Checkbox$1, { color: "primary", className: menuClasses.menuItemCheckbox, checked: tooltipCrosshairsVisible, onChange: (e3, v) => setTooltipCrosshairsVisible(v), slotProps: { input: { id: crosshairsVisibleId, "aria-label": "Toggle tooltip crosshair visibility" } } })] }), jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: legendVisibleId, children: "Legend Visible: " }), jsx$1(Checkbox$1, { color: "primary", className: menuClasses.menuItemCheckbox, checked: legendVisible, onChange: (e3, v) => setLegendVisible(v), slotProps: { input: { id: legendVisibleId, "aria-label": "Toggle legend visibility" } } })] })] });
263732
263800
  }
263733
263801
  function PointLayerController(props) {
263734
263802
  const { theme, layerScope, layerCoordination, setLayerCoordination, palette = null, pointMatrixIndicesData, tiledPointsLoadingProgress, layerPerFeatureForPoints } = props;
@@ -263758,7 +263826,7 @@ function PointLayerController(props) {
263758
263826
  }, [featureColor, featureSelection]);
263759
263827
  const isStaticColor = obsColorEncoding === "spatialLayerColor" || obsColorEncoding === "geneSelection";
263760
263828
  const showStaticColor = obsColorEncoding === "spatialLayerColor" || obsColorEncoding === "geneSelection" && hasUnspecifiedFeatureColors;
263761
- const isColormap = false;
263829
+ const isColormap = obsColorEncoding === "geneSelection";
263762
263830
  const hasSingleSelectedFeature = obsColorEncoding === "geneSelection" && Array.isArray(featureSelection) && featureSelection.length === 1;
263763
263831
  const color2 = useMemo$1(() => {
263764
263832
  if (showStaticColor) {
@@ -263805,7 +263873,7 @@ function PointLayerController(props) {
263805
263873
  featureSelection,
263806
263874
  showStaticColor
263807
263875
  ]);
263808
- const { classes: classes2 } = useStyles$e();
263876
+ const { classes: classes2 } = useStyles$f();
263809
263877
  const { classes: lcClasses } = useControllerSectionStyles();
263810
263878
  const { classes: menuClasses } = useEllipsisMenuStyles();
263811
263879
  const handleVisibleChange = useCallback(() => {
@@ -263817,9 +263885,24 @@ function PointLayerController(props) {
263817
263885
  const [coloringTabIndex, setColoringTabIndex] = useState(0);
263818
263886
  const { featureIndex } = pointMatrixIndicesData || {};
263819
263887
  const hasSelectedFeatures = Array.isArray(featureSelection) && featureSelection.length > 0;
263820
- return jsxs(Grid$1, { className: lcClasses.layerControllerGrid, children: [jsxs(Paper, { elevation: 4, className: lcClasses.layerControllerRoot, children: [jsxs(Grid$1, { container: true, direction: "row", justifyContent: "space-between", children: [jsx$1(Grid$1, { size: 1, children: jsx$1(Button, { onClick: handleVisibleChange, className: menuClasses.imageLayerVisibleButton, "aria-label": "Toggle layer visibility", children: jsx$1(Visibility, {}) }) }), jsx$1(Grid$1, { size: 1, children: jsx$1(ChannelColorPickerMenu, { theme, color: color2, setColor: setColor2, palette, isStaticColor, isColormap, featureValueColormap, visible }) }), jsx$1(Grid$1, { size: 6, children: jsx$1(Typography, { className: menuClasses.imageLayerName, children: label2 }) }), jsx$1(Grid$1, { size: 2, children: jsx$1(Slider, { value: opacity2, min: 0, max: 1, step: 1e-3, onChange: handleOpacityChange, className: menuClasses.imageLayerOpacitySlider, orientation: "horizontal", "aria-label": `Adjust opacity for layer ${label2}` }) }), jsx$1(Grid$1, { size: 1, children: jsx$1(PointLayerEllipsisMenu, { featureSelection, obsColorEncoding, setObsColorEncoding, featureValueColormapRange, setFeatureValueColormapRange, tooltipsVisible, setTooltipsVisible, tooltipCrosshairsVisible, setTooltipCrosshairsVisible, legendVisible, setLegendVisible, featureFilterMode, setFeatureFilterMode }) }), jsxs(Grid$1, { size: 1, container: true, direction: "row", children: [jsx$1(SvgPoints, { className: classes2.layerTypePointIcon }), null] })] }), loadingDoneFraction < 1 ? jsx$1(Grid$1, { size: 12, container: true, direction: "column", justifyContent: "space-between", className: classes2.pointFeatureControllerGrid, children: jsx$1(LinearProgress, { variant: loadingDoneFraction === 0 ? "indeterminate" : "determinate", value: loadingDoneFraction * 100 }) }) : null, null] }), layerPerFeatureForPoints && hasSelectedFeatures ? jsxs(Fragment, { children: [featureSelection.map((featureName) => jsx$1(LayerPerFeatureController, { theme, featureName, featureColor, setFeatureColor, spatialLayerColor, featureIndex, featureValueColormap, featureSelection, setFeatureSelection, obsColorEncoding, loadingDoneFraction, opacity: opacity2, handleOpacityChange }, featureName)), jsx$1(Grid$1, { className: lcClasses.layerControllerGrid, children: jsx$1(Paper, { elevation: 2, className: lcClasses.layerControllerSubRow, children: jsxs(Grid$1, { container: true, direction: "row", justifyContent: "space-between", children: [jsx$1(Grid$1, { size: 1, children: jsx$1(Button, { onClick: () => setFeatureFilterMode(featureFilterMode === "featureSelection" ? null : "featureSelection"), className: menuClasses.imageLayerVisibleButton, "aria-label": "Toggle visibility of unselected points", children: featureFilterMode === "featureSelection" ? jsx$1(VisibilityOffIcon, {}) : jsx$1(VisibilityIcon, {}) }) }), jsx$1(Grid$1, { size: 1, children: jsx$1(ChannelColorPickerMenu, { theme, color: getDefaultColor("dark"), setColor: null, isStaticColor: true, isColormap: false, visible: true }) }), jsx$1(Grid$1, { size: 6, children: jsx$1(Typography, { className: menuClasses.imageLayerName, children: "Unselected" }) }), jsx$1(Grid$1, { size: 2, sx: { paddingRight: "12px", overflow: "visible" }, children: jsx$1(Slider, { value: opacity2, min: 0, max: 1, step: 1e-3, onChange: handleOpacityChange, className: menuClasses.imageLayerOpacitySlider, orientation: "horizontal", "aria-label": "Adjust opacity for unselected layer" }) }), jsx$1(Grid$1, { size: 1, children: jsx$1(SvgPoints, { className: classes2.layerTypePointIcon }) })] }) }) })] }) : null] });
263888
+ useEffect(() => {
263889
+ if (obsColorEncoding !== "geneSelection" || !Array.isArray(featureSelection))
263890
+ return;
263891
+ const cleanedFeatureColor = (featureColor ?? []).filter((fc) => featureSelection.includes(fc.name));
263892
+ const missingEntries = featureSelection.filter((featureName) => !cleanedFeatureColor.find((fc) => fc.name === featureName)).map((featureName) => {
263893
+ const varIdx = featureIndex?.indexOf(featureName) ?? -1;
263894
+ const fColor = varIdx >= 0 ? PALETTE[varIdx % PALETTE.length] : getDefaultColor(theme);
263895
+ return { name: featureName, color: fColor };
263896
+ });
263897
+ const nextFeatureColor = [...cleanedFeatureColor, ...missingEntries];
263898
+ const changed = nextFeatureColor.length !== featureColor?.length || missingEntries.length > 0;
263899
+ if (changed) {
263900
+ setFeatureColor(nextFeatureColor);
263901
+ }
263902
+ }, [featureSelection, obsColorEncoding, featureIndex, featureColor]);
263903
+ return jsxs(Grid$1, { className: lcClasses.layerControllerGrid, children: [jsxs(Paper, { elevation: 4, className: lcClasses.layerControllerRoot, children: [jsxs(Grid$1, { container: true, direction: "row", justifyContent: "space-between", children: [jsx$1(Grid$1, { size: 1, children: jsx$1(Button, { onClick: handleVisibleChange, className: menuClasses.imageLayerVisibleButton, "aria-label": "Toggle layer visibility", children: jsx$1(Visibility, {}) }) }), jsx$1(Grid$1, { size: 1, children: jsx$1(ChannelColorPickerMenu, { theme, color: color2, setColor: setColor2, palette, isStaticColor, isColormap, featureValueColormap, visible }) }), jsx$1(Grid$1, { size: 6, children: jsx$1(Typography, { className: menuClasses.imageLayerName, children: label2 }) }), jsx$1(Grid$1, { size: 2, children: jsx$1(Slider, { value: opacity2, min: 0, max: 1, step: 1e-3, onChange: handleOpacityChange, className: menuClasses.imageLayerOpacitySlider, orientation: "horizontal", "aria-label": `Adjust opacity for layer ${label2}` }) }), jsx$1(Grid$1, { size: 1, children: jsx$1(PointLayerEllipsisMenu, { featureSelection, obsColorEncoding, setObsColorEncoding, featureValueColormapRange, setFeatureValueColormapRange, tooltipsVisible, setTooltipsVisible, tooltipCrosshairsVisible, setTooltipCrosshairsVisible, legendVisible, setLegendVisible, featureFilterMode, setFeatureFilterMode, featureValueColormap, setFeatureValueColormap }) }), jsxs(Grid$1, { size: 1, container: true, direction: "row", children: [jsx$1(SvgPoints, { className: classes2.layerTypePointIcon }), null] })] }), loadingDoneFraction < 1 ? jsx$1(Grid$1, { size: 12, container: true, direction: "column", justifyContent: "space-between", className: classes2.pointFeatureControllerGrid, children: jsx$1(LinearProgress, { variant: loadingDoneFraction === 0 ? "indeterminate" : "determinate", value: loadingDoneFraction * 100 }) }) : null, null] }), layerPerFeatureForPoints && hasSelectedFeatures ? jsxs(Fragment, { children: [featureSelection.map((featureName) => jsx$1(LayerPerFeatureController, { theme, featureName, featureColor, setFeatureColor, spatialLayerColor, featureIndex, featureValueColormap, featureSelection, setFeatureSelection, obsColorEncoding, loadingDoneFraction, opacity: opacity2, handleOpacityChange }, featureName)), jsx$1(Grid$1, { className: lcClasses.layerControllerGrid, children: jsx$1(Paper, { elevation: 2, className: lcClasses.layerControllerSubRow, children: jsxs(Grid$1, { container: true, direction: "row", justifyContent: "space-between", children: [jsx$1(Grid$1, { size: 1, children: jsx$1(Button, { onClick: () => setFeatureFilterMode(featureFilterMode === "featureSelection" ? null : "featureSelection"), className: menuClasses.imageLayerVisibleButton, "aria-label": "Toggle visibility of unselected points", children: featureFilterMode === "featureSelection" ? jsx$1(VisibilityOffIcon, {}) : jsx$1(VisibilityIcon, {}) }) }), jsx$1(Grid$1, { size: 1, children: jsx$1(ChannelColorPickerMenu, { theme, color: getDefaultColor("dark"), setColor: null, isStaticColor: true, isColormap: false, visible: true }) }), jsx$1(Grid$1, { size: 6, children: jsx$1(Typography, { className: menuClasses.imageLayerName, children: "Unselected" }) }), jsx$1(Grid$1, { size: 2, sx: { paddingRight: "12px", overflow: "visible" }, children: jsx$1(Slider, { value: opacity2, min: 0, max: 1, step: 1e-3, onChange: handleOpacityChange, className: menuClasses.imageLayerOpacitySlider, orientation: "horizontal", "aria-label": "Adjust opacity for unselected layer" }) }), jsx$1(Grid$1, { size: 1, children: jsx$1(SvgPoints, { className: classes2.layerTypePointIcon }) })] }) }) })] }) : null] });
263821
263904
  }
263822
- const useStyles$d = makeStyles()(() => ({
263905
+ const useStyles$e = makeStyles()(() => ({
263823
263906
  layerTypeSegmentationIcon: {
263824
263907
  height: "100%",
263825
263908
  marginLeft: "1px",
@@ -263832,7 +263915,7 @@ const useStyles$d = makeStyles()(() => ({
263832
263915
  function SegmentationChannelEllipsisMenu(props) {
263833
263916
  const { obsType, featureType, featureValueType, strokeWidth, setStrokeWidth, filled, setFilled, featureSelection, obsColorEncoding, setObsColorEncoding, featureValueColormap, setFeatureValueColormap, featureValueColormapRange, setFeatureValueColormapRange, tooltipsVisible, setTooltipsVisible, tooltipCrosshairsVisible, setTooltipCrosshairsVisible, legendVisible, setLegendVisible } = props;
263834
263917
  const [open2, setOpen] = useState(false);
263835
- const { classes: classes2 } = useStyles$d();
263918
+ const { classes: classes2 } = useStyles$e();
263836
263919
  const { classes: selectClasses2 } = useSelectStyles();
263837
263920
  const { classes: menuClasses } = useEllipsisMenuStyles();
263838
263921
  const filledId = $bdb11010cef70236$export$f680877a34711e37();
@@ -263851,7 +263934,7 @@ function SegmentationChannelController(props) {
263851
263934
  const Visibility = useMemo$1(() => visibleSetting ? VisibilityIcon : VisibilityOffIcon, [visibleSetting]);
263852
263935
  const isStaticColor = obsColorEncoding === "spatialChannelColor";
263853
263936
  const isColormap = obsColorEncoding === "geneSelection";
263854
- const { classes: classes2 } = useStyles$d();
263937
+ const { classes: classes2 } = useStyles$e();
263855
263938
  const { classes: lcClasses } = useControllerSectionStyles();
263856
263939
  const { classes: menuClasses } = useEllipsisMenuStyles();
263857
263940
  const handleVisibleChange = useCallback(() => {
@@ -265092,7 +265175,7 @@ function useBaseQuery(options, Observer, queryClient) {
265092
265175
  function useQuery(options, queryClient) {
265093
265176
  return useBaseQuery(options, QueryObserver);
265094
265177
  }
265095
- const useStyles$c = makeStyles()(() => ({
265178
+ const useStyles$d = makeStyles()(() => ({
265096
265179
  channelMenuButton: {
265097
265180
  backgroundColor: "transparent",
265098
265181
  padding: "3px 0",
@@ -265106,7 +265189,7 @@ const useStyles$c = makeStyles()(() => ({
265106
265189
  function ChannelOptions(props) {
265107
265190
  const { onRemove: onRemove2, onResetWindowUsingIQR, showValueExtent, setShowValueExtent, numResolutions, spatialMaxResolution, setSpatialMaxResolution } = props;
265108
265191
  const [open2, setOpen] = useState(false);
265109
- const { classes: classes2 } = useStyles$c();
265192
+ const { classes: classes2 } = useStyles$d();
265110
265193
  const { classes: selectClasses2 } = useSelectStyles();
265111
265194
  const { classes: menuClasses } = useEllipsisMenuStyles();
265112
265195
  function handleRemove() {
@@ -265119,7 +265202,7 @@ function ChannelOptions(props) {
265119
265202
  const domainTypeId = $bdb11010cef70236$export$f680877a34711e37();
265120
265203
  return jsxs(PopperMenu, { open: open2, setOpen, buttonIcon: jsx$1(MoreVertIcon, { fontSize: "small" }), buttonClassName: classes2.channelMenuButton, containerClassName: menuClasses.imageLayerPopperContainer, placement: "bottom-end", withPaper: true, "aria-label": "Open channel options menu", children: [jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: domainTypeId, children: "Best Resolution: " }), jsx$1(Slider, { value: spatialMaxResolution || 1, valueLabelDisplay: "auto", onChange: (e3, v) => setSpatialMaxResolution(v), min: 1, max: numResolutions - 1, step: 1, orientation: "horizontal" })] }), jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: domainTypeId, children: "Slider Extent: " }), jsxs(NativeSelect, { onChange: handleDomainTypeChange, value: showValueExtent ? "Value Min/Max" : "Dtype Min/Max", inputProps: { id: domainTypeId, "aria-label": "Slider extent selector" }, classes: { root: selectClasses2.selectRoot }, children: [jsx$1("option", { value: "Value Min/Max", children: "Value Min/Max" }), jsx$1("option", { value: "Dtype Min/Max", children: "Dtype Min/Max" })] })] }), jsx$1(MenuItem, { dense: true, disableGutters: true, component: "button", onClick: onResetWindowUsingIQR, className: classes2.menuItemButton, "aria-label": "Click to use IQR for channel", children: "Reset window using IQR" }), jsx$1(MenuItem, { dense: true, disableGutters: true, component: "button", onClick: handleRemove, className: classes2.menuItemButton, "aria-label": "Click to remove channel", children: "Remove channel" })] });
265121
265204
  }
265122
- const useStyles$b = makeStyles()(() => ({
265205
+ const useStyles$c = makeStyles()(() => ({
265123
265206
  channelSlider: {
265124
265207
  marginTop: "7px"
265125
265208
  }
@@ -265129,7 +265212,7 @@ function ChannelSlider(props) {
265129
265212
  const rgbColor = toRgbUIString(colormapOn, color2, theme);
265130
265213
  const dtype = image2?.getDtype();
265131
265214
  const fullDomain = dtype ? DOMAINS[dtype] : [0, 0];
265132
- const { classes: classes2 } = useStyles$b();
265215
+ const { classes: classes2 } = useStyles$c();
265133
265216
  useEffect(() => {
265134
265217
  if (!window2 && !disabled && Array.isArray(minMaxDomain)) {
265135
265218
  setWindow2(minMaxDomain);
@@ -265144,7 +265227,7 @@ function ChannelSlider(props) {
265144
265227
  return `${labelPrefix} for ${rgbColor} colormap channel`;
265145
265228
  }, getAriaValueText: () => `Current colormap values: ${rgbColor}-${window2}`, min: min2, max: max2, step, orientation: "horizontal", className: classes2.channelSlider, style: { color: rgbColor }, disabled });
265146
265229
  }
265147
- const useStyles$a = makeStyles()(() => ({
265230
+ const useStyles$b = makeStyles()(() => ({
265148
265231
  visibilityCheckbox: {
265149
265232
  padding: "10px 0"
265150
265233
  }
@@ -265158,10 +265241,10 @@ function getCheckboxColor(colormapOn, color2, theme) {
265158
265241
  function ChannelVisibilityCheckbox(props) {
265159
265242
  const { color: color2, visible, setVisible, disabled, theme, colormapOn } = props;
265160
265243
  const rgbColor = getCheckboxColor(colormapOn, color2, theme);
265161
- const { classes: classes2 } = useStyles$a();
265244
+ const { classes: classes2 } = useStyles$b();
265162
265245
  return jsx$1(Checkbox$1, { onChange: (e3, v) => setVisible(v), checked: visible, disabled, className: classes2.visibilityCheckbox, style: { color: rgbColor, "&$checked": { color: rgbColor } }, slotProps: { input: { "aria-label": "Toggle channel visibility" } } });
265163
265246
  }
265164
- const useStyles$9 = makeStyles()(() => ({
265247
+ const useStyles$a = makeStyles()(() => ({
265165
265248
  oneLineChannelSelect: {
265166
265249
  width: "90%",
265167
265250
  marginLeft: "5%",
@@ -265170,7 +265253,7 @@ const useStyles$9 = makeStyles()(() => ({
265170
265253
  }));
265171
265254
  function ChannelSelectionDropdown(props) {
265172
265255
  const { featureIndex, channelsSortOrder, targetC, setTargetC, setWindow: setWindow2, disabled } = props;
265173
- const { classes: classes2 } = useStyles$9();
265256
+ const { classes: classes2 } = useStyles$a();
265174
265257
  const { classes: selectClasses2 } = useSelectStyles();
265175
265258
  function handleChange(event2) {
265176
265259
  setTargetC(event2.target.value === "" ? null : Number(event2.target.value));
@@ -265305,7 +265388,7 @@ function LoadingStatusIndicator(props) {
265305
265388
  }
265306
265389
  return jsxs(Grid$1, { container: true, px: 0.5, children: [jsx$1(Grid$1, { size: 6, alignItems: "start", children: jsx$1(Grid$1, { container: true, alignItems: "center", gap: 1, children: statusColor !== "success" ? jsx$1(StatusChip, { label: statusLabel, color: statusColor, size: "small" }) : null }) }), jsx$1(Grid$1, { size: 6, px: 1, my: 0.25, children: jsx$1(LoadingText, { bricksLoaded, currentRequestCount, totalBricksRequested, isLoading }) }), jsx$1(Grid$1, { size: 12, children: jsxs(Grid$1, { container: true, direction: "row", spacing: 2, alignItems: "center", wrap: "nowrap", children: [jsx$1(LoadingControlButton, { startIcon: jsx$1(Stop, {}), onClick: onStopLoading, disabled: noNewRequests, ariaLabel: "Stop loading data and render at highest resolution", children: "Stop Loading" }), jsx$1(LoadingControlButton, { startIcon: jsx$1(Replay, {}), onClick: onRestartLoading, disabled: !noNewRequests && !isLoading, ariaLabel: "Restart data loading", children: "Restart Loading" })] }) }), jsx$1(Grid$1, { size: 12, visibility: isLoading ? "visible" : "hidden", mt: 1, mb: 0.5, children: jsx$1(LinearProgress, { variant: "determinate", value: percentage }) })] });
265307
265390
  }
265308
- const useStyles$8 = makeStyles()(() => ({
265391
+ const useStyles$9 = makeStyles()(() => ({
265309
265392
  imageLayerButton: {
265310
265393
  borderStyle: "dashed",
265311
265394
  marginTop: "10px",
@@ -265448,7 +265531,7 @@ function ImageLayerController(props) {
265448
265531
  }, [visible, setVisible]);
265449
265532
  const handleOpacityChange = useCallback((e3, v) => setOpacity2(v), [setOpacity2]);
265450
265533
  const handleOpenChange = useCallback(() => setOpen((prev2) => !prev2), []);
265451
- const { classes: classes2 } = useStyles$8();
265534
+ const { classes: classes2 } = useStyles$9();
265452
265535
  const { classes: menuClasses } = useEllipsisMenuStyles();
265453
265536
  const { classes: controllerSectionClasses } = useControllerSectionStyles();
265454
265537
  const isMultiChannel = photometricInterpretation !== "RGB";
@@ -265460,7 +265543,7 @@ function ImageLayerController(props) {
265460
265543
  return jsx$1(ImageChannelController, { theme, coordinationScopesRaw, layerScope, channelScope: cScope, targetT, targetZ, targetC: channelIndex, setTargetC: setSpatialTargetC, visible: spatialChannelVisible, setVisible: setSpatialChannelVisible, opacity: spatialChannelOpacity, setOpacity: setSpatialChannelOpacity, color: spatialChannelColor, setColor: setSpatialChannelColor, window: spatialChannelWindow, setWindow: setSpatialChannelWindow, colormapOn: colormap !== null, featureIndex, image: image2, channelsSortOrder, spatialRenderingMode, numResolutions, spatialMaxResolution, setSpatialMaxResolution }, cScope);
265461
265544
  }), jsx$1(Button, { disabled: channelScopes.length === MAX_CHANNELS$1 || channelScopes.length === imageNumChannels, onClick: handleChannelAdd, fullWidth: true, variant: "outlined", className: classes2.imageLayerButton, startIcon: jsx$1(AddIcon, {}), size: "small", "aria-label": "Add a channel to this layer", children: "Add Channel" })] }) : null, is3dMode && image2 ? jsxs(Grid$1, { container: true, direction: "column", justifyContent: "space-between", className: classes2.imageChannelControllerGrid, children: [jsx$1(Typography, { className: classes2.clippingPanesLabel, children: "Clipping planes:" }), jsx$1(Grid$1, { size: 12, className: classes2.clippingSliders, children: jsx$1(ClippingSliders, { image: image2, spatialSliceX, spatialSliceY, spatialSliceZ, setSpatialSliceX, setSpatialSliceY, setSpatialSliceZ }) }), jsx$1(LoadingStatusIndicator, { loadingProgress: volumeLoadingProgress, onStopLoading: onStopVolumeLoading, onRestartLoading: onRestartVolumeLoading, stillRef: volumeStillRef, spatialRenderingMode })] }) : null] }) });
265462
265545
  }
265463
- const useStyles$7 = makeStyles()((theme) => ({
265546
+ const useStyles$8 = makeStyles()((theme) => ({
265464
265547
  dimensionsIcon: {
265465
265548
  height: "40px",
265466
265549
  width: "40px",
@@ -265484,7 +265567,7 @@ const useStyles$7 = makeStyles()((theme) => ({
265484
265567
  function GlobalDimensionSlider(props) {
265485
265568
  const { label: label2, targetValue, setTargetValue, min: min2 = 0, max: max2 = 0, spatialRenderingMode = null, setSpatialRenderingMode = null } = props;
265486
265569
  const { classes: lcClasses } = useControllerSectionStyles();
265487
- const { classes: classes2 } = useStyles$7();
265570
+ const { classes: classes2 } = useStyles$8();
265488
265571
  const isForZ = spatialRenderingMode !== null;
265489
265572
  const is3dMode = spatialRenderingMode === "3D";
265490
265573
  function handleRenderingModeChange(event2) {
@@ -265495,17 +265578,112 @@ function GlobalDimensionSlider(props) {
265495
265578
  }
265496
265579
  return jsx$1(Grid$1, { className: lcClasses.layerControllerGrid, children: jsx$1(Paper, { elevation: 4, className: lcClasses.layerControllerRoot, children: jsxs(Grid$1, { container: true, direction: "row", justifyContent: "space-between", children: [jsx$1(Grid$1, { size: 1, children: jsx$1(SvgDimensions, { className: classes2.dimensionsIcon }) }), jsx$1(Grid$1, { size: 1, children: jsx$1(Typography, { className: classes2.dimensionLabel, children: label2 }) }), jsx$1(Grid$1, { size: 8, children: !is3dMode ? jsx$1(Slider, { value: targetValue, min: min2, max: max2, step: 1, onChange: (e3, v) => setTargetValue(v), className: classes2.dimensionSlider, valueLabelDisplay: "auto", orientation: "horizontal", disabled: spatialRenderingMode === "3D", "aria-label": `${label2}-slice slider` }) : null }), jsx$1(Grid$1, { size: 2, children: isForZ ? jsx$1(FormGroup, { row: true, className: classes2.switchFormGroup, children: jsx$1(FormControlLabel$1, { control: jsx$1(Switch, { color: "primary", checked: spatialRenderingMode === "3D", onChange: handleRenderingModeChange, name: "is3dMode" }), label: "3D" }) }) : null })] }) }) });
265497
265580
  }
265581
+ const useStyles$7 = makeStyles()(() => ({
265582
+ layerTypeCentroidSegmentationIcon: {
265583
+ height: "100%",
265584
+ marginLeft: "1px",
265585
+ fill: "currentColor",
265586
+ fontSize: "24px",
265587
+ width: "50%",
265588
+ maxWidth: "24px"
265589
+ },
265590
+ divider: {
265591
+ width: "1px",
265592
+ height: "20px",
265593
+ background: "currentColor",
265594
+ flexShrink: 0,
265595
+ margin: "0 10px"
265596
+ },
265597
+ inlineLabel: {
265598
+ margin: "0px !important",
265599
+ lineHeight: 1,
265600
+ alignSelf: "center"
265601
+ },
265602
+ iconCell: {
265603
+ display: "flex",
265604
+ alignItems: "center",
265605
+ justifyContent: "center"
265606
+ }
265607
+ }));
265608
+ function CombinedEllipsisMenu(props) {
265609
+ const { obsType, featureType, featureValueType, filled, setFilled, strokeWidth, setStrokeWidth, obsColorEncoding, setObsColorEncoding, featureValueColormap, setFeatureValueColormap, featureValueColormapRange, setFeatureValueColormapRange, tooltipsVisible, setTooltipsVisible, tooltipCrosshairsVisible, setTooltipCrosshairsVisible, legendVisible, setLegendVisible, setPointObsColorEncoding, setPointFeatureValueColormap, setPointFeatureValueColormapRange, onColorEncodingChange } = props;
265610
+ const [open2, setOpen] = useState(false);
265611
+ const { classes: selectClasses2 } = useSelectStyles();
265612
+ const { classes: menuClasses } = useEllipsisMenuStyles();
265613
+ const filledId = $bdb11010cef70236$export$f680877a34711e37();
265614
+ const strokeWidthId = $bdb11010cef70236$export$f680877a34711e37();
265615
+ const colorEncodingId = $bdb11010cef70236$export$f680877a34711e37();
265616
+ const colormapId = $bdb11010cef70236$export$f680877a34711e37();
265617
+ const colormapRangeId = $bdb11010cef70236$export$f680877a34711e37();
265618
+ const tooltipsId = $bdb11010cef70236$export$f680877a34711e37();
265619
+ const crosshairsId = $bdb11010cef70236$export$f680877a34711e37();
265620
+ const legendId = $bdb11010cef70236$export$f680877a34711e37();
265621
+ return jsxs(PopperMenu, { open: open2, setOpen, buttonIcon: jsx$1(MoreVertIcon, {}), buttonClassName: menuClasses.imageLayerMenuButton, containerClassName: menuClasses.imageLayerPopperContainer, withPaper: true, getAriaLabel: () => "Open combined centroid and segmentation layer options menu", children: [jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: filledId, children: "Filled: " }), jsx$1(Checkbox$1, { color: "primary", className: menuClasses.menuItemCheckbox, checked: filled, onChange: (e3, v) => setFilled(v), slotProps: { input: { id: filledId } } })] }), jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: strokeWidthId, children: "Stroke width:" }), jsx$1(Slider, { disabled: filled, value: strokeWidth, min: 0.01, max: 5, step: 0.01, onChange: (e3, v) => setStrokeWidth(v), className: menuClasses.menuItemSlider, orientation: "horizontal", id: strokeWidthId, getAriaLabel: () => "Stroke width" })] }), jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: colorEncodingId, children: "Color encoding: " }), jsxs(NativeSelect, { onChange: onColorEncodingChange, value: obsColorEncoding, inputProps: { id: colorEncodingId }, classes: { root: selectClasses2.selectRoot }, children: [jsx$1("option", { value: "spatialChannelColor", children: "Static Color" }), jsx$1("option", { value: "geneSelection", children: "Feature Value" }), jsx$1("option", { value: "cellSetSelection", children: "Set Selection" })] })] }), jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: colormapId, children: "Colormap: " }), jsx$1(NativeSelect, { onChange: (e3) => {
265622
+ setFeatureValueColormap(e3.target.value);
265623
+ setPointFeatureValueColormap?.(e3.target.value);
265624
+ }, value: featureValueColormap, inputProps: { id: colormapId }, classes: { root: selectClasses2.selectRoot }, children: GLSL_COLORMAPS.map((cmap) => jsx$1("option", { value: cmap, children: cmap }, cmap)) })] }), jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: colormapRangeId, children: "Colormap range: " }), jsx$1(Slider, { disabled: obsColorEncoding !== "geneSelection", value: featureValueColormapRange, min: 0, max: 1, step: 0.01, onChange: (e3, v) => {
265625
+ setFeatureValueColormapRange(v);
265626
+ setPointFeatureValueColormapRange?.(v);
265627
+ }, className: menuClasses.menuItemSlider, orientation: "horizontal", id: colormapRangeId, getAriaLabel: (i2) => i2 === 0 ? "Low colormap range" : "High colormap range" })] }), jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: tooltipsId, children: "Tooltips visible: " }), jsx$1(Checkbox$1, { color: "primary", className: menuClasses.menuItemCheckbox, checked: tooltipsVisible, onChange: (e3, v) => setTooltipsVisible(v), slotProps: { input: { id: tooltipsId } } })] }), jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: crosshairsId, children: "Crosshairs visible: " }), jsx$1(Checkbox$1, { color: "primary", className: menuClasses.menuItemCheckbox, checked: tooltipCrosshairsVisible, onChange: (e3, v) => setTooltipCrosshairsVisible(v), slotProps: { input: { id: crosshairsId } } })] }), jsxs(MenuItem, { dense: true, disableGutters: true, children: [jsx$1("label", { className: menuClasses.imageLayerMenuLabel, htmlFor: legendId, children: "Legend visible: " }), jsx$1(Checkbox$1, { color: "primary", className: menuClasses.menuItemCheckbox, checked: legendVisible, onChange: (e3, v) => setLegendVisible(v), slotProps: { input: { id: legendId } } })] })] });
265628
+ }
265629
+ function SegmentationCentroidsController(props) {
265630
+ const { theme, segScope, segLayerCoordination, segChannelScopesByLayer, segChannelCoordination, pointScope, pointLayerCoordination } = props;
265631
+ const { classes: classes2 } = useStyles$7();
265632
+ const { classes: lcClasses } = useControllerSectionStyles();
265633
+ const { classes: menuClasses } = useEllipsisMenuStyles();
265634
+ const channelScopes = segChannelScopesByLayer?.[segScope] ?? [];
265635
+ const primaryChanScope = channelScopes[0];
265636
+ const chanValues = segChannelCoordination[0]?.[segScope]?.[primaryChanScope] ?? {};
265637
+ const chanSetters = segChannelCoordination[1]?.[segScope]?.[primaryChanScope] ?? {};
265638
+ const { obsType, featureType, featureValueType, spatialChannelVisible: segVisible, spatialChannelOpacity: segOpacity, spatialChannelColor: segColor, spatialSegmentationFilled: filled, spatialSegmentationStrokeWidth: strokeWidth, obsColorEncoding, featureSelection, featureValueColormap, featureValueColormapRange, tooltipsVisible, tooltipCrosshairsVisible, legendVisible } = chanValues;
265639
+ const { setSpatialChannelVisible: setSegVisible, setSpatialChannelOpacity: setSegOpacity, setSpatialChannelColor: setSegColor, setSpatialSegmentationFilled: setFilled, setSpatialSegmentationStrokeWidth: setStrokeWidth, setObsColorEncoding, setFeatureValueColormap, setFeatureValueColormapRange, setTooltipsVisible, setTooltipCrosshairsVisible, setLegendVisible } = chanSetters;
265640
+ const pointValues = pointLayerCoordination[0]?.[pointScope] ?? {};
265641
+ const pointSetters = pointLayerCoordination[1]?.[pointScope] ?? {};
265642
+ const { spatialLayerVisible: pointVisible, spatialLayerOpacity: pointOpacity, spatialLayerColor: pointColor, obsColorEncoding: pointObsColorEncoding, featureValueColormap: pointFeatureValueColormap } = pointValues;
265643
+ const { setSpatialLayerVisible: setPointVisible, setSpatialLayerOpacity: setPointOpacity, setSpatialLayerColor: setPointColor, setObsColorEncoding: setPointObsColorEncoding, setFeatureValueColormap: setPointFeatureValueColormap, setFeatureValueColormapRange: setPointFeatureValueColormapRange } = pointSetters;
265644
+ const sharedOpacity = segOpacity ?? 1;
265645
+ const handleOpacityChange = useCallback((e3, v) => {
265646
+ setSegOpacity?.(v);
265647
+ setPointOpacity?.(v);
265648
+ }, [setSegOpacity, setPointOpacity]);
265649
+ const handleSegVisibleChange = useCallback(() => {
265650
+ setSegVisible?.(typeof segVisible === "boolean" ? !segVisible : false);
265651
+ }, [segVisible, setSegVisible]);
265652
+ const handlePointVisibleChange = useCallback(() => {
265653
+ setPointVisible?.(typeof pointVisible === "boolean" ? !pointVisible : false);
265654
+ }, [pointVisible, setPointVisible]);
265655
+ const handleColorEncodingChange = useCallback((e3) => {
265656
+ const val = e3.target.value;
265657
+ setObsColorEncoding(val);
265658
+ if (val === "geneSelection") {
265659
+ setPointObsColorEncoding?.("geneSelection");
265660
+ } else if (val === "cellSetSelection") {
265661
+ setPointObsColorEncoding?.("cellSetSelection");
265662
+ } else {
265663
+ setPointObsColorEncoding?.("spatialChannelColor");
265664
+ }
265665
+ }, [setObsColorEncoding, setPointObsColorEncoding]);
265666
+ const segVisibleSetting = typeof segVisible === "boolean" ? segVisible : true;
265667
+ const pointVisibleSetting = typeof pointVisible === "boolean" ? pointVisible : true;
265668
+ const SegVisibility = segVisibleSetting ? VisibilityIcon : VisibilityOffIcon;
265669
+ const PointVisibility = pointVisibleSetting ? VisibilityIcon : VisibilityOffIcon;
265670
+ const isStaticColor = obsColorEncoding === "spatialChannelColor";
265671
+ const isColormap = obsColorEncoding === "geneSelection";
265672
+ const pointIsStaticColor = pointObsColorEncoding === "spatialChannelColor";
265673
+ const pointIsColormap = pointObsColorEncoding === "geneSelection";
265674
+ return jsx$1(Grid$1, { className: lcClasses.layerControllerGrid, children: jsx$1(Paper, { elevation: 4, className: lcClasses.layerControllerRoot, children: jsxs(Grid$1, { container: true, direction: "row", justifyContent: "space-between", alignItems: "center", children: [jsxs(Grid$1, { size: "grow", style: { display: "flex", alignItems: "center", minWidth: 0 }, children: [jsx$1(Button, { onClick: handleSegVisibleChange, className: menuClasses.imageLayerVisibleButton, getAriaLabel: () => "Toggle mesh visibility", children: jsx$1(SegVisibility, {}) }), jsx$1(ChannelColorPickerMenu, { theme, color: segColor, setColor: setSegColor, isStaticColor, isColormap, featureValueColormap, visible: segVisibleSetting }), jsx$1(Typography, { className: `${menuClasses.imageLayerName} ${classes2.inlineLabel}`, children: "Cell" }), jsx$1("div", { className: classes2.divider }), jsx$1(Button, { onClick: handlePointVisibleChange, className: menuClasses.imageLayerVisibleButton, getAriaLabel: () => "Toggle centroid visibility", children: jsx$1(PointVisibility, {}) }), jsx$1(ChannelColorPickerMenu, { theme, color: pointColor, setColor: setPointColor, isStaticColor: pointIsStaticColor, isColormap: pointIsColormap, featureValueColormap: pointFeatureValueColormap, visible: pointVisibleSetting }), jsx$1(Typography, { className: `${menuClasses.imageLayerName} ${classes2.inlineLabel}`, children: "Point" })] }), jsx$1(Grid$1, { size: 2, children: jsx$1(Slider, { value: sharedOpacity, min: 0, max: 1, step: 1e-3, onChange: handleOpacityChange, className: menuClasses.imageLayerOpacitySlider, orientation: "horizontal", getAriaLabel: () => "Adjust shared opacity" }) }), jsx$1(Grid$1, { size: 1, children: jsx$1(CombinedEllipsisMenu, { obsType, featureType, featureValueType, filled, setFilled, strokeWidth, setStrokeWidth, obsColorEncoding, setObsColorEncoding, featureValueColormap, setFeatureValueColormap, featureValueColormapRange, setFeatureValueColormapRange, tooltipsVisible, setTooltipsVisible, tooltipCrosshairsVisible, setTooltipCrosshairsVisible, legendVisible, setLegendVisible, setPointObsColorEncoding, setPointFeatureValueColormap, setPointFeatureValueColormapRange, onColorEncodingChange: handleColorEncodingChange }) }), jsx$1(Grid$1, { size: 1, className: classes2.iconCell, children: jsx$1(SvgVector, { className: classes2.layerTypeCentroidSegmentationIcon }) })] }) }) });
265675
+ }
265498
265676
  function LayerController(props) {
265499
- const { theme, coordinationScopesRaw, segmentationLayerScopes, segmentationLayerCoordination, segmentationChannelScopesByLayer, segmentationChannelCoordination, images, imageLayerScopes, imageLayerCoordination, targetT, targetZ, setTargetT, setTargetZ, spatialRenderingMode, setSpatialRenderingMode, imageChannelScopesByLayer, imageChannelCoordination, spotLayerScopes, spotLayerCoordination, pointLayerScopes, pointLayerCoordination, pointMultiIndicesData, volumeLoadingStatus, tiledPointsLoadingProgress, layerPerFeatureForPoints } = props;
265677
+ const { theme, coordinationScopesRaw, segmentationLayerScopes, segmentationLayerCoordination, segmentationChannelScopesByLayer, segmentationChannelCoordination, images, imageLayerScopes, imageLayerCoordination, targetT, targetZ, setTargetT, setTargetZ, spatialRenderingMode, setSpatialRenderingMode, imageChannelScopesByLayer, imageChannelCoordination, spotLayerScopes, spotLayerCoordination, pointLayerScopes, pointLayerCoordination, pointMultiIndicesData, volumeLoadingStatus, tiledPointsLoadingProgress, layerPerFeatureForPoints, centroidSegmentationPairs = [], pairedSegScopes = /* @__PURE__ */ new Set(), pairedPointScopes = /* @__PURE__ */ new Set() } = props;
265500
265678
  const anyLayerHasT = Object.values(images || {}).some((image2) => image2?.image?.instance.hasTStack());
265501
265679
  const anyLayerHasZ = Object.values(images || {}).some((image2) => image2?.image?.instance.hasZStack());
265502
265680
  const maxT = Object.values(images || {}).reduce((a2, h2) => Math.max(a2, h2?.image?.instance.getNumT()), 1) - 1;
265503
265681
  const maxZ = Object.values(images || {}).reduce((a2, h2) => Math.max(a2, h2?.image?.instance.getNumZ()), 1) - 1;
265504
265682
  const reversedImageLayerScopes = useMemo$1(() => [...imageLayerScopes || []].reverse(), [imageLayerScopes]);
265505
- const reversedSegmentationLayerScopes = useMemo$1(() => [...segmentationLayerScopes || []].reverse(), [segmentationLayerScopes]);
265506
265683
  const reversedSpotLayerScopes = useMemo$1(() => [...spotLayerScopes || []].reverse(), [spotLayerScopes]);
265507
- const reversedPointLayerScopes = useMemo$1(() => [...pointLayerScopes || []].reverse(), [pointLayerScopes]);
265508
- return jsxs("div", { children: [anyLayerHasZ ? jsx$1(GlobalDimensionSlider, { label: "Z", targetValue: targetZ, setTargetValue: setTargetZ, max: maxZ, spatialRenderingMode, setSpatialRenderingMode }) : null, anyLayerHasT ? jsx$1(GlobalDimensionSlider, { label: "T", targetValue: targetT, setTargetValue: setTargetT, max: maxT }) : null, pointLayerScopes && reversedPointLayerScopes.map((layerScope) => jsx$1(PointLayerController, { theme, layerScope, layerCoordination: pointLayerCoordination[0][layerScope], setLayerCoordination: pointLayerCoordination[1][layerScope], pointMatrixIndicesData: pointMultiIndicesData?.[layerScope], tiledPointsLoadingProgress, layerPerFeatureForPoints }, layerScope)), spotLayerScopes && reversedSpotLayerScopes.map((layerScope) => jsx$1(SpotLayerController, { theme, layerScope, layerCoordination: spotLayerCoordination[0][layerScope], setLayerCoordination: spotLayerCoordination[1][layerScope] }, layerScope)), segmentationLayerScopes && reversedSegmentationLayerScopes.map((layerScope) => jsx$1(SegmentationLayerController, { theme, layerScope, layerCoordination: segmentationLayerCoordination[0][layerScope], setLayerCoordination: segmentationLayerCoordination[1][layerScope], channelScopes: segmentationChannelScopesByLayer[layerScope], channelCoordination: segmentationChannelCoordination[0][layerScope], setChannelCoordination: segmentationChannelCoordination[1][layerScope] }, layerScope)), imageLayerScopes && reversedImageLayerScopes.map((layerScope) => jsx$1(ImageLayerController, { theme, coordinationScopesRaw, layerScope, layerCoordination: imageLayerCoordination[0][layerScope], setLayerCoordination: imageLayerCoordination[1][layerScope], channelScopes: imageChannelScopesByLayer[layerScope], channelCoordination: imageChannelCoordination[0][layerScope], setChannelCoordination: imageChannelCoordination[1][layerScope], image: images[layerScope]?.image?.instance, featureIndex: images[layerScope]?.featureIndex, targetT, targetZ, spatialRenderingMode, volumeLoadingProgress: volumeLoadingStatus?.loadingProgress, onStopVolumeLoading: volumeLoadingStatus?.onStopLoading, onRestartVolumeLoading: volumeLoadingStatus?.onRestartLoading, volumeStillRef: volumeLoadingStatus?.stillRef }, layerScope))] });
265684
+ const reversedPointLayerScopes = useMemo$1(() => [...pointLayerScopes || []].filter((scope) => !pairedPointScopes.has(scope)).reverse(), [pointLayerScopes, pairedPointScopes]);
265685
+ const reversedSegmentationLayerScopes = useMemo$1(() => [...segmentationLayerScopes || []].filter((scope) => !pairedSegScopes.has(scope)).reverse(), [segmentationLayerScopes, pairedSegScopes]);
265686
+ return jsxs("div", { children: [anyLayerHasZ ? jsx$1(GlobalDimensionSlider, { label: "Z", targetValue: targetZ, setTargetValue: setTargetZ, max: maxZ, spatialRenderingMode, setSpatialRenderingMode }) : null, anyLayerHasT ? jsx$1(GlobalDimensionSlider, { label: "T", targetValue: targetT, setTargetValue: setTargetT, max: maxT }) : null, centroidSegmentationPairs.map(({ pointScope, segScope }) => jsx$1(SegmentationCentroidsController, { theme, segScope, segLayerCoordination: segmentationLayerCoordination, segChannelScopesByLayer: segmentationChannelScopesByLayer, segChannelCoordination: segmentationChannelCoordination, pointScope, pointLayerCoordination }, `combined-${segScope}-${pointScope}`)), pointLayerScopes && reversedPointLayerScopes.map((layerScope) => jsx$1(PointLayerController, { theme, layerScope, layerCoordination: pointLayerCoordination[0][layerScope], setLayerCoordination: pointLayerCoordination[1][layerScope], pointMatrixIndicesData: pointMultiIndicesData?.[layerScope], tiledPointsLoadingProgress, layerPerFeatureForPoints }, layerScope)), spotLayerScopes && reversedSpotLayerScopes.map((layerScope) => jsx$1(SpotLayerController, { theme, layerScope, layerCoordination: spotLayerCoordination[0][layerScope], setLayerCoordination: spotLayerCoordination[1][layerScope] }, layerScope)), segmentationLayerScopes && reversedSegmentationLayerScopes.map((layerScope) => jsx$1(SegmentationLayerController, { theme, layerScope, layerCoordination: segmentationLayerCoordination[0][layerScope], setLayerCoordination: segmentationLayerCoordination[1][layerScope], channelScopes: segmentationChannelScopesByLayer[layerScope], channelCoordination: segmentationChannelCoordination[0][layerScope], setChannelCoordination: segmentationChannelCoordination[1][layerScope] }, layerScope)), imageLayerScopes && reversedImageLayerScopes.map((layerScope) => jsx$1(ImageLayerController, { theme, coordinationScopesRaw, layerScope, layerCoordination: imageLayerCoordination[0][layerScope], setLayerCoordination: imageLayerCoordination[1][layerScope], channelScopes: imageChannelScopesByLayer[layerScope], channelCoordination: imageChannelCoordination[0][layerScope], setChannelCoordination: imageChannelCoordination[1][layerScope], image: images[layerScope]?.image?.instance, featureIndex: images[layerScope]?.featureIndex, targetT, targetZ, spatialRenderingMode, volumeLoadingProgress: volumeLoadingStatus?.loadingProgress, onStopVolumeLoading: volumeLoadingStatus?.onStopLoading, onRestartVolumeLoading: volumeLoadingStatus?.onRestartLoading, volumeStillRef: volumeLoadingStatus?.stillRef }, layerScope))] });
265509
265687
  }
265510
265688
  function LayerControllerSubscriber(props) {
265511
265689
  const { coordinationScopes: coordinationScopesRaw, coordinationScopesBy: coordinationScopesByRaw, closeButtonVisible, downloadButtonVisible, removeGridComponent, theme, title: title2 = "Spatial Layers", uuid: uuid2, layerPerFeatureForPoints = false, cameraPresets, helpText = ViewHelpMapping.LAYER_CONTROLLER_BETA } = props;
@@ -265638,6 +265816,40 @@ function LayerControllerSubscriber(props) {
265638
265816
  CoordinationType$1.TOOLTIP_CROSSHAIRS_VISIBLE,
265639
265817
  CoordinationType$1.LEGEND_VISIBLE
265640
265818
  ], coordinationScopes, coordinationScopesBy, CoordinationType$1.POINT_LAYER);
265819
+ const centroidSegmentationPairs = React__default.useMemo(() => {
265820
+ const pairs = [];
265821
+ const pairedSegScopes = /* @__PURE__ */ new Set();
265822
+ const pairedPointScopes = /* @__PURE__ */ new Set();
265823
+ pointLayerScopes.forEach((pointScope) => {
265824
+ const pointCoord = pointLayerCoordination[0][pointScope];
265825
+ if (!pointCoord)
265826
+ return;
265827
+ const pointObsType = pointCoord[CoordinationType$1.OBS_TYPE];
265828
+ if (!pointObsType)
265829
+ return;
265830
+ const matchingSegScope = segmentationLayerScopes.find((segScope) => {
265831
+ if (pairedSegScopes.has(segScope))
265832
+ return false;
265833
+ const channelScopes = segmentationChannelScopesByLayer[segScope] || [];
265834
+ return channelScopes.some((chanScope) => {
265835
+ const chanCoord = segmentationChannelCoordination[0]?.[segScope]?.[chanScope];
265836
+ return chanCoord?.[CoordinationType$1.OBS_TYPE] === pointObsType;
265837
+ });
265838
+ });
265839
+ if (matchingSegScope) {
265840
+ pairs.push({ pointScope, segScope: matchingSegScope });
265841
+ pairedSegScopes.add(matchingSegScope);
265842
+ pairedPointScopes.add(pointScope);
265843
+ }
265844
+ });
265845
+ return { pairs, pairedSegScopes, pairedPointScopes };
265846
+ }, [
265847
+ pointLayerScopes,
265848
+ pointLayerCoordination,
265849
+ segmentationLayerScopes,
265850
+ segmentationChannelScopesByLayer,
265851
+ segmentationChannelCoordination
265852
+ ]);
265641
265853
  const [{ volumeLoadingProgress: volumeLoadingStatus, tiledPointsLoadingProgress }] = useAuxiliaryCoordination([
265642
265854
  "spatialAcceleratedVolumeLoadingProgress",
265643
265855
  "spatialTiledPointsLoadingProgress"
@@ -265665,7 +265877,7 @@ function LayerControllerSubscriber(props) {
265665
265877
  imageDataStatus,
265666
265878
  pointMultiIndicesDataStatus
265667
265879
  ]);
265668
- return jsx$1(TitleInfo, { title: title2, isScroll: true, closeButtonVisible, downloadButtonVisible, removeGridComponent, theme, isReady, errors, helpText, children: jsx$1(LayerController, { theme, coordinationScopesRaw, segmentationLayerScopes, segmentationLayerCoordination, segmentationChannelScopesByLayer, segmentationChannelCoordination, images: imageData, imageLayerScopes, imageLayerCoordination, targetT, targetZ, setTargetT, setTargetZ, spatialRenderingMode, setSpatialRenderingMode, imageChannelScopesByLayer, imageChannelCoordination, spotLayerScopes, spotLayerCoordination, pointLayerScopes, pointLayerCoordination, pointMultiIndicesData, layerPerFeatureForPoints, volumeLoadingStatus, tiledPointsLoadingProgress }) });
265880
+ return jsx$1(TitleInfo, { title: title2, isScroll: true, closeButtonVisible, downloadButtonVisible, removeGridComponent, theme, isReady, errors, helpText, children: jsx$1(LayerController, { theme, coordinationScopesRaw, segmentationLayerScopes, segmentationLayerCoordination, segmentationChannelScopesByLayer, segmentationChannelCoordination, images: imageData, imageLayerScopes, imageLayerCoordination, targetT, targetZ, setTargetT, setTargetZ, spatialRenderingMode, setSpatialRenderingMode, imageChannelScopesByLayer, imageChannelCoordination, spotLayerScopes, spotLayerCoordination, pointLayerScopes, pointLayerCoordination, pointMultiIndicesData, layerPerFeatureForPoints, volumeLoadingStatus, tiledPointsLoadingProgress, centroidSegmentationPairs: centroidSegmentationPairs.pairs, pairedSegScopes: centroidSegmentationPairs.pairedSegScopes, pairedPointScopes: centroidSegmentationPairs.pairedPointScopes }) });
265669
265881
  }
265670
265882
  const isIterable$2 = (obj) => Symbol.iterator in obj;
265671
265883
  const hasIterableEntries = (value2) => (
@@ -267580,7 +267792,7 @@ function HiglassGlobalStyles(props) {
267580
267792
  }
267581
267793
  register({ dataFetcher: ZarrMultivecDataFetcher_default, config: ZarrMultivecDataFetcher_default.config }, { pluginType: "dataFetcher" });
267582
267794
  const LazyHiGlassComponent = React__default.lazy(async () => {
267583
- const { HiGlassComponent } = await import("./higlass-CGhpbMxK.js");
267795
+ const { HiGlassComponent } = await import("./higlass-4OKx7mdy.js");
267584
267796
  return { default: HiGlassComponent };
267585
267797
  });
267586
267798
  const HG_SIZE = 800;
@@ -267918,6 +268130,12 @@ const useStyles$4 = makeStyles()(() => ({
267918
268130
  neuroglancerWrapper: {
267919
268131
  position: "relative",
267920
268132
  height: "100%"
268133
+ },
268134
+ meshLoadingIndicator: {
268135
+ position: "absolute",
268136
+ bottom: 8,
268137
+ left: 8,
268138
+ zIndex: 50
267921
268139
  }
267922
268140
  }));
267923
268141
  const globalNeuroglancerCss = `
@@ -270531,7 +270749,7 @@ function NeuroglancerGlobalStyles(props) {
270531
270749
  const { classes: classes2 } = props;
270532
270750
  return jsxs(Fragment, { children: [jsx$1(GlobalStyles$3, { styles: globalNeuroglancerCss }), jsx$1(ScopedGlobalStyles, { styles: globalNeuroglancerStyles, parentClassName: classes2.neuroglancerWrapper })] });
270533
270751
  }
270534
- const LazyReactNeuroglancer = React__default.lazy(() => import("./ReactNeuroglancer-BuXB_Emb.js"));
270752
+ const LazyReactNeuroglancer = React__default.lazy(() => import("./ReactNeuroglancer-DSiLDSuM.js"));
270535
270753
  function createWorker() {
270536
270754
  const worker = new WorkerFactory$1();
270537
270755
  worker.AsyncComputationWorker = WorkerFactory;
@@ -270576,12 +270794,31 @@ class NeuroglancerComp extends PureComponent {
270576
270794
  }
270577
270795
  };
270578
270796
  remapWheelToZoom(viewer.inputEventBindings.perspectiveView);
270797
+ const { getMeshIdToCellId, onViewerReady, cellsUrl } = this.props;
270579
270798
  this.prevHoverHandler = () => {
270580
- if (viewer.mouseState.pickedValue !== void 0) {
270581
- const pickedSegment = viewer.mouseState.pickedValue;
270582
- this.latestOnSelectHoveredCoords?.(pickedSegment?.low);
270799
+ const ms = viewer.mouseState;
270800
+ if (ms.pickedAnnotationId != null) {
270801
+ const byIdKey = String(ms.pickedAnnotationId);
270802
+ fetch(`${cellsUrl}/by_id/${byIdKey}`).then((r3) => r3.arrayBuffer()).then((buf) => {
270803
+ const dv = new DataView(buf);
270804
+ const actualMeshId = String(dv.getInt32(12, true));
270805
+ const cellId = getMeshIdToCellId?.(actualMeshId) ?? actualMeshId;
270806
+ this.latestOnSelectHoveredCoords?.(cellId);
270807
+ });
270808
+ return;
270809
+ }
270810
+ if (ms.pickedValue !== void 0 && ms.pickedValue !== null) {
270811
+ const meshId = String(ms.pickedValue?.low);
270812
+ const cellId = getMeshIdToCellId?.(meshId) ?? meshId;
270813
+ this.latestOnSelectHoveredCoords?.(cellId);
270814
+ return;
270583
270815
  }
270816
+ this.latestOnSelectHoveredCoords?.(null);
270584
270817
  };
270818
+ onViewerReady?.(() => {
270819
+ const panel = [...viewer.display.panels][0];
270820
+ return panel?.projectionParameters?.value_?.viewProjectionMat;
270821
+ });
270585
270822
  viewer.mouseState.changed.add(this.prevHoverHandler);
270586
270823
  } else {
270587
270824
  if (this.prevMouseStateChanged && this.prevHoverHandler) {
@@ -270602,8 +270839,8 @@ class NeuroglancerComp extends PureComponent {
270602
270839
  }
270603
270840
  }
270604
270841
  render() {
270605
- const { classes: classes2, viewerState, cellColorMapping, onLayerLoadingChange } = this.props;
270606
- return jsxs(Fragment, { children: [jsx$1(NeuroglancerGlobalStyles, { classes: classes2 }), jsx$1("div", { className: classes2.neuroglancerWrapper, children: jsx$1(Suspense, { fallback: jsx$1("div", { children: "Loading..." }), children: jsx$1(LazyReactNeuroglancer, { brainMapsClientId: "NOT_A_VALID_ID", viewerState, onViewerStateChanged: this.onViewerStateChanged, onLayerLoadingChange, bundleRoot: this.bundleRoot, cellColorMapping, ref: this.onRef }) }) })] });
270842
+ const { classes: classes2, viewerState, cellColorMapping, onLayerLoadingChange, onAnnotationSourceReady, onViewerReady, meshOpacity } = this.props;
270843
+ return jsxs(Fragment, { children: [jsx$1(NeuroglancerGlobalStyles, { classes: classes2 }), jsx$1("div", { className: classes2.neuroglancerWrapper, children: jsx$1(Suspense, { fallback: jsx$1("div", { children: "Loading..." }), children: jsx$1(LazyReactNeuroglancer, { brainMapsClientId: "NOT_A_VALID_ID", viewerState, onViewerStateChanged: this.onViewerStateChanged, onLayerLoadingChange, bundleRoot: this.bundleRoot, cellColorMapping, ref: this.onRef, onAnnotationSourceReady, onViewerReady, meshOpacity }) }) })] });
270607
270844
  }
270608
270845
  }
270609
270846
  function useMemoCustomComparison(factory2, dependencies2, customIsEqual) {
@@ -270618,17 +270855,22 @@ function useMemoCustomComparison(factory2, dependencies2, customIsEqual) {
270618
270855
  }
270619
270856
  const shallowDiff = (prevDeps, nextDeps, depName) => prevDeps[depName] !== nextDeps[depName];
270620
270857
  const shallowDiffByLayer = (prevDeps, nextDeps, depName, scopeName) => prevDeps?.[depName]?.[scopeName] !== nextDeps?.[depName]?.[scopeName];
270858
+ const shallowDiffByChannel = (prevDeps, nextDeps, depName, firstName, secondName) => prevDeps?.[depName]?.[firstName]?.[secondName] !== nextDeps?.[depName]?.[firstName]?.[secondName];
270621
270859
  const shallowDiffByChannelWithKeys = (prevDeps, nextDeps, depName, firstName, secondName, keys2) => keys2.some((k) => prevDeps?.[depName]?.[firstName]?.[secondName]?.[k] !== nextDeps?.[depName]?.[firstName]?.[secondName]?.[k]);
270622
270860
  const shallowDiffByLayerCoordinationWithKeys = (prevDeps, nextDeps, depName, layerScope, keys2) => keys2.some((k) => prevDeps?.[depName]?.[0]?.[layerScope]?.[k] !== nextDeps?.[depName]?.[0]?.[layerScope]?.[k]);
270623
270861
  const shallowDiffByChannelCoordinationWithKeys = (prevDeps, nextDeps, depName, layerScope, channelScope, keys2) => keys2.some((k) => prevDeps?.[depName]?.[0]?.[layerScope]?.[channelScope]?.[k] !== nextDeps?.[depName]?.[0]?.[layerScope]?.[channelScope]?.[k]);
270624
270862
  function customIsEqualForCellColors(prevDeps, nextDeps) {
270625
270863
  let forceUpdate = false;
270626
270864
  const curriedShallowDiff = (depName) => shallowDiff(prevDeps, nextDeps, depName);
270865
+ const curriedShallowDiffByChannel = (depName, firstName, secondName) => shallowDiffByChannel(prevDeps, nextDeps, depName, firstName, secondName);
270627
270866
  const curriedShallowDiffByChannelWithKeys = (depName, firstName, secondName, keys2) => shallowDiffByChannelWithKeys(prevDeps, nextDeps, depName, firstName, secondName, keys2);
270628
270867
  const curriedShallowDiffByChannelCoordinationWithKeys = (depName, layerScope, channelScope, keys2) => shallowDiffByChannelCoordinationWithKeys(prevDeps, nextDeps, depName, layerScope, channelScope, keys2);
270629
270868
  if (curriedShallowDiff("theme")) {
270630
270869
  forceUpdate = true;
270631
270870
  }
270871
+ if (curriedShallowDiff("csvLoaded")) {
270872
+ forceUpdate = true;
270873
+ }
270632
270874
  if (["segmentationLayerScopes", "segmentationChannelScopesByLayer"].some(curriedShallowDiff)) {
270633
270875
  forceUpdate = true;
270634
270876
  } else {
@@ -270643,8 +270885,11 @@ function customIsEqualForCellColors(prevDeps, nextDeps) {
270643
270885
  "obsSetSelection",
270644
270886
  "additionalObsSets",
270645
270887
  "spatialChannelColor",
270646
- "spatialChannelOpacity"
270647
- ])) {
270888
+ "spatialChannelOpacity",
270889
+ "featureValueColormap",
270890
+ "featureValueColormapRange",
270891
+ "featureSelection"
270892
+ ]) || curriedShallowDiffByChannel("segmentationMultiIndicesData", layerScope, channelScope) || curriedShallowDiffByChannel("segmentationMultiExpressionNormData", layerScope, channelScope)) {
270648
270893
  forceUpdate = true;
270649
270894
  }
270650
270895
  });
@@ -270690,7 +270935,13 @@ function customIsEqualForInitialViewerState(prevDeps, nextDeps) {
270690
270935
  "featureSelection",
270691
270936
  "featureFilterMode",
270692
270937
  "featureColor",
270693
- "spatialPointStrokeWidth"
270938
+ "spatialPointStrokeWidth",
270939
+ "featureValueColormap",
270940
+ "featureValueColormapRange",
270941
+ "featureSelection",
270942
+ "obsSetColor",
270943
+ "obsSetSelection",
270944
+ "additionalObsSets"
270694
270945
  ]) || Math.abs(prevDeps?.pointLayerCoordination?.[0]?.[layerScope]?.spatialLayerOpacity - nextDeps?.pointLayerCoordination?.[0]?.[layerScope]?.spatialLayerOpacity) >= 0.05) {
270695
270946
  forceUpdate = true;
270696
270947
  }
@@ -270744,6 +270995,35 @@ function getSpatialLayerColorWithSelectionShader(staticColor, opacity2, featureI
270744
270995
  }
270745
270996
  `;
270746
270997
  }
270998
+ function getObsSetSelectionFilteredShader(phenotypeColors, selectedIndices, opacity2, borderWidth = 0) {
270999
+ const numColors = phenotypeColors.length;
271000
+ const numSelected = selectedIndices.length;
271001
+ const normColors = phenotypeColors.map((c2) => normalizeColor(c2));
271002
+ const colorsDecl = `vec3 phenotypeColors[${numColors}] = vec3[${numColors}](${normColors.map((c2) => toVec3(c2)).join(", ")});`;
271003
+ const selectedDecl = `int selectedIndices[${numSelected}] = int[${numSelected}](${selectedIndices.join(", ")});`;
271004
+ return `
271005
+ void main() {
271006
+ int phenotype = prop_phenotype();
271007
+ ${colorsDecl}
271008
+ ${selectedDecl}
271009
+ bool isSelected = false;
271010
+ for (int i = 0; i < ${numSelected}; ++i) {
271011
+ if (phenotype == selectedIndices[i]) {
271012
+ isSelected = true;
271013
+ }
271014
+ }
271015
+ if (!isSelected) {
271016
+ discard;
271017
+ }
271018
+ vec3 color = vec3(0.5, 0.5, 0.5);
271019
+ if (phenotype >= 0 && phenotype < ${numColors}) {
271020
+ color = phenotypeColors[phenotype];
271021
+ }
271022
+ setColor(vec4(color, ${opacity2.toFixed(4)}));
271023
+ ${borderWidthGlsl(borderWidth)}
271024
+ }
271025
+ `;
271026
+ }
270747
271027
  function getSpatialLayerColorFilteredShader(staticColor, opacity2, featureIndices, featureIndexProp, borderWidth = 0) {
270748
271028
  const normStatic = normalizeColor(staticColor);
270749
271029
  const numFeatures = featureIndices.length;
@@ -270925,6 +271205,23 @@ function getRandomPerPointShader(opacity2, featureIndexProp, pointIndexProp, bor
270925
271205
  }
270926
271206
  `;
270927
271207
  }
271208
+ function getQuantitativeColormapShader(quantitativeColorProp, opacity2, colormap = GLSL_COLORMAP_DEFAULT, colormapRange = [0, 1], borderWidth = 0, quantitativeColorMax = 1) {
271209
+ const [low, high] = colormapRange;
271210
+ const resolvedColormap = GLSL_COLORMAPS.includes(colormap) ? colormap : GLSL_COLORMAP_DEFAULT;
271211
+ return `
271212
+ #define COLORMAP_FUNC ${resolvedColormap}
271213
+ ${colormaps$1}
271214
+ void main() {
271215
+ float rawVal = float(prop_${quantitativeColorProp}());
271216
+ float normalizedVal = rawVal / ${quantitativeColorMax.toFixed(4)};
271217
+ float t = (normalizedVal - ${low.toFixed(4)}) / max(${(high - low).toFixed(4)}, 0.0001);
271218
+ t = clamp(t, 0.0, 1.0);
271219
+ vec4 col = COLORMAP_FUNC(t);
271220
+ setColor(vec4(col.rgb, ${opacity2.toFixed(4)}));
271221
+ ${borderWidthGlsl(borderWidth)}
271222
+ }
271223
+ `;
271224
+ }
270928
271225
  function getRandomPerPointWithSelectionShader(featureIndices, defaultColor, opacity2, featureIndexProp, pointIndexProp, borderWidth = 0) {
270929
271226
  const normDefault = normalizeColor(defaultColor);
270930
271227
  const numFeatures = featureIndices.length;
@@ -270980,8 +271277,25 @@ function getRandomPerPointFilteredShader(featureIndices, opacity2, featureIndexP
270980
271277
  }
270981
271278
  `;
270982
271279
  }
271280
+ function getObsSetSelectionShader(phenotypeColors, opacity2, borderWidth = 0) {
271281
+ const numColors = phenotypeColors.length;
271282
+ const normColors = phenotypeColors.map((c2) => normalizeColor(c2));
271283
+ const colorsDecl = `vec3 phenotypeColors[${numColors}] = vec3[${numColors}](${normColors.map((c2) => toVec3(c2)).join(", ")});`;
271284
+ return `
271285
+ void main() {
271286
+ int phenotype = prop_phenotype();
271287
+ ${colorsDecl}
271288
+ vec3 color = ${toVec3(normalizeColor([128, 128, 128]))};
271289
+ if (phenotype >= 0 && phenotype < ${numColors}) {
271290
+ color = phenotypeColors[phenotype];
271291
+ }
271292
+ setColor(vec4(color, ${opacity2.toFixed(4)}));
271293
+ ${borderWidthGlsl(borderWidth)}
271294
+ }
271295
+ `;
271296
+ }
270983
271297
  function getPointsShader(layerCoordination) {
270984
- const { theme, featureIndex, spatialLayerOpacity, obsColorEncoding, spatialLayerColor, featureSelection, featureFilterMode, featureColor, pointMarkerBorderWidth = 0, featureIndexProp, pointIndexProp } = layerCoordination;
271298
+ const { theme, featureIndex, spatialLayerOpacity, obsColorEncoding, spatialLayerColor, featureSelection, featureFilterMode, featureColor, pointMarkerBorderWidth = 0, featureIndexProp, pointIndexProp, quantitativeColorProp, featureValueColormap, featureValueColormapRange, obsSetColor, obsSetSelection, quantitativeColorMax, pointsAreSegmentationCentroids } = layerCoordination;
270985
271299
  const defaultColor = getDefaultColor(theme);
270986
271300
  const opacity2 = spatialLayerOpacity ?? 1;
270987
271301
  const staticColor = Array.isArray(spatialLayerColor) && spatialLayerColor.length === 3 ? spatialLayerColor : defaultColor;
@@ -271005,7 +271319,7 @@ function getPointsShader(layerCoordination) {
271005
271319
  }
271006
271320
  return getSpatialLayerColorWithSelectionShader(staticColor, opacity2, featureIndices, defaultColor, featureIndexProp, pointMarkerBorderWidth);
271007
271321
  }
271008
- if (obsColorEncoding === "geneSelection") {
271322
+ if (obsColorEncoding === "geneSelection" && !pointsAreSegmentationCentroids) {
271009
271323
  if (!featureIndexProp) {
271010
271324
  throw new Error("In order to use gene-based color encoding for Neuroglancer Points, options.featureIndexProp must be specified for the obsPoints.ng-annotations fileType in the Vitessce configuration.");
271011
271325
  }
@@ -271041,19 +271355,49 @@ function getPointsShader(layerCoordination) {
271041
271355
  }
271042
271356
  return getRandomPerPointWithSelectionShader(featureIndices, defaultColor, opacity2, featureIndexProp, pointIndexProp, pointMarkerBorderWidth);
271043
271357
  }
271358
+ if (obsColorEncoding === "geneSelection" && pointsAreSegmentationCentroids) {
271359
+ if (!quantitativeColorProp) {
271360
+ console.warn("In order to use quantitative colormap encoding for Neuroglancer Points, options.quantitativeColorProp must be specified for the obsPoints.ng-annotations fileType in the Vitessce configuration. Falling back to static color.");
271361
+ return getSpatialLayerColorShader(staticColor, opacity2, pointMarkerBorderWidth);
271362
+ }
271363
+ return getQuantitativeColormapShader(quantitativeColorProp, opacity2, featureValueColormap, featureValueColormapRange, pointMarkerBorderWidth, quantitativeColorMax);
271364
+ }
271365
+ if (obsColorEncoding === "cellSetSelection") {
271366
+ if (!obsSetColor || !obsSetSelection) {
271367
+ return getSpatialLayerColorShader(staticColor, opacity2, pointMarkerBorderWidth);
271368
+ }
271369
+ const colorByName = {};
271370
+ obsSetColor?.forEach(({ path: path2, color: color2 }) => {
271371
+ if (path2?.length > 1) {
271372
+ colorByName[path2[path2.length - 1]] = color2;
271373
+ }
271374
+ });
271375
+ const uniqueNames = Object.keys(colorByName).sort();
271376
+ const phenotypeColors = uniqueNames.map((name2) => colorByName[name2] ?? defaultColor);
271377
+ if (isFiltered) {
271378
+ const selectedNames = new Set(obsSetSelection?.map((path2) => path2[path2.length - 1]) ?? []);
271379
+ const selectedIndices = uniqueNames.map((name2, idx) => selectedNames.has(name2) ? idx : -1).filter((idx) => idx >= 0);
271380
+ if (selectedIndices.length === 0) {
271381
+ return "void main() { discard; }";
271382
+ }
271383
+ return getObsSetSelectionFilteredShader(phenotypeColors, selectedIndices, opacity2, pointMarkerBorderWidth);
271384
+ }
271385
+ return getObsSetSelectionShader(phenotypeColors, opacity2, pointMarkerBorderWidth);
271386
+ }
271044
271387
  return getSpatialLayerColorShader(staticColor, opacity2, pointMarkerBorderWidth);
271045
271388
  }
271389
+ const DEFAULT_NG_DIMENSIONS = {
271390
+ x: [1, "nm"],
271391
+ y: [1, "nm"],
271392
+ z: [1, "nm"]
271393
+ };
271046
271394
  const DEFAULT_NG_PROPS = {
271047
271395
  layout: "3d",
271048
271396
  position: [0, 0, 0],
271049
271397
  projectionOrientation: [0, 0, 0, 1],
271050
271398
  projectionScale: 1024,
271051
271399
  crossSectionScale: 1,
271052
- dimensions: {
271053
- x: [1, "nm"],
271054
- y: [1, "nm"],
271055
- z: [1, "nm"]
271056
- },
271400
+ dimensions: DEFAULT_NG_DIMENSIONS,
271057
271401
  layers: []
271058
271402
  };
271059
271403
  function toPrecomputedSource(url) {
@@ -271071,6 +271415,15 @@ function toNgLayerName(dataType, layerScope, channelScope = null) {
271071
271415
  }
271072
271416
  throw new Error(`Unsupported data type: ${dataType}`);
271073
271417
  }
271418
+ function pointsHaveMatchingSegmentation({ pointObsType, segmentationLayerScopes, segmentationChannelScopesByLayer, segmentationChannelCoordination }) {
271419
+ return segmentationLayerScopes.some((segmentationLayerScope) => {
271420
+ const segmentationChannelScopes = segmentationChannelScopesByLayer?.[segmentationLayerScope] || [];
271421
+ return segmentationChannelScopes.some((segmentationChannelScope) => {
271422
+ const segmentationObsType = segmentationChannelCoordination?.[0]?.[segmentationLayerScope]?.[segmentationChannelScope]?.obsType;
271423
+ return segmentationObsType && pointObsType && segmentationObsType === pointObsType;
271424
+ });
271425
+ });
271426
+ }
271074
271427
  function useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScopes, segmentationChannelScopesByLayer, segmentationLayerCoordination, segmentationChannelCoordination, obsSegmentationsUrls, obsSegmentationsData, pointLayerScopes, pointLayerCoordination, obsPointsUrls, obsPointsData, pointMultiIndicesData) {
271075
271428
  const viewerState = useMemoCustomComparison(() => {
271076
271429
  let result = cloneDeep$1(DEFAULT_NG_PROPS);
@@ -271093,6 +271446,9 @@ function useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScope
271093
271446
  } : toPrecomputedSource(layerUrl);
271094
271447
  result = {
271095
271448
  ...result,
271449
+ showAxisLines,
271450
+ showDefaultAnnotations: false,
271451
+ // this removes the yellow box around pointsLayer
271096
271452
  layers: [
271097
271453
  ...result.layers,
271098
271454
  {
@@ -271116,9 +271472,27 @@ function useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScope
271116
271472
  const layerCoordination = pointLayerCoordination[0][layerScope];
271117
271473
  const layerData = obsPointsData[layerScope];
271118
271474
  const layerUrl = obsPointsUrls[layerScope]?.[0]?.url;
271475
+ const ngOptions = layerData?.neuroglancerOptions;
271476
+ if (ngOptions?.transform?.matrix && ngOptions?.transform?.outputDimensions) {
271477
+ result = {
271478
+ ...result,
271479
+ dimensions: ngOptions.transform.outputDimensions ?? DEFAULT_NG_DIMENSIONS
271480
+ };
271481
+ }
271482
+ const pointsAreSegmentationCentroids = pointsHaveMatchingSegmentation({
271483
+ pointObsType: layerCoordination?.obsType,
271484
+ segmentationLayerScopes,
271485
+ segmentationChannelScopesByLayer,
271486
+ segmentationChannelCoordination
271487
+ });
271119
271488
  const featureIndex = pointMultiIndicesData[layerScope]?.featureIndex;
271120
271489
  if (layerUrl && layerData) {
271121
- const { spatialLayerVisible, spatialLayerOpacity, obsColorEncoding, spatialLayerColor, featureSelection, featureFilterMode, featureColor, spatialPointStrokeWidth } = layerCoordination || {};
271490
+ const { spatialLayerVisible, spatialLayerOpacity, obsColorEncoding, spatialLayerColor, featureSelection, featureFilterMode, featureColor, spatialPointStrokeWidth, featureValueColormap, featureValueColormapRange } = layerCoordination || {};
271491
+ const quantitativeColorMax = layerData.neuroglancerOptions?.quantitativeColorMax;
271492
+ if (!quantitativeColorMax && obsColorEncoding === "geneSelection") {
271493
+ console.warn("quantitativeColorMax not specified in options — defaulting to 1.0 (no normalization)");
271494
+ }
271495
+ const quantitativeColorMaxSelected = quantitativeColorMax ?? 1;
271122
271496
  const shader = getPointsShader({
271123
271497
  theme,
271124
271498
  featureIndex,
@@ -271128,9 +271502,18 @@ function useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScope
271128
271502
  featureSelection,
271129
271503
  featureFilterMode,
271130
271504
  featureColor,
271505
+ featureValueColormap,
271506
+ featureValueColormapRange,
271131
271507
  featureIndexProp: layerData.neuroglancerOptions?.featureIndexProp,
271132
271508
  pointIndexProp: layerData.neuroglancerOptions?.pointIndexProp,
271133
- pointMarkerBorderWidth: spatialPointStrokeWidth ?? 0
271509
+ quantitativeColorProp: layerData.neuroglancerOptions?.quantitativeColorProp,
271510
+ pointMarkerBorderWidth: spatialPointStrokeWidth ?? 0,
271511
+ obsSets: layerCoordination.obsSets,
271512
+ obsSetColor: layerCoordination.obsSetColor,
271513
+ obsSetSelection: layerCoordination.obsSetSelection,
271514
+ additionalObsSets: layerCoordination.additionalObsSets,
271515
+ quantitativeColorMax: quantitativeColorMaxSelected,
271516
+ pointsAreSegmentationCentroids
271134
271517
  });
271135
271518
  result = {
271136
271519
  ...result,
@@ -271143,7 +271526,13 @@ function useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScope
271143
271526
  subsources: {
271144
271527
  default: true
271145
271528
  },
271146
- enableDefaultSubsources: false
271529
+ enableDefaultSubsources: false,
271530
+ ...ngOptions?.transform?.matrix ? {
271531
+ transform: {
271532
+ matrix: ngOptions.transform.matrix,
271533
+ outputDimensions: ngOptions.transform?.outputDimensions ?? result.dimensions
271534
+ }
271535
+ } : {}
271147
271536
  },
271148
271537
  tab: "annotations",
271149
271538
  shader,
@@ -271151,7 +271540,7 @@ function useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScope
271151
271540
  visible: spatialLayerVisible,
271152
271541
  // Options from layerData.neuroglancerOptions
271153
271542
  // like projectionAnnotationSpacing:
271154
- projectionAnnotationSpacing: layerData.neuroglancerOptions?.projectionAnnotationSpacing ?? 1
271543
+ projectionAnnotationSpacing: ngOptions?.projectionAnnotationSpacing ?? 1
271155
271544
  }
271156
271545
  ],
271157
271546
  // TODO: is this needed?
@@ -271182,6 +271571,8 @@ function useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScope
271182
271571
  }, customIsEqualForInitialViewerState);
271183
271572
  return viewerState;
271184
271573
  }
271574
+ const ANNOTATION_HEADER_OFFSET = 8;
271575
+ const GREY_HEX = "#323232";
271185
271576
  const EPSILON_KEYS_MAPPING_NG = {
271186
271577
  projectionScale: 100,
271187
271578
  projectionOrientation: 0.02,
@@ -271202,6 +271593,17 @@ const multiplyQuat = (a2, b2) => {
271202
271593
  const conjQuat = (q) => [-q[0], -q[1], -q[2], q[3]];
271203
271594
  const rad2deg = (r3) => r3 * 180 / Math.PI;
271204
271595
  const deg2rad = (d) => d * Math.PI / 180;
271596
+ function applyColormap(colormap, t4) {
271597
+ const tClamped = Math.max(0, Math.min(1, t4));
271598
+ const interpolators = {
271599
+ plasma: interpolatePlasma,
271600
+ viridis: interpolateViridis,
271601
+ jet: interpolateJet,
271602
+ greys: interpolateGreys
271603
+ };
271604
+ const fn2 = interpolators[colormap] ?? interpolateViridis;
271605
+ return fn2(tClamped);
271606
+ }
271205
271607
  function valueGreaterThanEpsilon(a2, b2, epsilon3) {
271206
271608
  if (Array.isArray(a2) && Array.isArray(b2) && a2.length === b2.length) {
271207
271609
  return a2.some((val, i2) => Math.abs(val - b2[i2]) > epsilon3);
@@ -271268,13 +271670,57 @@ function makeVitNgZoomCalibrator(initialNgProjectionScale, initialDeckZoom = 0)
271268
271670
  }
271269
271671
  };
271270
271672
  }
271673
+ function parseAnnotationChunkSegmentsWithPositions(buffer2, serializer2) {
271674
+ const dv = new DataView(buffer2);
271675
+ if (buffer2.byteLength <= 8)
271676
+ return [];
271677
+ const count2 = dv.getUint32(0, true);
271678
+ if (count2 === 0)
271679
+ return [];
271680
+ const numBytes = serializer2.serializedBytes;
271681
+ const properties2 = [null, null, null, null, null];
271682
+ const results = [];
271683
+ for (let i2 = 0; i2 < count2; i2++) {
271684
+ serializer2.deserialize(dv, ANNOTATION_HEADER_OFFSET, i2, count2, true, properties2);
271685
+ const phenotype = properties2[0];
271686
+ const segId = properties2[1];
271687
+ if (segId > 0) {
271688
+ const propBase = ANNOTATION_HEADER_OFFSET + i2 * numBytes;
271689
+ const x2 = dv.getFloat32(propBase + 0, true);
271690
+ const y2 = dv.getFloat32(propBase + 4, true);
271691
+ const z2 = dv.getFloat32(propBase + 8, true);
271692
+ if (x2 !== 0 || y2 !== 0 || z2 !== 0) {
271693
+ results.push({
271694
+ id: String(segId),
271695
+ // mesh segment ID - use for segments array
271696
+ phenotype: Number(phenotype),
271697
+ x: x2,
271698
+ y: y2,
271699
+ z: z2
271700
+ });
271701
+ }
271702
+ }
271703
+ }
271704
+ return results;
271705
+ }
271706
+ const remapCellColors = (ngCellColors, cellIdToMeshIdRef) => {
271707
+ const remapped = {};
271708
+ Object.entries(ngCellColors).forEach(([cellId, color2]) => {
271709
+ const meshId = cellIdToMeshIdRef.current[cellId] ?? cellId;
271710
+ remapped[meshId] = color2;
271711
+ });
271712
+ return remapped;
271713
+ };
271271
271714
  const VITESSCE_INTERACTION_DELAY = 50;
271272
271715
  const INIT_VIT_ZOOM = -3.6;
271273
271716
  const ZOOM_EPS = 0.01;
271274
271717
  const ROTATION_EPS = 1e-3;
271275
271718
  const TARGET_EPS = 0.5;
271276
271719
  const NG_ROT_COOLDOWN_MS = 120;
271720
+ const MESH_LOAD_THRESHOLD = 100;
271721
+ const MESH_LOADING_OVERLAY_TIMEOUT = 1500;
271277
271722
  const GUIDE_URL = "https://vitessce.io/docs/ng-guide/";
271723
+ const MESH_OPACITY = 0.6;
271278
271724
  const LAST_INTERACTION_SOURCE = {
271279
271725
  vitessce: "vitessce",
271280
271726
  neuroglancer: "neuroglancer"
@@ -271291,10 +271737,11 @@ function NeuroglancerSubscriber(props) {
271291
271737
  downloadButtonVisible,
271292
271738
  removeGridComponent,
271293
271739
  theme,
271294
- showAxisLines = false,
271740
+ showAxisLines = true,
271295
271741
  title: title2 = "Spatial",
271296
271742
  subtitle = "Powered by Neuroglancer",
271297
271743
  helpText = ViewHelpMapping.NEUROGLANCER,
271744
+ meshLoadProjectionScaleThreshold,
271298
271745
  // Note: this is a temporary mechanism
271299
271746
  // to pass an initial NG camera state.
271300
271747
  // Ideally, all camera state should be passed via
@@ -271305,6 +271752,32 @@ function NeuroglancerSubscriber(props) {
271305
271752
  } = props;
271306
271753
  const loaders = useLoaders();
271307
271754
  const mergeCoordination = useMergeCoordination();
271755
+ const { classes: classes2 } = useStyles$4();
271756
+ const initialRotationPushedRef = useRef(false);
271757
+ const getViewProjectionMatRef = useRef(null);
271758
+ const obsIdToMeshIdRef = useRef({});
271759
+ const meshIdToCellIdRef = useRef({});
271760
+ const cellIdToMeshIdRef = useRef({});
271761
+ const ngRotPushAtRef = useRef(0);
271762
+ const lastInteractionSource = useRef(null);
271763
+ const applyNgUpdateTimeoutRef = useRef(null);
271764
+ const lastNgPushOrientationRef = useRef(null);
271765
+ const initialRenderCalibratorRef = useRef(null);
271766
+ const translationOffsetRef = useRef([0, 0, 0]);
271767
+ const zoomRafRef = useRef(null);
271768
+ const lastNgQuatRef = useRef([0, 0, 0, 1]);
271769
+ const lastNgScaleRef = useRef(null);
271770
+ const annotationInfoRef = useRef(null);
271771
+ const annotationTransformRef = useRef(null);
271772
+ const visibleSegmentIdsRef = useRef(null);
271773
+ const chunkCacheRef = useRef(/* @__PURE__ */ new Map());
271774
+ const resizeObserverRef = useRef(null);
271775
+ const [isLayersLoaded, setIsLayersLoaded] = useState(false);
271776
+ const [isMeshLoading, setIsMeshLoading] = useState(false);
271777
+ const [annotationReady, setAnnotationReady] = useState(false);
271778
+ const [csvLoaded, setCsvLoaded] = useState(false);
271779
+ const updateVisibleSegmentsThrottledRef = useRef(null);
271780
+ const viewportSizeRef = useRef({ width: 0, height: 0 });
271308
271781
  const coordinationScopes = useCoordinationScopes(coordinationScopesRaw);
271309
271782
  const coordinationScopesBy = useCoordinationScopesBy(coordinationScopes, coordinationScopesByRaw);
271310
271783
  const [{
@@ -271336,6 +271809,19 @@ function NeuroglancerSubscriber(props) {
271336
271809
  setSpatialRotationOrbit: setRotationOrbit,
271337
271810
  setSpatialZoom: setZoom
271338
271811
  }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType$1.NEUROGLANCER], coordinationScopes);
271812
+ const csvUrlRef = useRef(null);
271813
+ const csvUrl = useMemo$1(() => {
271814
+ if (csvUrlRef.current)
271815
+ return csvUrlRef.current;
271816
+ const obsSetsMap = loaders?.[dataset]?.loaders?.obsSets;
271817
+ if (!obsSetsMap)
271818
+ return null;
271819
+ const firstLoader = obsSetsMap.values().next().value;
271820
+ const url = firstLoader?.url ?? null;
271821
+ if (url)
271822
+ csvUrlRef.current = url;
271823
+ return url;
271824
+ }, [loaders, dataset]);
271339
271825
  const [ngWidth, ngHeight, containerRef] = useGridItemSize();
271340
271826
  const [segmentationLayerScopes, segmentationChannelScopesByLayer] = useMultiCoordinationScopesSecondaryNonNull(CoordinationType$1.SEGMENTATION_CHANNEL, CoordinationType$1.SEGMENTATION_LAYER, coordinationScopes, coordinationScopesBy);
271341
271827
  const pointLayerScopes = useMultiCoordinationScopesNonNull(CoordinationType$1.POINT_LAYER, coordinationScopes);
@@ -271364,7 +271850,9 @@ function NeuroglancerSubscriber(props) {
271364
271850
  CoordinationType$1.OBS_HIGHLIGHT,
271365
271851
  CoordinationType$1.TOOLTIPS_VISIBLE,
271366
271852
  CoordinationType$1.TOOLTIP_CROSSHAIRS_VISIBLE,
271367
- CoordinationType$1.LEGEND_VISIBLE
271853
+ CoordinationType$1.LEGEND_VISIBLE,
271854
+ CoordinationType$1.FEATURE_TYPE,
271855
+ CoordinationType$1.FEATURE_VALUE_TYPE
271368
271856
  ], coordinationScopes, coordinationScopesBy, CoordinationType$1.SEGMENTATION_LAYER, CoordinationType$1.SEGMENTATION_CHANNEL);
271369
271857
  const pointLayerCoordination = useComplexCoordination([
271370
271858
  CoordinationType$1.OBS_TYPE,
@@ -271381,7 +271869,10 @@ function NeuroglancerSubscriber(props) {
271381
271869
  CoordinationType$1.TOOLTIPS_VISIBLE,
271382
271870
  CoordinationType$1.TOOLTIP_CROSSHAIRS_VISIBLE,
271383
271871
  CoordinationType$1.LEGEND_VISIBLE,
271384
- CoordinationType$1.SPATIAL_POINT_STROKE_WIDTH
271872
+ CoordinationType$1.SPATIAL_POINT_STROKE_WIDTH,
271873
+ CoordinationType$1.OBS_SET_COLOR,
271874
+ CoordinationType$1.OBS_SET_SELECTION,
271875
+ CoordinationType$1.ADDITIONAL_OBS_SETS
271385
271876
  ], coordinationScopes, coordinationScopesBy, CoordinationType$1.POINT_LAYER);
271386
271877
  const [obsPointsData, obsPointsDataStatus, obsPointsUrls, obsPointsErrors] = useMultiObsPoints(coordinationScopes, coordinationScopesBy, loaders, dataset, mergeCoordination, uuid2);
271387
271878
  const [pointMultiIndicesData, pointMultiIndicesDataStatus, pointMultiIndicesDataErrors] = usePointMultiObsFeatureMatrixIndices(coordinationScopes, coordinationScopesBy, loaders, dataset);
@@ -271407,34 +271898,89 @@ function NeuroglancerSubscriber(props) {
271407
271898
  segmentationMultiFeatureSelectionStatus,
271408
271899
  segmentationMultiIndicesDataStatus
271409
271900
  ]);
271410
- const { classes: classes2 } = useStyles$4();
271901
+ const lastVitessceRotationRef = useRef({
271902
+ x: spatialRotationX,
271903
+ y: spatialRotationY,
271904
+ z: spatialRotationZ,
271905
+ orbit: spatialRotationOrbit
271906
+ });
271907
+ const prevCoordsRef = useRef({
271908
+ zoom: spatialZoom,
271909
+ rx: spatialRotationX,
271910
+ ry: spatialRotationY,
271911
+ rz: spatialRotationZ,
271912
+ orbit: spatialRotationOrbit,
271913
+ tx: spatialTargetX,
271914
+ ty: spatialTargetY
271915
+ });
271411
271916
  const segmentationColorMapping = useMemoCustomComparison(() => {
271412
271917
  const result = {};
271413
271918
  segmentationLayerScopes?.forEach((layerScope) => {
271414
271919
  result[layerScope] = {};
271415
271920
  segmentationChannelScopesByLayer?.[layerScope]?.forEach((channelScope) => {
271416
- const { obsSets: layerSets, obsIndex: layerIndex } = obsSegmentationsSetsData?.[layerScope]?.[channelScope] || {};
271417
- const { obsSetColor, obsColorEncoding, obsSetSelection, additionalObsSets, spatialChannelColor, spatialChannelOpacity } = segmentationChannelCoordination[0][layerScope][channelScope];
271921
+ const { obsSets: layerSets, obsIndex: layerIndexFromSets } = obsSegmentationsSetsData?.[layerScope]?.[channelScope] || {};
271922
+ const layerIndex = layerIndexFromSets ?? null;
271923
+ const { obsSetColor, obsColorEncoding, obsSetSelection, additionalObsSets, spatialChannelColor, spatialChannelOpacity, featureValueColormap, featureValueColormapRange } = segmentationChannelCoordination[0][layerScope][channelScope];
271418
271924
  if (obsColorEncoding === "spatialChannelColor") {
271419
- if (layerIndex && spatialChannelColor) {
271925
+ if (spatialChannelColor) {
271420
271926
  const hex2 = rgbToHex$1(spatialChannelColor);
271421
271927
  const ngCellColors = {};
271422
- if (obsSetSelection?.length > 0) {
271423
- const mergedCellSets = mergeObsSets(layerSets, additionalObsSets);
271424
- const selectedIds = /* @__PURE__ */ new Set();
271425
- obsSetSelection.forEach((setPath) => {
271426
- const rootNode = mergedCellSets?.tree?.find((n3) => n3.name === setPath[0]);
271427
- const leafNode = setPath.length > 1 ? rootNode?.children?.find((n3) => n3.name === setPath[1]) : rootNode;
271428
- leafNode?.set?.forEach(([id2]) => selectedIds.add(String(id2)));
271429
- });
271430
- layerIndex.forEach((id2) => {
271431
- if (selectedIds.has(String(id2))) {
271928
+ if (layerIndex) {
271929
+ if (obsSetSelection?.length > 0) {
271930
+ const mergedCellSets = mergeObsSets(layerSets, additionalObsSets);
271931
+ const selectedIds = /* @__PURE__ */ new Set();
271932
+ obsSetSelection.forEach((setPath) => {
271933
+ const rootNode = mergedCellSets?.tree?.find((n3) => n3.name === setPath[0]);
271934
+ const leafNode = setPath.length > 1 ? rootNode?.children?.find((n3) => n3.name === setPath[1]) : rootNode;
271935
+ leafNode?.set?.forEach(([id2]) => selectedIds.add(String(id2)));
271936
+ });
271937
+ layerIndex.forEach((id2) => {
271938
+ if (selectedIds.has(String(id2))) {
271939
+ ngCellColors[id2] = hex2;
271940
+ }
271941
+ });
271942
+ } else {
271943
+ layerIndex.forEach((id2) => {
271432
271944
  ngCellColors[id2] = hex2;
271433
- }
271434
- });
271945
+ });
271946
+ }
271435
271947
  }
271436
- result[layerScope][channelScope] = ngCellColors;
271948
+ result[layerScope][channelScope] = remapCellColors(ngCellColors, cellIdToMeshIdRef);
271949
+ result[layerScope].opacity = spatialChannelOpacity ?? 1;
271950
+ result[layerScope].defaultColor = hex2;
271951
+ }
271952
+ } else if (obsColorEncoding === "geneSelection") {
271953
+ const instanceObsIndex = obsSegmentationsSetsData?.[layerScope]?.[channelScope]?.obsIndex;
271954
+ const matrixObsIndex = segmentationMultiIndicesData?.[layerScope]?.[channelScope]?.obsIndex;
271955
+ const expressionData = segmentationMultiExpressionNormData?.[layerScope]?.[channelScope];
271956
+ if (instanceObsIndex && matrixObsIndex && expressionData?.[0]) {
271957
+ const matrixIndexMap = new Map(matrixObsIndex.map((key2, i2) => {
271958
+ const normalizedKey = key2.replace(/^.*_/, "");
271959
+ return [normalizedKey, i2];
271960
+ }));
271961
+ const toMatrixIndex = instanceObsIndex.map((key2) => matrixIndexMap.get(String(key2)));
271962
+ const [low, high] = featureValueColormapRange ?? [0, 1];
271963
+ const ngCellColors = {};
271964
+ instanceObsIndex.forEach((id2, i2) => {
271965
+ const rowIndex = toMatrixIndex[i2];
271966
+ const rawVal = expressionData[0][rowIndex] ?? 0;
271967
+ const t4 = rawVal / 255;
271968
+ const tScaled = (t4 - low) / Math.max(high - low, 1e-4);
271969
+ const tClamped = Math.max(0, Math.min(1, tScaled));
271970
+ const color2 = applyColormap(featureValueColormap ?? "viridis", tClamped);
271971
+ ngCellColors[id2] = rgbToHex$1(color2);
271972
+ });
271973
+ result[layerScope][channelScope] = remapCellColors(ngCellColors, cellIdToMeshIdRef);
271437
271974
  result[layerScope].opacity = spatialChannelOpacity ?? 1;
271975
+ } else if (instanceObsIndex) {
271976
+ const fallbackColor = spatialChannelColor ? rgbToHex$1(spatialChannelColor) : GREY_HEX;
271977
+ const ngCellColors = {};
271978
+ instanceObsIndex.forEach((id2) => {
271979
+ ngCellColors[id2] = fallbackColor;
271980
+ });
271981
+ result[layerScope][channelScope] = remapCellColors(ngCellColors, cellIdToMeshIdRef);
271982
+ result[layerScope].opacity = spatialChannelOpacity ?? 1;
271983
+ result[layerScope].defaultColor = fallbackColor;
271438
271984
  }
271439
271985
  } else if (layerSets && layerIndex) {
271440
271986
  const mergedCellSets = mergeObsSets(layerSets, additionalObsSets);
@@ -271446,10 +271992,10 @@ function NeuroglancerSubscriber(props) {
271446
271992
  theme
271447
271993
  });
271448
271994
  const ngCellColors = {};
271449
- cellColors.forEach((color2, i2) => {
271450
- ngCellColors[i2] = rgbToHex$1(color2);
271995
+ cellColors.forEach((color2, id2) => {
271996
+ ngCellColors[id2] = rgbToHex$1(color2);
271451
271997
  });
271452
- result[layerScope][channelScope] = ngCellColors;
271998
+ result[layerScope][channelScope] = remapCellColors(ngCellColors, cellIdToMeshIdRef);
271453
271999
  result[layerScope].opacity = spatialChannelOpacity ?? 1;
271454
272000
  }
271455
272001
  });
@@ -271462,14 +272008,201 @@ function NeuroglancerSubscriber(props) {
271462
272008
  segmentationChannelScopesByLayer,
271463
272009
  obsSegmentationsSetsData,
271464
272010
  segmentationChannelCoordination,
271465
- theme
272011
+ theme,
272012
+ segmentationMultiExpressionNormData,
272013
+ segmentationMultiIndicesData,
272014
+ csvLoaded
271466
272015
  }, customIsEqualForCellColors);
271467
- const initalViewerState = useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScopes, segmentationChannelScopesByLayer, segmentationLayerCoordination, segmentationChannelCoordination, obsSegmentationsUrls, obsSegmentationsData, pointLayerScopes, pointLayerCoordination, obsPointsUrls, obsPointsData, pointMultiIndicesData);
272016
+ const initialViewerState = useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScopes, segmentationChannelScopesByLayer, segmentationLayerCoordination, segmentationChannelCoordination, obsSegmentationsUrls, obsSegmentationsData, pointLayerScopes, pointLayerCoordination, obsPointsUrls, obsPointsData, pointMultiIndicesData);
271468
272017
  const [latestViewerStateIteration, incrementLatestViewerStateIteration] = useReducer((x2) => x2 + 1, 0);
271469
272018
  const latestViewerStateRef = useRef({
271470
- ...initalViewerState,
272019
+ ...initialViewerState,
271471
272020
  ...initialNgCameraState ?? {}
271472
272021
  });
272022
+ useEffect(() => {
272023
+ if (!csvUrl)
272024
+ return;
272025
+ fetch(csvUrl).then((r3) => r3.text()).then((text2) => {
272026
+ const lines = text2.split("\n");
272027
+ const header = lines[0].split(",");
272028
+ const meshIdIdx = header.indexOf("MeshID");
272029
+ const cellIdIdx = header.indexOf("CellID");
272030
+ const meshMap = {};
272031
+ const cellMap = {};
272032
+ lines.slice(1).forEach((l2) => {
272033
+ const cols = l2.split(",");
272034
+ const meshId = cols[meshIdIdx]?.trim();
272035
+ const cellId = cols[cellIdIdx]?.trim();
272036
+ if (meshId && cellId) {
272037
+ meshMap[meshId] = cellId;
272038
+ cellMap[cellId] = meshId;
272039
+ }
272040
+ });
272041
+ meshIdToCellIdRef.current = meshMap;
272042
+ cellIdToMeshIdRef.current = cellMap;
272043
+ setCsvLoaded(true);
272044
+ });
272045
+ }, [csvUrl]);
272046
+ useEffect(() => {
272047
+ chunkCacheRef.current.clear();
272048
+ }, [obsPointsData]);
272049
+ useEffect(() => {
272050
+ if (ngWidth && ngHeight) {
272051
+ viewportSizeRef.current = { width: ngWidth, height: ngHeight };
272052
+ }
272053
+ }, [ngWidth, ngHeight]);
272054
+ const setContainerNode = useCallback((node2) => {
272055
+ containerRef.current = node2;
272056
+ if (resizeObserverRef.current) {
272057
+ resizeObserverRef.current.disconnect();
272058
+ resizeObserverRef.current = null;
272059
+ }
272060
+ if (node2) {
272061
+ const rect2 = node2.getBoundingClientRect();
272062
+ if (rect2.width && rect2.height) {
272063
+ viewportSizeRef.current = { width: rect2.width, height: rect2.height };
272064
+ }
272065
+ const observer2 = new ResizeObserver((entries2) => {
272066
+ const entry2 = entries2[0];
272067
+ if (!entry2)
272068
+ return;
272069
+ const { width: width2, height: height2 } = entry2.contentRect;
272070
+ if (width2 && height2) {
272071
+ viewportSizeRef.current = { width: width2, height: height2 };
272072
+ }
272073
+ });
272074
+ observer2.observe(node2);
272075
+ resizeObserverRef.current = observer2;
272076
+ }
272077
+ }, []);
272078
+ const updateVisibleSegments = useCallback(async () => {
272079
+ if (!annotationInfoRef.current)
272080
+ return;
272081
+ if (!annotationTransformRef.current)
272082
+ return;
272083
+ if (!segmentationLayerScopes?.length)
272084
+ return;
272085
+ if (!pointLayerScopes?.length)
272086
+ return;
272087
+ const { position: position2, projectionScale } = latestViewerStateRef.current;
272088
+ if (!position2 || !projectionScale)
272089
+ return;
272090
+ const maxProjectionScale = meshLoadProjectionScaleThreshold ?? MESH_LOAD_THRESHOLD;
272091
+ if (projectionScale > maxProjectionScale) {
272092
+ if (visibleSegmentIdsRef.current?.length !== 0) {
272093
+ visibleSegmentIdsRef.current = [];
272094
+ incrementLatestViewerStateIteration();
272095
+ setIsMeshLoading(false);
272096
+ }
272097
+ return;
272098
+ }
272099
+ const { width: width2, height: height2 } = viewportSizeRef.current;
272100
+ if (!width2 || !height2)
272101
+ return;
272102
+ const transform3 = annotationTransformRef.current;
272103
+ const info2 = annotationInfoRef.current;
272104
+ const cellsUrl2 = info2.url;
272105
+ const allLevelCoords = info2.spatial.flatMap((level) => {
272106
+ const [gx, gy, gz] = level.grid_shape;
272107
+ const coords = [];
272108
+ for (let cx = 0; cx < gx; cx++) {
272109
+ for (let cy = 0; cy < gy; cy++) {
272110
+ for (let cz = 0; cz < gz; cz++) {
272111
+ coords.push({ level: level.key, cx, cy, cz });
272112
+ }
272113
+ }
272114
+ }
272115
+ return coords;
272116
+ });
272117
+ const fetchChunkWithPositions = async ({ level, cx, cy, cz }) => {
272118
+ const { serializers, serializer: defaultSerializer } = annotationTransformRef.current;
272119
+ const serializer2 = serializers?.[0] ?? defaultSerializer;
272120
+ if (!serializer2)
272121
+ return [];
272122
+ const cacheKey = `${cellsUrl2}/${level}/${cx}_${cy}_${cz}`;
272123
+ if (chunkCacheRef.current.has(cacheKey)) {
272124
+ return chunkCacheRef.current.get(cacheKey);
272125
+ }
272126
+ try {
272127
+ const res = await fetch(cacheKey);
272128
+ if (!res.ok) {
272129
+ chunkCacheRef.current.set(cacheKey, []);
272130
+ return [];
272131
+ }
272132
+ const buffer2 = await res.arrayBuffer();
272133
+ const entries2 = parseAnnotationChunkSegmentsWithPositions(buffer2, serializer2);
272134
+ chunkCacheRef.current.set(cacheKey, entries2);
272135
+ return entries2;
272136
+ } catch (e3) {
272137
+ chunkCacheRef.current.set(cacheKey, []);
272138
+ return [];
272139
+ }
272140
+ };
272141
+ try {
272142
+ const results = await Promise.all(allLevelCoords.map(fetchChunkWithPositions));
272143
+ const seenIds = /* @__PURE__ */ new Set();
272144
+ const allEntries = results.flat().filter(({ id: id2 }) => {
272145
+ if (seenIds.has(id2))
272146
+ return false;
272147
+ seenIds.add(id2);
272148
+ return true;
272149
+ });
272150
+ const obsIdToMeshId = {};
272151
+ allEntries.forEach(({ id: id2, obsId }) => {
272152
+ obsIdToMeshId[obsId] = id2;
272153
+ });
272154
+ obsIdToMeshIdRef.current = obsIdToMeshId;
272155
+ const mat = getViewProjectionMatRef.current?.();
272156
+ let visibleIds;
272157
+ if (!mat) {
272158
+ console.warn("No viewProjectionMatrix, loading all");
272159
+ visibleIds = [...new Set(allEntries.map(({ id: id2 }) => id2))];
272160
+ } else {
272161
+ const margin2 = Math.max(width2, height2) * 0.5;
272162
+ visibleIds = [...new Set(allEntries.filter(({ x: x2, y: y2, z: z2 }) => {
272163
+ const vx = x2 / transform3.x;
272164
+ const vy = y2 / transform3.y;
272165
+ const vz = (z2 || 0) / transform3.z;
272166
+ const cx = mat[0] * vx + mat[4] * vy + mat[8] * vz + mat[12];
272167
+ const cy = mat[1] * vx + mat[5] * vy + mat[9] * vz + mat[13];
272168
+ const cw2 = mat[3] * vx + mat[7] * vy + mat[11] * vz + mat[15];
272169
+ const screenX = (cx / cw2 + 1) * 0.5 * width2;
272170
+ const screenY = (1 - cy / cw2) * 0.5 * height2;
272171
+ return screenX >= -margin2 && screenX <= width2 + margin2 && screenY >= -margin2 && screenY <= height2 + margin2;
272172
+ }).map(({ id: id2 }) => id2))];
272173
+ }
272174
+ if (visibleIds.length === 0) {
272175
+ if (visibleSegmentIdsRef.current?.length !== 0) {
272176
+ visibleSegmentIdsRef.current = [];
272177
+ incrementLatestViewerStateIteration();
272178
+ }
272179
+ setIsMeshLoading(false);
272180
+ return;
272181
+ }
272182
+ const prevIds = new Set(visibleSegmentIdsRef.current ?? []);
272183
+ const addedIdsCount = visibleIds.filter((id2) => !prevIds.has(id2)).length;
272184
+ const hasSignificantChange = addedIdsCount > 20;
272185
+ if (hasSignificantChange && visibleIds.length > 0) {
272186
+ setIsMeshLoading(true);
272187
+ }
272188
+ const prevSorted = [...visibleSegmentIdsRef.current ?? []].sort().join(",");
272189
+ const nextSorted = [...visibleIds].sort().join(",");
272190
+ if (prevSorted !== nextSorted) {
272191
+ visibleSegmentIdsRef.current = visibleIds;
272192
+ incrementLatestViewerStateIteration();
272193
+ }
272194
+ if (hasSignificantChange) {
272195
+ setTimeout(() => setIsMeshLoading(false), MESH_LOADING_OVERLAY_TIMEOUT);
272196
+ }
272197
+ } catch (e3) {
272198
+ console.warn("[updateVisibleSegments] error:", e3);
272199
+ setIsMeshLoading(false);
272200
+ }
272201
+ }, [segmentationLayerScopes, pointLayerScopes, obsPointsData, meshLoadProjectionScaleThreshold]);
272202
+ useEffect(() => {
272203
+ updateVisibleSegmentsThrottledRef.current = throttle$1(updateVisibleSegments, 500);
272204
+ return () => updateVisibleSegmentsThrottledRef.current?.cancel();
272205
+ }, [updateVisibleSegments]);
271473
272206
  useEffect(() => {
271474
272207
  const prevNgCameraState = {
271475
272208
  position: latestViewerStateRef.current.position,
@@ -271477,37 +272210,50 @@ function NeuroglancerSubscriber(props) {
271477
272210
  projectionScale: latestViewerStateRef.current.projectionScale
271478
272211
  };
271479
272212
  latestViewerStateRef.current = {
271480
- ...initalViewerState,
272213
+ ...initialViewerState,
271481
272214
  ...prevNgCameraState
271482
272215
  };
271483
272216
  incrementLatestViewerStateIteration();
271484
- }, [initalViewerState]);
271485
- const initialRotationPushedRef = useRef(false);
271486
- const ngRotPushAtRef = useRef(0);
271487
- const lastInteractionSource = useRef(null);
271488
- const applyNgUpdateTimeoutRef = useRef(null);
271489
- const lastNgPushOrientationRef = useRef(null);
271490
- const initialRenderCalibratorRef = useRef(null);
271491
- const translationOffsetRef = useRef([0, 0, 0]);
271492
- const zoomRafRef = useRef(null);
271493
- const lastNgQuatRef = useRef([0, 0, 0, 1]);
271494
- const lastNgScaleRef = useRef(null);
271495
- const lastVitessceRotationRef = useRef({
271496
- x: spatialRotationX,
271497
- y: spatialRotationY,
271498
- z: spatialRotationZ,
271499
- orbit: spatialRotationOrbit
271500
- });
271501
- const [isLayersLoaded, setIsLayersLoaded] = useState(false);
271502
- const prevCoordsRef = useRef({
271503
- zoom: spatialZoom,
271504
- rx: spatialRotationX,
271505
- ry: spatialRotationY,
271506
- rz: spatialRotationZ,
271507
- orbit: spatialRotationOrbit,
271508
- tx: spatialTargetX,
271509
- ty: spatialTargetY
271510
- });
272217
+ }, [initialViewerState]);
272218
+ const cellsUrl = useMemo$1(() => {
272219
+ const firstScope = pointLayerScopes?.[0];
272220
+ return obsPointsUrls?.[firstScope]?.[0]?.url ?? null;
272221
+ }, [pointLayerScopes, obsPointsUrls]);
272222
+ const hasMatchingAnnotationSource = useMemo$1(() => {
272223
+ if (!cellsUrl)
272224
+ return false;
272225
+ const firstPointScope = pointLayerScopes?.[0];
272226
+ return pointsHaveMatchingSegmentation({
272227
+ pointObsType: pointLayerCoordination[0]?.[firstPointScope]?.obsType,
272228
+ segmentationLayerScopes,
272229
+ segmentationChannelScopesByLayer,
272230
+ segmentationChannelCoordination
272231
+ });
272232
+ }, [cellsUrl, pointLayerScopes, segmentationLayerScopes, segmentationChannelScopesByLayer]);
272233
+ useEffect(() => {
272234
+ if (!cellsUrl)
272235
+ return;
272236
+ fetch(`${cellsUrl}/info`).then((r3) => r3.json()).then((info2) => {
272237
+ const infoWithUrl = {
272238
+ ...info2,
272239
+ url: cellsUrl
272240
+ };
272241
+ annotationInfoRef.current = infoWithUrl;
272242
+ if (annotationTransformRef.current)
272243
+ setAnnotationReady(true);
272244
+ }).catch((err2) => console.error("failed to fetch annotation info:", err2));
272245
+ }, [cellsUrl]);
272246
+ useEffect(() => {
272247
+ if (annotationReady) {
272248
+ setIsLayersLoaded(true);
272249
+ updateVisibleSegments();
272250
+ }
272251
+ }, [annotationReady]);
272252
+ const onAnnotationSourceReady = useCallback((transform3) => {
272253
+ annotationTransformRef.current = transform3;
272254
+ if (annotationInfoRef.current)
272255
+ setAnnotationReady(true);
272256
+ }, []);
271511
272257
  const handleStateUpdate = useCallback((newState) => {
271512
272258
  lastInteractionSource.current = LAST_INTERACTION_SOURCE.neuroglancer;
271513
272259
  const { projectionScale, projectionOrientation, position: position2 } = newState;
@@ -271540,6 +272286,7 @@ function NeuroglancerSubscriber(props) {
271540
272286
  setZoom(vitZoomFromNg);
271541
272287
  zoomRafRef.current = null;
271542
272288
  });
272289
+ updateVisibleSegments();
271543
272290
  }
271544
272291
  lastNgScaleRef.current = projectionScale;
271545
272292
  }
@@ -271580,7 +272327,8 @@ function NeuroglancerSubscriber(props) {
271580
272327
  projectionScale,
271581
272328
  position: position2
271582
272329
  };
271583
- }, []);
272330
+ updateVisibleSegmentsThrottledRef.current?.();
272331
+ }, [updateVisibleSegmentsThrottledRef]);
271584
272332
  const onSegmentClick = useCallback((value2) => {
271585
272333
  if (value2) {
271586
272334
  const id2 = String(value2);
@@ -271605,11 +272353,18 @@ function NeuroglancerSubscriber(props) {
271605
272353
  const channelScope = segmentationChannelScopesByLayer?.[layerScope]?.[0];
271606
272354
  result[layerScope] = {
271607
272355
  colors: segmentationColorMapping?.[layerScope]?.[channelScope] ?? {},
271608
- opacity: segmentationColorMapping?.[layerScope]?.opacity ?? 1
272356
+ opacity: segmentationColorMapping?.[layerScope]?.opacity ?? 1,
272357
+ defaultColor: segmentationColorMapping?.[layerScope]?.defaultColor ?? null
271609
272358
  };
271610
272359
  });
271611
272360
  return result;
271612
272361
  }, [segmentationColorMapping, segmentationLayerScopes, segmentationChannelScopesByLayer]);
272362
+ useEffect(() => {
272363
+ if (!hasMatchingAnnotationSource && isReady && !segmentationLayerScopes?.length) {
272364
+ setIsLayersLoaded(true);
272365
+ }
272366
+ }, [hasMatchingAnnotationSource, isReady, segmentationLayerScopes]);
272367
+ const meshOpacity = hasMatchingAnnotationSource ? MESH_OPACITY : void 0;
271613
272368
  const derivedViewerState = useMemo$1(() => {
271614
272369
  const { current: current2 } = latestViewerStateRef;
271615
272370
  if (current2.layers.length <= 0) {
@@ -271668,13 +272423,33 @@ function NeuroglancerSubscriber(props) {
271668
272423
  lastInteractionSource.current = null;
271669
272424
  }
271670
272425
  const updatedLayers = current2?.layers?.map((layer, idx) => {
271671
- const layerScope = segmentationLayerScopes?.[idx];
272426
+ if (layer.type !== "segmentation")
272427
+ return layer;
272428
+ const layerScope = segmentationLayerScopes?.find((scope) => layer.name?.includes(scope));
272429
+ if (!layerScope)
272430
+ return layer;
271672
272431
  const layerColorMapping = cellColorMappingByLayer?.[layerScope]?.colors ?? {};
271673
- const layerSegments = Object.keys(layerColorMapping);
272432
+ const defaultColor = cellColorMappingByLayer?.[layerScope]?.defaultColor;
272433
+ let segments2 = [];
272434
+ if (hasMatchingAnnotationSource) {
272435
+ segments2 = visibleSegmentIdsRef.current ?? [];
272436
+ } else if (Object.keys(layerColorMapping).length > 0) {
272437
+ segments2 = Object.keys(layerColorMapping);
272438
+ }
272439
+ let derivedSegmentColors = {};
272440
+ if (hasMatchingAnnotationSource && segments2.length > 0) {
272441
+ segments2.forEach((meshId) => {
272442
+ const color2 = layerColorMapping[meshId] || defaultColor;
272443
+ if (color2)
272444
+ derivedSegmentColors[meshId] = color2;
272445
+ });
272446
+ } else if (!hasMatchingAnnotationSource) {
272447
+ derivedSegmentColors = layerColorMapping;
272448
+ }
271674
272449
  return {
271675
272450
  ...layer,
271676
- segments: layerSegments,
271677
- segmentColors: layerColorMapping,
272451
+ segments: segments2,
272452
+ segmentColors: derivedSegmentColors,
271678
272453
  objectAlpha: cellColorMappingByLayer?.[layerScope]?.opacity ?? 1
271679
272454
  };
271680
272455
  }) ?? [];
@@ -271704,17 +272479,21 @@ function NeuroglancerSubscriber(props) {
271704
272479
  spatialRotationZ,
271705
272480
  spatialTargetX,
271706
272481
  spatialTargetY,
271707
- initalViewerState,
271708
- latestViewerStateIteration
272482
+ initialViewerState,
272483
+ latestViewerStateIteration,
272484
+ hasMatchingAnnotationSource
271709
272485
  ]);
271710
272486
  const onSegmentHighlight = useCallback((obsId) => {
271711
272487
  setCellHighlight(String(obsId));
271712
272488
  }, [setCellHighlight]);
271713
272489
  const handleLayerLoadingChange = useCallback((isLoaded) => {
271714
- setIsLayersLoaded(isLoaded);
271715
- }, []);
272490
+ if (!isLayersLoaded && isLoaded) {
272491
+ setIsLayersLoaded(true);
272492
+ }
272493
+ }, [isLayersLoaded]);
271716
272494
  const hasLayers = derivedViewerState?.layers?.length > 0;
271717
- return jsx$1(TitleInfo, { title: title2, info: subtitle, helpText, isSpatial: true, theme, closeButtonVisible, downloadButtonVisible, removeGridComponent, isReady: isReady && isLayersLoaded, errors, withPadding: false, guideUrl: GUIDE_URL, children: hasLayers ? jsxs("div", { style: { position: "relative", width: "100%", height: "100%" }, ref: containerRef, children: [jsx$1("div", { style: { position: "absolute", top: 0, right: 0, zIndex: 50 }, children: jsx$1(MultiLegend, {
272495
+ const showPointsLegend = !hasMatchingAnnotationSource;
272496
+ return jsx$1(TitleInfo, { title: title2, info: subtitle, helpText, isSpatial: true, theme, closeButtonVisible, downloadButtonVisible, removeGridComponent, isReady: isReady && isLayersLoaded, errors, withPadding: false, guideUrl: GUIDE_URL, children: hasLayers ? jsxs("div", { style: { position: "relative", width: "100%", height: "100%" }, ref: setContainerNode, children: [jsx$1("div", { style: { position: "absolute", top: 0, right: 0, zIndex: 50 }, children: jsx$1(MultiLegend, {
271718
272497
  theme: "dark",
271719
272498
  maxHeight: ngHeight,
271720
272499
  // Segmentations
@@ -271723,10 +272502,12 @@ function NeuroglancerSubscriber(props) {
271723
272502
  segmentationChannelScopesByLayer,
271724
272503
  segmentationChannelCoordination,
271725
272504
  // Points
271726
- pointLayerScopes,
272505
+ pointLayerScopes: showPointsLegend ? pointLayerScopes : void 0,
271727
272506
  pointLayerCoordination,
271728
272507
  pointMultiIndicesData
271729
- }) }), jsx$1(NeuroglancerComp, { classes: classes2, onSegmentClick, onSelectHoveredCoords: onSegmentHighlight, viewerState: derivedViewerState, cellColorMapping: cellColorMappingByLayer, setViewerState: handleStateUpdate, onLayerLoadingChange: handleLayerLoadingChange })] }) : null });
272508
+ }) }), isMeshLoading ? jsx$1(Chip, { label: "Loading meshes...", size: "small", color: "primary", className: classes2.meshLoadingIndicator }) : null, jsx$1(NeuroglancerComp, { classes: classes2, onSegmentClick, onSelectHoveredCoords: onSegmentHighlight, viewerState: derivedViewerState, cellColorMapping: cellColorMappingByLayer, setViewerState: handleStateUpdate, onLayerLoadingChange: handleLayerLoadingChange, onAnnotationSourceReady, onViewerReady: (getFn) => {
272509
+ getViewProjectionMatRef.current = getFn;
272510
+ }, getMeshIdToCellId: (meshId) => meshIdToCellIdRef.current[meshId], cellsUrl, meshOpacity })] }) : null });
271730
272511
  }
271731
272512
  const FEATURE_AGGREGATION_STRATEGIES = ["first", "last", "sum", "mean"];
271732
272513
  function CellSetExpressionPlotOptions(props) {
@@ -324224,7 +325005,7 @@ const VEGA_THEMES = {
324224
325005
  };
324225
325006
  const DATASET_NAME = "table";
324226
325007
  function VegaGlobalStyles() {
324227
- const { theme } = useStyles$x();
325008
+ const { theme } = useStyles$y();
324228
325009
  return jsx$1(GlobalStyles$3, { styles: {
324229
325010
  "#vg-tooltip-element.vg-tooltip.custom-theme": {
324230
325011
  boxShadow: "0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.14),0px 1px 10px 0px rgba(0,0,0,0.12)",
@@ -324258,7 +325039,7 @@ function renderTooltipContents(tooltipText) {
324258
325039
  }
324259
325040
  function VegaPlot(props) {
324260
325041
  const { spec: partialSpec, data: data2, getTooltipText, signalListeners, renderer = "svg", onNewView } = props;
324261
- const { classes: tooltipClasses2 } = useStyles$s();
325042
+ const { classes: tooltipClasses2 } = useStyles$t();
324262
325043
  const tooltipHandler = useMemo$1(() => {
324263
325044
  if (typeof getTooltipText === "function") {
324264
325045
  const tooltipConfig = {
@@ -349548,7 +350329,7 @@ function Vitessce(props) {
349548
350329
  );
349549
350330
  }
349550
350331
  export {
349551
- diffCameraState as $,
350332
+ GREY_HEX as $,
349552
350333
  require_descriptors as A,
349553
350334
  BaseDecoder as B,
349554
350335
  CoordinationType$1 as C,
@@ -349577,13 +350358,14 @@ export {
349577
350358
  require_redefine as Z,
349578
350359
  require_objectAssign as _,
349579
350360
  getDefaultExportFromCjs$1 as a,
349580
- Vitessce as a0,
349581
- PluginFileType as a1,
349582
- PluginViewType as a2,
349583
- PluginCoordinationType as a3,
349584
- PluginJointFileType as a4,
349585
- PluginAsyncFunction as a5,
349586
- z as a6,
350361
+ diffCameraState as a0,
350362
+ Vitessce as a1,
350363
+ PluginFileType as a2,
350364
+ PluginViewType as a3,
350365
+ PluginCoordinationType as a4,
350366
+ PluginJointFileType as a5,
350367
+ PluginAsyncFunction as a6,
350368
+ z as a7,
349587
350369
  LercAddCompression as b,
349588
350370
  commonjsGlobal$1 as c,
349589
350371
  requireAssign as d,