@vitessce/launcher 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.
@@ -6375,12 +6375,12 @@ function baseIsEqual$3(value2, other, bitmask, customizer, stack2) {
6375
6375
  var now$4 = function() {
6376
6376
  return root$d.Date.now();
6377
6377
  };
6378
- var FUNC_ERROR_TEXT$2 = "Expected a function";
6378
+ var FUNC_ERROR_TEXT$3 = "Expected a function";
6379
6379
  var nativeMax$3 = Math.max, nativeMin$1 = Math.min;
6380
6380
  function debounce$5(func, wait2, options) {
6381
6381
  var lastArgs, lastThis, maxWait, result, timerId, lastCallTime, lastInvokeTime = 0, leading = false, maxing = false, trailing = true;
6382
6382
  if (typeof func != "function") {
6383
- throw new TypeError(FUNC_ERROR_TEXT$2);
6383
+ throw new TypeError(FUNC_ERROR_TEXT$3);
6384
6384
  }
6385
6385
  wait2 = toNumber$3(wait2) || 0;
6386
6386
  if (isObject$f(options)) {
@@ -6538,6 +6538,22 @@ var range$e = createRange();
6538
6538
  function sum$6(array2) {
6539
6539
  return array2 && array2.length ? baseSum(array2, identity$h) : 0;
6540
6540
  }
6541
+ var FUNC_ERROR_TEXT$2 = "Expected a function";
6542
+ function throttle$1(func, wait2, options) {
6543
+ var leading = true, trailing = true;
6544
+ if (typeof func != "function") {
6545
+ throw new TypeError(FUNC_ERROR_TEXT$2);
6546
+ }
6547
+ if (isObject$f(options)) {
6548
+ leading = "leading" in options ? !!options.leading : leading;
6549
+ trailing = "trailing" in options ? !!options.trailing : trailing;
6550
+ }
6551
+ return debounce$5(func, wait2, {
6552
+ "leading": leading,
6553
+ "maxWait": wait2,
6554
+ "trailing": trailing
6555
+ });
6556
+ }
6541
6557
  var INFINITY = 1 / 0;
6542
6558
  var createSet = !(Set$3 && 1 / setToArray$2(new Set$3([, -0]))[1] == INFINITY) ? noop$h : function(values3) {
6543
6559
  return new Set$3(values3);
@@ -11882,7 +11898,18 @@ const ngPrecomputedMeshSchema = z.object({
11882
11898
  const ngPointAnnotationSchema = z.object({
11883
11899
  projectionAnnotationSpacing: z.number(),
11884
11900
  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."),
11885
- 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.")
11901
+ 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."),
11902
+ transform: z.object({
11903
+ matrix: z.array(z.array(z.number())),
11904
+ outputDimensions: z.record(z.tuple([z.number(), z.string()]))
11905
+ }).optional().describe("A coordinate transformation matrix to apply to the annotation layer source to map annotation coordinates to the global coordinate space."),
11906
+ // Note: See comments in neuroglancer-sorger.js
11907
+ // We may remove these in the future, pending clarification
11908
+ // to questions in the tissue-map-tools repo
11909
+ // - https://github.com/hms-dbmi/tissue-map-tools/issues/20
11910
+ // - https://github.com/hms-dbmi/tissue-map-tools/issues/38
11911
+ quantitativeColorProp: z.string().optional(),
11912
+ quantitativeColorMax: z.number().optional()
11886
11913
  }).partial().nullable();
11887
11914
  const obsEmbeddingAnndataSchema = annDataObsEmbedding;
11888
11915
  const obsSpotsAnndataSchema = annDataObsLocations;
@@ -19295,7 +19322,7 @@ const { tss } = createTss({
19295
19322
  },
19296
19323
  "usePlugin": useMuiThemeStyleOverridesPlugin
19297
19324
  });
19298
- const useStyles$y = tss.create({});
19325
+ const useStyles$z = tss.create({});
19299
19326
  function createChainedFunction$1(...funcs) {
19300
19327
  return funcs.reduce((acc, func) => {
19301
19328
  if (func == null) {
@@ -74105,7 +74132,7 @@ const Popper = React.forwardRef((props, ref2) => {
74105
74132
  }, [handlePopperRef]);
74106
74133
  React.useImperativeHandle(popperRefProp, () => popperRef.current, []);
74107
74134
  const [exited, setExited] = React.useState(true);
74108
- const { theme } = useStyles$y();
74135
+ const { theme } = useStyles$z();
74109
74136
  const rtlPlacement = flipPlacement(initialPlacement, theme);
74110
74137
  const [placement, setPlacement] = React.useState(rtlPlacement);
74111
74138
  React.useEffect(() => {
@@ -74839,7 +74866,7 @@ var VariableSizeList$1 = /* @__PURE__ */ createListComponent({
74839
74866
  });
74840
74867
  const VariableSizeList = VariableSizeList$1;
74841
74868
  const LIST_ROW_HEIGHT$1 = 48;
74842
- const useStyles$x = makeStyles()(() => ({
74869
+ const useStyles$y = makeStyles()(() => ({
74843
74870
  searchInput: {
74844
74871
  lineHeight: "initial",
74845
74872
  height: "auto !important"
@@ -74879,7 +74906,7 @@ const ListboxComponent$1 = React__default.forwardRef((props, ref2) => {
74879
74906
  });
74880
74907
  function SimpleAutocomplete(props) {
74881
74908
  const { getMatches, onChange, textInputLabel = "Enter a gene", getItemLabel = (option) => option.label } = props;
74882
- const { classes: classes2 } = useStyles$x();
74909
+ const { classes: classes2 } = useStyles$y();
74883
74910
  const [potentialItems, setPotentialItems] = useState([]);
74884
74911
  const handleInputChange = async (_event, value2) => {
74885
74912
  if (!value2) {
@@ -84429,7 +84456,7 @@ function defaultUrlTransform(value2) {
84429
84456
  }
84430
84457
  return "";
84431
84458
  }
84432
- const useStyles$w = makeStyles()((theme) => ({
84459
+ const useStyles$x = makeStyles()((theme) => ({
84433
84460
  description: {
84434
84461
  "& p, details, table": {
84435
84462
  fontSize: "80%",
@@ -84466,7 +84493,7 @@ const useStyles$w = makeStyles()((theme) => ({
84466
84493
  }));
84467
84494
  function Description(props) {
84468
84495
  const { description: description2, metadata: metadata2, descriptionType } = props;
84469
- const { classes: classes2 } = useStyles$w();
84496
+ const { classes: classes2 } = useStyles$x();
84470
84497
  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)] });
84471
84498
  }
84472
84499
  function DescriptionSubscriber(props) {
@@ -92718,6 +92745,37 @@ function dataToCellSetsTree(data2, options) {
92718
92745
  return cellSetsTree;
92719
92746
  }
92720
92747
  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]];
92748
+ 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]];
92749
+ 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]];
92750
+ const schemeJet = Array.from({ length: 256 }, (_, i2) => {
92751
+ const t4 = i2 / 255;
92752
+ let r3;
92753
+ let g2;
92754
+ let b2;
92755
+ if (t4 < 0.125) {
92756
+ r3 = 0;
92757
+ g2 = 0;
92758
+ b2 = 0.5 + t4 * 4;
92759
+ } else if (t4 < 0.375) {
92760
+ r3 = 0;
92761
+ g2 = (t4 - 0.125) * 4;
92762
+ b2 = 1;
92763
+ } else if (t4 < 0.625) {
92764
+ r3 = (t4 - 0.375) * 4;
92765
+ g2 = 1;
92766
+ b2 = 1 - (t4 - 0.375) * 4;
92767
+ } else if (t4 < 0.875) {
92768
+ r3 = 1;
92769
+ g2 = 1 - (t4 - 0.625) * 4;
92770
+ b2 = 0;
92771
+ } else {
92772
+ r3 = 1 - (t4 - 0.875) * 4;
92773
+ g2 = 0;
92774
+ b2 = 0;
92775
+ }
92776
+ return [Math.round(r3 * 255), Math.round(g2 * 255), Math.round(b2 * 255)];
92777
+ });
92778
+ const schemeGreys = Array.from({ length: 256 }, (_, i2) => [i2, i2, i2]);
92721
92779
  function rgbSpline$1(spline) {
92722
92780
  return (colors2) => {
92723
92781
  const n3 = colors2.length;
@@ -92755,7 +92813,15 @@ function basis$2(values3) {
92755
92813
  };
92756
92814
  }
92757
92815
  const interpolateRgbBasis = rgbSpline$1(basis$2);
92816
+ function interpolateSequentialMulti(range2) {
92817
+ const n3 = range2.length;
92818
+ return (t4) => range2[Math.max(0, Math.min(n3 - 1, Math.floor(t4 * n3)))];
92819
+ }
92758
92820
  interpolateRgbBasis(schemeRdBu);
92821
+ const interpolatePlasma = interpolateSequentialMulti(schemePlasma);
92822
+ const interpolateViridis = interpolateSequentialMulti(schemeViridis);
92823
+ const interpolateJet = interpolateSequentialMulti(schemeJet);
92824
+ const interpolateGreys = interpolateSequentialMulti(schemeGreys);
92759
92825
  function getCellColors(params2) {
92760
92826
  const { cellSets, cellSetSelection, cellSetColor, obsIndex, theme } = params2;
92761
92827
  if (cellSetSelection && cellSets) {
@@ -99537,7 +99603,7 @@ const Tooltip$2 = /* @__PURE__ */ React.forwardRef((props, ref2) => {
99537
99603
  }, extraProps), getChildren());
99538
99604
  });
99539
99605
  const nodeHeight = 32;
99540
- const useStyles$v = makeStyles()((theme) => ({
99606
+ const useStyles$w = makeStyles()((theme) => ({
99541
99607
  setsManager: {
99542
99608
  position: "relative",
99543
99609
  width: "100%",
@@ -99704,7 +99770,7 @@ const useStyles$v = makeStyles()((theme) => ({
99704
99770
  }));
99705
99771
  function SetsManagerTreeGlobalStyles(props) {
99706
99772
  const { classes: classes2 } = props;
99707
- const { theme } = useStyles$y();
99773
+ const { theme } = useStyles$z();
99708
99774
  return jsx$1(ScopedGlobalStyles, { parentClassName: classes2.setsManagerTree, styles: {
99709
99775
  ".rc-tree": {
99710
99776
  paddingLeft: "0",
@@ -100045,7 +100111,7 @@ function HelpTooltipGlobalStyles(props) {
100045
100111
  }
100046
100112
  function PopoverGlobalStyles(props) {
100047
100113
  const { classes: classes2 } = props;
100048
- const { theme } = useStyles$y();
100114
+ const { theme } = useStyles$z();
100049
100115
  return jsx$1(ScopedGlobalStyles, { parentClassName: classes2.popover, styles: {
100050
100116
  ".rc-tooltip-inner": {
100051
100117
  boxSizing: "border-box",
@@ -104729,7 +104795,7 @@ function NamedSetNodeStatic(props) {
104729
104795
  const onClick = level === 0 && !expanded ? () => onCheckLevel(nodeKey, nextLevelToCheck) : () => onNodeView(path2);
104730
104796
  const tooltipProps = disableTooltip ? { visible: false } : {};
104731
104797
  const popoverMenuConfig = makeNodeViewMenuConfig(props);
104732
- const { classes: classes2 } = useStyles$v();
104798
+ const { classes: classes2 } = useStyles$w();
104733
104799
  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 })] });
104734
104800
  }
104735
104801
  function NamedSetNodeEditing(props) {
@@ -104741,7 +104807,7 @@ function NamedSetNodeEditing(props) {
104741
104807
  onNodeSetName(path2, currentTitle, true);
104742
104808
  }
104743
104809
  }
104744
- const { classes: classes2 } = useStyles$v();
104810
+ const { classes: classes2 } = useStyles$w();
104745
104811
  return jsxs("span", { className: classes2.titleButtonWithInput, children: [jsx$1("input", {
104746
104812
  // eslint-disable-next-line jsx-a11y/no-autofocus
104747
104813
  autoFocus: true,
@@ -104767,7 +104833,7 @@ function LevelsButtons(props) {
104767
104833
  onCheckLevel(nodeKey, newLevel);
104768
104834
  }
104769
104835
  }
104770
- const { classes: classes2 } = useStyles$v();
104836
+ const { classes: classes2 } = useStyles$w();
104771
104837
  return jsx$1("div", { className: classes2.levelButtonsContainer, children: range$e(1, height2 + 1).map((i2) => {
104772
104838
  const isChecked = isEqual$3(path2, checkedLevelPath) && i2 === checkedLevelIndex;
104773
104839
  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);
@@ -104851,7 +104917,7 @@ let TreeNode$1 = class TreeNode extends TreeNode$2 {
104851
104917
  };
104852
104918
  function PlusButton(props) {
104853
104919
  const { datatype, onError, onImportTree, onCreateLevelZeroNode, importable, editable } = props;
104854
- const { classes: classes2 } = useStyles$v();
104920
+ const { classes: classes2 } = useStyles$w();
104855
104921
  const onImport = useCallback((importHandler, mimeType) => () => {
104856
104922
  const uploadInputNode = document.createElement("input");
104857
104923
  uploadInputNode.setAttribute("type", "file");
@@ -104968,7 +105034,7 @@ function SetsManager(props) {
104968
105034
  }, onNodeCheckNewName, onNodeSetIsEditing: setIsEditingNodeName, onNodeRemove, onExportLevelZeroNodeJSON, onExportLevelZeroNodeTabular, onExportSetJSON, disableTooltip: isDragging, onDragStart: () => setIsDragging(true), onDragEnd: () => setIsDragging(false), children: renderTreeNodes(node2.children, readOnly, newPath) }, pathToKey(newPath));
104969
105035
  });
104970
105036
  }
104971
- const { classes: classes2 } = useStyles$v();
105037
+ const { classes: classes2 } = useStyles$w();
104972
105038
  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) => {
104973
105039
  const { eventKey: dropKey } = info2.node.props;
104974
105040
  const { eventKey: dragKey } = info2.dragNode.props;
@@ -210933,22 +210999,22 @@ async function getDecoder(fileDirectory) {
210933
210999
  const Decoder = await importFn();
210934
211000
  return new Decoder(fileDirectory);
210935
211001
  }
210936
- addDecoder([void 0, 1], () => import("./raw-CCLdztVu.js").then((m2) => m2.default));
210937
- addDecoder(5, () => import("./lzw-CKCb2yMZ.js").then((m2) => m2.default));
211002
+ addDecoder([void 0, 1], () => import("./raw-DMNeLVd7.js").then((m2) => m2.default));
211003
+ addDecoder(5, () => import("./lzw-BmHVONBc.js").then((m2) => m2.default));
210938
211004
  addDecoder(6, () => {
210939
211005
  throw new Error("old style JPEG compression is not supported.");
210940
211006
  });
210941
- addDecoder(7, () => import("./jpeg-Co4ZcifY.js").then((m2) => m2.default));
210942
- addDecoder([8, 32946], () => import("./deflate-Bbx7Vqb7.js").then((m2) => m2.default));
210943
- addDecoder(32773, () => import("./packbits-BNHyrTcI.js").then((m2) => m2.default));
211007
+ addDecoder(7, () => import("./jpeg-BGB-_EKG.js").then((m2) => m2.default));
211008
+ addDecoder([8, 32946], () => import("./deflate-38TS7div.js").then((m2) => m2.default));
211009
+ addDecoder(32773, () => import("./packbits-DKF2IGBX.js").then((m2) => m2.default));
210944
211010
  addDecoder(
210945
211011
  34887,
210946
- () => import("./lerc-_s2Wotdl.js").then(async (m2) => {
211012
+ () => import("./lerc-Db0ZEZ3c.js").then(async (m2) => {
210947
211013
  await m2.zstd.init();
210948
211014
  return m2;
210949
211015
  }).then((m2) => m2.default)
210950
211016
  );
210951
- addDecoder(50001, () => import("./webimage-BozDrenu.js").then((m2) => m2.default));
211017
+ addDecoder(50001, () => import("./webimage-BTp31_-j.js").then((m2) => m2.default));
210952
211018
  function copyNewSize(array2, width2, height2, samplesPerPixel = 1) {
210953
211019
  return new (Object.getPrototypeOf(array2)).constructor(width2 * height2 * samplesPerPixel);
210954
211020
  }
@@ -227722,7 +227788,7 @@ class ContourLayerWithText extends ContourLayer {
227722
227788
  }
227723
227789
  ContourLayerWithText.layerName = "ContourLayerWithText";
227724
227790
  ContourLayerWithText.defaultProps = defaultProps;
227725
- const useStyles$u = makeStyles()(() => ({
227791
+ const useStyles$v = makeStyles()(() => ({
227726
227792
  toolButton: {
227727
227793
  display: "inline-flex",
227728
227794
  "&:active": {
@@ -227784,12 +227850,12 @@ const useStyles$u = makeStyles()(() => ({
227784
227850
  }));
227785
227851
  function IconTool(props) {
227786
227852
  const { alt, onClick, isActive: isActive2, children: children3 } = props;
227787
- const { classes: classes2 } = useStyles$u();
227853
+ const { classes: classes2 } = useStyles$v();
227788
227854
  return jsx$1("button", { className: clsx(classes2.toolIcon, { [classes2.toolActive]: isActive2 }), onClick, type: "button", title: alt, children: children3 });
227789
227855
  }
227790
227856
  function IconButton(props) {
227791
227857
  const { alt, onClick, children: children3 } = props;
227792
- const { classes: classes2 } = useStyles$u();
227858
+ const { classes: classes2 } = useStyles$v();
227793
227859
  return jsx$1("button", { className: clsx(classes2.toolIcon, classes2.toolButton), onClick, type: "button", title: alt, children: children3 });
227794
227860
  }
227795
227861
  function ToolMenu(props) {
@@ -227797,7 +227863,7 @@ function ToolMenu(props) {
227797
227863
  const lassoIconAltText = "Select lasso";
227798
227864
  const { setActiveTool, activeTool, visibleTools = { pan: true, selectLasso: true, recenter: true }, recenterOnClick = () => {
227799
227865
  } } = props;
227800
- const { classes: classes2 } = useStyles$u();
227866
+ const { classes: classes2 } = useStyles$v();
227801
227867
  const onRecenterButtonCLick = () => {
227802
227868
  recenterOnClick();
227803
227869
  };
@@ -228954,7 +229020,7 @@ function ScatterplotOptions(props) {
228954
229020
  id: `feature-aggregation-strategy-${scatterplotOptionsId}`
228955
229021
  }, children: FEATURE_AGGREGATION_STRATEGIES$2.map((opt) => jsx$1("option", { value: opt, children: capitalize$3(opt) }, opt)) }) })] }) : null] });
228956
229022
  }
228957
- const useStyles$t = makeStyles()((theme) => ({
229023
+ const useStyles$u = makeStyles()((theme) => ({
228958
229024
  tooltipAnchor: {
228959
229025
  position: "relative",
228960
229026
  width: "0px",
@@ -228985,7 +229051,7 @@ const useStyles$t = makeStyles()((theme) => ({
228985
229051
  function Tooltip3(props) {
228986
229052
  const { x: x2, y: y2, parentWidth, parentHeight, children: children3 } = props;
228987
229053
  const ref2 = useRef();
228988
- const { classes: classes2 } = useStyles$t();
229054
+ const { classes: classes2 } = useStyles$u();
228989
229055
  const [placementX, setPlacementX] = useState("start");
228990
229056
  const [placementY, setPlacementY] = useState("bottom");
228991
229057
  const getTooltipContainer = useVitessceContainer(ref2);
@@ -229001,7 +229067,7 @@ function Tooltip3(props) {
229001
229067
  }, [x2, y2, parentWidth, parentHeight]);
229002
229068
  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 });
229003
229069
  }
229004
- const useStyles$s = makeStyles()((theme) => ({
229070
+ const useStyles$t = makeStyles()((theme) => ({
229005
229071
  cellEmphasisCrosshair: {
229006
229072
  zIndex: 50,
229007
229073
  position: "absolute",
@@ -229012,7 +229078,7 @@ const useStyles$s = makeStyles()((theme) => ({
229012
229078
  }));
229013
229079
  function Tooltip2D(props) {
229014
229080
  const { parentUuid, sourceUuid, x: x2, y: y2, parentWidth, parentHeight, children: children3 } = props;
229015
- const { classes: classes2 } = useStyles$s();
229081
+ const { classes: classes2 } = useStyles$t();
229016
229082
  if (x2 < 0 || x2 > parentWidth || y2 < 0 || y2 > parentHeight) {
229017
229083
  return null;
229018
229084
  }
@@ -232924,7 +232990,7 @@ function getXlinkHref(cmap) {
232924
232990
  const xlinkHref = ramp(color2.copy().domain(quantize$3(interpolate$1$1(0, 1), n3))).toDataURL();
232925
232991
  return xlinkHref;
232926
232992
  }
232927
- const useStyles$r = makeStyles()(() => ({
232993
+ const useStyles$s = makeStyles()(() => ({
232928
232994
  legend: {
232929
232995
  position: "relative",
232930
232996
  // Needed for absolute positioning of slider overlay
@@ -233121,7 +233187,7 @@ function Legend(props) {
233121
233187
  contourThresholds
233122
233188
  } = props;
233123
233189
  const svgRef = useRef(null);
233124
- const { classes: classes2 } = useStyles$r();
233190
+ const { classes: classes2 } = useStyles$s();
233125
233191
  const [localRange, setLocalRange] = useState(featureValueColormapRange);
233126
233192
  useEffect(() => {
233127
233193
  setLocalRange(featureValueColormapRange);
@@ -233455,7 +233521,7 @@ function Legend(props) {
233455
233521
  }
233456
233522
  } })] })] });
233457
233523
  }
233458
- const useStyles$q = makeStyles()(() => ({
233524
+ const useStyles$r = makeStyles()(() => ({
233459
233525
  multiLegend: {
233460
233526
  position: "absolute",
233461
233527
  top: "0px",
@@ -233482,7 +233548,7 @@ function MultiLegend(props) {
233482
233548
  pointLayerCoordination,
233483
233549
  pointMultiIndicesData
233484
233550
  } = props;
233485
- const { classes: classes2 } = useStyles$q();
233551
+ const { classes: classes2 } = useStyles$r();
233486
233552
  const reversedSegmentationLayerScopes = useMemo$1(() => [...segmentationLayerScopes || []].reverse(), [segmentationLayerScopes]);
233487
233553
  const reversedSpotLayerScopes = useMemo$1(() => [...spotLayerScopes || []].reverse(), [spotLayerScopes]);
233488
233554
  const reversedPointLayerScopes = useMemo$1(() => [...pointLayerScopes || []].reverse(), [pointLayerScopes]);
@@ -233575,7 +233641,7 @@ function MultiLegend(props) {
233575
233641
  }) : null;
233576
233642
  }) : null] });
233577
233643
  }
233578
- const useStyles$p = makeStyles()(() => ({
233644
+ const useStyles$q = makeStyles()(() => ({
233579
233645
  channelNamesLegendContainer: {
233580
233646
  position: "absolute",
233581
233647
  bottom: "0px",
@@ -233598,7 +233664,7 @@ const useStyles$p = makeStyles()(() => ({
233598
233664
  }));
233599
233665
  function ChannelNamesLegend(props) {
233600
233666
  const { images, imageLayerScopes, imageLayerCoordination, imageChannelScopesByLayer, imageChannelCoordination } = props;
233601
- const { classes: classes2 } = useStyles$p();
233667
+ const { classes: classes2 } = useStyles$q();
233602
233668
  const reversedImageLayerScopes = useMemo$1(() => [...imageLayerScopes || []].reverse(), [imageLayerScopes]);
233603
233669
  return jsx$1("div", { className: classes2.channelNamesLegendContainer, children: imageLayerScopes ? reversedImageLayerScopes.map((layerScope) => {
233604
233670
  const layerCoordination = imageLayerCoordination[0][layerScope];
@@ -255205,7 +255271,7 @@ class Spatial2 extends AbstractSpatialOrScatterplot {
255205
255271
  this.viewInfoDidUpdate();
255206
255272
  const shallowDiff2 = (propName) => prevProps[propName] !== this.props[propName];
255207
255273
  const shallowDiffByLayer2 = (propName, scopeName) => prevProps?.[propName]?.[scopeName] !== this.props?.[propName]?.[scopeName];
255208
- const shallowDiffByChannel = (propName, firstName, secondName) => prevProps?.[propName]?.[firstName]?.[secondName] !== this.props?.[propName]?.[firstName]?.[secondName];
255274
+ const shallowDiffByChannel2 = (propName, firstName, secondName) => prevProps?.[propName]?.[firstName]?.[secondName] !== this.props?.[propName]?.[firstName]?.[secondName];
255209
255275
  const shallowDiffByLayerCoordination = (propName, layerScope) => prevProps?.[propName]?.[0]?.[layerScope] !== this.props?.[propName]?.[0]?.[layerScope];
255210
255276
  const shallowDiffByChannelCoordination = (propName, layerScope, channelScope) => prevProps?.[propName]?.[0]?.[layerScope]?.[channelScope] !== this.props?.[propName]?.[0]?.[layerScope]?.[channelScope];
255211
255277
  let forceUpdate = false;
@@ -255226,7 +255292,7 @@ class Spatial2 extends AbstractSpatialOrScatterplot {
255226
255292
  } else {
255227
255293
  this.props.segmentationLayerScopes?.forEach((layerScope) => {
255228
255294
  this.props.segmentationChannelScopesByLayer?.[layerScope]?.forEach((channelScope) => {
255229
- if (shallowDiffByChannel("obsSegmentationsLocations", layerScope, channelScope)) {
255295
+ if (shallowDiffByChannel2("obsSegmentationsLocations", layerScope, channelScope)) {
255230
255296
  this.onUpdateSegmentationsLocationsData(layerScope, channelScope);
255231
255297
  forceUpdate = true;
255232
255298
  }
@@ -255239,7 +255305,7 @@ class Spatial2 extends AbstractSpatialOrScatterplot {
255239
255305
  } else {
255240
255306
  this.props.segmentationLayerScopes?.forEach((layerScope) => {
255241
255307
  this.props.segmentationChannelScopesByLayer?.[layerScope]?.forEach((channelScope) => {
255242
- if (shallowDiffByChannel("obsSegmentationsSets", layerScope, channelScope) || shallowDiffByChannelCoordination("segmentationChannelCoordination", layerScope, channelScope)) {
255308
+ if (shallowDiffByChannel2("obsSegmentationsSets", layerScope, channelScope) || shallowDiffByChannelCoordination("segmentationChannelCoordination", layerScope, channelScope)) {
255243
255309
  this.onUpdateSegmentationsSetsData(layerScope, channelScope);
255244
255310
  forceUpdate = true;
255245
255311
  }
@@ -255252,7 +255318,7 @@ class Spatial2 extends AbstractSpatialOrScatterplot {
255252
255318
  } else {
255253
255319
  this.props.segmentationLayerScopes?.forEach((layerScope) => {
255254
255320
  this.props.segmentationChannelScopesByLayer?.[layerScope]?.forEach((channelScope) => {
255255
- if (shallowDiffByChannel("segmentationMatrixIndices", layerScope, channelScope)) {
255321
+ if (shallowDiffByChannel2("segmentationMatrixIndices", layerScope, channelScope)) {
255256
255322
  this.onUpdateSegmentationsIndexData(layerScope, channelScope);
255257
255323
  forceUpdate = true;
255258
255324
  }
@@ -255265,7 +255331,7 @@ class Spatial2 extends AbstractSpatialOrScatterplot {
255265
255331
  } else {
255266
255332
  this.props.segmentationLayerScopes.forEach((layerScope) => {
255267
255333
  this.props.segmentationChannelScopesByLayer?.[layerScope]?.forEach((channelScope) => {
255268
- if (shallowDiffByChannel("segmentationMatrixIndices", layerScope, channelScope) || shallowDiffByChannel("segmentationMultiExpressionData", layerScope, channelScope)) {
255334
+ if (shallowDiffByChannel2("segmentationMatrixIndices", layerScope, channelScope) || shallowDiffByChannel2("segmentationMultiExpressionData", layerScope, channelScope)) {
255269
255335
  this.onUpdateSegmentationsExpressionData(layerScope, channelScope);
255270
255336
  forceUpdate = true;
255271
255337
  }
@@ -255519,12 +255585,12 @@ class ErrorBoundary extends React__default.Component {
255519
255585
  }
255520
255586
  }
255521
255587
  const LazySpatialThree = React__default.lazy(async () => {
255522
- const { SpatialWrapper: SpatialWrapper2 } = await import("./index-BlOIc49f.js").then((n3) => n3.a);
255588
+ const { SpatialWrapper: SpatialWrapper2 } = await import("./index-CA6Y_Dls.js").then((n3) => n3.a);
255523
255589
  return { default: SpatialWrapper2 };
255524
255590
  });
255525
255591
  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 }) }) }) }));
255526
255592
  const LazySpatialAccelerated = React__default.lazy(async () => {
255527
- const { SpatialWrapper: SpatialWrapper2 } = await import("./index-BSOXpbtb.js");
255593
+ const { SpatialWrapper: SpatialWrapper2 } = await import("./index-gStG4FEQ.js");
255528
255594
  return { default: SpatialWrapper2 };
255529
255595
  });
255530
255596
  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 }) }) }) }));
@@ -263597,7 +263663,7 @@ _defineProperty$3(WindowScroller, "defaultProps", {
263597
263663
  serverHeight: 0,
263598
263664
  serverWidth: 0
263599
263665
  });
263600
- const useStyles$o = makeStyles()((theme) => ({
263666
+ const useStyles$p = makeStyles()((theme) => ({
263601
263667
  selectableTable: {
263602
263668
  flex: "1 1 auto",
263603
263669
  outline: "none"
@@ -263746,7 +263812,7 @@ function SelectableTable(props) {
263746
263812
  onChange(null);
263747
263813
  }
263748
263814
  }, [selectedRows, allowMultiple]);
263749
- const { classes: classes2 } = useStyles$o();
263815
+ const { classes: classes2 } = useStyles$p();
263750
263816
  const inputUuid = v4$1();
263751
263817
  const rowRenderer = ({ index: index2, style: style2 }) => (
263752
263818
  // eslint-disable-next-line jsx-a11y/interactive-supports-focus
@@ -263771,7 +263837,7 @@ const FEATURELIST_SORT_OPTIONS = [
263771
263837
  "original"
263772
263838
  ];
263773
263839
  const ALT_COLNAME = "Alternate ID";
263774
- const useStyles$n = makeStyles()(() => ({
263840
+ const useStyles$o = makeStyles()(() => ({
263775
263841
  searchBar: {
263776
263842
  marginTop: "10px",
263777
263843
  marginLeft: "10px",
@@ -263784,7 +263850,7 @@ const useStyles$n = makeStyles()(() => ({
263784
263850
  }));
263785
263851
  function FeatureList(props) {
263786
263852
  const { width: width2, height: height2, hasColorEncoding, geneList = [], featureLabelsMap, geneSelection = [], geneFilter = null, setGeneSelection, enableMultiSelect, showFeatureTable, featureListSort, featureListSortKey, hasFeatureLabels, primaryColumnName } = props;
263787
- const { classes: classes2 } = useStyles$n();
263853
+ const { classes: classes2 } = useStyles$o();
263788
263854
  const [searchTerm, setSearchTerm] = useState("");
263789
263855
  const selectableTableSortKey = featureListSortKey === "featureIndex" ? "key" : "name";
263790
263856
  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]);
@@ -263857,7 +263923,7 @@ function FeatureListOptions(props) {
263857
263923
  id: `feature-list-show-alternative-ids-${featureListId}`
263858
263924
  } } }) })] })] }) : null] });
263859
263925
  }
263860
- const useStyles$m = makeStyles()((theme) => ({
263926
+ const useStyles$n = makeStyles()((theme) => ({
263861
263927
  featureListContainer: {
263862
263928
  width: "100%",
263863
263929
  height: "100%",
@@ -263872,7 +263938,7 @@ function FeatureListSubscriber(props) {
263872
263938
  const loaders = useLoaders();
263873
263939
  const coordinationScopes = useCoordinationScopes(coordinationScopesRaw);
263874
263940
  const [width2, height2, containerRef] = useGridItemSize();
263875
- const { classes: classes2 } = useStyles$m();
263941
+ const { classes: classes2 } = useStyles$n();
263876
263942
  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);
263877
263943
  const variablesLabel = variablesLabelOverride || featureType;
263878
263944
  const title2 = titleOverride || `${capitalize$3(variablesLabel)} List`;
@@ -264007,7 +264073,7 @@ const useSelectionSliderStyles = makeStyles()(() => ({
264007
264073
  backgroundColor: "rgba(128, 128, 128, 0.7)"
264008
264074
  }
264009
264075
  }));
264010
- const useStyles$l = makeStyles()((theme) => ({
264076
+ const useStyles$m = makeStyles()((theme) => ({
264011
264077
  paletteContainer: {
264012
264078
  width: "70px",
264013
264079
  height: "40px",
@@ -264028,10 +264094,10 @@ const useStyles$l = makeStyles()((theme) => ({
264028
264094
  }
264029
264095
  }));
264030
264096
  const ColorPalette = ({ handleChange }) => {
264031
- const { classes: classes2 } = useStyles$l();
264097
+ const { classes: classes2 } = useStyles$m();
264032
264098
  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)) });
264033
264099
  };
264034
- const useStyles$k = makeStyles()(() => ({
264100
+ const useStyles$l = makeStyles()(() => ({
264035
264101
  menuButton: {
264036
264102
  backgroundColor: "transparent"
264037
264103
  },
@@ -264050,7 +264116,7 @@ function MuiSpan(props) {
264050
264116
  }
264051
264117
  function ChannelOptions$1({ handlePropertyChange, handleChannelRemove, handleIQRUpdate }) {
264052
264118
  const [open2, setOpen] = useState(false);
264053
- const { classes: classes2 } = useStyles$k();
264119
+ const { classes: classes2 } = useStyles$l();
264054
264120
  const handleColorSelect = (color2) => {
264055
264121
  handlePropertyChange("color", color2);
264056
264122
  };
@@ -264667,7 +264733,7 @@ function LayerController$1(props) {
264667
264733
  minWidth: "50%"
264668
264734
  } })] }), 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] })] });
264669
264735
  }
264670
- const useStyles$j = makeStyles()(() => ({
264736
+ const useStyles$k = makeStyles()(() => ({
264671
264737
  addButton: {
264672
264738
  marginTop: "10px",
264673
264739
  marginBottom: "10px",
@@ -264679,7 +264745,7 @@ function ImageAddIcon() {
264679
264745
  }
264680
264746
  function ImageAddButton({ imageOptions, handleImageAdd }) {
264681
264747
  const [open2, setOpen] = useState(false);
264682
- const { classes: classes2 } = useStyles$j();
264748
+ const { classes: classes2 } = useStyles$k();
264683
264749
  const handleAdd = (imgData) => {
264684
264750
  setOpen((prev2) => !prev2);
264685
264751
  handleImageAdd(imgData);
@@ -265011,7 +265077,7 @@ const useEllipsisMenuStyles = makeStyles()(() => ({
265011
265077
  padding: "4px"
265012
265078
  }
265013
265079
  }));
265014
- const useStyles$i = makeStyles()(() => ({
265080
+ const useStyles$j = makeStyles()(() => ({
265015
265081
  colorPickerPaper: {
265016
265082
  overflow: "hidden",
265017
265083
  "& > ul": {
@@ -265069,7 +265135,7 @@ function ChannelColorPickerMenu(props) {
265069
265135
  setColor2([rgb2.r, rgb2.g, rgb2.b]);
265070
265136
  }
265071
265137
  }
265072
- const { classes: classes2 } = useStyles$i();
265138
+ const { classes: classes2 } = useStyles$j();
265073
265139
  const currentColor = color2 ? colorArrayToString(color2) : colorArrayToString(getDefaultColor(theme));
265074
265140
  const isWritable = typeof setColor2 === "function";
265075
265141
  const derivedOpen = isWritable ? isStaticColor && visible ? open2 : false : false;
@@ -265077,7 +265143,7 @@ function ChannelColorPickerMenu(props) {
265077
265143
  [classes2.colorIconOutline]: theme !== "dark" && isEqual$3(color2, [255, 255, 255])
265078
265144
  }), 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 }) });
265079
265145
  }
265080
- const useStyles$h = makeStyles()(() => ({
265146
+ const useStyles$i = makeStyles()(() => ({
265081
265147
  layerTypeSpotIcon: {
265082
265148
  height: "100%",
265083
265149
  marginLeft: "1px",
@@ -265090,7 +265156,7 @@ const useStyles$h = makeStyles()(() => ({
265090
265156
  function SpotLayerEllipsisMenu(props) {
265091
265157
  const { strokeWidth, setStrokeWidth, filled, setFilled, featureSelection, obsColorEncoding, setObsColorEncoding, featureValueColormap, setFeatureValueColormap, featureValueColormapRange, setFeatureValueColormapRange, tooltipsVisible, setTooltipsVisible, tooltipCrosshairsVisible, setTooltipCrosshairsVisible, legendVisible, setLegendVisible } = props;
265092
265158
  const [open2, setOpen] = useState(false);
265093
- const { classes: classes2 } = useStyles$h();
265159
+ const { classes: classes2 } = useStyles$i();
265094
265160
  const { classes: selectClasses2 } = useSelectStyles();
265095
265161
  const { classes: menuClasses } = useEllipsisMenuStyles();
265096
265162
  const filledId = $bdb11010cef70236$export$f680877a34711e37();
@@ -265112,7 +265178,7 @@ function SpotLayerController(props) {
265112
265178
  const Visibility = useMemo$1(() => visibleSetting ? VisibilityIcon : VisibilityOffIcon, [visibleSetting]);
265113
265179
  const isStaticColor = obsColorEncoding === "spatialLayerColor";
265114
265180
  const isColormap = obsColorEncoding === "geneSelection";
265115
- const { classes: classes2 } = useStyles$h();
265181
+ const { classes: classes2 } = useStyles$i();
265116
265182
  const { classes: lcClasses } = useControllerSectionStyles();
265117
265183
  const { classes: menuClasses } = useEllipsisMenuStyles();
265118
265184
  const handleVisibleChange = useCallback(() => {
@@ -265122,7 +265188,7 @@ function SpotLayerController(props) {
265122
265188
  const handleOpacityChange = useCallback((e3, v) => setOpacity2(v), [setOpacity2]);
265123
265189
  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 }) })] }) }) });
265124
265190
  }
265125
- const useStyles$g = makeStyles()(() => ({
265191
+ const useStyles$h = makeStyles()(() => ({
265126
265192
  layerFeatureControllerGrid: {
265127
265193
  padding: "0",
265128
265194
  flexWrap: "nowrap"
@@ -265167,14 +265233,15 @@ function LayerPerFeatureController(props) {
265167
265233
  ]);
265168
265234
  }
265169
265235
  }, [featureName, featureColor, setFeatureColor, featureColorIndex]);
265170
- const { classes: classes2 } = useStyles$g();
265236
+ const { classes: classes2 } = useStyles$h();
265171
265237
  const { classes: lcClasses } = useControllerSectionStyles();
265172
265238
  const { classes: menuClasses } = useEllipsisMenuStyles();
265173
265239
  const [open2, setOpen] = useState(false);
265174
265240
  const { colorPickerColor, colorPickerReadable, colorPickerWritable, colorPickerTooltip } = useMemo$1(() => {
265175
265241
  if (obsColorEncoding === "geneSelection") {
265242
+ const storedColor = featureColor?.find((fc) => fc.name === featureName)?.color;
265176
265243
  return {
265177
- colorPickerColor: featureColor?.[featureColorIndex]?.color ?? spatialLayerColor,
265244
+ colorPickerColor: storedColor ?? randomByFeatureColor ?? spatialLayerColor,
265178
265245
  colorPickerReadable: true,
265179
265246
  colorPickerWritable: true,
265180
265247
  colorPickerTooltip: null
@@ -265220,7 +265287,7 @@ function LayerPerFeatureController(props) {
265220
265287
  ]);
265221
265288
  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);
265222
265289
  }
265223
- const useStyles$f = makeStyles()(() => ({
265290
+ const useStyles$g = makeStyles()(() => ({
265224
265291
  pointLayerButton: {
265225
265292
  borderStyle: "dashed",
265226
265293
  marginTop: "10px",
@@ -265249,7 +265316,7 @@ const useStyles$f = makeStyles()(() => ({
265249
265316
  }
265250
265317
  }));
265251
265318
  function PointLayerEllipsisMenu(props) {
265252
- const { featureSelection, featureFilterMode, setFeatureFilterMode, obsColorEncoding, setObsColorEncoding, featureValueColormapRange, setFeatureValueColormapRange, tooltipsVisible, setTooltipsVisible, tooltipCrosshairsVisible, setTooltipCrosshairsVisible, legendVisible, setLegendVisible } = props;
265319
+ const { featureSelection, featureFilterMode, setFeatureFilterMode, obsColorEncoding, setObsColorEncoding, featureValueColormap, setFeatureValueColormap, featureValueColormapRange, setFeatureValueColormapRange, tooltipsVisible, setTooltipsVisible, tooltipCrosshairsVisible, setTooltipCrosshairsVisible, legendVisible, setLegendVisible } = props;
265253
265320
  const [open2, setOpen] = useState(false);
265254
265321
  const { classes: selectClasses2 } = useSelectStyles();
265255
265322
  const { classes: menuClasses } = useEllipsisMenuStyles();
@@ -265259,7 +265326,8 @@ function PointLayerEllipsisMenu(props) {
265259
265326
  const crosshairsVisibleId = $bdb11010cef70236$export$f680877a34711e37();
265260
265327
  const legendVisibleId = $bdb11010cef70236$export$f680877a34711e37();
265261
265328
  const featureFilterModeId = $bdb11010cef70236$export$f680877a34711e37();
265262
- 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" } } })] })] });
265329
+ const colormapSelectorId = $bdb11010cef70236$export$f680877a34711e37();
265330
+ 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" } } })] })] });
265263
265331
  }
265264
265332
  function PointLayerController(props) {
265265
265333
  const { theme, layerScope, layerCoordination, setLayerCoordination, palette = null, pointMatrixIndicesData, tiledPointsLoadingProgress, layerPerFeatureForPoints } = props;
@@ -265289,7 +265357,7 @@ function PointLayerController(props) {
265289
265357
  }, [featureColor, featureSelection]);
265290
265358
  const isStaticColor = obsColorEncoding === "spatialLayerColor" || obsColorEncoding === "geneSelection";
265291
265359
  const showStaticColor = obsColorEncoding === "spatialLayerColor" || obsColorEncoding === "geneSelection" && hasUnspecifiedFeatureColors;
265292
- const isColormap = false;
265360
+ const isColormap = obsColorEncoding === "geneSelection";
265293
265361
  const hasSingleSelectedFeature = obsColorEncoding === "geneSelection" && Array.isArray(featureSelection) && featureSelection.length === 1;
265294
265362
  const color2 = useMemo$1(() => {
265295
265363
  if (showStaticColor) {
@@ -265336,7 +265404,7 @@ function PointLayerController(props) {
265336
265404
  featureSelection,
265337
265405
  showStaticColor
265338
265406
  ]);
265339
- const { classes: classes2 } = useStyles$f();
265407
+ const { classes: classes2 } = useStyles$g();
265340
265408
  const { classes: lcClasses } = useControllerSectionStyles();
265341
265409
  const { classes: menuClasses } = useEllipsisMenuStyles();
265342
265410
  const handleVisibleChange = useCallback(() => {
@@ -265348,9 +265416,24 @@ function PointLayerController(props) {
265348
265416
  const [coloringTabIndex, setColoringTabIndex] = useState(0);
265349
265417
  const { featureIndex } = pointMatrixIndicesData || {};
265350
265418
  const hasSelectedFeatures = Array.isArray(featureSelection) && featureSelection.length > 0;
265351
- 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] });
265419
+ useEffect(() => {
265420
+ if (obsColorEncoding !== "geneSelection" || !Array.isArray(featureSelection))
265421
+ return;
265422
+ const cleanedFeatureColor = (featureColor ?? []).filter((fc) => featureSelection.includes(fc.name));
265423
+ const missingEntries = featureSelection.filter((featureName) => !cleanedFeatureColor.find((fc) => fc.name === featureName)).map((featureName) => {
265424
+ const varIdx = featureIndex?.indexOf(featureName) ?? -1;
265425
+ const fColor = varIdx >= 0 ? PALETTE[varIdx % PALETTE.length] : getDefaultColor(theme);
265426
+ return { name: featureName, color: fColor };
265427
+ });
265428
+ const nextFeatureColor = [...cleanedFeatureColor, ...missingEntries];
265429
+ const changed = nextFeatureColor.length !== featureColor?.length || missingEntries.length > 0;
265430
+ if (changed) {
265431
+ setFeatureColor(nextFeatureColor);
265432
+ }
265433
+ }, [featureSelection, obsColorEncoding, featureIndex, featureColor]);
265434
+ 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] });
265352
265435
  }
265353
- const useStyles$e = makeStyles()(() => ({
265436
+ const useStyles$f = makeStyles()(() => ({
265354
265437
  layerTypeSegmentationIcon: {
265355
265438
  height: "100%",
265356
265439
  marginLeft: "1px",
@@ -265363,7 +265446,7 @@ const useStyles$e = makeStyles()(() => ({
265363
265446
  function SegmentationChannelEllipsisMenu(props) {
265364
265447
  const { obsType, featureType, featureValueType, strokeWidth, setStrokeWidth, filled, setFilled, featureSelection, obsColorEncoding, setObsColorEncoding, featureValueColormap, setFeatureValueColormap, featureValueColormapRange, setFeatureValueColormapRange, tooltipsVisible, setTooltipsVisible, tooltipCrosshairsVisible, setTooltipCrosshairsVisible, legendVisible, setLegendVisible } = props;
265365
265448
  const [open2, setOpen] = useState(false);
265366
- const { classes: classes2 } = useStyles$e();
265449
+ const { classes: classes2 } = useStyles$f();
265367
265450
  const { classes: selectClasses2 } = useSelectStyles();
265368
265451
  const { classes: menuClasses } = useEllipsisMenuStyles();
265369
265452
  const filledId = $bdb11010cef70236$export$f680877a34711e37();
@@ -265382,7 +265465,7 @@ function SegmentationChannelController(props) {
265382
265465
  const Visibility = useMemo$1(() => visibleSetting ? VisibilityIcon : VisibilityOffIcon, [visibleSetting]);
265383
265466
  const isStaticColor = obsColorEncoding === "spatialChannelColor";
265384
265467
  const isColormap = obsColorEncoding === "geneSelection";
265385
- const { classes: classes2 } = useStyles$e();
265468
+ const { classes: classes2 } = useStyles$f();
265386
265469
  const { classes: lcClasses } = useControllerSectionStyles();
265387
265470
  const { classes: menuClasses } = useEllipsisMenuStyles();
265388
265471
  const handleVisibleChange = useCallback(() => {
@@ -268100,7 +268183,7 @@ function useBaseQuery(options, Observer, queryClient) {
268100
268183
  function useQuery(options, queryClient) {
268101
268184
  return useBaseQuery(options, QueryObserver);
268102
268185
  }
268103
- const useStyles$d = makeStyles()(() => ({
268186
+ const useStyles$e = makeStyles()(() => ({
268104
268187
  channelMenuButton: {
268105
268188
  backgroundColor: "transparent",
268106
268189
  padding: "3px 0",
@@ -268114,7 +268197,7 @@ const useStyles$d = makeStyles()(() => ({
268114
268197
  function ChannelOptions(props) {
268115
268198
  const { onRemove: onRemove2, onResetWindowUsingIQR, showValueExtent, setShowValueExtent, numResolutions, spatialMaxResolution, setSpatialMaxResolution } = props;
268116
268199
  const [open2, setOpen] = useState(false);
268117
- const { classes: classes2 } = useStyles$d();
268200
+ const { classes: classes2 } = useStyles$e();
268118
268201
  const { classes: selectClasses2 } = useSelectStyles();
268119
268202
  const { classes: menuClasses } = useEllipsisMenuStyles();
268120
268203
  function handleRemove() {
@@ -268127,7 +268210,7 @@ function ChannelOptions(props) {
268127
268210
  const domainTypeId = $bdb11010cef70236$export$f680877a34711e37();
268128
268211
  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" })] });
268129
268212
  }
268130
- const useStyles$c = makeStyles()(() => ({
268213
+ const useStyles$d = makeStyles()(() => ({
268131
268214
  channelSlider: {
268132
268215
  marginTop: "7px"
268133
268216
  }
@@ -268137,7 +268220,7 @@ function ChannelSlider(props) {
268137
268220
  const rgbColor = toRgbUIString(colormapOn, color2, theme);
268138
268221
  const dtype = image2?.getDtype();
268139
268222
  const fullDomain = dtype ? DOMAINS[dtype] : [0, 0];
268140
- const { classes: classes2 } = useStyles$c();
268223
+ const { classes: classes2 } = useStyles$d();
268141
268224
  useEffect(() => {
268142
268225
  if (!window2 && !disabled && Array.isArray(minMaxDomain)) {
268143
268226
  setWindow2(minMaxDomain);
@@ -268152,7 +268235,7 @@ function ChannelSlider(props) {
268152
268235
  return `${labelPrefix} for ${rgbColor} colormap channel`;
268153
268236
  }, getAriaValueText: () => `Current colormap values: ${rgbColor}-${window2}`, min: min2, max: max2, step, orientation: "horizontal", className: classes2.channelSlider, style: { color: rgbColor }, disabled });
268154
268237
  }
268155
- const useStyles$b = makeStyles()(() => ({
268238
+ const useStyles$c = makeStyles()(() => ({
268156
268239
  visibilityCheckbox: {
268157
268240
  padding: "10px 0"
268158
268241
  }
@@ -268166,10 +268249,10 @@ function getCheckboxColor(colormapOn, color2, theme) {
268166
268249
  function ChannelVisibilityCheckbox(props) {
268167
268250
  const { color: color2, visible, setVisible, disabled, theme, colormapOn } = props;
268168
268251
  const rgbColor = getCheckboxColor(colormapOn, color2, theme);
268169
- const { classes: classes2 } = useStyles$b();
268252
+ const { classes: classes2 } = useStyles$c();
268170
268253
  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" } } });
268171
268254
  }
268172
- const useStyles$a = makeStyles()(() => ({
268255
+ const useStyles$b = makeStyles()(() => ({
268173
268256
  oneLineChannelSelect: {
268174
268257
  width: "90%",
268175
268258
  marginLeft: "5%",
@@ -268178,7 +268261,7 @@ const useStyles$a = makeStyles()(() => ({
268178
268261
  }));
268179
268262
  function ChannelSelectionDropdown(props) {
268180
268263
  const { featureIndex, channelsSortOrder, targetC, setTargetC, setWindow: setWindow2, disabled } = props;
268181
- const { classes: classes2 } = useStyles$a();
268264
+ const { classes: classes2 } = useStyles$b();
268182
268265
  const { classes: selectClasses2 } = useSelectStyles();
268183
268266
  function handleChange(event2) {
268184
268267
  setTargetC(event2.target.value === "" ? null : Number(event2.target.value));
@@ -268313,7 +268396,7 @@ function LoadingStatusIndicator(props) {
268313
268396
  }
268314
268397
  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 }) })] });
268315
268398
  }
268316
- const useStyles$9 = makeStyles()(() => ({
268399
+ const useStyles$a = makeStyles()(() => ({
268317
268400
  imageLayerButton: {
268318
268401
  borderStyle: "dashed",
268319
268402
  marginTop: "10px",
@@ -268456,7 +268539,7 @@ function ImageLayerController(props) {
268456
268539
  }, [visible, setVisible]);
268457
268540
  const handleOpacityChange = useCallback((e3, v) => setOpacity2(v), [setOpacity2]);
268458
268541
  const handleOpenChange = useCallback(() => setOpen((prev2) => !prev2), []);
268459
- const { classes: classes2 } = useStyles$9();
268542
+ const { classes: classes2 } = useStyles$a();
268460
268543
  const { classes: menuClasses } = useEllipsisMenuStyles();
268461
268544
  const { classes: controllerSectionClasses } = useControllerSectionStyles();
268462
268545
  const isMultiChannel = photometricInterpretation !== "RGB";
@@ -268468,7 +268551,7 @@ function ImageLayerController(props) {
268468
268551
  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);
268469
268552
  }), 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] }) });
268470
268553
  }
268471
- const useStyles$8 = makeStyles()((theme) => ({
268554
+ const useStyles$9 = makeStyles()((theme) => ({
268472
268555
  dimensionsIcon: {
268473
268556
  height: "40px",
268474
268557
  width: "40px",
@@ -268492,7 +268575,7 @@ const useStyles$8 = makeStyles()((theme) => ({
268492
268575
  function GlobalDimensionSlider(props) {
268493
268576
  const { label: label2, targetValue, setTargetValue, min: min2 = 0, max: max2 = 0, spatialRenderingMode = null, setSpatialRenderingMode = null } = props;
268494
268577
  const { classes: lcClasses } = useControllerSectionStyles();
268495
- const { classes: classes2 } = useStyles$8();
268578
+ const { classes: classes2 } = useStyles$9();
268496
268579
  const isForZ = spatialRenderingMode !== null;
268497
268580
  const is3dMode = spatialRenderingMode === "3D";
268498
268581
  function handleRenderingModeChange(event2) {
@@ -268503,17 +268586,112 @@ function GlobalDimensionSlider(props) {
268503
268586
  }
268504
268587
  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 })] }) }) });
268505
268588
  }
268589
+ const useStyles$8 = makeStyles()(() => ({
268590
+ layerTypeCentroidSegmentationIcon: {
268591
+ height: "100%",
268592
+ marginLeft: "1px",
268593
+ fill: "currentColor",
268594
+ fontSize: "24px",
268595
+ width: "50%",
268596
+ maxWidth: "24px"
268597
+ },
268598
+ divider: {
268599
+ width: "1px",
268600
+ height: "20px",
268601
+ background: "currentColor",
268602
+ flexShrink: 0,
268603
+ margin: "0 10px"
268604
+ },
268605
+ inlineLabel: {
268606
+ margin: "0px !important",
268607
+ lineHeight: 1,
268608
+ alignSelf: "center"
268609
+ },
268610
+ iconCell: {
268611
+ display: "flex",
268612
+ alignItems: "center",
268613
+ justifyContent: "center"
268614
+ }
268615
+ }));
268616
+ function CombinedEllipsisMenu(props) {
268617
+ 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;
268618
+ const [open2, setOpen] = useState(false);
268619
+ const { classes: selectClasses2 } = useSelectStyles();
268620
+ const { classes: menuClasses } = useEllipsisMenuStyles();
268621
+ const filledId = $bdb11010cef70236$export$f680877a34711e37();
268622
+ const strokeWidthId = $bdb11010cef70236$export$f680877a34711e37();
268623
+ const colorEncodingId = $bdb11010cef70236$export$f680877a34711e37();
268624
+ const colormapId = $bdb11010cef70236$export$f680877a34711e37();
268625
+ const colormapRangeId = $bdb11010cef70236$export$f680877a34711e37();
268626
+ const tooltipsId = $bdb11010cef70236$export$f680877a34711e37();
268627
+ const crosshairsId = $bdb11010cef70236$export$f680877a34711e37();
268628
+ const legendId = $bdb11010cef70236$export$f680877a34711e37();
268629
+ 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) => {
268630
+ setFeatureValueColormap(e3.target.value);
268631
+ setPointFeatureValueColormap?.(e3.target.value);
268632
+ }, 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) => {
268633
+ setFeatureValueColormapRange(v);
268634
+ setPointFeatureValueColormapRange?.(v);
268635
+ }, 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 } } })] })] });
268636
+ }
268637
+ function SegmentationCentroidsController(props) {
268638
+ const { theme, segScope, segLayerCoordination, segChannelScopesByLayer, segChannelCoordination, pointScope, pointLayerCoordination } = props;
268639
+ const { classes: classes2 } = useStyles$8();
268640
+ const { classes: lcClasses } = useControllerSectionStyles();
268641
+ const { classes: menuClasses } = useEllipsisMenuStyles();
268642
+ const channelScopes = segChannelScopesByLayer?.[segScope] ?? [];
268643
+ const primaryChanScope = channelScopes[0];
268644
+ const chanValues = segChannelCoordination[0]?.[segScope]?.[primaryChanScope] ?? {};
268645
+ const chanSetters = segChannelCoordination[1]?.[segScope]?.[primaryChanScope] ?? {};
268646
+ const { obsType, featureType, featureValueType, spatialChannelVisible: segVisible, spatialChannelOpacity: segOpacity, spatialChannelColor: segColor, spatialSegmentationFilled: filled, spatialSegmentationStrokeWidth: strokeWidth, obsColorEncoding, featureSelection, featureValueColormap, featureValueColormapRange, tooltipsVisible, tooltipCrosshairsVisible, legendVisible } = chanValues;
268647
+ const { setSpatialChannelVisible: setSegVisible, setSpatialChannelOpacity: setSegOpacity, setSpatialChannelColor: setSegColor, setSpatialSegmentationFilled: setFilled, setSpatialSegmentationStrokeWidth: setStrokeWidth, setObsColorEncoding, setFeatureValueColormap, setFeatureValueColormapRange, setTooltipsVisible, setTooltipCrosshairsVisible, setLegendVisible } = chanSetters;
268648
+ const pointValues = pointLayerCoordination[0]?.[pointScope] ?? {};
268649
+ const pointSetters = pointLayerCoordination[1]?.[pointScope] ?? {};
268650
+ const { spatialLayerVisible: pointVisible, spatialLayerOpacity: pointOpacity, spatialLayerColor: pointColor, obsColorEncoding: pointObsColorEncoding, featureValueColormap: pointFeatureValueColormap } = pointValues;
268651
+ const { setSpatialLayerVisible: setPointVisible, setSpatialLayerOpacity: setPointOpacity, setSpatialLayerColor: setPointColor, setObsColorEncoding: setPointObsColorEncoding, setFeatureValueColormap: setPointFeatureValueColormap, setFeatureValueColormapRange: setPointFeatureValueColormapRange } = pointSetters;
268652
+ const sharedOpacity = segOpacity ?? 1;
268653
+ const handleOpacityChange = useCallback((e3, v) => {
268654
+ setSegOpacity?.(v);
268655
+ setPointOpacity?.(v);
268656
+ }, [setSegOpacity, setPointOpacity]);
268657
+ const handleSegVisibleChange = useCallback(() => {
268658
+ setSegVisible?.(typeof segVisible === "boolean" ? !segVisible : false);
268659
+ }, [segVisible, setSegVisible]);
268660
+ const handlePointVisibleChange = useCallback(() => {
268661
+ setPointVisible?.(typeof pointVisible === "boolean" ? !pointVisible : false);
268662
+ }, [pointVisible, setPointVisible]);
268663
+ const handleColorEncodingChange = useCallback((e3) => {
268664
+ const val = e3.target.value;
268665
+ setObsColorEncoding(val);
268666
+ if (val === "geneSelection") {
268667
+ setPointObsColorEncoding?.("geneSelection");
268668
+ } else if (val === "cellSetSelection") {
268669
+ setPointObsColorEncoding?.("cellSetSelection");
268670
+ } else {
268671
+ setPointObsColorEncoding?.("spatialChannelColor");
268672
+ }
268673
+ }, [setObsColorEncoding, setPointObsColorEncoding]);
268674
+ const segVisibleSetting = typeof segVisible === "boolean" ? segVisible : true;
268675
+ const pointVisibleSetting = typeof pointVisible === "boolean" ? pointVisible : true;
268676
+ const SegVisibility = segVisibleSetting ? VisibilityIcon : VisibilityOffIcon;
268677
+ const PointVisibility = pointVisibleSetting ? VisibilityIcon : VisibilityOffIcon;
268678
+ const isStaticColor = obsColorEncoding === "spatialChannelColor";
268679
+ const isColormap = obsColorEncoding === "geneSelection";
268680
+ const pointIsStaticColor = pointObsColorEncoding === "spatialChannelColor";
268681
+ const pointIsColormap = pointObsColorEncoding === "geneSelection";
268682
+ 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 }) })] }) }) });
268683
+ }
268506
268684
  function LayerController(props) {
268507
- 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;
268685
+ 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;
268508
268686
  const anyLayerHasT = Object.values(images || {}).some((image2) => image2?.image?.instance.hasTStack());
268509
268687
  const anyLayerHasZ = Object.values(images || {}).some((image2) => image2?.image?.instance.hasZStack());
268510
268688
  const maxT = Object.values(images || {}).reduce((a2, h2) => Math.max(a2, h2?.image?.instance.getNumT()), 1) - 1;
268511
268689
  const maxZ = Object.values(images || {}).reduce((a2, h2) => Math.max(a2, h2?.image?.instance.getNumZ()), 1) - 1;
268512
268690
  const reversedImageLayerScopes = useMemo$1(() => [...imageLayerScopes || []].reverse(), [imageLayerScopes]);
268513
- const reversedSegmentationLayerScopes = useMemo$1(() => [...segmentationLayerScopes || []].reverse(), [segmentationLayerScopes]);
268514
268691
  const reversedSpotLayerScopes = useMemo$1(() => [...spotLayerScopes || []].reverse(), [spotLayerScopes]);
268515
- const reversedPointLayerScopes = useMemo$1(() => [...pointLayerScopes || []].reverse(), [pointLayerScopes]);
268516
- 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))] });
268692
+ const reversedPointLayerScopes = useMemo$1(() => [...pointLayerScopes || []].filter((scope) => !pairedPointScopes.has(scope)).reverse(), [pointLayerScopes, pairedPointScopes]);
268693
+ const reversedSegmentationLayerScopes = useMemo$1(() => [...segmentationLayerScopes || []].filter((scope) => !pairedSegScopes.has(scope)).reverse(), [segmentationLayerScopes, pairedSegScopes]);
268694
+ 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))] });
268517
268695
  }
268518
268696
  function LayerControllerSubscriber(props) {
268519
268697
  const { coordinationScopes: coordinationScopesRaw, coordinationScopesBy: coordinationScopesByRaw, closeButtonVisible, downloadButtonVisible, removeGridComponent, theme, title: title2 = "Spatial Layers", uuid: uuid2, layerPerFeatureForPoints = false, cameraPresets, helpText = ViewHelpMapping.LAYER_CONTROLLER_BETA } = props;
@@ -268646,6 +268824,40 @@ function LayerControllerSubscriber(props) {
268646
268824
  CoordinationType$1.TOOLTIP_CROSSHAIRS_VISIBLE,
268647
268825
  CoordinationType$1.LEGEND_VISIBLE
268648
268826
  ], coordinationScopes, coordinationScopesBy, CoordinationType$1.POINT_LAYER);
268827
+ const centroidSegmentationPairs = React__default.useMemo(() => {
268828
+ const pairs = [];
268829
+ const pairedSegScopes = /* @__PURE__ */ new Set();
268830
+ const pairedPointScopes = /* @__PURE__ */ new Set();
268831
+ pointLayerScopes.forEach((pointScope) => {
268832
+ const pointCoord = pointLayerCoordination[0][pointScope];
268833
+ if (!pointCoord)
268834
+ return;
268835
+ const pointObsType = pointCoord[CoordinationType$1.OBS_TYPE];
268836
+ if (!pointObsType)
268837
+ return;
268838
+ const matchingSegScope = segmentationLayerScopes.find((segScope) => {
268839
+ if (pairedSegScopes.has(segScope))
268840
+ return false;
268841
+ const channelScopes = segmentationChannelScopesByLayer[segScope] || [];
268842
+ return channelScopes.some((chanScope) => {
268843
+ const chanCoord = segmentationChannelCoordination[0]?.[segScope]?.[chanScope];
268844
+ return chanCoord?.[CoordinationType$1.OBS_TYPE] === pointObsType;
268845
+ });
268846
+ });
268847
+ if (matchingSegScope) {
268848
+ pairs.push({ pointScope, segScope: matchingSegScope });
268849
+ pairedSegScopes.add(matchingSegScope);
268850
+ pairedPointScopes.add(pointScope);
268851
+ }
268852
+ });
268853
+ return { pairs, pairedSegScopes, pairedPointScopes };
268854
+ }, [
268855
+ pointLayerScopes,
268856
+ pointLayerCoordination,
268857
+ segmentationLayerScopes,
268858
+ segmentationChannelScopesByLayer,
268859
+ segmentationChannelCoordination
268860
+ ]);
268649
268861
  const [{ volumeLoadingProgress: volumeLoadingStatus, tiledPointsLoadingProgress }] = useAuxiliaryCoordination([
268650
268862
  "spatialAcceleratedVolumeLoadingProgress",
268651
268863
  "spatialTiledPointsLoadingProgress"
@@ -268673,7 +268885,7 @@ function LayerControllerSubscriber(props) {
268673
268885
  imageDataStatus,
268674
268886
  pointMultiIndicesDataStatus
268675
268887
  ]);
268676
- 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 }) });
268888
+ 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 }) });
268677
268889
  }
268678
268890
  const isIterable$2 = (obj) => Symbol.iterator in obj;
268679
268891
  const hasIterableEntries = (value2) => (
@@ -270588,7 +270800,7 @@ function HiglassGlobalStyles(props) {
270588
270800
  }
270589
270801
  register({ dataFetcher: ZarrMultivecDataFetcher_default, config: ZarrMultivecDataFetcher_default.config }, { pluginType: "dataFetcher" });
270590
270802
  const LazyHiGlassComponent = React__default.lazy(async () => {
270591
- const { HiGlassComponent } = await import("./higlass-C6IxqTVD.js");
270803
+ const { HiGlassComponent } = await import("./higlass-CKuPDg4y.js");
270592
270804
  return { default: HiGlassComponent };
270593
270805
  });
270594
270806
  const HG_SIZE = 800;
@@ -270926,6 +271138,12 @@ const useStyles$5 = makeStyles()(() => ({
270926
271138
  neuroglancerWrapper: {
270927
271139
  position: "relative",
270928
271140
  height: "100%"
271141
+ },
271142
+ meshLoadingIndicator: {
271143
+ position: "absolute",
271144
+ bottom: 8,
271145
+ left: 8,
271146
+ zIndex: 50
270929
271147
  }
270930
271148
  }));
270931
271149
  const globalNeuroglancerCss = `
@@ -273539,7 +273757,7 @@ function NeuroglancerGlobalStyles(props) {
273539
273757
  const { classes: classes2 } = props;
273540
273758
  return jsxs(Fragment, { children: [jsx$1(GlobalStyles$3, { styles: globalNeuroglancerCss }), jsx$1(ScopedGlobalStyles, { styles: globalNeuroglancerStyles, parentClassName: classes2.neuroglancerWrapper })] });
273541
273759
  }
273542
- const LazyReactNeuroglancer = React__default.lazy(() => import("./ReactNeuroglancer-Bw5gQWL3.js"));
273760
+ const LazyReactNeuroglancer = React__default.lazy(() => import("./ReactNeuroglancer-C2C-vEVu.js"));
273543
273761
  function createWorker() {
273544
273762
  const worker = new WorkerFactory$1();
273545
273763
  worker.AsyncComputationWorker = WorkerFactory;
@@ -273584,12 +273802,31 @@ class NeuroglancerComp extends PureComponent {
273584
273802
  }
273585
273803
  };
273586
273804
  remapWheelToZoom(viewer.inputEventBindings.perspectiveView);
273805
+ const { getMeshIdToCellId, onViewerReady, cellsUrl } = this.props;
273587
273806
  this.prevHoverHandler = () => {
273588
- if (viewer.mouseState.pickedValue !== void 0) {
273589
- const pickedSegment = viewer.mouseState.pickedValue;
273590
- this.latestOnSelectHoveredCoords?.(pickedSegment?.low);
273807
+ const ms = viewer.mouseState;
273808
+ if (ms.pickedAnnotationId != null) {
273809
+ const byIdKey = String(ms.pickedAnnotationId);
273810
+ fetch(`${cellsUrl}/by_id/${byIdKey}`).then((r3) => r3.arrayBuffer()).then((buf) => {
273811
+ const dv = new DataView(buf);
273812
+ const actualMeshId = String(dv.getInt32(12, true));
273813
+ const cellId = getMeshIdToCellId?.(actualMeshId) ?? actualMeshId;
273814
+ this.latestOnSelectHoveredCoords?.(cellId);
273815
+ });
273816
+ return;
273817
+ }
273818
+ if (ms.pickedValue !== void 0 && ms.pickedValue !== null) {
273819
+ const meshId = String(ms.pickedValue?.low);
273820
+ const cellId = getMeshIdToCellId?.(meshId) ?? meshId;
273821
+ this.latestOnSelectHoveredCoords?.(cellId);
273822
+ return;
273591
273823
  }
273824
+ this.latestOnSelectHoveredCoords?.(null);
273592
273825
  };
273826
+ onViewerReady?.(() => {
273827
+ const panel = [...viewer.display.panels][0];
273828
+ return panel?.projectionParameters?.value_?.viewProjectionMat;
273829
+ });
273593
273830
  viewer.mouseState.changed.add(this.prevHoverHandler);
273594
273831
  } else {
273595
273832
  if (this.prevMouseStateChanged && this.prevHoverHandler) {
@@ -273610,8 +273847,8 @@ class NeuroglancerComp extends PureComponent {
273610
273847
  }
273611
273848
  }
273612
273849
  render() {
273613
- const { classes: classes2, viewerState, cellColorMapping, onLayerLoadingChange } = this.props;
273614
- 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 }) }) })] });
273850
+ const { classes: classes2, viewerState, cellColorMapping, onLayerLoadingChange, onAnnotationSourceReady, onViewerReady, meshOpacity } = this.props;
273851
+ 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 }) }) })] });
273615
273852
  }
273616
273853
  }
273617
273854
  function useMemoCustomComparison(factory2, dependencies2, customIsEqual) {
@@ -273626,17 +273863,22 @@ function useMemoCustomComparison(factory2, dependencies2, customIsEqual) {
273626
273863
  }
273627
273864
  const shallowDiff = (prevDeps, nextDeps, depName) => prevDeps[depName] !== nextDeps[depName];
273628
273865
  const shallowDiffByLayer = (prevDeps, nextDeps, depName, scopeName) => prevDeps?.[depName]?.[scopeName] !== nextDeps?.[depName]?.[scopeName];
273866
+ const shallowDiffByChannel = (prevDeps, nextDeps, depName, firstName, secondName) => prevDeps?.[depName]?.[firstName]?.[secondName] !== nextDeps?.[depName]?.[firstName]?.[secondName];
273629
273867
  const shallowDiffByChannelWithKeys = (prevDeps, nextDeps, depName, firstName, secondName, keys2) => keys2.some((k) => prevDeps?.[depName]?.[firstName]?.[secondName]?.[k] !== nextDeps?.[depName]?.[firstName]?.[secondName]?.[k]);
273630
273868
  const shallowDiffByLayerCoordinationWithKeys = (prevDeps, nextDeps, depName, layerScope, keys2) => keys2.some((k) => prevDeps?.[depName]?.[0]?.[layerScope]?.[k] !== nextDeps?.[depName]?.[0]?.[layerScope]?.[k]);
273631
273869
  const shallowDiffByChannelCoordinationWithKeys = (prevDeps, nextDeps, depName, layerScope, channelScope, keys2) => keys2.some((k) => prevDeps?.[depName]?.[0]?.[layerScope]?.[channelScope]?.[k] !== nextDeps?.[depName]?.[0]?.[layerScope]?.[channelScope]?.[k]);
273632
273870
  function customIsEqualForCellColors(prevDeps, nextDeps) {
273633
273871
  let forceUpdate = false;
273634
273872
  const curriedShallowDiff = (depName) => shallowDiff(prevDeps, nextDeps, depName);
273873
+ const curriedShallowDiffByChannel = (depName, firstName, secondName) => shallowDiffByChannel(prevDeps, nextDeps, depName, firstName, secondName);
273635
273874
  const curriedShallowDiffByChannelWithKeys = (depName, firstName, secondName, keys2) => shallowDiffByChannelWithKeys(prevDeps, nextDeps, depName, firstName, secondName, keys2);
273636
273875
  const curriedShallowDiffByChannelCoordinationWithKeys = (depName, layerScope, channelScope, keys2) => shallowDiffByChannelCoordinationWithKeys(prevDeps, nextDeps, depName, layerScope, channelScope, keys2);
273637
273876
  if (curriedShallowDiff("theme")) {
273638
273877
  forceUpdate = true;
273639
273878
  }
273879
+ if (curriedShallowDiff("csvLoaded")) {
273880
+ forceUpdate = true;
273881
+ }
273640
273882
  if (["segmentationLayerScopes", "segmentationChannelScopesByLayer"].some(curriedShallowDiff)) {
273641
273883
  forceUpdate = true;
273642
273884
  } else {
@@ -273651,8 +273893,11 @@ function customIsEqualForCellColors(prevDeps, nextDeps) {
273651
273893
  "obsSetSelection",
273652
273894
  "additionalObsSets",
273653
273895
  "spatialChannelColor",
273654
- "spatialChannelOpacity"
273655
- ])) {
273896
+ "spatialChannelOpacity",
273897
+ "featureValueColormap",
273898
+ "featureValueColormapRange",
273899
+ "featureSelection"
273900
+ ]) || curriedShallowDiffByChannel("segmentationMultiIndicesData", layerScope, channelScope) || curriedShallowDiffByChannel("segmentationMultiExpressionNormData", layerScope, channelScope)) {
273656
273901
  forceUpdate = true;
273657
273902
  }
273658
273903
  });
@@ -273698,7 +273943,13 @@ function customIsEqualForInitialViewerState(prevDeps, nextDeps) {
273698
273943
  "featureSelection",
273699
273944
  "featureFilterMode",
273700
273945
  "featureColor",
273701
- "spatialPointStrokeWidth"
273946
+ "spatialPointStrokeWidth",
273947
+ "featureValueColormap",
273948
+ "featureValueColormapRange",
273949
+ "featureSelection",
273950
+ "obsSetColor",
273951
+ "obsSetSelection",
273952
+ "additionalObsSets"
273702
273953
  ]) || Math.abs(prevDeps?.pointLayerCoordination?.[0]?.[layerScope]?.spatialLayerOpacity - nextDeps?.pointLayerCoordination?.[0]?.[layerScope]?.spatialLayerOpacity) >= 0.05) {
273703
273954
  forceUpdate = true;
273704
273955
  }
@@ -273752,6 +274003,35 @@ function getSpatialLayerColorWithSelectionShader(staticColor, opacity2, featureI
273752
274003
  }
273753
274004
  `;
273754
274005
  }
274006
+ function getObsSetSelectionFilteredShader(phenotypeColors, selectedIndices, opacity2, borderWidth = 0) {
274007
+ const numColors = phenotypeColors.length;
274008
+ const numSelected = selectedIndices.length;
274009
+ const normColors = phenotypeColors.map((c2) => normalizeColor(c2));
274010
+ const colorsDecl = `vec3 phenotypeColors[${numColors}] = vec3[${numColors}](${normColors.map((c2) => toVec3(c2)).join(", ")});`;
274011
+ const selectedDecl = `int selectedIndices[${numSelected}] = int[${numSelected}](${selectedIndices.join(", ")});`;
274012
+ return `
274013
+ void main() {
274014
+ int phenotype = prop_phenotype();
274015
+ ${colorsDecl}
274016
+ ${selectedDecl}
274017
+ bool isSelected = false;
274018
+ for (int i = 0; i < ${numSelected}; ++i) {
274019
+ if (phenotype == selectedIndices[i]) {
274020
+ isSelected = true;
274021
+ }
274022
+ }
274023
+ if (!isSelected) {
274024
+ discard;
274025
+ }
274026
+ vec3 color = vec3(0.5, 0.5, 0.5);
274027
+ if (phenotype >= 0 && phenotype < ${numColors}) {
274028
+ color = phenotypeColors[phenotype];
274029
+ }
274030
+ setColor(vec4(color, ${opacity2.toFixed(4)}));
274031
+ ${borderWidthGlsl(borderWidth)}
274032
+ }
274033
+ `;
274034
+ }
273755
274035
  function getSpatialLayerColorFilteredShader(staticColor, opacity2, featureIndices, featureIndexProp, borderWidth = 0) {
273756
274036
  const normStatic = normalizeColor(staticColor);
273757
274037
  const numFeatures = featureIndices.length;
@@ -273933,6 +274213,23 @@ function getRandomPerPointShader(opacity2, featureIndexProp, pointIndexProp, bor
273933
274213
  }
273934
274214
  `;
273935
274215
  }
274216
+ function getQuantitativeColormapShader(quantitativeColorProp, opacity2, colormap = GLSL_COLORMAP_DEFAULT, colormapRange = [0, 1], borderWidth = 0, quantitativeColorMax = 1) {
274217
+ const [low, high] = colormapRange;
274218
+ const resolvedColormap = GLSL_COLORMAPS.includes(colormap) ? colormap : GLSL_COLORMAP_DEFAULT;
274219
+ return `
274220
+ #define COLORMAP_FUNC ${resolvedColormap}
274221
+ ${colormaps$1}
274222
+ void main() {
274223
+ float rawVal = float(prop_${quantitativeColorProp}());
274224
+ float normalizedVal = rawVal / ${quantitativeColorMax.toFixed(4)};
274225
+ float t = (normalizedVal - ${low.toFixed(4)}) / max(${(high - low).toFixed(4)}, 0.0001);
274226
+ t = clamp(t, 0.0, 1.0);
274227
+ vec4 col = COLORMAP_FUNC(t);
274228
+ setColor(vec4(col.rgb, ${opacity2.toFixed(4)}));
274229
+ ${borderWidthGlsl(borderWidth)}
274230
+ }
274231
+ `;
274232
+ }
273936
274233
  function getRandomPerPointWithSelectionShader(featureIndices, defaultColor, opacity2, featureIndexProp, pointIndexProp, borderWidth = 0) {
273937
274234
  const normDefault = normalizeColor(defaultColor);
273938
274235
  const numFeatures = featureIndices.length;
@@ -273988,8 +274285,25 @@ function getRandomPerPointFilteredShader(featureIndices, opacity2, featureIndexP
273988
274285
  }
273989
274286
  `;
273990
274287
  }
274288
+ function getObsSetSelectionShader(phenotypeColors, opacity2, borderWidth = 0) {
274289
+ const numColors = phenotypeColors.length;
274290
+ const normColors = phenotypeColors.map((c2) => normalizeColor(c2));
274291
+ const colorsDecl = `vec3 phenotypeColors[${numColors}] = vec3[${numColors}](${normColors.map((c2) => toVec3(c2)).join(", ")});`;
274292
+ return `
274293
+ void main() {
274294
+ int phenotype = prop_phenotype();
274295
+ ${colorsDecl}
274296
+ vec3 color = ${toVec3(normalizeColor([128, 128, 128]))};
274297
+ if (phenotype >= 0 && phenotype < ${numColors}) {
274298
+ color = phenotypeColors[phenotype];
274299
+ }
274300
+ setColor(vec4(color, ${opacity2.toFixed(4)}));
274301
+ ${borderWidthGlsl(borderWidth)}
274302
+ }
274303
+ `;
274304
+ }
273991
274305
  function getPointsShader(layerCoordination) {
273992
- const { theme, featureIndex, spatialLayerOpacity, obsColorEncoding, spatialLayerColor, featureSelection, featureFilterMode, featureColor, pointMarkerBorderWidth = 0, featureIndexProp, pointIndexProp } = layerCoordination;
274306
+ const { theme, featureIndex, spatialLayerOpacity, obsColorEncoding, spatialLayerColor, featureSelection, featureFilterMode, featureColor, pointMarkerBorderWidth = 0, featureIndexProp, pointIndexProp, quantitativeColorProp, featureValueColormap, featureValueColormapRange, obsSetColor, obsSetSelection, quantitativeColorMax, pointsAreSegmentationCentroids } = layerCoordination;
273993
274307
  const defaultColor = getDefaultColor(theme);
273994
274308
  const opacity2 = spatialLayerOpacity ?? 1;
273995
274309
  const staticColor = Array.isArray(spatialLayerColor) && spatialLayerColor.length === 3 ? spatialLayerColor : defaultColor;
@@ -274013,7 +274327,7 @@ function getPointsShader(layerCoordination) {
274013
274327
  }
274014
274328
  return getSpatialLayerColorWithSelectionShader(staticColor, opacity2, featureIndices, defaultColor, featureIndexProp, pointMarkerBorderWidth);
274015
274329
  }
274016
- if (obsColorEncoding === "geneSelection") {
274330
+ if (obsColorEncoding === "geneSelection" && !pointsAreSegmentationCentroids) {
274017
274331
  if (!featureIndexProp) {
274018
274332
  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.");
274019
274333
  }
@@ -274049,19 +274363,49 @@ function getPointsShader(layerCoordination) {
274049
274363
  }
274050
274364
  return getRandomPerPointWithSelectionShader(featureIndices, defaultColor, opacity2, featureIndexProp, pointIndexProp, pointMarkerBorderWidth);
274051
274365
  }
274366
+ if (obsColorEncoding === "geneSelection" && pointsAreSegmentationCentroids) {
274367
+ if (!quantitativeColorProp) {
274368
+ 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.");
274369
+ return getSpatialLayerColorShader(staticColor, opacity2, pointMarkerBorderWidth);
274370
+ }
274371
+ return getQuantitativeColormapShader(quantitativeColorProp, opacity2, featureValueColormap, featureValueColormapRange, pointMarkerBorderWidth, quantitativeColorMax);
274372
+ }
274373
+ if (obsColorEncoding === "cellSetSelection") {
274374
+ if (!obsSetColor || !obsSetSelection) {
274375
+ return getSpatialLayerColorShader(staticColor, opacity2, pointMarkerBorderWidth);
274376
+ }
274377
+ const colorByName = {};
274378
+ obsSetColor?.forEach(({ path: path2, color: color2 }) => {
274379
+ if (path2?.length > 1) {
274380
+ colorByName[path2[path2.length - 1]] = color2;
274381
+ }
274382
+ });
274383
+ const uniqueNames = Object.keys(colorByName).sort();
274384
+ const phenotypeColors = uniqueNames.map((name2) => colorByName[name2] ?? defaultColor);
274385
+ if (isFiltered) {
274386
+ const selectedNames = new Set(obsSetSelection?.map((path2) => path2[path2.length - 1]) ?? []);
274387
+ const selectedIndices = uniqueNames.map((name2, idx) => selectedNames.has(name2) ? idx : -1).filter((idx) => idx >= 0);
274388
+ if (selectedIndices.length === 0) {
274389
+ return "void main() { discard; }";
274390
+ }
274391
+ return getObsSetSelectionFilteredShader(phenotypeColors, selectedIndices, opacity2, pointMarkerBorderWidth);
274392
+ }
274393
+ return getObsSetSelectionShader(phenotypeColors, opacity2, pointMarkerBorderWidth);
274394
+ }
274052
274395
  return getSpatialLayerColorShader(staticColor, opacity2, pointMarkerBorderWidth);
274053
274396
  }
274397
+ const DEFAULT_NG_DIMENSIONS = {
274398
+ x: [1, "nm"],
274399
+ y: [1, "nm"],
274400
+ z: [1, "nm"]
274401
+ };
274054
274402
  const DEFAULT_NG_PROPS = {
274055
274403
  layout: "3d",
274056
274404
  position: [0, 0, 0],
274057
274405
  projectionOrientation: [0, 0, 0, 1],
274058
274406
  projectionScale: 1024,
274059
274407
  crossSectionScale: 1,
274060
- dimensions: {
274061
- x: [1, "nm"],
274062
- y: [1, "nm"],
274063
- z: [1, "nm"]
274064
- },
274408
+ dimensions: DEFAULT_NG_DIMENSIONS,
274065
274409
  layers: []
274066
274410
  };
274067
274411
  function toPrecomputedSource(url) {
@@ -274079,6 +274423,15 @@ function toNgLayerName(dataType, layerScope, channelScope = null) {
274079
274423
  }
274080
274424
  throw new Error(`Unsupported data type: ${dataType}`);
274081
274425
  }
274426
+ function pointsHaveMatchingSegmentation({ pointObsType, segmentationLayerScopes, segmentationChannelScopesByLayer, segmentationChannelCoordination }) {
274427
+ return segmentationLayerScopes.some((segmentationLayerScope) => {
274428
+ const segmentationChannelScopes = segmentationChannelScopesByLayer?.[segmentationLayerScope] || [];
274429
+ return segmentationChannelScopes.some((segmentationChannelScope) => {
274430
+ const segmentationObsType = segmentationChannelCoordination?.[0]?.[segmentationLayerScope]?.[segmentationChannelScope]?.obsType;
274431
+ return segmentationObsType && pointObsType && segmentationObsType === pointObsType;
274432
+ });
274433
+ });
274434
+ }
274082
274435
  function useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScopes, segmentationChannelScopesByLayer, segmentationLayerCoordination, segmentationChannelCoordination, obsSegmentationsUrls, obsSegmentationsData, pointLayerScopes, pointLayerCoordination, obsPointsUrls, obsPointsData, pointMultiIndicesData) {
274083
274436
  const viewerState = useMemoCustomComparison(() => {
274084
274437
  let result = cloneDeep$1(DEFAULT_NG_PROPS);
@@ -274101,6 +274454,9 @@ function useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScope
274101
274454
  } : toPrecomputedSource(layerUrl);
274102
274455
  result = {
274103
274456
  ...result,
274457
+ showAxisLines,
274458
+ showDefaultAnnotations: false,
274459
+ // this removes the yellow box around pointsLayer
274104
274460
  layers: [
274105
274461
  ...result.layers,
274106
274462
  {
@@ -274124,9 +274480,27 @@ function useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScope
274124
274480
  const layerCoordination = pointLayerCoordination[0][layerScope];
274125
274481
  const layerData = obsPointsData[layerScope];
274126
274482
  const layerUrl = obsPointsUrls[layerScope]?.[0]?.url;
274483
+ const ngOptions = layerData?.neuroglancerOptions;
274484
+ if (ngOptions?.transform?.matrix && ngOptions?.transform?.outputDimensions) {
274485
+ result = {
274486
+ ...result,
274487
+ dimensions: ngOptions.transform.outputDimensions ?? DEFAULT_NG_DIMENSIONS
274488
+ };
274489
+ }
274490
+ const pointsAreSegmentationCentroids = pointsHaveMatchingSegmentation({
274491
+ pointObsType: layerCoordination?.obsType,
274492
+ segmentationLayerScopes,
274493
+ segmentationChannelScopesByLayer,
274494
+ segmentationChannelCoordination
274495
+ });
274127
274496
  const featureIndex = pointMultiIndicesData[layerScope]?.featureIndex;
274128
274497
  if (layerUrl && layerData) {
274129
- const { spatialLayerVisible, spatialLayerOpacity, obsColorEncoding, spatialLayerColor, featureSelection, featureFilterMode, featureColor, spatialPointStrokeWidth } = layerCoordination || {};
274498
+ const { spatialLayerVisible, spatialLayerOpacity, obsColorEncoding, spatialLayerColor, featureSelection, featureFilterMode, featureColor, spatialPointStrokeWidth, featureValueColormap, featureValueColormapRange } = layerCoordination || {};
274499
+ const quantitativeColorMax = layerData.neuroglancerOptions?.quantitativeColorMax;
274500
+ if (!quantitativeColorMax && obsColorEncoding === "geneSelection") {
274501
+ console.warn("quantitativeColorMax not specified in options — defaulting to 1.0 (no normalization)");
274502
+ }
274503
+ const quantitativeColorMaxSelected = quantitativeColorMax ?? 1;
274130
274504
  const shader = getPointsShader({
274131
274505
  theme,
274132
274506
  featureIndex,
@@ -274136,9 +274510,18 @@ function useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScope
274136
274510
  featureSelection,
274137
274511
  featureFilterMode,
274138
274512
  featureColor,
274513
+ featureValueColormap,
274514
+ featureValueColormapRange,
274139
274515
  featureIndexProp: layerData.neuroglancerOptions?.featureIndexProp,
274140
274516
  pointIndexProp: layerData.neuroglancerOptions?.pointIndexProp,
274141
- pointMarkerBorderWidth: spatialPointStrokeWidth ?? 0
274517
+ quantitativeColorProp: layerData.neuroglancerOptions?.quantitativeColorProp,
274518
+ pointMarkerBorderWidth: spatialPointStrokeWidth ?? 0,
274519
+ obsSets: layerCoordination.obsSets,
274520
+ obsSetColor: layerCoordination.obsSetColor,
274521
+ obsSetSelection: layerCoordination.obsSetSelection,
274522
+ additionalObsSets: layerCoordination.additionalObsSets,
274523
+ quantitativeColorMax: quantitativeColorMaxSelected,
274524
+ pointsAreSegmentationCentroids
274142
274525
  });
274143
274526
  result = {
274144
274527
  ...result,
@@ -274151,7 +274534,13 @@ function useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScope
274151
274534
  subsources: {
274152
274535
  default: true
274153
274536
  },
274154
- enableDefaultSubsources: false
274537
+ enableDefaultSubsources: false,
274538
+ ...ngOptions?.transform?.matrix ? {
274539
+ transform: {
274540
+ matrix: ngOptions.transform.matrix,
274541
+ outputDimensions: ngOptions.transform?.outputDimensions ?? result.dimensions
274542
+ }
274543
+ } : {}
274155
274544
  },
274156
274545
  tab: "annotations",
274157
274546
  shader,
@@ -274159,7 +274548,7 @@ function useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScope
274159
274548
  visible: spatialLayerVisible,
274160
274549
  // Options from layerData.neuroglancerOptions
274161
274550
  // like projectionAnnotationSpacing:
274162
- projectionAnnotationSpacing: layerData.neuroglancerOptions?.projectionAnnotationSpacing ?? 1
274551
+ projectionAnnotationSpacing: ngOptions?.projectionAnnotationSpacing ?? 1
274163
274552
  }
274164
274553
  ],
274165
274554
  // TODO: is this needed?
@@ -274190,6 +274579,8 @@ function useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScope
274190
274579
  }, customIsEqualForInitialViewerState);
274191
274580
  return viewerState;
274192
274581
  }
274582
+ const ANNOTATION_HEADER_OFFSET = 8;
274583
+ const GREY_HEX = "#323232";
274193
274584
  const EPSILON_KEYS_MAPPING_NG = {
274194
274585
  projectionScale: 100,
274195
274586
  projectionOrientation: 0.02,
@@ -274210,6 +274601,17 @@ const multiplyQuat = (a2, b2) => {
274210
274601
  const conjQuat = (q) => [-q[0], -q[1], -q[2], q[3]];
274211
274602
  const rad2deg = (r3) => r3 * 180 / Math.PI;
274212
274603
  const deg2rad = (d) => d * Math.PI / 180;
274604
+ function applyColormap(colormap, t4) {
274605
+ const tClamped = Math.max(0, Math.min(1, t4));
274606
+ const interpolators = {
274607
+ plasma: interpolatePlasma,
274608
+ viridis: interpolateViridis,
274609
+ jet: interpolateJet,
274610
+ greys: interpolateGreys
274611
+ };
274612
+ const fn2 = interpolators[colormap] ?? interpolateViridis;
274613
+ return fn2(tClamped);
274614
+ }
274213
274615
  function valueGreaterThanEpsilon(a2, b2, epsilon3) {
274214
274616
  if (Array.isArray(a2) && Array.isArray(b2) && a2.length === b2.length) {
274215
274617
  return a2.some((val, i2) => Math.abs(val - b2[i2]) > epsilon3);
@@ -274276,13 +274678,57 @@ function makeVitNgZoomCalibrator(initialNgProjectionScale, initialDeckZoom = 0)
274276
274678
  }
274277
274679
  };
274278
274680
  }
274681
+ function parseAnnotationChunkSegmentsWithPositions(buffer2, serializer2) {
274682
+ const dv = new DataView(buffer2);
274683
+ if (buffer2.byteLength <= 8)
274684
+ return [];
274685
+ const count2 = dv.getUint32(0, true);
274686
+ if (count2 === 0)
274687
+ return [];
274688
+ const numBytes = serializer2.serializedBytes;
274689
+ const properties2 = [null, null, null, null, null];
274690
+ const results = [];
274691
+ for (let i2 = 0; i2 < count2; i2++) {
274692
+ serializer2.deserialize(dv, ANNOTATION_HEADER_OFFSET, i2, count2, true, properties2);
274693
+ const phenotype = properties2[0];
274694
+ const segId = properties2[1];
274695
+ if (segId > 0) {
274696
+ const propBase = ANNOTATION_HEADER_OFFSET + i2 * numBytes;
274697
+ const x2 = dv.getFloat32(propBase + 0, true);
274698
+ const y2 = dv.getFloat32(propBase + 4, true);
274699
+ const z2 = dv.getFloat32(propBase + 8, true);
274700
+ if (x2 !== 0 || y2 !== 0 || z2 !== 0) {
274701
+ results.push({
274702
+ id: String(segId),
274703
+ // mesh segment ID - use for segments array
274704
+ phenotype: Number(phenotype),
274705
+ x: x2,
274706
+ y: y2,
274707
+ z: z2
274708
+ });
274709
+ }
274710
+ }
274711
+ }
274712
+ return results;
274713
+ }
274714
+ const remapCellColors = (ngCellColors, cellIdToMeshIdRef) => {
274715
+ const remapped = {};
274716
+ Object.entries(ngCellColors).forEach(([cellId, color2]) => {
274717
+ const meshId = cellIdToMeshIdRef.current[cellId] ?? cellId;
274718
+ remapped[meshId] = color2;
274719
+ });
274720
+ return remapped;
274721
+ };
274279
274722
  const VITESSCE_INTERACTION_DELAY = 50;
274280
274723
  const INIT_VIT_ZOOM = -3.6;
274281
274724
  const ZOOM_EPS = 0.01;
274282
274725
  const ROTATION_EPS = 1e-3;
274283
274726
  const TARGET_EPS = 0.5;
274284
274727
  const NG_ROT_COOLDOWN_MS = 120;
274728
+ const MESH_LOAD_THRESHOLD = 100;
274729
+ const MESH_LOADING_OVERLAY_TIMEOUT = 1500;
274285
274730
  const GUIDE_URL = "https://vitessce.io/docs/ng-guide/";
274731
+ const MESH_OPACITY = 0.6;
274286
274732
  const LAST_INTERACTION_SOURCE = {
274287
274733
  vitessce: "vitessce",
274288
274734
  neuroglancer: "neuroglancer"
@@ -274299,10 +274745,11 @@ function NeuroglancerSubscriber(props) {
274299
274745
  downloadButtonVisible,
274300
274746
  removeGridComponent,
274301
274747
  theme,
274302
- showAxisLines = false,
274748
+ showAxisLines = true,
274303
274749
  title: title2 = "Spatial",
274304
274750
  subtitle = "Powered by Neuroglancer",
274305
274751
  helpText = ViewHelpMapping.NEUROGLANCER,
274752
+ meshLoadProjectionScaleThreshold,
274306
274753
  // Note: this is a temporary mechanism
274307
274754
  // to pass an initial NG camera state.
274308
274755
  // Ideally, all camera state should be passed via
@@ -274313,6 +274760,32 @@ function NeuroglancerSubscriber(props) {
274313
274760
  } = props;
274314
274761
  const loaders = useLoaders();
274315
274762
  const mergeCoordination = useMergeCoordination();
274763
+ const { classes: classes2 } = useStyles$5();
274764
+ const initialRotationPushedRef = useRef(false);
274765
+ const getViewProjectionMatRef = useRef(null);
274766
+ const obsIdToMeshIdRef = useRef({});
274767
+ const meshIdToCellIdRef = useRef({});
274768
+ const cellIdToMeshIdRef = useRef({});
274769
+ const ngRotPushAtRef = useRef(0);
274770
+ const lastInteractionSource = useRef(null);
274771
+ const applyNgUpdateTimeoutRef = useRef(null);
274772
+ const lastNgPushOrientationRef = useRef(null);
274773
+ const initialRenderCalibratorRef = useRef(null);
274774
+ const translationOffsetRef = useRef([0, 0, 0]);
274775
+ const zoomRafRef = useRef(null);
274776
+ const lastNgQuatRef = useRef([0, 0, 0, 1]);
274777
+ const lastNgScaleRef = useRef(null);
274778
+ const annotationInfoRef = useRef(null);
274779
+ const annotationTransformRef = useRef(null);
274780
+ const visibleSegmentIdsRef = useRef(null);
274781
+ const chunkCacheRef = useRef(/* @__PURE__ */ new Map());
274782
+ const resizeObserverRef = useRef(null);
274783
+ const [isLayersLoaded, setIsLayersLoaded] = useState(false);
274784
+ const [isMeshLoading, setIsMeshLoading] = useState(false);
274785
+ const [annotationReady, setAnnotationReady] = useState(false);
274786
+ const [csvLoaded, setCsvLoaded] = useState(false);
274787
+ const updateVisibleSegmentsThrottledRef = useRef(null);
274788
+ const viewportSizeRef = useRef({ width: 0, height: 0 });
274316
274789
  const coordinationScopes = useCoordinationScopes(coordinationScopesRaw);
274317
274790
  const coordinationScopesBy = useCoordinationScopesBy(coordinationScopes, coordinationScopesByRaw);
274318
274791
  const [{
@@ -274344,6 +274817,19 @@ function NeuroglancerSubscriber(props) {
274344
274817
  setSpatialRotationOrbit: setRotationOrbit,
274345
274818
  setSpatialZoom: setZoom
274346
274819
  }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType$1.NEUROGLANCER], coordinationScopes);
274820
+ const csvUrlRef = useRef(null);
274821
+ const csvUrl = useMemo$1(() => {
274822
+ if (csvUrlRef.current)
274823
+ return csvUrlRef.current;
274824
+ const obsSetsMap = loaders?.[dataset]?.loaders?.obsSets;
274825
+ if (!obsSetsMap)
274826
+ return null;
274827
+ const firstLoader = obsSetsMap.values().next().value;
274828
+ const url = firstLoader?.url ?? null;
274829
+ if (url)
274830
+ csvUrlRef.current = url;
274831
+ return url;
274832
+ }, [loaders, dataset]);
274347
274833
  const [ngWidth, ngHeight, containerRef] = useGridItemSize();
274348
274834
  const [segmentationLayerScopes, segmentationChannelScopesByLayer] = useMultiCoordinationScopesSecondaryNonNull(CoordinationType$1.SEGMENTATION_CHANNEL, CoordinationType$1.SEGMENTATION_LAYER, coordinationScopes, coordinationScopesBy);
274349
274835
  const pointLayerScopes = useMultiCoordinationScopesNonNull(CoordinationType$1.POINT_LAYER, coordinationScopes);
@@ -274372,7 +274858,9 @@ function NeuroglancerSubscriber(props) {
274372
274858
  CoordinationType$1.OBS_HIGHLIGHT,
274373
274859
  CoordinationType$1.TOOLTIPS_VISIBLE,
274374
274860
  CoordinationType$1.TOOLTIP_CROSSHAIRS_VISIBLE,
274375
- CoordinationType$1.LEGEND_VISIBLE
274861
+ CoordinationType$1.LEGEND_VISIBLE,
274862
+ CoordinationType$1.FEATURE_TYPE,
274863
+ CoordinationType$1.FEATURE_VALUE_TYPE
274376
274864
  ], coordinationScopes, coordinationScopesBy, CoordinationType$1.SEGMENTATION_LAYER, CoordinationType$1.SEGMENTATION_CHANNEL);
274377
274865
  const pointLayerCoordination = useComplexCoordination([
274378
274866
  CoordinationType$1.OBS_TYPE,
@@ -274389,7 +274877,10 @@ function NeuroglancerSubscriber(props) {
274389
274877
  CoordinationType$1.TOOLTIPS_VISIBLE,
274390
274878
  CoordinationType$1.TOOLTIP_CROSSHAIRS_VISIBLE,
274391
274879
  CoordinationType$1.LEGEND_VISIBLE,
274392
- CoordinationType$1.SPATIAL_POINT_STROKE_WIDTH
274880
+ CoordinationType$1.SPATIAL_POINT_STROKE_WIDTH,
274881
+ CoordinationType$1.OBS_SET_COLOR,
274882
+ CoordinationType$1.OBS_SET_SELECTION,
274883
+ CoordinationType$1.ADDITIONAL_OBS_SETS
274393
274884
  ], coordinationScopes, coordinationScopesBy, CoordinationType$1.POINT_LAYER);
274394
274885
  const [obsPointsData, obsPointsDataStatus, obsPointsUrls, obsPointsErrors] = useMultiObsPoints(coordinationScopes, coordinationScopesBy, loaders, dataset, mergeCoordination, uuid2);
274395
274886
  const [pointMultiIndicesData, pointMultiIndicesDataStatus, pointMultiIndicesDataErrors] = usePointMultiObsFeatureMatrixIndices(coordinationScopes, coordinationScopesBy, loaders, dataset);
@@ -274415,34 +274906,89 @@ function NeuroglancerSubscriber(props) {
274415
274906
  segmentationMultiFeatureSelectionStatus,
274416
274907
  segmentationMultiIndicesDataStatus
274417
274908
  ]);
274418
- const { classes: classes2 } = useStyles$5();
274909
+ const lastVitessceRotationRef = useRef({
274910
+ x: spatialRotationX,
274911
+ y: spatialRotationY,
274912
+ z: spatialRotationZ,
274913
+ orbit: spatialRotationOrbit
274914
+ });
274915
+ const prevCoordsRef = useRef({
274916
+ zoom: spatialZoom,
274917
+ rx: spatialRotationX,
274918
+ ry: spatialRotationY,
274919
+ rz: spatialRotationZ,
274920
+ orbit: spatialRotationOrbit,
274921
+ tx: spatialTargetX,
274922
+ ty: spatialTargetY
274923
+ });
274419
274924
  const segmentationColorMapping = useMemoCustomComparison(() => {
274420
274925
  const result = {};
274421
274926
  segmentationLayerScopes?.forEach((layerScope) => {
274422
274927
  result[layerScope] = {};
274423
274928
  segmentationChannelScopesByLayer?.[layerScope]?.forEach((channelScope) => {
274424
- const { obsSets: layerSets, obsIndex: layerIndex } = obsSegmentationsSetsData?.[layerScope]?.[channelScope] || {};
274425
- const { obsSetColor, obsColorEncoding, obsSetSelection, additionalObsSets, spatialChannelColor, spatialChannelOpacity } = segmentationChannelCoordination[0][layerScope][channelScope];
274929
+ const { obsSets: layerSets, obsIndex: layerIndexFromSets } = obsSegmentationsSetsData?.[layerScope]?.[channelScope] || {};
274930
+ const layerIndex = layerIndexFromSets ?? null;
274931
+ const { obsSetColor, obsColorEncoding, obsSetSelection, additionalObsSets, spatialChannelColor, spatialChannelOpacity, featureValueColormap, featureValueColormapRange } = segmentationChannelCoordination[0][layerScope][channelScope];
274426
274932
  if (obsColorEncoding === "spatialChannelColor") {
274427
- if (layerIndex && spatialChannelColor) {
274933
+ if (spatialChannelColor) {
274428
274934
  const hex2 = rgbToHex$1(spatialChannelColor);
274429
274935
  const ngCellColors = {};
274430
- if (obsSetSelection?.length > 0) {
274431
- const mergedCellSets = mergeObsSets(layerSets, additionalObsSets);
274432
- const selectedIds = /* @__PURE__ */ new Set();
274433
- obsSetSelection.forEach((setPath) => {
274434
- const rootNode = mergedCellSets?.tree?.find((n3) => n3.name === setPath[0]);
274435
- const leafNode = setPath.length > 1 ? rootNode?.children?.find((n3) => n3.name === setPath[1]) : rootNode;
274436
- leafNode?.set?.forEach(([id2]) => selectedIds.add(String(id2)));
274437
- });
274438
- layerIndex.forEach((id2) => {
274439
- if (selectedIds.has(String(id2))) {
274936
+ if (layerIndex) {
274937
+ if (obsSetSelection?.length > 0) {
274938
+ const mergedCellSets = mergeObsSets(layerSets, additionalObsSets);
274939
+ const selectedIds = /* @__PURE__ */ new Set();
274940
+ obsSetSelection.forEach((setPath) => {
274941
+ const rootNode = mergedCellSets?.tree?.find((n3) => n3.name === setPath[0]);
274942
+ const leafNode = setPath.length > 1 ? rootNode?.children?.find((n3) => n3.name === setPath[1]) : rootNode;
274943
+ leafNode?.set?.forEach(([id2]) => selectedIds.add(String(id2)));
274944
+ });
274945
+ layerIndex.forEach((id2) => {
274946
+ if (selectedIds.has(String(id2))) {
274947
+ ngCellColors[id2] = hex2;
274948
+ }
274949
+ });
274950
+ } else {
274951
+ layerIndex.forEach((id2) => {
274440
274952
  ngCellColors[id2] = hex2;
274441
- }
274442
- });
274953
+ });
274954
+ }
274443
274955
  }
274444
- result[layerScope][channelScope] = ngCellColors;
274956
+ result[layerScope][channelScope] = remapCellColors(ngCellColors, cellIdToMeshIdRef);
274957
+ result[layerScope].opacity = spatialChannelOpacity ?? 1;
274958
+ result[layerScope].defaultColor = hex2;
274959
+ }
274960
+ } else if (obsColorEncoding === "geneSelection") {
274961
+ const instanceObsIndex = obsSegmentationsSetsData?.[layerScope]?.[channelScope]?.obsIndex;
274962
+ const matrixObsIndex = segmentationMultiIndicesData?.[layerScope]?.[channelScope]?.obsIndex;
274963
+ const expressionData = segmentationMultiExpressionNormData?.[layerScope]?.[channelScope];
274964
+ if (instanceObsIndex && matrixObsIndex && expressionData?.[0]) {
274965
+ const matrixIndexMap = new Map(matrixObsIndex.map((key2, i2) => {
274966
+ const normalizedKey = key2.replace(/^.*_/, "");
274967
+ return [normalizedKey, i2];
274968
+ }));
274969
+ const toMatrixIndex = instanceObsIndex.map((key2) => matrixIndexMap.get(String(key2)));
274970
+ const [low, high] = featureValueColormapRange ?? [0, 1];
274971
+ const ngCellColors = {};
274972
+ instanceObsIndex.forEach((id2, i2) => {
274973
+ const rowIndex = toMatrixIndex[i2];
274974
+ const rawVal = expressionData[0][rowIndex] ?? 0;
274975
+ const t4 = rawVal / 255;
274976
+ const tScaled = (t4 - low) / Math.max(high - low, 1e-4);
274977
+ const tClamped = Math.max(0, Math.min(1, tScaled));
274978
+ const color2 = applyColormap(featureValueColormap ?? "viridis", tClamped);
274979
+ ngCellColors[id2] = rgbToHex$1(color2);
274980
+ });
274981
+ result[layerScope][channelScope] = remapCellColors(ngCellColors, cellIdToMeshIdRef);
274982
+ result[layerScope].opacity = spatialChannelOpacity ?? 1;
274983
+ } else if (instanceObsIndex) {
274984
+ const fallbackColor = spatialChannelColor ? rgbToHex$1(spatialChannelColor) : GREY_HEX;
274985
+ const ngCellColors = {};
274986
+ instanceObsIndex.forEach((id2) => {
274987
+ ngCellColors[id2] = fallbackColor;
274988
+ });
274989
+ result[layerScope][channelScope] = remapCellColors(ngCellColors, cellIdToMeshIdRef);
274445
274990
  result[layerScope].opacity = spatialChannelOpacity ?? 1;
274991
+ result[layerScope].defaultColor = fallbackColor;
274446
274992
  }
274447
274993
  } else if (layerSets && layerIndex) {
274448
274994
  const mergedCellSets = mergeObsSets(layerSets, additionalObsSets);
@@ -274454,10 +275000,10 @@ function NeuroglancerSubscriber(props) {
274454
275000
  theme
274455
275001
  });
274456
275002
  const ngCellColors = {};
274457
- cellColors.forEach((color2, i2) => {
274458
- ngCellColors[i2] = rgbToHex$1(color2);
275003
+ cellColors.forEach((color2, id2) => {
275004
+ ngCellColors[id2] = rgbToHex$1(color2);
274459
275005
  });
274460
- result[layerScope][channelScope] = ngCellColors;
275006
+ result[layerScope][channelScope] = remapCellColors(ngCellColors, cellIdToMeshIdRef);
274461
275007
  result[layerScope].opacity = spatialChannelOpacity ?? 1;
274462
275008
  }
274463
275009
  });
@@ -274470,14 +275016,201 @@ function NeuroglancerSubscriber(props) {
274470
275016
  segmentationChannelScopesByLayer,
274471
275017
  obsSegmentationsSetsData,
274472
275018
  segmentationChannelCoordination,
274473
- theme
275019
+ theme,
275020
+ segmentationMultiExpressionNormData,
275021
+ segmentationMultiIndicesData,
275022
+ csvLoaded
274474
275023
  }, customIsEqualForCellColors);
274475
- const initalViewerState = useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScopes, segmentationChannelScopesByLayer, segmentationLayerCoordination, segmentationChannelCoordination, obsSegmentationsUrls, obsSegmentationsData, pointLayerScopes, pointLayerCoordination, obsPointsUrls, obsPointsData, pointMultiIndicesData);
275024
+ const initialViewerState = useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScopes, segmentationChannelScopesByLayer, segmentationLayerCoordination, segmentationChannelCoordination, obsSegmentationsUrls, obsSegmentationsData, pointLayerScopes, pointLayerCoordination, obsPointsUrls, obsPointsData, pointMultiIndicesData);
274476
275025
  const [latestViewerStateIteration, incrementLatestViewerStateIteration] = useReducer((x2) => x2 + 1, 0);
274477
275026
  const latestViewerStateRef = useRef({
274478
- ...initalViewerState,
275027
+ ...initialViewerState,
274479
275028
  ...initialNgCameraState ?? {}
274480
275029
  });
275030
+ useEffect(() => {
275031
+ if (!csvUrl)
275032
+ return;
275033
+ fetch(csvUrl).then((r3) => r3.text()).then((text2) => {
275034
+ const lines = text2.split("\n");
275035
+ const header = lines[0].split(",");
275036
+ const meshIdIdx = header.indexOf("MeshID");
275037
+ const cellIdIdx = header.indexOf("CellID");
275038
+ const meshMap = {};
275039
+ const cellMap = {};
275040
+ lines.slice(1).forEach((l2) => {
275041
+ const cols = l2.split(",");
275042
+ const meshId = cols[meshIdIdx]?.trim();
275043
+ const cellId = cols[cellIdIdx]?.trim();
275044
+ if (meshId && cellId) {
275045
+ meshMap[meshId] = cellId;
275046
+ cellMap[cellId] = meshId;
275047
+ }
275048
+ });
275049
+ meshIdToCellIdRef.current = meshMap;
275050
+ cellIdToMeshIdRef.current = cellMap;
275051
+ setCsvLoaded(true);
275052
+ });
275053
+ }, [csvUrl]);
275054
+ useEffect(() => {
275055
+ chunkCacheRef.current.clear();
275056
+ }, [obsPointsData]);
275057
+ useEffect(() => {
275058
+ if (ngWidth && ngHeight) {
275059
+ viewportSizeRef.current = { width: ngWidth, height: ngHeight };
275060
+ }
275061
+ }, [ngWidth, ngHeight]);
275062
+ const setContainerNode = useCallback((node2) => {
275063
+ containerRef.current = node2;
275064
+ if (resizeObserverRef.current) {
275065
+ resizeObserverRef.current.disconnect();
275066
+ resizeObserverRef.current = null;
275067
+ }
275068
+ if (node2) {
275069
+ const rect2 = node2.getBoundingClientRect();
275070
+ if (rect2.width && rect2.height) {
275071
+ viewportSizeRef.current = { width: rect2.width, height: rect2.height };
275072
+ }
275073
+ const observer2 = new ResizeObserver((entries2) => {
275074
+ const entry2 = entries2[0];
275075
+ if (!entry2)
275076
+ return;
275077
+ const { width: width2, height: height2 } = entry2.contentRect;
275078
+ if (width2 && height2) {
275079
+ viewportSizeRef.current = { width: width2, height: height2 };
275080
+ }
275081
+ });
275082
+ observer2.observe(node2);
275083
+ resizeObserverRef.current = observer2;
275084
+ }
275085
+ }, []);
275086
+ const updateVisibleSegments = useCallback(async () => {
275087
+ if (!annotationInfoRef.current)
275088
+ return;
275089
+ if (!annotationTransformRef.current)
275090
+ return;
275091
+ if (!segmentationLayerScopes?.length)
275092
+ return;
275093
+ if (!pointLayerScopes?.length)
275094
+ return;
275095
+ const { position: position2, projectionScale } = latestViewerStateRef.current;
275096
+ if (!position2 || !projectionScale)
275097
+ return;
275098
+ const maxProjectionScale = meshLoadProjectionScaleThreshold ?? MESH_LOAD_THRESHOLD;
275099
+ if (projectionScale > maxProjectionScale) {
275100
+ if (visibleSegmentIdsRef.current?.length !== 0) {
275101
+ visibleSegmentIdsRef.current = [];
275102
+ incrementLatestViewerStateIteration();
275103
+ setIsMeshLoading(false);
275104
+ }
275105
+ return;
275106
+ }
275107
+ const { width: width2, height: height2 } = viewportSizeRef.current;
275108
+ if (!width2 || !height2)
275109
+ return;
275110
+ const transform3 = annotationTransformRef.current;
275111
+ const info2 = annotationInfoRef.current;
275112
+ const cellsUrl2 = info2.url;
275113
+ const allLevelCoords = info2.spatial.flatMap((level) => {
275114
+ const [gx, gy, gz] = level.grid_shape;
275115
+ const coords = [];
275116
+ for (let cx = 0; cx < gx; cx++) {
275117
+ for (let cy = 0; cy < gy; cy++) {
275118
+ for (let cz = 0; cz < gz; cz++) {
275119
+ coords.push({ level: level.key, cx, cy, cz });
275120
+ }
275121
+ }
275122
+ }
275123
+ return coords;
275124
+ });
275125
+ const fetchChunkWithPositions = async ({ level, cx, cy, cz }) => {
275126
+ const { serializers, serializer: defaultSerializer } = annotationTransformRef.current;
275127
+ const serializer2 = serializers?.[0] ?? defaultSerializer;
275128
+ if (!serializer2)
275129
+ return [];
275130
+ const cacheKey = `${cellsUrl2}/${level}/${cx}_${cy}_${cz}`;
275131
+ if (chunkCacheRef.current.has(cacheKey)) {
275132
+ return chunkCacheRef.current.get(cacheKey);
275133
+ }
275134
+ try {
275135
+ const res = await fetch(cacheKey);
275136
+ if (!res.ok) {
275137
+ chunkCacheRef.current.set(cacheKey, []);
275138
+ return [];
275139
+ }
275140
+ const buffer2 = await res.arrayBuffer();
275141
+ const entries2 = parseAnnotationChunkSegmentsWithPositions(buffer2, serializer2);
275142
+ chunkCacheRef.current.set(cacheKey, entries2);
275143
+ return entries2;
275144
+ } catch (e3) {
275145
+ chunkCacheRef.current.set(cacheKey, []);
275146
+ return [];
275147
+ }
275148
+ };
275149
+ try {
275150
+ const results = await Promise.all(allLevelCoords.map(fetchChunkWithPositions));
275151
+ const seenIds = /* @__PURE__ */ new Set();
275152
+ const allEntries = results.flat().filter(({ id: id2 }) => {
275153
+ if (seenIds.has(id2))
275154
+ return false;
275155
+ seenIds.add(id2);
275156
+ return true;
275157
+ });
275158
+ const obsIdToMeshId = {};
275159
+ allEntries.forEach(({ id: id2, obsId }) => {
275160
+ obsIdToMeshId[obsId] = id2;
275161
+ });
275162
+ obsIdToMeshIdRef.current = obsIdToMeshId;
275163
+ const mat = getViewProjectionMatRef.current?.();
275164
+ let visibleIds;
275165
+ if (!mat) {
275166
+ console.warn("No viewProjectionMatrix, loading all");
275167
+ visibleIds = [...new Set(allEntries.map(({ id: id2 }) => id2))];
275168
+ } else {
275169
+ const margin2 = Math.max(width2, height2) * 0.5;
275170
+ visibleIds = [...new Set(allEntries.filter(({ x: x2, y: y2, z: z2 }) => {
275171
+ const vx = x2 / transform3.x;
275172
+ const vy = y2 / transform3.y;
275173
+ const vz = (z2 || 0) / transform3.z;
275174
+ const cx = mat[0] * vx + mat[4] * vy + mat[8] * vz + mat[12];
275175
+ const cy = mat[1] * vx + mat[5] * vy + mat[9] * vz + mat[13];
275176
+ const cw2 = mat[3] * vx + mat[7] * vy + mat[11] * vz + mat[15];
275177
+ const screenX = (cx / cw2 + 1) * 0.5 * width2;
275178
+ const screenY = (1 - cy / cw2) * 0.5 * height2;
275179
+ return screenX >= -margin2 && screenX <= width2 + margin2 && screenY >= -margin2 && screenY <= height2 + margin2;
275180
+ }).map(({ id: id2 }) => id2))];
275181
+ }
275182
+ if (visibleIds.length === 0) {
275183
+ if (visibleSegmentIdsRef.current?.length !== 0) {
275184
+ visibleSegmentIdsRef.current = [];
275185
+ incrementLatestViewerStateIteration();
275186
+ }
275187
+ setIsMeshLoading(false);
275188
+ return;
275189
+ }
275190
+ const prevIds = new Set(visibleSegmentIdsRef.current ?? []);
275191
+ const addedIdsCount = visibleIds.filter((id2) => !prevIds.has(id2)).length;
275192
+ const hasSignificantChange = addedIdsCount > 20;
275193
+ if (hasSignificantChange && visibleIds.length > 0) {
275194
+ setIsMeshLoading(true);
275195
+ }
275196
+ const prevSorted = [...visibleSegmentIdsRef.current ?? []].sort().join(",");
275197
+ const nextSorted = [...visibleIds].sort().join(",");
275198
+ if (prevSorted !== nextSorted) {
275199
+ visibleSegmentIdsRef.current = visibleIds;
275200
+ incrementLatestViewerStateIteration();
275201
+ }
275202
+ if (hasSignificantChange) {
275203
+ setTimeout(() => setIsMeshLoading(false), MESH_LOADING_OVERLAY_TIMEOUT);
275204
+ }
275205
+ } catch (e3) {
275206
+ console.warn("[updateVisibleSegments] error:", e3);
275207
+ setIsMeshLoading(false);
275208
+ }
275209
+ }, [segmentationLayerScopes, pointLayerScopes, obsPointsData, meshLoadProjectionScaleThreshold]);
275210
+ useEffect(() => {
275211
+ updateVisibleSegmentsThrottledRef.current = throttle$1(updateVisibleSegments, 500);
275212
+ return () => updateVisibleSegmentsThrottledRef.current?.cancel();
275213
+ }, [updateVisibleSegments]);
274481
275214
  useEffect(() => {
274482
275215
  const prevNgCameraState = {
274483
275216
  position: latestViewerStateRef.current.position,
@@ -274485,37 +275218,50 @@ function NeuroglancerSubscriber(props) {
274485
275218
  projectionScale: latestViewerStateRef.current.projectionScale
274486
275219
  };
274487
275220
  latestViewerStateRef.current = {
274488
- ...initalViewerState,
275221
+ ...initialViewerState,
274489
275222
  ...prevNgCameraState
274490
275223
  };
274491
275224
  incrementLatestViewerStateIteration();
274492
- }, [initalViewerState]);
274493
- const initialRotationPushedRef = useRef(false);
274494
- const ngRotPushAtRef = useRef(0);
274495
- const lastInteractionSource = useRef(null);
274496
- const applyNgUpdateTimeoutRef = useRef(null);
274497
- const lastNgPushOrientationRef = useRef(null);
274498
- const initialRenderCalibratorRef = useRef(null);
274499
- const translationOffsetRef = useRef([0, 0, 0]);
274500
- const zoomRafRef = useRef(null);
274501
- const lastNgQuatRef = useRef([0, 0, 0, 1]);
274502
- const lastNgScaleRef = useRef(null);
274503
- const lastVitessceRotationRef = useRef({
274504
- x: spatialRotationX,
274505
- y: spatialRotationY,
274506
- z: spatialRotationZ,
274507
- orbit: spatialRotationOrbit
274508
- });
274509
- const [isLayersLoaded, setIsLayersLoaded] = useState(false);
274510
- const prevCoordsRef = useRef({
274511
- zoom: spatialZoom,
274512
- rx: spatialRotationX,
274513
- ry: spatialRotationY,
274514
- rz: spatialRotationZ,
274515
- orbit: spatialRotationOrbit,
274516
- tx: spatialTargetX,
274517
- ty: spatialTargetY
274518
- });
275225
+ }, [initialViewerState]);
275226
+ const cellsUrl = useMemo$1(() => {
275227
+ const firstScope = pointLayerScopes?.[0];
275228
+ return obsPointsUrls?.[firstScope]?.[0]?.url ?? null;
275229
+ }, [pointLayerScopes, obsPointsUrls]);
275230
+ const hasMatchingAnnotationSource = useMemo$1(() => {
275231
+ if (!cellsUrl)
275232
+ return false;
275233
+ const firstPointScope = pointLayerScopes?.[0];
275234
+ return pointsHaveMatchingSegmentation({
275235
+ pointObsType: pointLayerCoordination[0]?.[firstPointScope]?.obsType,
275236
+ segmentationLayerScopes,
275237
+ segmentationChannelScopesByLayer,
275238
+ segmentationChannelCoordination
275239
+ });
275240
+ }, [cellsUrl, pointLayerScopes, segmentationLayerScopes, segmentationChannelScopesByLayer]);
275241
+ useEffect(() => {
275242
+ if (!cellsUrl)
275243
+ return;
275244
+ fetch(`${cellsUrl}/info`).then((r3) => r3.json()).then((info2) => {
275245
+ const infoWithUrl = {
275246
+ ...info2,
275247
+ url: cellsUrl
275248
+ };
275249
+ annotationInfoRef.current = infoWithUrl;
275250
+ if (annotationTransformRef.current)
275251
+ setAnnotationReady(true);
275252
+ }).catch((err2) => console.error("failed to fetch annotation info:", err2));
275253
+ }, [cellsUrl]);
275254
+ useEffect(() => {
275255
+ if (annotationReady) {
275256
+ setIsLayersLoaded(true);
275257
+ updateVisibleSegments();
275258
+ }
275259
+ }, [annotationReady]);
275260
+ const onAnnotationSourceReady = useCallback((transform3) => {
275261
+ annotationTransformRef.current = transform3;
275262
+ if (annotationInfoRef.current)
275263
+ setAnnotationReady(true);
275264
+ }, []);
274519
275265
  const handleStateUpdate = useCallback((newState) => {
274520
275266
  lastInteractionSource.current = LAST_INTERACTION_SOURCE.neuroglancer;
274521
275267
  const { projectionScale, projectionOrientation, position: position2 } = newState;
@@ -274548,6 +275294,7 @@ function NeuroglancerSubscriber(props) {
274548
275294
  setZoom(vitZoomFromNg);
274549
275295
  zoomRafRef.current = null;
274550
275296
  });
275297
+ updateVisibleSegments();
274551
275298
  }
274552
275299
  lastNgScaleRef.current = projectionScale;
274553
275300
  }
@@ -274588,7 +275335,8 @@ function NeuroglancerSubscriber(props) {
274588
275335
  projectionScale,
274589
275336
  position: position2
274590
275337
  };
274591
- }, []);
275338
+ updateVisibleSegmentsThrottledRef.current?.();
275339
+ }, [updateVisibleSegmentsThrottledRef]);
274592
275340
  const onSegmentClick = useCallback((value2) => {
274593
275341
  if (value2) {
274594
275342
  const id2 = String(value2);
@@ -274613,11 +275361,18 @@ function NeuroglancerSubscriber(props) {
274613
275361
  const channelScope = segmentationChannelScopesByLayer?.[layerScope]?.[0];
274614
275362
  result[layerScope] = {
274615
275363
  colors: segmentationColorMapping?.[layerScope]?.[channelScope] ?? {},
274616
- opacity: segmentationColorMapping?.[layerScope]?.opacity ?? 1
275364
+ opacity: segmentationColorMapping?.[layerScope]?.opacity ?? 1,
275365
+ defaultColor: segmentationColorMapping?.[layerScope]?.defaultColor ?? null
274617
275366
  };
274618
275367
  });
274619
275368
  return result;
274620
275369
  }, [segmentationColorMapping, segmentationLayerScopes, segmentationChannelScopesByLayer]);
275370
+ useEffect(() => {
275371
+ if (!hasMatchingAnnotationSource && isReady && !segmentationLayerScopes?.length) {
275372
+ setIsLayersLoaded(true);
275373
+ }
275374
+ }, [hasMatchingAnnotationSource, isReady, segmentationLayerScopes]);
275375
+ const meshOpacity = hasMatchingAnnotationSource ? MESH_OPACITY : void 0;
274621
275376
  const derivedViewerState = useMemo$1(() => {
274622
275377
  const { current: current2 } = latestViewerStateRef;
274623
275378
  if (current2.layers.length <= 0) {
@@ -274676,13 +275431,33 @@ function NeuroglancerSubscriber(props) {
274676
275431
  lastInteractionSource.current = null;
274677
275432
  }
274678
275433
  const updatedLayers = current2?.layers?.map((layer, idx) => {
274679
- const layerScope = segmentationLayerScopes?.[idx];
275434
+ if (layer.type !== "segmentation")
275435
+ return layer;
275436
+ const layerScope = segmentationLayerScopes?.find((scope) => layer.name?.includes(scope));
275437
+ if (!layerScope)
275438
+ return layer;
274680
275439
  const layerColorMapping = cellColorMappingByLayer?.[layerScope]?.colors ?? {};
274681
- const layerSegments = Object.keys(layerColorMapping);
275440
+ const defaultColor = cellColorMappingByLayer?.[layerScope]?.defaultColor;
275441
+ let segments2 = [];
275442
+ if (hasMatchingAnnotationSource) {
275443
+ segments2 = visibleSegmentIdsRef.current ?? [];
275444
+ } else if (Object.keys(layerColorMapping).length > 0) {
275445
+ segments2 = Object.keys(layerColorMapping);
275446
+ }
275447
+ let derivedSegmentColors = {};
275448
+ if (hasMatchingAnnotationSource && segments2.length > 0) {
275449
+ segments2.forEach((meshId) => {
275450
+ const color2 = layerColorMapping[meshId] || defaultColor;
275451
+ if (color2)
275452
+ derivedSegmentColors[meshId] = color2;
275453
+ });
275454
+ } else if (!hasMatchingAnnotationSource) {
275455
+ derivedSegmentColors = layerColorMapping;
275456
+ }
274682
275457
  return {
274683
275458
  ...layer,
274684
- segments: layerSegments,
274685
- segmentColors: layerColorMapping,
275459
+ segments: segments2,
275460
+ segmentColors: derivedSegmentColors,
274686
275461
  objectAlpha: cellColorMappingByLayer?.[layerScope]?.opacity ?? 1
274687
275462
  };
274688
275463
  }) ?? [];
@@ -274712,17 +275487,21 @@ function NeuroglancerSubscriber(props) {
274712
275487
  spatialRotationZ,
274713
275488
  spatialTargetX,
274714
275489
  spatialTargetY,
274715
- initalViewerState,
274716
- latestViewerStateIteration
275490
+ initialViewerState,
275491
+ latestViewerStateIteration,
275492
+ hasMatchingAnnotationSource
274717
275493
  ]);
274718
275494
  const onSegmentHighlight = useCallback((obsId) => {
274719
275495
  setCellHighlight(String(obsId));
274720
275496
  }, [setCellHighlight]);
274721
275497
  const handleLayerLoadingChange = useCallback((isLoaded) => {
274722
- setIsLayersLoaded(isLoaded);
274723
- }, []);
275498
+ if (!isLayersLoaded && isLoaded) {
275499
+ setIsLayersLoaded(true);
275500
+ }
275501
+ }, [isLayersLoaded]);
274724
275502
  const hasLayers = derivedViewerState?.layers?.length > 0;
274725
- 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, {
275503
+ const showPointsLegend = !hasMatchingAnnotationSource;
275504
+ 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, {
274726
275505
  theme: "dark",
274727
275506
  maxHeight: ngHeight,
274728
275507
  // Segmentations
@@ -274731,10 +275510,12 @@ function NeuroglancerSubscriber(props) {
274731
275510
  segmentationChannelScopesByLayer,
274732
275511
  segmentationChannelCoordination,
274733
275512
  // Points
274734
- pointLayerScopes,
275513
+ pointLayerScopes: showPointsLegend ? pointLayerScopes : void 0,
274735
275514
  pointLayerCoordination,
274736
275515
  pointMultiIndicesData
274737
- }) }), jsx$1(NeuroglancerComp, { classes: classes2, onSegmentClick, onSelectHoveredCoords: onSegmentHighlight, viewerState: derivedViewerState, cellColorMapping: cellColorMappingByLayer, setViewerState: handleStateUpdate, onLayerLoadingChange: handleLayerLoadingChange })] }) : null });
275516
+ }) }), 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) => {
275517
+ getViewProjectionMatRef.current = getFn;
275518
+ }, getMeshIdToCellId: (meshId) => meshIdToCellIdRef.current[meshId], cellsUrl, meshOpacity })] }) : null });
274738
275519
  }
274739
275520
  const FEATURE_AGGREGATION_STRATEGIES = ["first", "last", "sum", "mean"];
274740
275521
  function CellSetExpressionPlotOptions(props) {
@@ -327232,7 +328013,7 @@ const VEGA_THEMES = {
327232
328013
  };
327233
328014
  const DATASET_NAME = "table";
327234
328015
  function VegaGlobalStyles() {
327235
- const { theme } = useStyles$y();
328016
+ const { theme } = useStyles$z();
327236
328017
  return jsx$1(GlobalStyles$3, { styles: {
327237
328018
  "#vg-tooltip-element.vg-tooltip.custom-theme": {
327238
328019
  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)",
@@ -327266,7 +328047,7 @@ function renderTooltipContents(tooltipText) {
327266
328047
  }
327267
328048
  function VegaPlot(props) {
327268
328049
  const { spec: partialSpec, data: data2, getTooltipText, signalListeners, renderer = "svg", onNewView } = props;
327269
- const { classes: tooltipClasses2 } = useStyles$t();
328050
+ const { classes: tooltipClasses2 } = useStyles$u();
327270
328051
  const tooltipHandler = useMemo$1(() => {
327271
328052
  if (typeof getTooltipText === "function") {
327272
328053
  const tooltipConfig = {
@@ -353760,7 +354541,7 @@ function UncontrolledLauncher(props) {
353760
354541
  ));
353761
354542
  }
353762
354543
  export {
353763
- diffCameraState as $,
354544
+ GREY_HEX as $,
353764
354545
  require_descriptors as A,
353765
354546
  BaseDecoder as B,
353766
354547
  CoordinationType$1 as C,
@@ -353789,8 +354570,9 @@ export {
353789
354570
  require_redefine as Z,
353790
354571
  require_objectAssign as _,
353791
354572
  getDefaultExportFromCjs$1 as a,
353792
- UncontrolledLauncher as a0,
353793
- ControlledLauncher as a1,
354573
+ diffCameraState as a0,
354574
+ UncontrolledLauncher as a1,
354575
+ ControlledLauncher as a2,
353794
354576
  LercAddCompression as b,
353795
354577
  commonjsGlobal$1 as c,
353796
354578
  requireAssign as d,