@vitessce/all 4.0.8 → 4.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/{GeometryAndMeshXR-Caba1QEa.js → GeometryAndMeshXR-BovcVfOy.js} +1 -1
  2. package/dist/{ReactNeuroglancer-Cm_ERWOf.js → ReactNeuroglancer-Ch1xO_u5.js} +1 -1
  3. package/dist/{XREnterButton-C7Q7e8rD.js → XREnterButton-Dz4r7Jjx.js} +1 -1
  4. package/dist/{XRSceneComponents-Dgayh6-j.js → XRSceneComponents-CGcz34cr.js} +1 -1
  5. package/dist/{XRWrapper-BrRGN2E4.js → XRWrapper-C1AfBFtW.js} +2 -2
  6. package/dist/{deflate-D1RcEgtg.js → deflate-BKJk_mLF.js} +1 -1
  7. package/dist/{higlass-BLYM2C4s.js → higlass-BzshVp46.js} +1 -1
  8. package/dist/{index-Bub8madF.js → index-CNXTPnLt.js} +1 -1
  9. package/dist/{index-B8xbImKw.js → index-CfweEgJW.js} +5 -5
  10. package/dist/{index-CzTdHRMx.js → index-EoA5CfO8.js} +1907 -150
  11. package/dist/index.js +8 -1
  12. package/dist/{jpeg-g9i0I1mK.js → jpeg-DXSq15HV.js} +1 -1
  13. package/dist/{lerc-B4lSBwWt.js → lerc-uTsWAc7Y.js} +1 -1
  14. package/dist/{lzw-63HBqTiG.js → lzw-CIpLYaya.js} +1 -1
  15. package/dist/{packbits-i970u6wg.js → packbits-Cf0TB9M_.js} +1 -1
  16. package/dist/{raw-akfNT_LS.js → raw-CR3kVxsB.js} +1 -1
  17. package/dist/{webimage-CA4EiJGO.js → webimage-Dp3HLfwF.js} +1 -1
  18. package/dist/{xrStore--BrpHLm6.js → xrStore-B2qW6VGJ.js} +1 -1
  19. package/dist-tsc/base-plugins.d.ts +268 -1
  20. package/dist-tsc/base-plugins.d.ts.map +1 -1
  21. package/dist-tsc/base-plugins.js +13 -2
  22. package/dist-tsc/base-plugins.js.map +1 -1
  23. package/dist-tsc/index.d.ts +1 -0
  24. package/dist-tsc/index.d.ts.map +1 -1
  25. package/dist-tsc/index.js +1 -0
  26. package/dist-tsc/index.js.map +1 -1
  27. package/dist-tsc/joint-file-types-legacy.d.ts +18 -18
  28. package/dist-tsc/joint-file-types-legacy.d.ts.map +1 -1
  29. package/package.json +34 -33
  30. package/src/base-plugins.ts +14 -0
  31. package/src/index.ts +7 -0
@@ -8,7 +8,7 @@ var __privateSet = (obj, member, value2, setter2) => (__accessCheck(obj, member,
8
8
  var _inner;
9
9
  import * as React from "react";
10
10
  import React__default, { useContext, forwardRef as forwardRef$1, useRef, useMemo as useMemo$1, createContext as createContext$1, createElement, isValidElement, cloneElement, Children, PureComponent, useEffect, useState, version as version$6, useLayoutEffect as useLayoutEffect$1, useImperativeHandle, Component as Component$1, useCallback, useDeferredValue, Suspense, useReducer } from "react";
11
- import { useLoaders, useCoordinationScopes, useCoordination, useDescription, useImageData, useReady, TitleInfo, useVitessceContainer, useSetWarning, useObsSetsData, useUrls, usePlotOptionsStyles, OptionsContainer, CellColorEncodingOption, OptionSelect, useComponentHover, useComponentViewInfo, useSetComponentHover, useSetComponentViewInfo, useInitialCoordination, useDeckCanvasSize, useMultiObsLabels, useObsEmbeddingData, useFeatureSelection, useObsFeatureMatrixIndices, useFeatureLabelsData, useExpandedFeatureLabelsMap, useSampleSetsData, useSampleEdgesData, useGetObsInfo, useUint8FeatureSelection, useExpressionValueGetter, useAuxiliaryCoordination, useHasLoader, useObsLocationsData, useObsLabelsData, useObsSegmentationsData, useNeighborhoodsData, useMergeCoordination, useCoordinationScopesBy, useMultiCoordinationScopesSecondaryNonNull, useMultiCoordinationScopesNonNull, useComplexCoordination, useComplexCoordinationSecondary, useGridItemSize, useMultiObsPoints, usePointMultiObsLabels, usePointMultiObsFeatureMatrixIndices, useMultiObsSpots, useSpotMultiObsSets, useSpotMultiFeatureLabels, useSpotMultiFeatureSelection, useSpotMultiObsFeatureMatrixIndices, useSegmentationMultiObsLocations, useMultiObsSegmentations, useSegmentationMultiObsSets, useSegmentationMultiFeatureSelection, useSegmentationMultiObsFeatureMatrixIndices, useMultiImages, useObsFeatureMatrixData, useUint8ObsFeatureMatrix, useGetObsMembership, PopperMenu, useComponentLayout, useClosestVitessceContainerSize, useWindowDimensions, useRemoveImageChannelInMetaCoordinationScopes, useAddImageChannelInMetaCoordinationScopes, useViewConfigStoreApi, useViewConfig, useSetViewConfig, createLoaders, useWarning, useGenomicProfilesData, shallowDiff, shallowDiffByChannelWithKeys, shallowDiffByLayer, shallowDiffByChannelCoordinationWithKeys, shallowDiffByChannel, shallowDiffByLayerCoordinationWithKeys, useMemoCustomComparison, useSegmentationMultiObsColors, useMatchingLoader, useColumnNameMapping, useFeatureStatsData, useObsSetStatsData, useAsyncFunction, useFeatureSetStatsData, useMultiCoordinationScopes, useComparisonMetadata, logConfig, VitSContainer } from "@vitessce/vit-s";
11
+ import { useLoaders, useCoordinationScopes, useCoordination, useDescription, useImageData, useReady, TitleInfo, useVitessceContainer, useSetWarning, useObsSetsData, useUrls, usePlotOptionsStyles, OptionsContainer, CellColorEncodingOption, OptionSelect, useComponentHover, useComponentViewInfo, useViewMapping, useSetComponentHover, useSetComponentViewInfo, useAnnotationFrameCoordination, useInitialCoordination, useDeckCanvasSize, useAnnotationStoryData, useMultiObsLabels, useObsEmbeddingData, useFeatureSelection, useObsFeatureMatrixIndices, useFeatureLabelsData, useExpandedFeatureLabelsMap, useSampleSetsData, useSampleEdgesData, useGetObsInfo, useUint8FeatureSelection, useExpressionValueGetter, useAuxiliaryCoordination, useHasLoader, useObsLocationsData, useObsLabelsData, useObsSegmentationsData, useNeighborhoodsData, useMergeCoordination, useCoordinationScopesBy, useMultiCoordinationScopesSecondaryNonNull, useMultiCoordinationScopesNonNull, useComplexCoordination, useComplexCoordinationSecondary, useGridItemSize, useMultiObsPoints, usePointMultiObsLabels, usePointMultiObsFeatureMatrixIndices, useMultiObsSpots, useSpotMultiObsSets, useSpotMultiFeatureLabels, useSpotMultiFeatureSelection, useSpotMultiObsFeatureMatrixIndices, useSegmentationMultiObsLocations, useMultiObsSegmentations, useSegmentationMultiObsSets, useSegmentationMultiFeatureSelection, useSegmentationMultiObsFeatureMatrixIndices, useMultiImages, useObsFeatureMatrixData, useUint8ObsFeatureMatrix, useGetObsMembership, PopperMenu, useComponentLayout, useClosestVitessceContainerSize, useWindowDimensions, useRemoveImageChannelInMetaCoordinationScopes, useAddImageChannelInMetaCoordinationScopes, useViewConfigStoreApi, useViewConfig, useSetViewConfig, createLoaders, useWarning, useGenomicProfilesData, shallowDiff, shallowDiffByChannelWithKeys, shallowDiffByLayer, shallowDiffByChannelCoordinationWithKeys, shallowDiffByChannel, shallowDiffByLayerCoordinationWithKeys, useMemoCustomComparison, useSegmentationMultiObsColors, useMatchingLoader, useColumnNameMapping, useFeatureStatsData, useObsSetStatsData, useAsyncFunction, useFeatureSetStatsData, useMultiCoordinationScopes, useComparisonMetadata, logConfig, VitSContainer } from "@vitessce/vit-s";
12
12
  import { jsx as jsx$1, jsxs, Fragment } from "react/jsx-runtime";
13
13
  import * as ReactDOM from "react-dom";
14
14
  import ReactDOM__default, { createPortal } from "react-dom";
@@ -4261,7 +4261,7 @@ const latestFileDefSchema = z.object({
4261
4261
  options: fileOptionsSchema.optional(),
4262
4262
  requestInit: requestInit.optional(),
4263
4263
  // New: file def can have coordinationValues.
4264
- coordinationValues: z.record(z.string()).optional()
4264
+ coordinationValues: z.record(z.any()).optional()
4265
4265
  });
4266
4266
  const datasetsSchema1_0_13 = z.array(z.object({
4267
4267
  uid: z.string(),
@@ -4292,7 +4292,8 @@ const configSchema1_0_16 = configSchema1_0_13.extend({
4292
4292
  h: z.number().int().optional(),
4293
4293
  // Updates coordinationScopes and coordinationScopesBy
4294
4294
  coordinationScopes: componentCoordinationScopes.optional(),
4295
- coordinationScopesBy: componentCoordinationScopesBy.optional()
4295
+ coordinationScopesBy: componentCoordinationScopesBy.optional(),
4296
+ coordinationValues: z.record(z.any()).optional()
4296
4297
  }))
4297
4298
  });
4298
4299
  const configSchema1_0_17 = configSchema1_0_16.extend({
@@ -9167,7 +9168,8 @@ const ViewType$1 = {
9167
9168
  TREEMAP: "treemap",
9168
9169
  SAMPLE_SET_PAIR_MANAGER: "sampleSetPairManager",
9169
9170
  FEATURE_STATS_TABLE: "featureStatsTable",
9170
- SINGLE_OBS_SET_SELECTOR: "singleObsSetSelector"
9171
+ SINGLE_OBS_SET_SELECTOR: "singleObsSetSelector",
9172
+ ANNOTATION_CONTROLLER: "annotationController"
9171
9173
  };
9172
9174
  const DataType$3 = {
9173
9175
  OBS_LABELS: "obsLabels",
@@ -9188,7 +9190,8 @@ const DataType$3 = {
9188
9190
  COMPARISON_METADATA: "comparisonMetadata",
9189
9191
  FEATURE_STATS: "featureStats",
9190
9192
  FEATURE_SET_STATS: "featureSetStats",
9191
- OBS_SET_STATS: "obsSetStats"
9193
+ OBS_SET_STATS: "obsSetStats",
9194
+ ANNOTATION_STORY: "annotationStory"
9192
9195
  };
9193
9196
  const AsyncFunctionType = {
9194
9197
  // String input (rather than Node input)
@@ -9220,6 +9223,7 @@ const FileType$1 = {
9220
9223
  OBS_SETS_CSV: "obsSets.csv",
9221
9224
  OBS_SETS_JSON: "obsSets.json",
9222
9225
  SAMPLE_SETS_CSV: "sampleSets.csv",
9226
+ ANNOTATION_STORY_JSON: "annotationStory.json",
9223
9227
  // OME-Zarr
9224
9228
  IMAGE_OME_ZARR: "image.ome-zarr",
9225
9229
  OBS_SEGMENTATIONS_OME_ZARR: "obsSegmentations.ome-zarr",
@@ -9504,7 +9508,15 @@ const CoordinationType$1 = {
9504
9508
  FEATURE_POINT_FOLD_CHANGE_THRESHOLD: "featurePointFoldChangeThreshold",
9505
9509
  FEATURE_LABEL_FOLD_CHANGE_THRESHOLD: "featureLabelFoldChangeThreshold",
9506
9510
  // Treemap
9507
- HIERARCHY_LEVELS: "hierarchyLevels"
9511
+ HIERARCHY_LEVELS: "hierarchyLevels",
9512
+ // Annotation frames
9513
+ ANNOTATION_STORY: "annotationStory",
9514
+ ANNOTATION_FRAME_INDEX: "annotationFrameIndex",
9515
+ ANNOTATION_SHAPES: "annotationShapes",
9516
+ ANNOTATION_OVERLAY_VISIBLE: "annotationOverlayVisible",
9517
+ ANNOTATION_TRANSITION_DURATION: "annotationTransitionDuration",
9518
+ ANNOTATION_SEMANTIC_ZOOM: "annotationSemanticZoom",
9519
+ ANNOTATION_EDITABLE: "annotationEditable"
9508
9520
  };
9509
9521
  const STATUS$1 = {
9510
9522
  SUCCESS: "success"
@@ -9536,7 +9548,8 @@ const ViewHelpMapping = {
9536
9548
  SAMPLE_SET_PAIR_MANAGER: "Select pairs of sample groups.",
9537
9549
  FEATURE_STATS_TABLE: "This table displays per-feature statistics, for example, from a differential expression test.",
9538
9550
  LAYER_CONTROLLER_BETA: "The spatial layer controller provides an interface for manipulating the visualization layers displayed in the spatial view.",
9539
- SPATIAL_BETA: "The spatial view displays (potentially layered) spatially-resolved data including RGB or multiplexed images, segmentations of observations (bitmask- or polygon-based), and/or points (e.g., representing FISH transcripts)."
9551
+ SPATIAL_BETA: "The spatial view displays (potentially layered) spatially-resolved data including RGB or multiplexed images, segmentations of observations (bitmask- or polygon-based), and/or points (e.g., representing FISH transcripts).",
9552
+ ANNOTATION_CONTROLLER: "The annotation controller displays a guided sequence of narrative frames, each with descriptive text and optional shapes (rectangles, lines, arrows, ellipses, or polygons)."
9540
9553
  };
9541
9554
  ({
9542
9555
  // For new file types
@@ -9552,6 +9565,7 @@ const ViewHelpMapping = {
9552
9565
  [FileType$1.OBS_SEGMENTATIONS_JSON]: DataType$3.OBS_SEGMENTATIONS,
9553
9566
  [FileType$1.OBS_SETS_CSV]: DataType$3.OBS_SETS,
9554
9567
  [FileType$1.OBS_SETS_JSON]: DataType$3.OBS_SETS,
9568
+ [FileType$1.ANNOTATION_STORY_JSON]: DataType$3.ANNOTATION_STORY,
9555
9569
  [FileType$1.IMAGE_OME_ZARR]: DataType$3.IMAGE,
9556
9570
  [FileType$1.OBS_SEGMENTATIONS_OME_ZARR]: DataType$3.OBS_SEGMENTATIONS,
9557
9571
  [FileType$1.OBS_FEATURE_MATRIX_ANNDATA_ZARR]: DataType$3.OBS_FEATURE_MATRIX,
@@ -9674,7 +9688,8 @@ const ViewHelpMapping = {
9674
9688
  [DataType$3.OBS_SET_STATS]: [
9675
9689
  CoordinationType$1.OBS_TYPE
9676
9690
  // TODO: should sampleType, obsSetSelection, and/or sampleSetSelection be used here?
9677
- ]
9691
+ ],
9692
+ [DataType$3.ANNOTATION_STORY]: []
9678
9693
  });
9679
9694
  const ALT_ZARR_STORE_TYPES = {
9680
9695
  // For AnnData:
@@ -9807,7 +9822,9 @@ const ALT_ZARR_STORE_TYPES = {
9807
9822
  CoordinationType$1.SPATIAL_TARGET_Z,
9808
9823
  CoordinationType$1.SPATIAL_AXIS_FIXED,
9809
9824
  CoordinationType$1.SPATIAL_ORBIT_AXIS,
9810
- CoordinationType$1.FEATURE_AGGREGATION_STRATEGY
9825
+ CoordinationType$1.FEATURE_AGGREGATION_STRATEGY,
9826
+ // Annotation shapes are specific to a view's coordinate system.
9827
+ CoordinationType$1.ANNOTATION_SHAPES
9811
9828
  ];
9812
9829
  const COMPONENT_COORDINATION_TYPES = {
9813
9830
  [ViewType$1.NEUROGLANCER]: [
@@ -9899,7 +9916,14 @@ const COMPONENT_COORDINATION_TYPES = {
9899
9916
  CoordinationType$1.EMBEDDING_CONTOURS_FILLED,
9900
9917
  CoordinationType$1.EMBEDDING_CONTOUR_PERCENTILES,
9901
9918
  CoordinationType$1.CONTOUR_COLOR_ENCODING,
9902
- CoordinationType$1.CONTOUR_COLOR
9919
+ CoordinationType$1.CONTOUR_COLOR,
9920
+ CoordinationType$1.ANNOTATION_STORY,
9921
+ CoordinationType$1.ANNOTATION_SHAPES,
9922
+ CoordinationType$1.ANNOTATION_FRAME_INDEX,
9923
+ CoordinationType$1.ANNOTATION_OVERLAY_VISIBLE,
9924
+ CoordinationType$1.ANNOTATION_TRANSITION_DURATION,
9925
+ CoordinationType$1.ANNOTATION_EDITABLE,
9926
+ CoordinationType$1.ANNOTATION_SEMANTIC_ZOOM
9903
9927
  ],
9904
9928
  [ViewType$1.DUAL_SCATTERPLOT]: [
9905
9929
  CoordinationType$1.DATASET,
@@ -10015,7 +10039,14 @@ const COMPONENT_COORDINATION_TYPES = {
10015
10039
  CoordinationType$1.ADDITIONAL_OBS_SETS,
10016
10040
  CoordinationType$1.MOLECULE_HIGHLIGHT,
10017
10041
  CoordinationType$1.TOOLTIPS_VISIBLE,
10018
- CoordinationType$1.PHOTOMETRIC_INTERPRETATION
10042
+ CoordinationType$1.PHOTOMETRIC_INTERPRETATION,
10043
+ CoordinationType$1.ANNOTATION_STORY,
10044
+ CoordinationType$1.ANNOTATION_SHAPES,
10045
+ CoordinationType$1.ANNOTATION_FRAME_INDEX,
10046
+ CoordinationType$1.ANNOTATION_OVERLAY_VISIBLE,
10047
+ CoordinationType$1.ANNOTATION_TRANSITION_DURATION,
10048
+ CoordinationType$1.ANNOTATION_EDITABLE,
10049
+ CoordinationType$1.ANNOTATION_SEMANTIC_ZOOM
10019
10050
  ],
10020
10051
  [ViewType$1.SPATIAL_BETA]: [
10021
10052
  CoordinationType$1.META_COORDINATION_SCOPES,
@@ -10093,7 +10124,27 @@ const COMPONENT_COORDINATION_TYPES = {
10093
10124
  CoordinationType$1.SPATIAL_CHANNEL_LABELS_ORIENTATION,
10094
10125
  CoordinationType$1.SPATIAL_CHANNEL_LABEL_SIZE,
10095
10126
  CoordinationType$1.SPATIAL_CHANNELS_SORT_ORDER,
10096
- CoordinationType$1.PHOTOMETRIC_INTERPRETATION
10127
+ CoordinationType$1.PHOTOMETRIC_INTERPRETATION,
10128
+ CoordinationType$1.ANNOTATION_STORY,
10129
+ CoordinationType$1.ANNOTATION_SHAPES,
10130
+ CoordinationType$1.ANNOTATION_FRAME_INDEX,
10131
+ CoordinationType$1.ANNOTATION_OVERLAY_VISIBLE,
10132
+ CoordinationType$1.ANNOTATION_TRANSITION_DURATION,
10133
+ CoordinationType$1.ANNOTATION_EDITABLE,
10134
+ CoordinationType$1.ANNOTATION_SEMANTIC_ZOOM
10135
+ ],
10136
+ [ViewType$1.ANNOTATION_CONTROLLER]: [
10137
+ CoordinationType$1.DATASET,
10138
+ CoordinationType$1.ANNOTATION_STORY,
10139
+ CoordinationType$1.ANNOTATION_FRAME_INDEX,
10140
+ CoordinationType$1.ANNOTATION_OVERLAY_VISIBLE,
10141
+ CoordinationType$1.ANNOTATION_TRANSITION_DURATION,
10142
+ CoordinationType$1.ANNOTATION_EDITABLE,
10143
+ CoordinationType$1.ANNOTATION_SEMANTIC_ZOOM
10144
+ // Note: the annotation controller also needs to read the coordination state
10145
+ // of all other views/coordination types via the full config.
10146
+ // Note: we get/set the CoordinationType.ANNOTATION_SHAPES from the full config,
10147
+ // as this info is view-specific rather than global to the story.
10097
10148
  ],
10098
10149
  [ViewType$1.HEATMAP]: [
10099
10150
  CoordinationType$1.DATASET,
@@ -11193,6 +11244,124 @@ const moleculesLayerObj = z.object({
11193
11244
  radius: z.number(),
11194
11245
  opacity: z.number()
11195
11246
  });
11247
+ const shapeBaseObj = z.object({
11248
+ uid: z.string(),
11249
+ text: z.string().optional(),
11250
+ strokeColor: z.array(z.number()).length(3).optional(),
11251
+ strokeWidth: z.number().optional(),
11252
+ // SVG-style dash pattern, e.g. "10 5" or "10 20 30 10". "none" or omitted = solid.
11253
+ strokeDashArray: z.string().optional(),
11254
+ visible: z.boolean().optional()
11255
+ // TODO: define unit modes for x and y axes
11256
+ // ("pixels" vs "normalized" vs "physical" etc.)
11257
+ // TODO: a coordinate system identifier (e.g., "DeckGL" for now,
11258
+ // but a coordinate system from OME-NGFF/SpatialData eventually?)
11259
+ });
11260
+ const rectangleShapeObj = shapeBaseObj.extend({
11261
+ type: z.literal("rectangle"),
11262
+ // OME-XML field names verbatim (upper-left origin)
11263
+ x: z.number(),
11264
+ y: z.number(),
11265
+ width: z.number(),
11266
+ height: z.number(),
11267
+ fillColor: z.array(z.number()).length(3).optional(),
11268
+ fillOpacity: z.number().optional()
11269
+ });
11270
+ const lineShapeObj = shapeBaseObj.extend({
11271
+ type: z.literal("line"),
11272
+ // OME-XML field names verbatim
11273
+ x1: z.number(),
11274
+ y1: z.number(),
11275
+ x2: z.number(),
11276
+ y2: z.number(),
11277
+ markerStart: z.enum(["Arrow", "Tick"]).nullable().optional(),
11278
+ markerEnd: z.enum(["Arrow", "Tick"]).nullable().optional(),
11279
+ textPosition: z.enum(["start", "middle", "end"]).optional(),
11280
+ textBufferPx: z.number().optional()
11281
+ });
11282
+ const ellipseShapeObj = shapeBaseObj.extend({
11283
+ type: z.literal("ellipse"),
11284
+ // OME-XML field names verbatim (centre point + radii)
11285
+ x1: z.number(),
11286
+ y1: z.number(),
11287
+ radiusX: z.number(),
11288
+ radiusY: z.number(),
11289
+ fillColor: z.array(z.number()).length(3).optional(),
11290
+ fillOpacity: z.number().optional()
11291
+ });
11292
+ const polygonShapeObj = shapeBaseObj.extend({
11293
+ type: z.literal("polygon"),
11294
+ // OME-XML: array of [x, y] coordinate pairs, closed automatically
11295
+ points: z.array(z.tuple([z.number(), z.number()])),
11296
+ fillColor: z.array(z.number()).length(3).optional(),
11297
+ fillOpacity: z.number().optional()
11298
+ });
11299
+ const polylineShapeObj = shapeBaseObj.extend({
11300
+ type: z.literal("polyline"),
11301
+ // OME-XML: array of [x, y] coordinate pairs, open (not closed)
11302
+ points: z.array(z.tuple([z.number(), z.number()])),
11303
+ markerStart: z.enum(["Arrow", "Tick"]).nullable().optional(),
11304
+ markerEnd: z.enum(["Arrow", "Tick"]).nullable().optional()
11305
+ });
11306
+ const annotationShapeObj = z.discriminatedUnion("type", [
11307
+ rectangleShapeObj,
11308
+ lineShapeObj,
11309
+ ellipseShapeObj,
11310
+ polygonShapeObj,
11311
+ polylineShapeObj
11312
+ ]);
11313
+ const annotationShapesArray = z.array(annotationShapeObj);
11314
+ const annotationFrameObj = z.object({
11315
+ uid: z.string(),
11316
+ title: z.string().optional(),
11317
+ description: z.string().optional(),
11318
+ descriptionType: z.enum(["text", "markdown"]).optional(),
11319
+ // A frame can define per-view: coordinationValues
11320
+ // (if the view's state should not be coordinated with other views)
11321
+ // or per-view coordinationScopes/coordinationScopesBy
11322
+ // (if at this frame, the view should be coordinated with other views).
11323
+ // We can also define a frame-level coordination space,
11324
+ // which will be merged with the global coordination space.
11325
+ // TODO: datasets
11326
+ // TODO: coordinationSpace
11327
+ layout: z.array(
11328
+ // Note: for now, we will infer view objects which have a uid
11329
+ // that is present in the global layout, and do NOT have x/y/w/h defined,
11330
+ // to mean that in this frame, the layout and set of views is unchanged
11331
+ // from the global layout.
11332
+ // This will allow each frame to omit the full list of views from the layout,
11333
+ // and only specify the views whose coordination state is changing in this frame.
11334
+ // If a frame includes a view with a uid that is not present
11335
+ // in the global layout, then that view will be added to the layout at this frame.
11336
+ // This frame should also define x/y/w/h for all views,
11337
+ // since it will otherwise be ambiguous how to layout the updated set of views
11338
+ // for this frame.
11339
+ z.object({
11340
+ // The view UID. If this view uid is not present in the global layout,
11341
+ // it will be added to the layout at this frame,
11342
+ // and will need to have its coordination values initialized to their defaults
11343
+ // for any that were not fully specified.
11344
+ uid: z.string(),
11345
+ coordinationValues: z.record(
11346
+ z.string(),
11347
+ // The coordination type name, e.g. "embeddingType"
11348
+ z.any()
11349
+ ).optional()
11350
+ // TODO: support coordinationScopes/coordinationScopesBy for per-view coordination
11351
+ // at this frame.
11352
+ // TODO: view x/y/w/h once we support view layout changes per-frame.
11353
+ })
11354
+ ).optional()
11355
+ });
11356
+ const annotationStoryObj = z.object({
11357
+ // TODO: schema version for the story schema?
11358
+ // Same as current Vitessce schema version?
11359
+ uid: z.string(),
11360
+ title: z.string().optional(),
11361
+ description: z.string().optional(),
11362
+ descriptionType: z.enum(["text", "markdown"]).optional(),
11363
+ frames: z.array(annotationFrameObj)
11364
+ });
11196
11365
  class PluginViewType {
11197
11366
  name;
11198
11367
  component;
@@ -18426,7 +18595,7 @@ const { tss } = createTss({
18426
18595
  },
18427
18596
  "usePlugin": useMuiThemeStyleOverridesPlugin
18428
18597
  });
18429
- const useStyles$z = tss.create({});
18598
+ const useStyles$A = tss.create({});
18430
18599
  function createChainedFunction$1(...funcs) {
18431
18600
  return funcs.reduce((acc, func) => {
18432
18601
  if (func == null) {
@@ -19537,7 +19706,7 @@ var values$5 = Object.values || function(obj) {
19537
19706
  return obj[k];
19538
19707
  });
19539
19708
  };
19540
- var defaultProps$Q = {
19709
+ var defaultProps$R = {
19541
19710
  component: "div",
19542
19711
  childFactory: function childFactory(child) {
19543
19712
  return child;
@@ -19664,7 +19833,7 @@ TransitionGroup.propTypes = {
19664
19833
  */
19665
19834
  childFactory: _pt.func
19666
19835
  };
19667
- TransitionGroup.defaultProps = defaultProps$Q;
19836
+ TransitionGroup.defaultProps = defaultProps$R;
19668
19837
  const UNINITIALIZED = {};
19669
19838
  function useLazyRef(init2, initArg) {
19670
19839
  const ref2 = React.useRef(UNINITIALIZED);
@@ -45818,6 +45987,12 @@ InputAdornment$1.propTypes = {
45818
45987
  const AddIcon = createSvgIcon(/* @__PURE__ */ jsx$1("path", {
45819
45988
  d: "M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6z"
45820
45989
  }), "Add");
45990
+ const ArrowRight = createSvgIcon(/* @__PURE__ */ jsx$1("path", {
45991
+ d: "m10 17 5-5-5-5z"
45992
+ }), "ArrowRight");
45993
+ const ArrowLeft = createSvgIcon(/* @__PURE__ */ jsx$1("path", {
45994
+ d: "m14 7-5 5 5 5z"
45995
+ }), "ArrowLeft");
45821
45996
  const CenterFocusStrong = createSvgIcon(/* @__PURE__ */ jsx$1("path", {
45822
45997
  d: "M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4m-7 7H3v4c0 1.1.9 2 2 2h4v-2H5zM5 5h4V3H5c-1.1 0-2 .9-2 2v4h2zm14-2h-4v2h4v4h2V5c0-1.1-.9-2-2-2m0 16h-4v2h4c1.1 0 2-.9 2-2v-4h-2z"
45823
45998
  }), "CenterFocusStrong");
@@ -72714,7 +72889,7 @@ const Popper = React.forwardRef((props, ref2) => {
72714
72889
  }, [handlePopperRef]);
72715
72890
  React.useImperativeHandle(popperRefProp, () => popperRef.current, []);
72716
72891
  const [exited, setExited] = React.useState(true);
72717
- const { theme } = useStyles$z();
72892
+ const { theme } = useStyles$A();
72718
72893
  const rtlPlacement = flipPlacement(initialPlacement, theme);
72719
72894
  const [placement, setPlacement] = React.useState(rtlPlacement);
72720
72895
  React.useEffect(() => {
@@ -73448,7 +73623,7 @@ var VariableSizeList$1 = /* @__PURE__ */ createListComponent({
73448
73623
  });
73449
73624
  const VariableSizeList = VariableSizeList$1;
73450
73625
  const LIST_ROW_HEIGHT$1 = 48;
73451
- const useStyles$y = makeStyles()(() => ({
73626
+ const useStyles$z = makeStyles()(() => ({
73452
73627
  searchInput: {
73453
73628
  lineHeight: "initial",
73454
73629
  height: "auto !important"
@@ -73488,7 +73663,7 @@ const ListboxComponent$1 = React__default.forwardRef((props, ref2) => {
73488
73663
  });
73489
73664
  function SimpleAutocomplete(props) {
73490
73665
  const { getMatches, onChange, textInputLabel = "Enter a gene", getItemLabel = (option) => option.label } = props;
73491
- const { classes: classes2 } = useStyles$y();
73666
+ const { classes: classes2 } = useStyles$z();
73492
73667
  const [potentialItems, setPotentialItems] = useState([]);
73493
73668
  const handleInputChange = async (_event, value2) => {
73494
73669
  if (!value2) {
@@ -83038,7 +83213,7 @@ function defaultUrlTransform(value2) {
83038
83213
  }
83039
83214
  return "";
83040
83215
  }
83041
- const useStyles$x = makeStyles()((theme) => ({
83216
+ const useStyles$y = makeStyles()((theme) => ({
83042
83217
  description: {
83043
83218
  "& p, details, table": {
83044
83219
  fontSize: "80%",
@@ -83075,7 +83250,7 @@ const useStyles$x = makeStyles()((theme) => ({
83075
83250
  }));
83076
83251
  function Description(props) {
83077
83252
  const { description: description2, metadata: metadata2, descriptionType } = props;
83078
- const { classes: classes2 } = useStyles$x();
83253
+ const { classes: classes2 } = useStyles$y();
83079
83254
  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)] });
83080
83255
  }
83081
83256
  function DescriptionSubscriber(props) {
@@ -93697,7 +93872,7 @@ const Tooltip$2 = /* @__PURE__ */ React.forwardRef((props, ref2) => {
93697
93872
  }, extraProps), getChildren());
93698
93873
  });
93699
93874
  const nodeHeight = 32;
93700
- const useStyles$w = makeStyles()((theme) => ({
93875
+ const useStyles$x = makeStyles()((theme) => ({
93701
93876
  setsManager: {
93702
93877
  position: "relative",
93703
93878
  width: "100%",
@@ -93864,7 +94039,7 @@ const useStyles$w = makeStyles()((theme) => ({
93864
94039
  }));
93865
94040
  function SetsManagerTreeGlobalStyles(props) {
93866
94041
  const { classes: classes2 } = props;
93867
- const { theme } = useStyles$z();
94042
+ const { theme } = useStyles$A();
93868
94043
  return jsx$1(ScopedGlobalStyles, { parentClassName: classes2.setsManagerTree, styles: {
93869
94044
  ".rc-tree": {
93870
94045
  paddingLeft: "0",
@@ -94205,7 +94380,7 @@ function HelpTooltipGlobalStyles(props) {
94205
94380
  }
94206
94381
  function PopoverGlobalStyles(props) {
94207
94382
  const { classes: classes2 } = props;
94208
- const { theme } = useStyles$z();
94383
+ const { theme } = useStyles$A();
94209
94384
  return jsx$1(ScopedGlobalStyles, { parentClassName: classes2.popover, styles: {
94210
94385
  ".rc-tooltip-inner": {
94211
94386
  boxSizing: "border-box",
@@ -98889,7 +99064,7 @@ function NamedSetNodeStatic(props) {
98889
99064
  const onClick = level === 0 && !expanded ? () => onCheckLevel(nodeKey, nextLevelToCheck) : () => onNodeView(path2);
98890
99065
  const tooltipProps = disableTooltip ? { visible: false } : {};
98891
99066
  const popoverMenuConfig = makeNodeViewMenuConfig(props);
98892
- const { classes: classes2 } = useStyles$w();
99067
+ const { classes: classes2 } = useStyles$x();
98893
99068
  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 })] });
98894
99069
  }
98895
99070
  function NamedSetNodeEditing(props) {
@@ -98901,7 +99076,7 @@ function NamedSetNodeEditing(props) {
98901
99076
  onNodeSetName(path2, currentTitle, true);
98902
99077
  }
98903
99078
  }
98904
- const { classes: classes2 } = useStyles$w();
99079
+ const { classes: classes2 } = useStyles$x();
98905
99080
  return jsxs("span", { className: classes2.titleButtonWithInput, children: [jsx$1("input", {
98906
99081
  // eslint-disable-next-line jsx-a11y/no-autofocus
98907
99082
  autoFocus: true,
@@ -98927,7 +99102,7 @@ function LevelsButtons(props) {
98927
99102
  onCheckLevel(nodeKey, newLevel);
98928
99103
  }
98929
99104
  }
98930
- const { classes: classes2 } = useStyles$w();
99105
+ const { classes: classes2 } = useStyles$x();
98931
99106
  return jsx$1("div", { className: classes2.levelButtonsContainer, children: range$e(1, height2 + 1).map((i2) => {
98932
99107
  const isChecked = isEqual$3(path2, checkedLevelPath) && i2 === checkedLevelIndex;
98933
99108
  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);
@@ -99011,7 +99186,7 @@ let TreeNode$1 = class TreeNode extends TreeNode$2 {
99011
99186
  };
99012
99187
  function PlusButton(props) {
99013
99188
  const { datatype, onError, onImportTree, onCreateLevelZeroNode, importable, editable } = props;
99014
- const { classes: classes2 } = useStyles$w();
99189
+ const { classes: classes2 } = useStyles$x();
99015
99190
  const onImport = useCallback((importHandler, mimeType) => () => {
99016
99191
  const uploadInputNode = document.createElement("input");
99017
99192
  uploadInputNode.setAttribute("type", "file");
@@ -99128,7 +99303,7 @@ function SetsManager(props) {
99128
99303
  }, onNodeCheckNewName, onNodeSetIsEditing: setIsEditingNodeName, onNodeRemove, onExportLevelZeroNodeJSON, onExportLevelZeroNodeTabular, onExportSetJSON, disableTooltip: isDragging, onDragStart: () => setIsDragging(true), onDragEnd: () => setIsDragging(false), children: renderTreeNodes(node2.children, readOnly, newPath) }, pathToKey$1(newPath));
99129
99304
  });
99130
99305
  }
99131
- const { classes: classes2 } = useStyles$w();
99306
+ const { classes: classes2 } = useStyles$x();
99132
99307
  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) => {
99133
99308
  const { eventKey: dropKey } = info2.node.props;
99134
99309
  const { eventKey: dragKey } = info2.dragNode.props;
@@ -111499,6 +111674,12 @@ function multiply$4(out, a2, b2) {
111499
111674
  out[2] = a2[2] * b2[2];
111500
111675
  return out;
111501
111676
  }
111677
+ function distance$3(a2, b2) {
111678
+ var x2 = b2[0] - a2[0];
111679
+ var y2 = b2[1] - a2[1];
111680
+ var z2 = b2[2] - a2[2];
111681
+ return Math.hypot(x2, y2, z2);
111682
+ }
111502
111683
  function squaredLength$2(a2) {
111503
111684
  var x2 = a2[0];
111504
111685
  var y2 = a2[1];
@@ -111622,6 +111803,7 @@ function angle$3(a2, b2) {
111622
111803
  }
111623
111804
  var sub$1 = subtract$1;
111624
111805
  var mul$1 = multiply$4;
111806
+ var dist$1 = distance$3;
111625
111807
  var len = length$5;
111626
111808
  var sqrLen = squaredLength$2;
111627
111809
  (function() {
@@ -126092,7 +126274,7 @@ function noop$9() {
126092
126274
  const getCursor$1 = ({
126093
126275
  isDragging
126094
126276
  }) => isDragging ? "grabbing" : "grab";
126095
- const defaultProps$P = {
126277
+ const defaultProps$Q = {
126096
126278
  id: "",
126097
126279
  width: "100%",
126098
126280
  height: "100%",
@@ -126223,7 +126405,7 @@ class Deck {
126223
126405
  this._lastPointerDownInfo = pickedInfo.result[0] || pickedInfo.emptyInfo;
126224
126406
  });
126225
126407
  this.props = {
126226
- ...defaultProps$P,
126408
+ ...defaultProps$Q,
126227
126409
  ...props
126228
126410
  };
126229
126411
  props = this.props;
@@ -126725,7 +126907,7 @@ class Deck {
126725
126907
  metrics.gpuMemory = memoryStats.get("GPU Memory").count;
126726
126908
  }
126727
126909
  }
126728
- _defineProperty$3(Deck, "defaultProps", defaultProps$P);
126910
+ _defineProperty$3(Deck, "defaultProps", defaultProps$Q);
126729
126911
  _defineProperty$3(Deck, "VERSION", deckGlobal.VERSION);
126730
126912
  class ShaderAttribute {
126731
126913
  constructor(dataColumn, opts2) {
@@ -129480,7 +129662,7 @@ const areViewportsEqual = memoize$3(({
129480
129662
  return oldViewport.equals(viewport2);
129481
129663
  });
129482
129664
  let pickingColorCache = new Uint8ClampedArray(0);
129483
- const defaultProps$O = {
129665
+ const defaultProps$P = {
129484
129666
  data: {
129485
129667
  type: "data",
129486
129668
  value: EMPTY_ARRAY$1,
@@ -130355,7 +130537,7 @@ class Layer extends Component {
130355
130537
  this.setNeedsUpdate();
130356
130538
  }
130357
130539
  }
130358
- _defineProperty$3(Layer, "defaultProps", defaultProps$O);
130540
+ _defineProperty$3(Layer, "defaultProps", defaultProps$P);
130359
130541
  _defineProperty$3(Layer, "layerName", "Layer");
130360
130542
  const TRACE_RENDER_LAYERS = "compositeLayer.renderLayers";
130361
130543
  class CompositeLayer extends Layer {
@@ -131514,7 +131696,7 @@ const parameters = {
131514
131696
  blendEquation: [32774, 32774],
131515
131697
  depthTest: false
131516
131698
  };
131517
- const defaultProps$N = {
131699
+ const defaultProps$O = {
131518
131700
  getFilterValue: {
131519
131701
  type: "accessor",
131520
131702
  value: 0
@@ -131690,8 +131872,126 @@ class DataFilterExtension extends LayerExtension {
131690
131872
  }
131691
131873
  }
131692
131874
  }
131693
- _defineProperty$3(DataFilterExtension, "defaultProps", defaultProps$N);
131875
+ _defineProperty$3(DataFilterExtension, "defaultProps", defaultProps$O);
131694
131876
  _defineProperty$3(DataFilterExtension, "extensionName", "DataFilterExtension");
131877
+ const dashShaders = {
131878
+ inject: {
131879
+ "vs:#decl": "\nattribute vec2 instanceDashArrays;\nattribute float instanceDashOffsets;\nvarying vec2 vDashArray;\nvarying float vDashOffset;\n",
131880
+ "vs:#main-end": "\nvDashArray = instanceDashArrays;\nvDashOffset = instanceDashOffsets / width.x;\n",
131881
+ "fs:#decl": "\nuniform float dashAlignMode;\nuniform float capType;\nuniform bool dashGapPickable;\nvarying vec2 vDashArray;\nvarying float vDashOffset;\n\nfloat round(float x) {\n return floor(x + 0.5);\n}\n",
131882
+ "fs:#main-start": "\n float solidLength = vDashArray.x;\n float gapLength = vDashArray.y;\n float unitLength = solidLength + gapLength;\n\n float offset;\n\n if (unitLength > 0.0) {\n if (dashAlignMode == 0.0) {\n offset = vDashOffset;\n } else {\n unitLength = vPathLength / round(vPathLength / unitLength);\n offset = solidLength / 2.0;\n }\n\n float unitOffset = mod(clamp(vPathPosition.y, 0.0, vPathLength) + offset, unitLength);\n\n if (gapLength > 0.0 && unitOffset > solidLength) {\n if (capType <= 0.5) {\n if (!(dashGapPickable && picking_uActive)) {\n discard;\n }\n } else {\n // caps are rounded, test the distance to solid ends\n float distToEnd = length(vec2(\n min(unitOffset - solidLength, unitLength - unitOffset),\n vPathPosition.x\n ));\n if (distToEnd > 1.0) {\n if (!(dashGapPickable && picking_uActive)) {\n discard;\n }\n }\n }\n }\n }\n"
131883
+ }
131884
+ };
131885
+ const offsetShaders = {
131886
+ inject: {
131887
+ "vs:#decl": "\nattribute float instanceOffsets;\n",
131888
+ "vs:DECKGL_FILTER_SIZE": "\n float offsetWidth = abs(instanceOffsets * 2.0) + 1.0;\n size *= offsetWidth;\n",
131889
+ "vCornerOffset = offsetVec;": "\n float offsetWidth = abs(instanceOffsets * 2.0) + 1.0;\n vec2 offsetCenter = -instanceOffsets * (isCap ? perp : miterVec * miterSize) * 2.0;\n vCornerOffset = vCornerOffset * offsetWidth - offsetCenter;\n",
131890
+ "fs:#main-start": "\n float isInside;\n isInside = step(-1.0, vPathPosition.x) * step(vPathPosition.x, 1.0);\n if (isInside == 0.0) {\n discard;\n }\n"
131891
+ }
131892
+ };
131893
+ const defaultProps$N = {
131894
+ getDashArray: {
131895
+ type: "accessor",
131896
+ value: [0, 0]
131897
+ },
131898
+ getOffset: {
131899
+ type: "accessor",
131900
+ value: 0
131901
+ },
131902
+ dashJustified: false,
131903
+ dashGapPickable: false
131904
+ };
131905
+ class PathStyleExtension extends LayerExtension {
131906
+ constructor({
131907
+ dash: dash2 = false,
131908
+ offset: offset2 = false,
131909
+ highPrecisionDash = false
131910
+ } = {}) {
131911
+ super({
131912
+ dash: dash2 || highPrecisionDash,
131913
+ offset: offset2,
131914
+ highPrecisionDash
131915
+ });
131916
+ }
131917
+ isEnabled(layer) {
131918
+ return "pathTesselator" in layer.state;
131919
+ }
131920
+ getShaders(extension2) {
131921
+ if (!extension2.isEnabled(this)) {
131922
+ return null;
131923
+ }
131924
+ let result = {};
131925
+ if (extension2.opts.dash) {
131926
+ result = mergeShaders(result, dashShaders);
131927
+ }
131928
+ if (extension2.opts.offset) {
131929
+ result = mergeShaders(result, offsetShaders);
131930
+ }
131931
+ return result;
131932
+ }
131933
+ initializeState(context2, extension2) {
131934
+ const attributeManager = this.getAttributeManager();
131935
+ if (!attributeManager || !extension2.isEnabled(this)) {
131936
+ return;
131937
+ }
131938
+ if (extension2.opts.dash) {
131939
+ attributeManager.addInstanced({
131940
+ instanceDashArrays: {
131941
+ size: 2,
131942
+ accessor: "getDashArray"
131943
+ }
131944
+ });
131945
+ }
131946
+ if (extension2.opts.highPrecisionDash) {
131947
+ attributeManager.addInstanced({
131948
+ instanceDashOffsets: {
131949
+ size: 1,
131950
+ accessor: "getPath",
131951
+ transform: extension2.getDashOffsets.bind(this)
131952
+ }
131953
+ });
131954
+ }
131955
+ if (extension2.opts.offset) {
131956
+ attributeManager.addInstanced({
131957
+ instanceOffsets: {
131958
+ size: 1,
131959
+ accessor: "getOffset"
131960
+ }
131961
+ });
131962
+ }
131963
+ }
131964
+ updateState(params2, extension2) {
131965
+ if (!extension2.isEnabled(this)) {
131966
+ return;
131967
+ }
131968
+ const uniforms = {};
131969
+ if (extension2.opts.dash) {
131970
+ uniforms.dashAlignMode = this.props.dashJustified ? 1 : 0;
131971
+ uniforms.dashGapPickable = Boolean(this.props.dashGapPickable);
131972
+ }
131973
+ this.state.model.setUniforms(uniforms);
131974
+ }
131975
+ getDashOffsets(path2) {
131976
+ const result = [0];
131977
+ const positionSize = this.props.positionFormat === "XY" ? 2 : 3;
131978
+ const isNested2 = Array.isArray(path2[0]);
131979
+ const geometrySize = isNested2 ? path2.length : path2.length / positionSize;
131980
+ let p;
131981
+ let prevP;
131982
+ for (let i2 = 0; i2 < geometrySize - 1; i2++) {
131983
+ p = isNested2 ? path2[i2] : path2.slice(i2 * positionSize, i2 * positionSize + positionSize);
131984
+ p = this.projectPosition(p);
131985
+ if (i2 > 0) {
131986
+ result[i2] = result[i2 - 1] + dist$1(prevP, p);
131987
+ }
131988
+ prevP = p;
131989
+ }
131990
+ return result;
131991
+ }
131992
+ }
131993
+ _defineProperty$3(PathStyleExtension, "defaultProps", defaultProps$N);
131994
+ _defineProperty$3(PathStyleExtension, "extensionName", "PathStyleExtension");
131695
131995
  const defaultProps$M = {
131696
131996
  clipBounds: [0, 0, 1, 1]
131697
131997
  };
@@ -205110,22 +205410,22 @@ async function getDecoder(fileDirectory) {
205110
205410
  const Decoder = await importFn();
205111
205411
  return new Decoder(fileDirectory);
205112
205412
  }
205113
- addDecoder([void 0, 1], () => import("./raw-akfNT_LS.js").then((m2) => m2.default));
205114
- addDecoder(5, () => import("./lzw-63HBqTiG.js").then((m2) => m2.default));
205413
+ addDecoder([void 0, 1], () => import("./raw-CR3kVxsB.js").then((m2) => m2.default));
205414
+ addDecoder(5, () => import("./lzw-CIpLYaya.js").then((m2) => m2.default));
205115
205415
  addDecoder(6, () => {
205116
205416
  throw new Error("old style JPEG compression is not supported.");
205117
205417
  });
205118
- addDecoder(7, () => import("./jpeg-g9i0I1mK.js").then((m2) => m2.default));
205119
- addDecoder([8, 32946], () => import("./deflate-D1RcEgtg.js").then((m2) => m2.default));
205120
- addDecoder(32773, () => import("./packbits-i970u6wg.js").then((m2) => m2.default));
205418
+ addDecoder(7, () => import("./jpeg-DXSq15HV.js").then((m2) => m2.default));
205419
+ addDecoder([8, 32946], () => import("./deflate-BKJk_mLF.js").then((m2) => m2.default));
205420
+ addDecoder(32773, () => import("./packbits-Cf0TB9M_.js").then((m2) => m2.default));
205121
205421
  addDecoder(
205122
205422
  34887,
205123
- () => import("./lerc-B4lSBwWt.js").then(async (m2) => {
205423
+ () => import("./lerc-uTsWAc7Y.js").then(async (m2) => {
205124
205424
  await m2.zstd.init();
205125
205425
  return m2;
205126
205426
  }).then((m2) => m2.default)
205127
205427
  );
205128
- addDecoder(50001, () => import("./webimage-CA4EiJGO.js").then((m2) => m2.default));
205428
+ addDecoder(50001, () => import("./webimage-Dp3HLfwF.js").then((m2) => m2.default));
205129
205429
  function copyNewSize(array2, width2, height2, samplesPerPixel = 1) {
205130
205430
  return new (Object.getPrototypeOf(array2)).constructor(width2 * height2 * samplesPerPixel);
205131
205431
  }
@@ -215621,7 +215921,7 @@ function padColors({ colors: colors2, channelsVisible }) {
215621
215921
  ).reduce((acc, val) => acc.concat(val), []);
215622
215922
  return paddedColors;
215623
215923
  }
215624
- const defaultProps$3$1 = {
215924
+ const defaultProps$3$2 = {
215625
215925
  colors: { type: "array", value: null, compare: true },
215626
215926
  opacity: { type: "number", value: 1, compare: true },
215627
215927
  transparentColor: { type: "array", value: null, compare: true },
@@ -215638,9 +215938,9 @@ const ColorPaletteExtension = class extends LayerExtension {
215638
215938
  const {
215639
215939
  colors: colors2,
215640
215940
  channelsVisible,
215641
- opacity: opacity2 = defaultProps$3$1.opacity.value,
215642
- transparentColor = defaultProps$3$1.transparentColor.value,
215643
- useTransparentColor = defaultProps$3$1.useTransparentColor.value
215941
+ opacity: opacity2 = defaultProps$3$2.opacity.value,
215942
+ transparentColor = defaultProps$3$2.transparentColor.value,
215943
+ useTransparentColor = defaultProps$3$2.useTransparentColor.value
215644
215944
  } = this.props;
215645
215945
  const paddedColors = padColors({
215646
215946
  channelsVisible: channelsVisible || this.selections.map(() => true),
@@ -215656,7 +215956,7 @@ const ColorPaletteExtension = class extends LayerExtension {
215656
215956
  }
215657
215957
  };
215658
215958
  ColorPaletteExtension.extensionName = "ColorPaletteExtension";
215659
- ColorPaletteExtension.defaultProps = defaultProps$3$1;
215959
+ ColorPaletteExtension.defaultProps = defaultProps$3$2;
215660
215960
  const fs$4 = `// lens bounds for ellipse
215661
215961
  uniform float majorLensAxis;
215662
215962
  uniform float minorLensAxis;
@@ -216958,7 +217258,7 @@ const MultiscaleImageLayer = class extends CompositeLayer {
216958
217258
  };
216959
217259
  MultiscaleImageLayer.layerName = "MultiscaleImageLayer";
216960
217260
  MultiscaleImageLayer.defaultProps = defaultProps$4;
216961
- const defaultProps$3 = {
217261
+ const defaultProps$3$1 = {
216962
217262
  pickable: { type: "boolean", value: true, compare: true },
216963
217263
  loader: {
216964
217264
  type: "object",
@@ -217045,7 +217345,7 @@ const OverviewLayer = class extends CompositeLayer {
217045
217345
  }
217046
217346
  };
217047
217347
  OverviewLayer.layerName = "OverviewLayer";
217048
- OverviewLayer.defaultProps = defaultProps$3;
217348
+ OverviewLayer.defaultProps = defaultProps$3$1;
217049
217349
  function getPosition$1(boundingBox, position2, length2) {
217050
217350
  const viewWidth2 = boundingBox[2][0] - boundingBox[0][0];
217051
217351
  const viewHeight2 = boundingBox[2][1] - boundingBox[0][1];
@@ -218058,7 +218358,7 @@ function padWithDefault$1(arr, defaultValue2, padWidth) {
218058
218358
  }
218059
218359
  return newArr;
218060
218360
  }
218061
- const defaultProps$2 = {
218361
+ const defaultProps$3 = {
218062
218362
  hoveredCell: { type: "number", value: null, compare: true },
218063
218363
  // We do not want to deep-compare cellColorData,
218064
218364
  // as it is potentially a TypedArray with millions of elements.
@@ -218175,7 +218475,7 @@ let BitmaskLayer$1 = class BitmaskLayer extends XRLayer {
218175
218475
  }
218176
218476
  };
218177
218477
  BitmaskLayer$1.layerName = "BitmaskLayer";
218178
- BitmaskLayer$1.defaultProps = defaultProps$2;
218478
+ BitmaskLayer$1.defaultProps = defaultProps$3;
218179
218479
  const vs = `
218180
218480
  #define SHADER_NAME bitmask-layer-vertex-shader
218181
218481
 
@@ -218517,7 +218817,7 @@ function isEqualShallow(prevArr, nextArr) {
218517
218817
  }
218518
218818
  return prevArr === nextArr;
218519
218819
  }
218520
- const defaultProps$1 = {
218820
+ const defaultProps$2 = {
218521
218821
  channelStrokeWidths: { type: "array", value: null, compare: true },
218522
218822
  channelsFilled: { type: "array", value: null, compare: true },
218523
218823
  channelOpacities: { type: "array", value: null, compare: true },
@@ -218781,7 +219081,7 @@ class BitmaskLayer2 extends XRLayer {
218781
219081
  }
218782
219082
  }
218783
219083
  BitmaskLayer2.layerName = "BitmaskLayer";
218784
- BitmaskLayer2.defaultProps = defaultProps$1;
219084
+ BitmaskLayer2.defaultProps = defaultProps$2;
218785
219085
  var isNumeric$2 = /^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i, mathceil = Math.ceil, mathfloor$1 = Math.floor, bignumberError = "[BigNumber Error] ", tooManyDigits = bignumberError + "Number primitive has more than 15 significant digits: ", BASE$1 = 1e14, LOG_BASE$1 = 14, MAX_SAFE_INTEGER$3 = 9007199254740991, POWS_TEN = [1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13], SQRT_BASE = 1e7, MAX$1 = 1e9;
218786
219086
  function clone$3(configObject) {
218787
219087
  var div2, convertBase2, parseNumeric, P2 = BigNumber2.prototype = { constructor: BigNumber2, toString: null, valueOf: null }, ONE = new BigNumber2(1), DECIMAL_PLACES = 20, ROUNDING_MODE = 4, TO_EXP_NEG = -7, TO_EXP_POS = 21, MIN_EXP = -1e7, MAX_EXP = 1e7, CRYPTO = false, MODULO_MODE = 1, POW_PRECISION = 0, FORMAT = {
@@ -221724,7 +222024,7 @@ function ringArea(coords) {
221724
222024
  return total * FACTOR;
221725
222025
  }
221726
222026
  const DEFAULT_THRESHOLD = 1;
221727
- const defaultProps = {
222027
+ const defaultProps$1 = {
221728
222028
  // for text
221729
222029
  obsSetPath: { type: "object" },
221730
222030
  sampleSetPath: { type: "object" },
@@ -221898,8 +222198,1078 @@ class ContourLayerWithText extends ContourLayer {
221898
222198
  }
221899
222199
  }
221900
222200
  ContourLayerWithText.layerName = "ContourLayerWithText";
221901
- ContourLayerWithText.defaultProps = defaultProps;
221902
- const useStyles$v = makeStyles()(() => ({
222201
+ ContourLayerWithText.defaultProps = defaultProps$1;
222202
+ const ARROW_SCREEN_PX = 14;
222203
+ const LABEL_FONT_FAMILY$1 = '"Roboto", "Helvetica", "Arial", sans-serif';
222204
+ const LABEL_FONT_WEIGHT = "bold";
222205
+ const LABEL_FONT_SIZE = 14;
222206
+ const RATIO_CLUSTER = 0.1;
222207
+ const RATIO_DOT = 0.3;
222208
+ const RATIO_GEO_FADE = 0.55;
222209
+ const RATIO_TIPS = 0.7;
222210
+ const RATIO_LABEL = 0.55;
222211
+ const RATIO_LABEL_FULL = 0.75;
222212
+ function textBgColor(strokeColor) {
222213
+ const [r3, g2, b2] = strokeColor;
222214
+ const lum = (0.299 * r3 + 0.587 * g2 + 0.114 * b2) / 255;
222215
+ return lum > 0.45 ? [20, 20, 20, 210] : [245, 245, 245, 210];
222216
+ }
222217
+ function parseDashArray(strokeDashArray) {
222218
+ if (!strokeDashArray || strokeDashArray === "none")
222219
+ return null;
222220
+ const nums = strokeDashArray.trim().split(/\s+/).map(Number).filter((n3) => !Number.isNaN(n3) && n3 >= 0);
222221
+ return nums.length >= 2 ? [nums[0], nums[1]] : null;
222222
+ }
222223
+ function computeEllipsePolygon(cx, cy, rx, ry, numSegments = 64) {
222224
+ const pts = [];
222225
+ for (let i2 = 0; i2 < numSegments; i2++) {
222226
+ const angle2 = 2 * Math.PI * i2 / numSegments;
222227
+ pts.push([cx + rx * Math.cos(angle2), cy + ry * Math.sin(angle2)]);
222228
+ }
222229
+ return pts;
222230
+ }
222231
+ function createPreviewLayer(inProgress, hoverCoord, strokeColor = [255, 255, 0]) {
222232
+ if (!inProgress || inProgress.vertices.length === 0)
222233
+ return null;
222234
+ const { type: type2, vertices } = inProgress;
222235
+ const base2 = { coordinateSystem: COORDINATE_SYSTEM.CARTESIAN };
222236
+ const previewColor = [...strokeColor, 160];
222237
+ const cursor2 = hoverCoord ?? vertices[vertices.length - 1];
222238
+ if (type2 === "rectangle" && vertices.length === 1) {
222239
+ const [ax, ay] = vertices[0];
222240
+ const [bx, by] = cursor2;
222241
+ const path2 = [[ax, ay], [bx, ay], [bx, by], [ax, by], [ax, ay]];
222242
+ return new PathLayer({
222243
+ ...base2,
222244
+ id: "annotation-preview-rectangle",
222245
+ data: [{ path: path2 }],
222246
+ getPath: (d) => d.path,
222247
+ getColor: previewColor,
222248
+ getWidth: 1,
222249
+ widthUnits: "pixels",
222250
+ extensions: [new PathStyleExtension({ dash: true })],
222251
+ getDashArray: [8, 4]
222252
+ });
222253
+ }
222254
+ if (type2 === "line" && vertices.length === 1) {
222255
+ const path2 = [vertices[0], cursor2];
222256
+ return new PathLayer({
222257
+ ...base2,
222258
+ id: "annotation-preview-line",
222259
+ data: [{ path: path2 }],
222260
+ getPath: (d) => d.path,
222261
+ getColor: previewColor,
222262
+ getWidth: 1,
222263
+ widthUnits: "pixels",
222264
+ extensions: [new PathStyleExtension({ dash: true })],
222265
+ getDashArray: [8, 4]
222266
+ });
222267
+ }
222268
+ if (type2 === "ellipse" && vertices.length === 1) {
222269
+ const [cx, cy] = vertices[0];
222270
+ const [ex, ey] = cursor2;
222271
+ const rx = Math.abs(ex - cx);
222272
+ const ry = Math.abs(ey - cy);
222273
+ if (rx < 1 && ry < 1)
222274
+ return null;
222275
+ const pts = computeEllipsePolygon(cx, cy, Math.max(rx, 1), Math.max(ry, 1));
222276
+ const path2 = [...pts, pts[0]];
222277
+ return new PathLayer({
222278
+ ...base2,
222279
+ id: "annotation-preview-ellipse",
222280
+ data: [{ path: path2 }],
222281
+ getPath: (d) => d.path,
222282
+ getColor: previewColor,
222283
+ getWidth: 1,
222284
+ widthUnits: "pixels",
222285
+ extensions: [new PathStyleExtension({ dash: true })],
222286
+ getDashArray: [8, 4]
222287
+ });
222288
+ }
222289
+ if ((type2 === "polygon" || type2 === "polyline") && vertices.length >= 1) {
222290
+ const path2 = [...vertices, cursor2];
222291
+ if (type2 === "polygon" && vertices.length >= 2)
222292
+ path2.push(vertices[0]);
222293
+ return new PathLayer({
222294
+ ...base2,
222295
+ id: `annotation-preview-${type2}`,
222296
+ data: [{ path: path2 }],
222297
+ getPath: (d) => d.path,
222298
+ getColor: previewColor,
222299
+ getWidth: 1,
222300
+ widthUnits: "pixels",
222301
+ jointRounded: true,
222302
+ capRounded: true,
222303
+ extensions: [new PathStyleExtension({ dash: true })],
222304
+ getDashArray: [8, 4]
222305
+ });
222306
+ }
222307
+ return null;
222308
+ }
222309
+ function computeTickCap(tipX, tipY, dirX, dirY, dataSize) {
222310
+ const len2 = Math.sqrt(dirX * dirX + dirY * dirY);
222311
+ if (len2 === 0)
222312
+ return null;
222313
+ const ux = dirX / len2;
222314
+ const uy = dirY / len2;
222315
+ const px2 = -uy;
222316
+ const py2 = ux;
222317
+ const half = dataSize * 0.55;
222318
+ return [
222319
+ [tipX + px2 * half, tipY + py2 * half],
222320
+ [tipX - px2 * half, tipY - py2 * half]
222321
+ ];
222322
+ }
222323
+ function computeArrowhead(tipX, tipY, dirX, dirY, dataSize) {
222324
+ const len2 = Math.sqrt(dirX * dirX + dirY * dirY);
222325
+ if (len2 === 0)
222326
+ return null;
222327
+ const ux = dirX / len2;
222328
+ const uy = dirY / len2;
222329
+ const px2 = -uy;
222330
+ const py2 = ux;
222331
+ const halfBase = dataSize * 0.4;
222332
+ const bx = tipX - ux * dataSize;
222333
+ const by = tipY - uy * dataSize;
222334
+ return [
222335
+ [tipX, tipY],
222336
+ [bx + px2 * halfBase, by + py2 * halfBase],
222337
+ [bx - px2 * halfBase, by - py2 * halfBase]
222338
+ ];
222339
+ }
222340
+ function fmtCommas(n3) {
222341
+ return Math.round(n3).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
222342
+ }
222343
+ function getMeasurementLabel(shape2, physicalPixelSize, viewType = ViewType$1.SPATIAL) {
222344
+ const pps = physicalPixelSize;
222345
+ const px2 = pps?.x ?? 1;
222346
+ const py2 = pps?.y ?? 1;
222347
+ const unit2 = pps ? pps.unit ?? "" : null;
222348
+ const hasPhys = !!pps;
222349
+ const isEmbedding = viewType === ViewType$1.SCATTERPLOT;
222350
+ const fmtDist = (d) => {
222351
+ if (hasPhys)
222352
+ return `${d.toFixed(2)} ${unit2}`;
222353
+ if (isEmbedding)
222354
+ return d.toFixed(2);
222355
+ return `${fmtCommas(d)} px`;
222356
+ };
222357
+ const fmtArea = (a2) => {
222358
+ if (hasPhys)
222359
+ return `${a2.toFixed(2)} ${unit2}²`;
222360
+ if (isEmbedding)
222361
+ return a2.toFixed(2);
222362
+ return `${fmtCommas(a2)} px²`;
222363
+ };
222364
+ if (shape2.type === "line") {
222365
+ const dx = (shape2.x2 ?? 0) - (shape2.x1 ?? 0);
222366
+ const dy = (shape2.y2 ?? 0) - (shape2.y1 ?? 0);
222367
+ const dist2 = hasPhys ? Math.sqrt((dx * px2) ** 2 + (dy * py2) ** 2) : Math.sqrt(dx * dx + dy * dy);
222368
+ return fmtDist(dist2);
222369
+ }
222370
+ if (shape2.type === "rectangle") {
222371
+ return fmtArea(hasPhys ? Math.abs(shape2.width ?? 0) * px2 * Math.abs(shape2.height ?? 0) * py2 : Math.abs((shape2.width ?? 0) * (shape2.height ?? 0)));
222372
+ }
222373
+ if (shape2.type === "ellipse") {
222374
+ const rx = Math.abs(shape2.radiusX ?? 0);
222375
+ const ry = Math.abs(shape2.radiusY ?? 0);
222376
+ return fmtArea(hasPhys ? Math.PI * rx * px2 * ry * py2 : Math.PI * rx * ry);
222377
+ }
222378
+ if (shape2.type === "polygon" && shape2.points?.length >= 3) {
222379
+ const pts = shape2.points;
222380
+ let shoelace = 0;
222381
+ for (let i2 = 0; i2 < pts.length; i2++) {
222382
+ const [ax, ay] = pts[i2];
222383
+ const [bx, by] = pts[(i2 + 1) % pts.length];
222384
+ shoelace += (hasPhys ? ax * px2 : ax) * (hasPhys ? by * py2 : by) - (hasPhys ? bx * px2 : bx) * (hasPhys ? ay * py2 : ay);
222385
+ }
222386
+ return fmtArea(Math.abs(shoelace / 2));
222387
+ }
222388
+ if (shape2.type === "polyline" && shape2.points?.length >= 2) {
222389
+ let total = 0;
222390
+ for (let i2 = 0; i2 < shape2.points.length - 1; i2++) {
222391
+ const [ax, ay] = shape2.points[i2];
222392
+ const [bx, by] = shape2.points[i2 + 1];
222393
+ total += hasPhys ? Math.sqrt(((bx - ax) * px2) ** 2 + ((by - ay) * py2) ** 2) : Math.sqrt((bx - ax) ** 2 + (by - ay) ** 2);
222394
+ }
222395
+ return fmtDist(total);
222396
+ }
222397
+ return null;
222398
+ }
222399
+ function pickBelowPerp(ux, uy) {
222400
+ const rp = [uy, -ux];
222401
+ const lp = [-uy, ux];
222402
+ return rp[1] > lp[1] || rp[1] === lp[1] && rp[0] > lp[0] ? rp : lp;
222403
+ }
222404
+ function readableAngle(deg) {
222405
+ let a2 = deg;
222406
+ if (a2 > 90)
222407
+ a2 -= 180;
222408
+ if (a2 < -90)
222409
+ a2 += 180;
222410
+ return a2;
222411
+ }
222412
+ function getShapeCentroid(shape2) {
222413
+ if (shape2.type === "rectangle") {
222414
+ return [(shape2.x ?? 0) + (shape2.width ?? 0) / 2, (shape2.y ?? 0) + (shape2.height ?? 0) / 2];
222415
+ }
222416
+ if (shape2.type === "line") {
222417
+ return [((shape2.x1 ?? 0) + (shape2.x2 ?? 0)) / 2, ((shape2.y1 ?? 0) + (shape2.y2 ?? 0)) / 2];
222418
+ }
222419
+ if (shape2.type === "ellipse") {
222420
+ return [shape2.x1 ?? 0, shape2.y1 ?? 0];
222421
+ }
222422
+ if ((shape2.type === "polygon" || shape2.type === "polyline") && shape2.points?.length >= 1) {
222423
+ const mx = shape2.points.reduce((s2, p) => s2 + p[0], 0) / shape2.points.length;
222424
+ const my = shape2.points.reduce((s2, p) => s2 + p[1], 0) / shape2.points.length;
222425
+ return [mx, my];
222426
+ }
222427
+ return [0, 0];
222428
+ }
222429
+ function computeClusters(shapeList, zoom2, cellPx = 50) {
222430
+ const cellSize = cellPx / 2 ** Math.floor(zoom2);
222431
+ const cells = /* @__PURE__ */ new Map();
222432
+ shapeList.forEach(({ shape: shape2 }) => {
222433
+ const [cx, cy] = getShapeCentroid(shape2);
222434
+ const key2 = `${Math.floor(cx / cellSize)},${Math.floor(cy / cellSize)}`;
222435
+ if (!cells.has(key2)) {
222436
+ cells.set(key2, { sumX: 0, sumY: 0, count: 0, color: shape2.strokeColor ?? [255, 255, 255] });
222437
+ }
222438
+ const c2 = cells.get(key2);
222439
+ c2.sumX += cx;
222440
+ c2.sumY += cy;
222441
+ c2.count++;
222442
+ });
222443
+ return Array.from(cells.values()).map((c2) => ({
222444
+ position: [c2.sumX / c2.count, c2.sumY / c2.count, 0],
222445
+ count: c2.count,
222446
+ color: c2.color
222447
+ }));
222448
+ }
222449
+ function createAnnotationLayers(shapes = [], zoom2 = 0, selectedShapeUid = null, physicalPixelSize = null, authoredZoom = null, semanticZoom = true, viewType = ViewType$1.SPATIAL) {
222450
+ const selectionLayers = [];
222451
+ if (selectedShapeUid) {
222452
+ const sel = shapes.find((s2) => s2.uid === selectedShapeUid);
222453
+ if (sel) {
222454
+ const { uid: sUid, type: sType, strokeWidth: sStrokeWidth = 3 } = sel;
222455
+ const haloColor = [255, 220, 50, 70];
222456
+ const haloWidth = Math.max(sStrokeWidth + 8, 10);
222457
+ const base2 = { coordinateSystem: COORDINATE_SYSTEM.CARTESIAN };
222458
+ if (sType === "rectangle") {
222459
+ const { x: x2, y: y2, width: width2, height: height2 } = sel;
222460
+ selectionLayers.push(new PathLayer({
222461
+ ...base2,
222462
+ id: `annotation-select-${sUid}`,
222463
+ data: [{
222464
+ path: [
222465
+ [x2, y2],
222466
+ [x2 + width2, y2],
222467
+ [x2 + width2, y2 + height2],
222468
+ [x2, y2 + height2],
222469
+ [x2, y2]
222470
+ ]
222471
+ }],
222472
+ getPath: (d) => d.path,
222473
+ getColor: haloColor,
222474
+ getWidth: haloWidth,
222475
+ widthUnits: "pixels",
222476
+ jointRounded: true,
222477
+ capRounded: true
222478
+ }));
222479
+ }
222480
+ if (sType === "line") {
222481
+ const { x1: x12, y1: y12, x2, y2 } = sel;
222482
+ selectionLayers.push(new PathLayer({
222483
+ ...base2,
222484
+ id: `annotation-select-${sUid}`,
222485
+ data: [{ path: [[x12, y12], [x2, y2]] }],
222486
+ getPath: (d) => d.path,
222487
+ getColor: haloColor,
222488
+ getWidth: haloWidth,
222489
+ widthUnits: "pixels",
222490
+ capRounded: true
222491
+ }));
222492
+ }
222493
+ if (sType === "ellipse") {
222494
+ const polygon2 = computeEllipsePolygon(sel.x1, sel.y1, sel.radiusX, sel.radiusY);
222495
+ selectionLayers.push(new PathLayer({
222496
+ ...base2,
222497
+ id: `annotation-select-${sUid}`,
222498
+ data: [{ path: [...polygon2, polygon2[0]] }],
222499
+ getPath: (d) => d.path,
222500
+ getColor: haloColor,
222501
+ getWidth: haloWidth,
222502
+ widthUnits: "pixels",
222503
+ jointRounded: true
222504
+ }));
222505
+ }
222506
+ if (sType === "polygon" && sel.points?.length >= 3) {
222507
+ selectionLayers.push(new PathLayer({
222508
+ ...base2,
222509
+ id: `annotation-select-${sUid}`,
222510
+ data: [{ path: [...sel.points, sel.points[0]] }],
222511
+ getPath: (d) => d.path,
222512
+ getColor: haloColor,
222513
+ getWidth: haloWidth,
222514
+ widthUnits: "pixels",
222515
+ jointRounded: true
222516
+ }));
222517
+ }
222518
+ if (sType === "polyline" && sel.points?.length >= 2) {
222519
+ selectionLayers.push(new PathLayer({
222520
+ ...base2,
222521
+ id: `annotation-select-${sUid}`,
222522
+ data: [{ path: sel.points }],
222523
+ getPath: (d) => d.path,
222524
+ getColor: haloColor,
222525
+ getWidth: haloWidth,
222526
+ widthUnits: "pixels",
222527
+ jointRounded: true,
222528
+ capRounded: true
222529
+ }));
222530
+ }
222531
+ }
222532
+ }
222533
+ const layers = [];
222534
+ const dotShapes = [];
222535
+ const clusterDotShapes = [];
222536
+ shapes.forEach((shape2, i2) => {
222537
+ const { uid: uid2, type: type2, strokeColor = [255, 255, 255], strokeWidth = 3, strokeDashArray } = shape2;
222538
+ const isSelected = uid2 === selectedShapeUid;
222539
+ let shapeOpacity = 1;
222540
+ let tipsOpacity = 1;
222541
+ let labelOpacity = 0;
222542
+ if (isSelected || !semanticZoom || authoredZoom === null) {
222543
+ tipsOpacity = 1;
222544
+ labelOpacity = 1;
222545
+ } else {
222546
+ const zoomRatio = 2 ** (zoom2 - authoredZoom);
222547
+ if (zoomRatio < RATIO_CLUSTER) {
222548
+ clusterDotShapes.push({ shape: shape2 });
222549
+ return;
222550
+ }
222551
+ if (zoomRatio < RATIO_DOT) {
222552
+ dotShapes.push({ shape: shape2, alpha: 200 });
222553
+ return;
222554
+ }
222555
+ if (zoomRatio < RATIO_GEO_FADE) {
222556
+ const t4 = (zoomRatio - RATIO_DOT) / (RATIO_GEO_FADE - RATIO_DOT);
222557
+ shapeOpacity = t4;
222558
+ dotShapes.push({ shape: shape2, alpha: Math.round((1 - t4) * 200) });
222559
+ }
222560
+ tipsOpacity = zoomRatio >= RATIO_TIPS ? shapeOpacity : Math.max(0, (zoomRatio - RATIO_GEO_FADE) / (RATIO_TIPS - RATIO_GEO_FADE) * shapeOpacity);
222561
+ if (zoomRatio >= RATIO_LABEL_FULL) {
222562
+ labelOpacity = shapeOpacity;
222563
+ } else if (zoomRatio >= RATIO_LABEL) {
222564
+ labelOpacity = (zoomRatio - RATIO_LABEL) / (RATIO_LABEL_FULL - RATIO_LABEL) * shapeOpacity;
222565
+ }
222566
+ }
222567
+ const arrowDataSize = Math.max(ARROW_SCREEN_PX, strokeWidth * 4) / 2 ** zoom2;
222568
+ const base2 = { coordinateSystem: COORDINATE_SYSTEM.CARTESIAN, opacity: shapeOpacity };
222569
+ const tipsBase = { coordinateSystem: COORDINATE_SYSTEM.CARTESIAN, opacity: tipsOpacity };
222570
+ const labelBase = { coordinateSystem: COORDINATE_SYSTEM.CARTESIAN, opacity: labelOpacity };
222571
+ const labelBgProps = shape2.labelBackground ? {
222572
+ background: true,
222573
+ getBackgroundColor: textBgColor(strokeColor),
222574
+ backgroundPadding: [3, 1, 3, 2]
222575
+ } : {};
222576
+ const labelProps = { ...labelBgProps };
222577
+ const dashArray = parseDashArray(strokeDashArray);
222578
+ const dashExt = [new PathStyleExtension({ dash: true })];
222579
+ const getDash = dashArray ?? [0, 0];
222580
+ if (type2 === "rectangle") {
222581
+ const { x: x2, y: y2, width: width2, height: height2, fillColor = strokeColor, fillOpacity = 0 } = shape2;
222582
+ const filled = fillOpacity > 0;
222583
+ const rectPolygon = [[x2, y2], [x2 + width2, y2], [x2 + width2, y2 + height2], [x2, y2 + height2]];
222584
+ layers.push(new PolygonLayer({
222585
+ ...base2,
222586
+ id: `annotation-rect-${uid2}-${i2}`,
222587
+ data: [{ polygon: rectPolygon }],
222588
+ getPolygon: (d) => d.polygon,
222589
+ filled,
222590
+ stroked: true,
222591
+ getFillColor: filled ? [...fillColor, Math.round(fillOpacity * 255)] : [0, 0, 0, 0],
222592
+ getLineColor: strokeColor,
222593
+ getLineWidth: strokeWidth,
222594
+ lineWidthUnits: "pixels",
222595
+ ...dashArray ? {
222596
+ extensions: [new PathStyleExtension({ dash: true })],
222597
+ getDashArray: dashArray
222598
+ } : {}
222599
+ }));
222600
+ if (shape2.text && labelOpacity > 0) {
222601
+ layers.push(new TextLayer({
222602
+ ...labelBase,
222603
+ ...labelProps,
222604
+ id: `annotation-rect-text-${uid2}-${i2}`,
222605
+ data: [{ position: [x2, y2], text: shape2.text }],
222606
+ getPosition: (d) => d.position,
222607
+ getText: (d) => d.text,
222608
+ getColor: strokeColor,
222609
+ getSize: LABEL_FONT_SIZE,
222610
+ sizeUnits: "pixels",
222611
+ fontFamily: LABEL_FONT_FAMILY$1,
222612
+ fontWeight: LABEL_FONT_WEIGHT,
222613
+ getPixelOffset: [3, -7],
222614
+ getTextAnchor: "start",
222615
+ getAlignmentBaseline: "bottom"
222616
+ }));
222617
+ }
222618
+ }
222619
+ if (type2 === "line") {
222620
+ const { x1: x12, y1: y12, x2, y2, markerStart, markerEnd } = shape2;
222621
+ const dx = x2 - x12;
222622
+ const dy = y2 - y12;
222623
+ const len2 = Math.sqrt(dx * dx + dy * dy);
222624
+ const ux = len2 > 0 ? dx / len2 : 0;
222625
+ const uy = len2 > 0 ? dy / len2 : 0;
222626
+ const srcX = markerStart === "Arrow" ? x12 + ux * arrowDataSize : x12;
222627
+ const srcY = markerStart === "Arrow" ? y12 + uy * arrowDataSize : y12;
222628
+ const tgtX = markerEnd === "Arrow" ? x2 - ux * arrowDataSize : x2;
222629
+ const tgtY = markerEnd === "Arrow" ? y2 - uy * arrowDataSize : y2;
222630
+ layers.push(new PathLayer({
222631
+ ...base2,
222632
+ id: `annotation-line-${uid2}-${i2}`,
222633
+ extensions: [new PathStyleExtension({ dash: true })],
222634
+ getDashArray: dashArray ?? [0, 0],
222635
+ data: [{ path: [[srcX, srcY], [tgtX, tgtY]] }],
222636
+ getPath: (d) => d.path,
222637
+ getColor: strokeColor,
222638
+ getWidth: strokeWidth,
222639
+ widthUnits: "pixels",
222640
+ capRounded: true
222641
+ }));
222642
+ if (markerEnd === "Arrow" && tipsOpacity > 0) {
222643
+ const head = computeArrowhead(x2, y2, x2 - x12, y2 - y12, arrowDataSize);
222644
+ if (head) {
222645
+ layers.push(new PolygonLayer({
222646
+ ...tipsBase,
222647
+ id: `annotation-end-arrow-${uid2}-${i2}`,
222648
+ data: [{ polygon: head }],
222649
+ getPolygon: (d) => d.polygon,
222650
+ filled: true,
222651
+ stroked: false,
222652
+ getFillColor: strokeColor
222653
+ }));
222654
+ }
222655
+ }
222656
+ if (markerStart === "Arrow" && tipsOpacity > 0) {
222657
+ const head = computeArrowhead(x12, y12, x12 - x2, y12 - y2, arrowDataSize);
222658
+ if (head) {
222659
+ layers.push(new PolygonLayer({
222660
+ ...tipsBase,
222661
+ id: `annotation-start-arrow-${uid2}-${i2}`,
222662
+ data: [{ polygon: head }],
222663
+ getPolygon: (d) => d.polygon,
222664
+ filled: true,
222665
+ stroked: false,
222666
+ getFillColor: strokeColor
222667
+ }));
222668
+ }
222669
+ }
222670
+ if (markerEnd === "Tick" && tipsOpacity > 0) {
222671
+ const tick2 = computeTickCap(x2, y2, x2 - x12, y2 - y12, arrowDataSize);
222672
+ if (tick2) {
222673
+ layers.push(new PathLayer({
222674
+ ...tipsBase,
222675
+ id: `annotation-end-tick-${uid2}-${i2}`,
222676
+ data: [{ path: tick2 }],
222677
+ getPath: (d) => d.path,
222678
+ getColor: strokeColor,
222679
+ getWidth: strokeWidth,
222680
+ widthUnits: "pixels",
222681
+ capRounded: true
222682
+ }));
222683
+ }
222684
+ }
222685
+ if (markerStart === "Tick" && tipsOpacity > 0) {
222686
+ const tick2 = computeTickCap(x12, y12, x12 - x2, y12 - y2, arrowDataSize);
222687
+ if (tick2) {
222688
+ layers.push(new PathLayer({
222689
+ ...tipsBase,
222690
+ id: `annotation-start-tick-${uid2}-${i2}`,
222691
+ data: [{ path: tick2 }],
222692
+ getPath: (d) => d.path,
222693
+ getColor: strokeColor,
222694
+ getWidth: strokeWidth,
222695
+ widthUnits: "pixels",
222696
+ capRounded: true
222697
+ }));
222698
+ }
222699
+ }
222700
+ if (shape2.text) {
222701
+ const pos = shape2.textPosition ?? "start";
222702
+ let textX;
222703
+ let textY;
222704
+ if (pos === "start") {
222705
+ textX = x12;
222706
+ textY = y12;
222707
+ } else if (pos === "end") {
222708
+ textX = x2;
222709
+ textY = y2;
222710
+ } else {
222711
+ textX = (x12 + x2) / 2;
222712
+ textY = (y12 + y2) / 2;
222713
+ }
222714
+ let pixelOffset = [0, 0];
222715
+ let textAnchor = "middle";
222716
+ let alignmentBaseline = "center";
222717
+ if (pos !== "middle") {
222718
+ const dxT = x2 - x12;
222719
+ const dyT = y2 - y12;
222720
+ const lenT = Math.sqrt(dxT * dxT + dyT * dyT);
222721
+ if (lenT > 0) {
222722
+ const uxT = dxT / lenT;
222723
+ const uyT = dyT / lenT;
222724
+ const sign2 = pos === "start" ? -1 : 1;
222725
+ const bufferPx = shape2.textBufferPx ?? 8;
222726
+ const offsetX = sign2 * uxT * bufferPx;
222727
+ const offsetY = sign2 * uyT * bufferPx;
222728
+ pixelOffset = [offsetX, offsetY];
222729
+ const absX = Math.abs(offsetX);
222730
+ const absY = Math.abs(offsetY);
222731
+ if (absX >= absY) {
222732
+ textAnchor = offsetX > 0 ? "start" : "end";
222733
+ }
222734
+ if (absY >= absX) {
222735
+ alignmentBaseline = offsetY > 0 ? "top" : "bottom";
222736
+ }
222737
+ }
222738
+ } else if (len2 > 0) {
222739
+ const belowPerp = pickBelowPerp(ux, uy);
222740
+ const bufferPx = shape2.textBufferPx ?? 14;
222741
+ pixelOffset = [-belowPerp[0] * bufferPx, -belowPerp[1] * bufferPx];
222742
+ textAnchor = "middle";
222743
+ alignmentBaseline = "center";
222744
+ }
222745
+ const middleAngle = pos === "middle" && len2 > 0 ? -readableAngle(Math.atan2(dy, dx) * 180 / Math.PI) : null;
222746
+ if (labelOpacity <= 0)
222747
+ return;
222748
+ layers.push(new TextLayer({
222749
+ ...labelBase,
222750
+ ...labelProps,
222751
+ id: `annotation-line-text-${uid2}-${i2}`,
222752
+ data: [{ position: [textX, textY], text: shape2.text }],
222753
+ getPosition: (d) => d.position,
222754
+ getText: (d) => d.text,
222755
+ getColor: strokeColor,
222756
+ getSize: LABEL_FONT_SIZE,
222757
+ sizeUnits: "pixels",
222758
+ fontFamily: LABEL_FONT_FAMILY$1,
222759
+ fontWeight: LABEL_FONT_WEIGHT,
222760
+ getPixelOffset: pixelOffset,
222761
+ getTextAnchor: textAnchor,
222762
+ getAlignmentBaseline: alignmentBaseline,
222763
+ ...middleAngle !== null && { getAngle: middleAngle }
222764
+ }));
222765
+ }
222766
+ }
222767
+ if (type2 === "ellipse") {
222768
+ const { x1: x12, y1: y12, radiusX, radiusY, fillColor = strokeColor, fillOpacity = 0 } = shape2;
222769
+ const polygon2 = computeEllipsePolygon(x12, y12, radiusX, radiusY);
222770
+ const filled = fillOpacity > 0;
222771
+ layers.push(new PolygonLayer({
222772
+ ...base2,
222773
+ id: `annotation-ellipse-${uid2}-${i2}`,
222774
+ data: [{ polygon: polygon2 }],
222775
+ getPolygon: (d) => d.polygon,
222776
+ filled,
222777
+ stroked: true,
222778
+ getFillColor: filled ? [...fillColor, Math.round(fillOpacity * 255)] : [0, 0, 0, 0],
222779
+ getLineColor: strokeColor,
222780
+ getLineWidth: strokeWidth,
222781
+ lineWidthUnits: "pixels",
222782
+ ...dashArray ? {
222783
+ extensions: [new PathStyleExtension({ dash: true })],
222784
+ getDashArray: dashArray
222785
+ } : {}
222786
+ }));
222787
+ if (shape2.text && labelOpacity > 0) {
222788
+ layers.push(new TextLayer({
222789
+ ...labelBase,
222790
+ ...labelProps,
222791
+ id: `annotation-ellipse-text-${uid2}-${i2}`,
222792
+ data: [{ position: [x12, y12 - radiusY], text: shape2.text }],
222793
+ getPosition: (d) => d.position,
222794
+ getText: (d) => d.text,
222795
+ getColor: strokeColor,
222796
+ getSize: LABEL_FONT_SIZE,
222797
+ sizeUnits: "pixels",
222798
+ fontFamily: LABEL_FONT_FAMILY$1,
222799
+ fontWeight: LABEL_FONT_WEIGHT,
222800
+ getPixelOffset: [0, -4],
222801
+ getTextAnchor: "middle",
222802
+ getAlignmentBaseline: "bottom"
222803
+ }));
222804
+ }
222805
+ }
222806
+ if (type2 === "polygon") {
222807
+ const { points: points2, fillColor = strokeColor, fillOpacity = 0 } = shape2;
222808
+ if (!points2 || points2.length < 3)
222809
+ return;
222810
+ const closedPath = [...points2, points2[0]];
222811
+ if (fillOpacity > 0) {
222812
+ layers.push(new PolygonLayer({
222813
+ ...base2,
222814
+ id: `annotation-polygon-fill-${uid2}-${i2}`,
222815
+ data: [{ polygon: points2 }],
222816
+ getPolygon: (d) => d.polygon,
222817
+ stroked: false,
222818
+ filled: true,
222819
+ getFillColor: [...fillColor, Math.round(fillOpacity * 255)]
222820
+ }));
222821
+ }
222822
+ layers.push(new PathLayer({
222823
+ ...base2,
222824
+ id: `annotation-polygon-${uid2}-${i2}`,
222825
+ extensions: dashExt,
222826
+ getDashArray: getDash,
222827
+ data: [{ path: closedPath }],
222828
+ getPath: (d) => d.path,
222829
+ getColor: strokeColor,
222830
+ getWidth: strokeWidth,
222831
+ widthUnits: "pixels",
222832
+ jointRounded: true
222833
+ }));
222834
+ if (shape2.text && labelOpacity > 0) {
222835
+ const minX = Math.min(...points2.map((p) => p[0]));
222836
+ const maxX = Math.max(...points2.map((p) => p[0]));
222837
+ const minY = Math.min(...points2.map((p) => p[1]));
222838
+ const topCenterX = (minX + maxX) / 2;
222839
+ layers.push(new TextLayer({
222840
+ ...labelBase,
222841
+ ...labelProps,
222842
+ id: `annotation-polygon-text-${uid2}-${i2}`,
222843
+ data: [{ position: [topCenterX, minY], text: shape2.text }],
222844
+ getPosition: (d) => d.position,
222845
+ getText: (d) => d.text,
222846
+ getColor: strokeColor,
222847
+ getSize: LABEL_FONT_SIZE,
222848
+ sizeUnits: "pixels",
222849
+ fontFamily: LABEL_FONT_FAMILY$1,
222850
+ fontWeight: LABEL_FONT_WEIGHT,
222851
+ getPixelOffset: [0, -4],
222852
+ getTextAnchor: "middle",
222853
+ getAlignmentBaseline: "bottom"
222854
+ }));
222855
+ }
222856
+ }
222857
+ if (type2 === "polyline") {
222858
+ const { points: points2, markerStart, markerEnd } = shape2;
222859
+ if (!points2 || points2.length < 2)
222860
+ return;
222861
+ let pathPoints = points2;
222862
+ if (markerStart === "Arrow") {
222863
+ const [x02, y02] = points2[0];
222864
+ const [x12, y12] = points2[1];
222865
+ const dx = x02 - x12;
222866
+ const dy = y02 - y12;
222867
+ const len2 = Math.sqrt(dx * dx + dy * dy);
222868
+ if (len2 > 0) {
222869
+ const ux = dx / len2;
222870
+ const uy = dy / len2;
222871
+ pathPoints = [[x02 - ux * arrowDataSize, y02 - uy * arrowDataSize], ...pathPoints.slice(1)];
222872
+ }
222873
+ }
222874
+ if (markerEnd === "Arrow") {
222875
+ const last = pathPoints[pathPoints.length - 1];
222876
+ const prev2 = pathPoints[pathPoints.length - 2];
222877
+ const dx = last[0] - prev2[0];
222878
+ const dy = last[1] - prev2[1];
222879
+ const len2 = Math.sqrt(dx * dx + dy * dy);
222880
+ if (len2 > 0) {
222881
+ const ux = dx / len2;
222882
+ const uy = dy / len2;
222883
+ const shortened = [last[0] - ux * arrowDataSize, last[1] - uy * arrowDataSize];
222884
+ pathPoints = [...pathPoints.slice(0, -1), shortened];
222885
+ }
222886
+ }
222887
+ layers.push(new PathLayer({
222888
+ ...base2,
222889
+ id: `annotation-polyline-${uid2}-${i2}`,
222890
+ extensions: dashExt,
222891
+ getDashArray: getDash,
222892
+ data: [{ path: pathPoints }],
222893
+ getPath: (d) => d.path,
222894
+ getColor: strokeColor,
222895
+ getWidth: strokeWidth,
222896
+ widthUnits: "pixels",
222897
+ jointRounded: true,
222898
+ capRounded: true
222899
+ }));
222900
+ if (markerStart === "Arrow" && tipsOpacity > 0) {
222901
+ const [x02, y02] = points2[0];
222902
+ const [x12, y12] = points2[1];
222903
+ const head = computeArrowhead(x02, y02, x02 - x12, y02 - y12, arrowDataSize);
222904
+ if (head) {
222905
+ layers.push(new PolygonLayer({
222906
+ ...tipsBase,
222907
+ id: `annotation-polyline-start-arrow-${uid2}-${i2}`,
222908
+ data: [{ polygon: head }],
222909
+ getPolygon: (d) => d.polygon,
222910
+ filled: true,
222911
+ stroked: false,
222912
+ getFillColor: strokeColor
222913
+ }));
222914
+ }
222915
+ }
222916
+ if (markerEnd === "Arrow" && tipsOpacity > 0) {
222917
+ const last = points2[points2.length - 1];
222918
+ const prev2 = points2[points2.length - 2];
222919
+ const dxArrow = last[0] - prev2[0];
222920
+ const dyArrow = last[1] - prev2[1];
222921
+ const head = computeArrowhead(last[0], last[1], dxArrow, dyArrow, arrowDataSize);
222922
+ if (head) {
222923
+ layers.push(new PolygonLayer({
222924
+ ...tipsBase,
222925
+ id: `annotation-polyline-end-arrow-${uid2}-${i2}`,
222926
+ data: [{ polygon: head }],
222927
+ getPolygon: (d) => d.polygon,
222928
+ filled: true,
222929
+ stroked: false,
222930
+ getFillColor: strokeColor
222931
+ }));
222932
+ }
222933
+ }
222934
+ if (markerStart === "Tick" && tipsOpacity > 0) {
222935
+ const [x02, y02] = points2[0];
222936
+ const [x12, y12] = points2[1];
222937
+ const tick2 = computeTickCap(x02, y02, x02 - x12, y02 - y12, arrowDataSize);
222938
+ if (tick2) {
222939
+ layers.push(new PathLayer({
222940
+ ...tipsBase,
222941
+ id: `annotation-polyline-start-tick-${uid2}-${i2}`,
222942
+ data: [{ path: tick2 }],
222943
+ getPath: (d) => d.path,
222944
+ getColor: strokeColor,
222945
+ getWidth: strokeWidth,
222946
+ widthUnits: "pixels",
222947
+ capRounded: true
222948
+ }));
222949
+ }
222950
+ }
222951
+ if (markerEnd === "Tick" && tipsOpacity > 0) {
222952
+ const last = points2[points2.length - 1];
222953
+ const prev2 = points2[points2.length - 2];
222954
+ const dxTick = last[0] - prev2[0];
222955
+ const dyTick = last[1] - prev2[1];
222956
+ const tick2 = computeTickCap(last[0], last[1], dxTick, dyTick, arrowDataSize);
222957
+ if (tick2) {
222958
+ layers.push(new PathLayer({
222959
+ ...tipsBase,
222960
+ id: `annotation-polyline-end-tick-${uid2}-${i2}`,
222961
+ data: [{ path: tick2 }],
222962
+ getPath: (d) => d.path,
222963
+ getColor: strokeColor,
222964
+ getWidth: strokeWidth,
222965
+ widthUnits: "pixels",
222966
+ capRounded: true
222967
+ }));
222968
+ }
222969
+ }
222970
+ if (shape2.text) {
222971
+ const pos = shape2.textPosition ?? "start";
222972
+ const bufferPx = shape2.textBufferPx ?? 8;
222973
+ let textPt;
222974
+ let dirX;
222975
+ let dirY;
222976
+ if (pos === "end") {
222977
+ const last = points2[points2.length - 1];
222978
+ const prev2 = points2[points2.length - 2];
222979
+ textPt = last;
222980
+ dirX = last[0] - prev2[0];
222981
+ dirY = last[1] - prev2[1];
222982
+ } else if (pos === "middle") {
222983
+ const mx = points2.reduce((s2, p) => s2 + p[0], 0) / points2.length;
222984
+ const my = points2.reduce((s2, p) => s2 + p[1], 0) / points2.length;
222985
+ textPt = [mx, my];
222986
+ dirX = 0;
222987
+ dirY = 0;
222988
+ } else {
222989
+ [textPt] = points2;
222990
+ dirX = points2[0][0] - points2[1][0];
222991
+ dirY = points2[0][1] - points2[1][1];
222992
+ }
222993
+ const [textX, textY] = textPt;
222994
+ const lenT = Math.sqrt(dirX * dirX + dirY * dirY);
222995
+ let polylinePixelOffset = [0, 0];
222996
+ let polylineTextAnchor = "middle";
222997
+ let polylineAlignmentBaseline = pos === "middle" ? "center" : "bottom";
222998
+ if (lenT > 0) {
222999
+ const uxT = dirX / lenT;
223000
+ const uyT = dirY / lenT;
223001
+ const offX = uxT * bufferPx;
223002
+ const offY = uyT * bufferPx;
223003
+ polylinePixelOffset = [offX, offY];
223004
+ if (Math.abs(offX) >= Math.abs(offY)) {
223005
+ polylineTextAnchor = offX > 0 ? "start" : "end";
223006
+ }
223007
+ if (Math.abs(offY) >= Math.abs(offX)) {
223008
+ polylineAlignmentBaseline = offY > 0 ? "top" : "bottom";
223009
+ }
223010
+ }
223011
+ if (labelOpacity <= 0)
223012
+ return;
223013
+ layers.push(new TextLayer({
223014
+ ...labelBase,
223015
+ ...labelProps,
223016
+ id: `annotation-polyline-text-${uid2}-${i2}`,
223017
+ data: [{ position: [textX, textY], text: shape2.text }],
223018
+ getPosition: (d) => d.position,
223019
+ getText: (d) => d.text,
223020
+ getColor: strokeColor,
223021
+ getSize: LABEL_FONT_SIZE,
223022
+ sizeUnits: "pixels",
223023
+ fontFamily: LABEL_FONT_FAMILY$1,
223024
+ fontWeight: LABEL_FONT_WEIGHT,
223025
+ getPixelOffset: polylinePixelOffset,
223026
+ getTextAnchor: polylineTextAnchor,
223027
+ getAlignmentBaseline: polylineAlignmentBaseline
223028
+ }));
223029
+ }
223030
+ }
223031
+ });
223032
+ if (dotShapes.length > 0) {
223033
+ layers.push(new ScatterplotLayer({
223034
+ coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
223035
+ id: "annotation-dots",
223036
+ data: dotShapes.map(({ shape: s2, alpha: alpha2 }) => ({
223037
+ position: getShapeCentroid(s2),
223038
+ color: [...s2.strokeColor ?? [255, 255, 255], alpha2]
223039
+ })),
223040
+ getPosition: (d) => d.position,
223041
+ getFillColor: (d) => d.color,
223042
+ getRadius: 4,
223043
+ radiusUnits: "pixels",
223044
+ filled: true,
223045
+ stroked: false
223046
+ }));
223047
+ }
223048
+ if (clusterDotShapes.length > 0) {
223049
+ const clusters = computeClusters(clusterDotShapes, zoom2);
223050
+ layers.push(new ScatterplotLayer({
223051
+ coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
223052
+ id: "annotation-clusters",
223053
+ data: clusters,
223054
+ getPosition: (d) => d.position,
223055
+ getFillColor: (d) => [...d.color, 180],
223056
+ getRadius: (d) => Math.max(6, 4 + Math.sqrt(d.count) * 2),
223057
+ radiusUnits: "pixels",
223058
+ filled: true,
223059
+ stroked: true,
223060
+ getLineColor: (d) => [...d.color, 220],
223061
+ lineWidthMinPixels: 1.5
223062
+ }));
223063
+ const multiClusters = clusters.filter((c2) => c2.count > 1);
223064
+ if (multiClusters.length > 0) {
223065
+ layers.push(new TextLayer({
223066
+ coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
223067
+ id: "annotation-cluster-counts",
223068
+ data: multiClusters,
223069
+ getPosition: (d) => d.position,
223070
+ getText: (d) => String(d.count),
223071
+ getColor: [255, 255, 255],
223072
+ getSize: 10,
223073
+ sizeUnits: "pixels",
223074
+ fontWeight: "bold",
223075
+ getTextAnchor: "middle",
223076
+ getAlignmentBaseline: "center"
223077
+ }));
223078
+ }
223079
+ }
223080
+ const measureLayers = [];
223081
+ shapes.forEach((shape2) => {
223082
+ if (!shape2.showMeasure)
223083
+ return;
223084
+ const mIsSelected = shape2.uid === selectedShapeUid;
223085
+ const label2 = getMeasurementLabel(shape2, physicalPixelSize, viewType);
223086
+ if (!label2)
223087
+ return;
223088
+ const color2 = shape2.strokeColor ?? [255, 255, 255];
223089
+ let mShapeOpacity = 1;
223090
+ let mLabelFactor = 0;
223091
+ if (mIsSelected || !semanticZoom || authoredZoom === null) {
223092
+ mLabelFactor = 1;
223093
+ } else {
223094
+ const mZoomRatio = 2 ** (zoom2 - authoredZoom);
223095
+ if (mZoomRatio < RATIO_DOT)
223096
+ return;
223097
+ if (mZoomRatio < RATIO_GEO_FADE) {
223098
+ mShapeOpacity = (mZoomRatio - RATIO_DOT) / (RATIO_GEO_FADE - RATIO_DOT);
223099
+ }
223100
+ if (mZoomRatio >= RATIO_LABEL_FULL) {
223101
+ mLabelFactor = 1;
223102
+ } else if (mZoomRatio >= RATIO_LABEL) {
223103
+ mLabelFactor = (mZoomRatio - RATIO_LABEL) / (RATIO_LABEL_FULL - RATIO_LABEL);
223104
+ }
223105
+ }
223106
+ const measureOpacity = mShapeOpacity * mLabelFactor;
223107
+ if (measureOpacity <= 0)
223108
+ return;
223109
+ const base2 = {
223110
+ coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
223111
+ opacity: measureOpacity,
223112
+ getText: (d) => d.text,
223113
+ getPosition: (d) => d.position,
223114
+ getColor: color2,
223115
+ getSize: 13,
223116
+ sizeUnits: "pixels",
223117
+ fontFamily: LABEL_FONT_FAMILY$1,
223118
+ fontWeight: "bold",
223119
+ // 'auto' builds the font atlas from the actual rendered characters (e.g. µ, ²)
223120
+ // instead of deck.gl's default ASCII-only atlas.
223121
+ characterSet: "auto",
223122
+ ...shape2.measureBackground ? {
223123
+ background: true,
223124
+ getBackgroundColor: textBgColor(color2),
223125
+ backgroundPadding: [3, 1, 3, 2]
223126
+ } : {}
223127
+ };
223128
+ if (shape2.type === "line") {
223129
+ const mx = ((shape2.x1 ?? 0) + (shape2.x2 ?? 0)) / 2;
223130
+ const my = ((shape2.y1 ?? 0) + (shape2.y2 ?? 0)) / 2;
223131
+ const dx = (shape2.x2 ?? 0) - (shape2.x1 ?? 0);
223132
+ const dy = (shape2.y2 ?? 0) - (shape2.y1 ?? 0);
223133
+ const len2 = Math.sqrt(dx * dx + dy * dy);
223134
+ if (len2 === 0)
223135
+ return;
223136
+ const perp = pickBelowPerp(dx / len2, dy / len2);
223137
+ const angle2 = readableAngle(Math.atan2(dy, dx) * 180 / Math.PI);
223138
+ measureLayers.push(new TextLayer({
223139
+ ...base2,
223140
+ id: `annotation-measure-${shape2.uid}`,
223141
+ data: [{ position: [mx, my], text: label2 }],
223142
+ getAngle: -angle2,
223143
+ getTextAnchor: "middle",
223144
+ getAlignmentBaseline: "center",
223145
+ getPixelOffset: [perp[0] * 16, perp[1] * 16]
223146
+ }));
223147
+ } else if (shape2.type === "polyline" && shape2.points?.length >= 2) {
223148
+ const pts = shape2.points;
223149
+ const mi = Math.floor((pts.length - 1) / 2);
223150
+ const [ax, ay] = pts[mi];
223151
+ const [bx, by] = pts[mi + 1];
223152
+ const dx = bx - ax;
223153
+ const dy = by - ay;
223154
+ const len2 = Math.sqrt(dx * dx + dy * dy);
223155
+ if (len2 === 0)
223156
+ return;
223157
+ const perp = pickBelowPerp(dx / len2, dy / len2);
223158
+ const angle2 = readableAngle(Math.atan2(dy, dx) * 180 / Math.PI);
223159
+ measureLayers.push(new TextLayer({
223160
+ ...base2,
223161
+ id: `annotation-measure-${shape2.uid}`,
223162
+ data: [{ position: [(ax + bx) / 2, (ay + by) / 2], text: label2 }],
223163
+ getAngle: -angle2,
223164
+ getTextAnchor: "middle",
223165
+ getAlignmentBaseline: "center",
223166
+ getPixelOffset: [perp[0] * 16, perp[1] * 16]
223167
+ }));
223168
+ } else if (shape2.type === "rectangle") {
223169
+ const x02 = shape2.x ?? 0;
223170
+ const y02 = shape2.y ?? 0;
223171
+ const w2 = shape2.width ?? 0;
223172
+ const h2 = shape2.height ?? 0;
223173
+ const rx = x02 + (w2 >= 0 ? w2 : 0);
223174
+ const ry = y02 + (h2 >= 0 ? h2 : 0);
223175
+ measureLayers.push(new TextLayer({
223176
+ ...base2,
223177
+ id: `annotation-measure-${shape2.uid}`,
223178
+ data: [{ position: [rx, ry], text: label2 }],
223179
+ getTextAnchor: "end",
223180
+ getAlignmentBaseline: "bottom",
223181
+ getPixelOffset: [-6, -6]
223182
+ }));
223183
+ } else if (shape2.type === "ellipse") {
223184
+ const cx = shape2.x1 ?? 0;
223185
+ const cy = shape2.y1 ?? 0;
223186
+ const ry = Math.abs(shape2.radiusY ?? 0);
223187
+ measureLayers.push(new TextLayer({
223188
+ ...base2,
223189
+ id: `annotation-measure-${shape2.uid}`,
223190
+ data: [{ position: [cx, cy + ry], text: label2 }],
223191
+ getTextAnchor: "middle",
223192
+ getAlignmentBaseline: "top",
223193
+ getPixelOffset: [0, 8]
223194
+ }));
223195
+ } else if (shape2.type === "polygon" && shape2.points?.length >= 3) {
223196
+ const xs = shape2.points.map((p) => p[0]);
223197
+ const ys = shape2.points.map((p) => p[1]);
223198
+ const cx = (Math.min(...xs) + Math.max(...xs)) / 2;
223199
+ const maxY2 = Math.max(...ys);
223200
+ measureLayers.push(new TextLayer({
223201
+ ...base2,
223202
+ id: `annotation-measure-${shape2.uid}`,
223203
+ data: [{ position: [cx, maxY2], text: label2 }],
223204
+ getTextAnchor: "middle",
223205
+ getAlignmentBaseline: "top",
223206
+ getPixelOffset: [0, 8]
223207
+ }));
223208
+ }
223209
+ });
223210
+ return [...selectionLayers, ...layers, ...measureLayers];
223211
+ }
223212
+ const defaultProps = {
223213
+ // Array of annotation shape objects (see annotationShapeObj in @vitessce/schemas).
223214
+ data: { type: "object", value: [], compare: true },
223215
+ // UID of the shape to highlight with a selection halo.
223216
+ selectedShapeUid: { type: "string", value: null },
223217
+ // { x, y, unit } used to format measurement labels in physical units.
223218
+ physicalPixelSize: { type: "object", value: null, compare: true },
223219
+ // The zoom level at which the shapes were authored (reference for semantic zoom).
223220
+ authoredZoom: { type: "number", value: null },
223221
+ // Whether to apply semantic zoom (level-of-detail) based on authoredZoom.
223222
+ semanticZoom: { type: "boolean", value: true },
223223
+ // The view type in which the layer is rendered (affects measurement label formatting).
223224
+ viewType: { type: "string", value: ViewType$1.SPATIAL },
223225
+ // In-progress shape being drawn, as { type, vertices }.
223226
+ inProgressShape: { type: "object", value: null, compare: true },
223227
+ // Current mouse position (in data coordinates) while drawing.
223228
+ hoverCoord: { type: "array", value: null, compare: true },
223229
+ // Stroke color [r, g, b] of the in-progress shape preview.
223230
+ previewStrokeColor: { type: "color", value: [255, 255, 0] }
223231
+ };
223232
+ class AnnotationLayer extends CompositeLayer {
223233
+ // The semantic zoom and arrowhead sizes depend on the current zoom level,
223234
+ // so the sublayers must also be re-rendered when the viewport changes.
223235
+ // eslint-disable-next-line class-methods-use-this
223236
+ shouldUpdateState({ changeFlags }) {
223237
+ return changeFlags.propsOrDataChanged || changeFlags.viewportChanged;
223238
+ }
223239
+ /**
223240
+ * Prefix the sublayer ID with this layer's ID (so that multiple
223241
+ * AnnotationLayers can co-exist), and combine this layer's
223242
+ * opacity with the per-shape opacity of the sublayer.
223243
+ * Note: we avoid getSubLayerProps here, since it would
223244
+ * overwrite the sublayer extensions (e.g., PathStyleExtension)
223245
+ * and opacity values.
223246
+ * @param {object} layer A DeckGL layer instance.
223247
+ * @returns {object} The cloned layer instance.
223248
+ */
223249
+ wrapSubLayer(layer) {
223250
+ const { id: id2, opacity: opacity2, pickable, visible } = this.props;
223251
+ return layer.clone({
223252
+ id: `${id2}-${layer.id}`,
223253
+ opacity: layer.props.opacity * opacity2,
223254
+ pickable,
223255
+ visible
223256
+ });
223257
+ }
223258
+ renderLayers() {
223259
+ const { data: data2, selectedShapeUid, physicalPixelSize, authoredZoom, semanticZoom, viewType, inProgressShape, hoverCoord, previewStrokeColor } = this.props;
223260
+ const zoom2 = this.context.viewport?.zoom ?? 0;
223261
+ const visibleShapes = (data2 ?? []).filter((shape2) => shape2.visible !== false);
223262
+ const annotationLayers = createAnnotationLayers(visibleShapes, zoom2, selectedShapeUid, physicalPixelSize, authoredZoom, semanticZoom, viewType);
223263
+ const previewLayer = createPreviewLayer(inProgressShape, hoverCoord, previewStrokeColor.slice(0, 3));
223264
+ return [
223265
+ ...annotationLayers,
223266
+ ...previewLayer ? [previewLayer] : []
223267
+ ].map((layer) => this.wrapSubLayer(layer));
223268
+ }
223269
+ }
223270
+ AnnotationLayer.layerName = "AnnotationLayer";
223271
+ AnnotationLayer.defaultProps = defaultProps;
223272
+ const useStyles$w = makeStyles()(() => ({
221903
223273
  toolButton: {
221904
223274
  display: "inline-flex",
221905
223275
  "&:active": {
@@ -221961,12 +223331,12 @@ const useStyles$v = makeStyles()(() => ({
221961
223331
  }));
221962
223332
  function IconTool(props) {
221963
223333
  const { alt, onClick, isActive: isActive2, children: children3 } = props;
221964
- const { classes: classes2 } = useStyles$v();
223334
+ const { classes: classes2 } = useStyles$w();
221965
223335
  return jsx$1("button", { className: clsx(classes2.toolIcon, { [classes2.toolActive]: isActive2 }), onClick, type: "button", title: alt, children: children3 });
221966
223336
  }
221967
223337
  function IconButton(props) {
221968
223338
  const { alt, onClick, children: children3 } = props;
221969
- const { classes: classes2 } = useStyles$v();
223339
+ const { classes: classes2 } = useStyles$w();
221970
223340
  return jsx$1("button", { className: clsx(classes2.toolIcon, classes2.toolButton), onClick, type: "button", title: alt, children: children3 });
221971
223341
  }
221972
223342
  function ToolMenu(props) {
@@ -221974,7 +223344,7 @@ function ToolMenu(props) {
221974
223344
  const lassoIconAltText = "Select lasso";
221975
223345
  const { setActiveTool, activeTool, visibleTools = { pan: true, selectLasso: true, recenter: true }, recenterOnClick = () => {
221976
223346
  } } = props;
221977
- const { classes: classes2 } = useStyles$v();
223347
+ const { classes: classes2 } = useStyles$w();
221978
223348
  const onRecenterButtonCLick = () => {
221979
223349
  recenterOnClick();
221980
223350
  };
@@ -222456,11 +223826,13 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
222456
223826
  this.cellSetsLabelPrevZoom = null;
222457
223827
  this.cellSetsLayers = [];
222458
223828
  this.contourLayers = [];
223829
+ this.annotationLayer = null;
222459
223830
  this.onUpdateCellsData();
222460
223831
  this.onUpdateCellsLayer();
222461
223832
  this.onUpdateCellSetsLayers();
222462
223833
  this.onUpdateStratifiedData();
222463
223834
  this.onUpdateContourLayers();
223835
+ this.onUpdateAnnotationLayer();
222464
223836
  }
222465
223837
  // Want to support multiple types of contour layers
222466
223838
  // - One layer for all data points (no filtering)
@@ -222524,6 +223896,21 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
222524
223896
  }));
222525
223897
  return layers;
222526
223898
  }
223899
+ createAnnotationLayer() {
223900
+ const { annotationShapes, annotationOverlayVisible, annotationSemanticZoom, annotationShapeSelection } = this.props;
223901
+ return new AnnotationLayer({
223902
+ data: annotationShapes,
223903
+ visible: annotationOverlayVisible,
223904
+ semanticZoom: annotationSemanticZoom,
223905
+ selectedShapeUid: annotationShapeSelection,
223906
+ viewType: ViewType$1.SCATTERPLOT
223907
+ // TODO: is this needed?
223908
+ // TODO: these are view-level properties, rather than layer-level.
223909
+ // annotationTransitionDuration,
223910
+ // annotationEditable,
223911
+ // Other coordination types related to editing...
223912
+ });
223913
+ }
222527
223914
  createCellsLayer() {
222528
223915
  const {
222529
223916
  obsEmbeddingIndex: obsIndex,
@@ -222657,12 +224044,13 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
222657
224044
  ], flipYTooltip, this.props.onSelectionBusy);
222658
224045
  }
222659
224046
  getLayers() {
222660
- const { cellsLayer, cellSetsLayers, contourLayers } = this;
224047
+ const { cellsLayer, cellSetsLayers, contourLayers, annotationLayer } = this;
222661
224048
  return [
222662
224049
  cellsLayer,
222663
224050
  ...contourLayers,
222664
224051
  ...cellSetsLayers,
222665
- this.createSelectionLayer()
224052
+ this.createSelectionLayer(),
224053
+ annotationLayer
222666
224054
  ];
222667
224055
  }
222668
224056
  /**
@@ -222699,6 +224087,14 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
222699
224087
  this.cellsLayer = null;
222700
224088
  }
222701
224089
  }
224090
+ onUpdateAnnotationLayer() {
224091
+ const { annotationShapes } = this.props;
224092
+ if (annotationShapes) {
224093
+ this.annotationLayer = this.createAnnotationLayer();
224094
+ } else {
224095
+ this.annotationLayer = null;
224096
+ }
224097
+ }
222702
224098
  onUpdateStratifiedData() {
222703
224099
  const { stratifiedData } = this.props;
222704
224100
  if (stratifiedData) {
@@ -222814,6 +224210,16 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
222814
224210
  this.onUpdateCellSetsLayers(true);
222815
224211
  forceUpdate = true;
222816
224212
  }
224213
+ if ([
224214
+ "annotationShapes",
224215
+ "annotationOverlayVisible",
224216
+ "annotationSemanticZoom",
224217
+ "annotationTransitionDuration",
224218
+ "annotationEditable"
224219
+ ].some(shallowDiff2)) {
224220
+ this.onUpdateAnnotationLayer();
224221
+ forceUpdate = true;
224222
+ }
222817
224223
  if (forceUpdate) {
222818
224224
  this.forceUpdate();
222819
224225
  }
@@ -223169,7 +224575,7 @@ function ScatterplotOptions(props) {
223169
224575
  id: `feature-aggregation-strategy-${scatterplotOptionsId}`
223170
224576
  }, children: FEATURE_AGGREGATION_STRATEGIES$2.map((opt) => jsx$1("option", { value: opt, children: capitalize$3(opt) }, opt)) }) })] }) : null] });
223171
224577
  }
223172
- const useStyles$u = makeStyles()((theme) => ({
224578
+ const useStyles$v = makeStyles()((theme) => ({
223173
224579
  tooltipAnchor: {
223174
224580
  position: "relative",
223175
224581
  width: "0px",
@@ -223200,7 +224606,7 @@ const useStyles$u = makeStyles()((theme) => ({
223200
224606
  function Tooltip3(props) {
223201
224607
  const { x: x2, y: y2, parentWidth, parentHeight, children: children3 } = props;
223202
224608
  const ref2 = useRef();
223203
- const { classes: classes2 } = useStyles$u();
224609
+ const { classes: classes2 } = useStyles$v();
223204
224610
  const [placementX, setPlacementX] = useState("start");
223205
224611
  const [placementY, setPlacementY] = useState("bottom");
223206
224612
  const getTooltipContainer = useVitessceContainer(ref2);
@@ -223216,7 +224622,7 @@ function Tooltip3(props) {
223216
224622
  }, [x2, y2, parentWidth, parentHeight]);
223217
224623
  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 });
223218
224624
  }
223219
- const useStyles$t = makeStyles()((theme) => ({
224625
+ const useStyles$u = makeStyles()((theme) => ({
223220
224626
  cellEmphasisCrosshair: {
223221
224627
  zIndex: 50,
223222
224628
  position: "absolute",
@@ -223227,7 +224633,7 @@ const useStyles$t = makeStyles()((theme) => ({
223227
224633
  }));
223228
224634
  function Tooltip2D(props) {
223229
224635
  const { parentUuid, sourceUuid, x: x2, y: y2, parentWidth, parentHeight, children: children3 } = props;
223230
- const { classes: classes2 } = useStyles$t();
224636
+ const { classes: classes2 } = useStyles$u();
223231
224637
  if (x2 < 0 || x2 > parentWidth || y2 < 0 || y2 > parentHeight) {
223232
224638
  return null;
223233
224639
  }
@@ -227139,7 +228545,7 @@ function getXlinkHref(cmap) {
227139
228545
  const xlinkHref = ramp(color2.copy().domain(quantize$3(interpolate$1$1(0, 1), n3))).toDataURL();
227140
228546
  return xlinkHref;
227141
228547
  }
227142
- const useStyles$s = makeStyles()(() => ({
228548
+ const useStyles$t = makeStyles()(() => ({
227143
228549
  legend: {
227144
228550
  position: "relative",
227145
228551
  // Needed for absolute positioning of slider overlay
@@ -227336,7 +228742,7 @@ function Legend(props) {
227336
228742
  contourThresholds
227337
228743
  } = props;
227338
228744
  const svgRef = useRef(null);
227339
- const { classes: classes2 } = useStyles$s();
228745
+ const { classes: classes2 } = useStyles$t();
227340
228746
  const [localRange, setLocalRange] = useState(featureValueColormapRange);
227341
228747
  useEffect(() => {
227342
228748
  setLocalRange(featureValueColormapRange);
@@ -227670,7 +229076,7 @@ function Legend(props) {
227670
229076
  }
227671
229077
  } })] })] });
227672
229078
  }
227673
- const useStyles$r = makeStyles()(() => ({
229079
+ const useStyles$s = makeStyles()(() => ({
227674
229080
  multiLegend: {
227675
229081
  position: "absolute",
227676
229082
  top: "0px",
@@ -227697,7 +229103,7 @@ function MultiLegend(props) {
227697
229103
  pointLayerCoordination,
227698
229104
  pointMultiIndicesData
227699
229105
  } = props;
227700
- const { classes: classes2 } = useStyles$r();
229106
+ const { classes: classes2 } = useStyles$s();
227701
229107
  const reversedSegmentationLayerScopes = useMemo$1(() => [...segmentationLayerScopes || []].reverse(), [segmentationLayerScopes]);
227702
229108
  const reversedSpotLayerScopes = useMemo$1(() => [...spotLayerScopes || []].reverse(), [spotLayerScopes]);
227703
229109
  const reversedPointLayerScopes = useMemo$1(() => [...pointLayerScopes || []].reverse(), [pointLayerScopes]);
@@ -227790,7 +229196,7 @@ function MultiLegend(props) {
227790
229196
  }) : null;
227791
229197
  }) : null] });
227792
229198
  }
227793
- const useStyles$q = makeStyles()(() => ({
229199
+ const useStyles$r = makeStyles()(() => ({
227794
229200
  channelNamesLegendContainer: {
227795
229201
  position: "absolute",
227796
229202
  bottom: "0px",
@@ -227813,7 +229219,7 @@ const useStyles$q = makeStyles()(() => ({
227813
229219
  }));
227814
229220
  function ChannelNamesLegend(props) {
227815
229221
  const { images, imageLayerScopes, imageLayerCoordination, imageChannelScopesByLayer, imageChannelCoordination } = props;
227816
- const { classes: classes2 } = useStyles$q();
229222
+ const { classes: classes2 } = useStyles$r();
227817
229223
  const reversedImageLayerScopes = useMemo$1(() => [...imageLayerScopes || []].reverse(), [imageLayerScopes]);
227818
229224
  return jsx$1("div", { className: classes2.channelNamesLegendContainer, children: imageLayerScopes ? reversedImageLayerScopes.map((layerScope) => {
227819
229225
  const layerCoordination = imageLayerCoordination[0][layerScope];
@@ -227843,7 +229249,6 @@ const DEFAULT_FEATURE_AGGREGATION_STRATEGY$3 = "first";
227843
229249
  function EmbeddingScatterplotSubscriber(props) {
227844
229250
  const {
227845
229251
  uuid: uuid2,
227846
- coordinationScopes: coordinationScopesRaw,
227847
229252
  closeButtonVisible,
227848
229253
  downloadButtonVisible,
227849
229254
  removeGridComponent,
@@ -227859,16 +229264,23 @@ function EmbeddingScatterplotSubscriber(props) {
227859
229264
  circleScaleFactor = 0.8
227860
229265
  } = props;
227861
229266
  const loaders = useLoaders();
227862
- const coordinationScopes = useCoordinationScopes(coordinationScopesRaw);
229267
+ const [
229268
+ // eslint-disable-next-line no-unused-vars
229269
+ coordinationScopes,
229270
+ _coordinationScopesBy,
229271
+ coordinationValues
229272
+ ] = useViewMapping(uuid2);
227863
229273
  const setComponentHover = useSetComponentHover();
227864
229274
  const setComponentViewInfo = useSetComponentViewInfo(uuid2);
227865
- const [{ dataset, obsType, featureType, featureValueType, sampleType, embeddingZoom: zoom2, embeddingTargetX: targetX2, embeddingTargetY: targetY2, embeddingTargetZ: targetZ, embeddingType: mapping, obsFilter: cellFilter, obsHighlight: cellHighlight, featureSelection: geneSelection, obsSetSelection: cellSetSelection, obsSetColor: cellSetColor, obsColorEncoding: cellColorEncoding, additionalObsSets: additionalCellSets, embeddingObsSetPolygonsVisible: cellSetPolygonsVisible, embeddingObsSetLabelsVisible: cellSetLabelsVisible, embeddingObsSetLabelSize: cellSetLabelSize, embeddingObsRadius: cellRadiusFixed, embeddingObsRadiusMode: cellRadiusMode, embeddingObsOpacity: cellOpacityFixed, embeddingObsOpacityMode: cellOpacityMode, featureValueColormap: geneExpressionColormap, featureValueColormapRange: geneExpressionColormapRange, tooltipsVisible, sampleSetSelection: sampleSetSelectionFromCoordination, sampleSetColor, embeddingPointsVisible, embeddingContoursVisible, embeddingContoursFilled, embeddingContourPercentiles: contourPercentiles, contourColorEncoding, contourColor, featureAggregationStrategy }, { setEmbeddingZoom: setZoom, setEmbeddingTargetX: setTargetX, setEmbeddingTargetY: setTargetY, setEmbeddingTargetZ: setTargetZ, setObsFilter: setCellFilter, setObsSetSelection: setCellSetSelection, setObsHighlight: setCellHighlight, setObsSetColor: setCellSetColor, setObsColorEncoding: setCellColorEncoding, setAdditionalObsSets: setAdditionalCellSets, setEmbeddingObsSetPolygonsVisible: setCellSetPolygonsVisible, setEmbeddingObsSetLabelsVisible: setCellSetLabelsVisible, setEmbeddingObsSetLabelSize: setCellSetLabelSize, setEmbeddingObsRadius: setCellRadiusFixed, setEmbeddingObsRadiusMode: setCellRadiusMode, setEmbeddingObsOpacity: setCellOpacityFixed, setEmbeddingObsOpacityMode: setCellOpacityMode, setFeatureValueColormap: setGeneExpressionColormap, setFeatureValueColormapRange: setGeneExpressionColormapRange, setTooltipsVisible, setEmbeddingPointsVisible, setEmbeddingContoursVisible, setEmbeddingContoursFilled, setEmbeddingContourPercentiles: setContourPercentiles, setContourColorEncoding, setFeatureAggregationStrategy }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType$1.SCATTERPLOT], coordinationScopes);
227866
- const { embeddingZoom: initialZoom, embeddingTargetX: initialTargetX, embeddingTargetY: initialTargetY } = useInitialCoordination(COMPONENT_COORDINATION_TYPES[ViewType$1.SCATTERPLOT], coordinationScopes);
229275
+ useAnnotationFrameCoordination(uuid2, coordinationScopes, coordinationValues);
229276
+ const [{ dataset, obsType, featureType, featureValueType, sampleType, embeddingZoom: zoom2, embeddingTargetX: targetX2, embeddingTargetY: targetY2, embeddingTargetZ: targetZ, embeddingType: mapping, obsFilter: cellFilter, obsHighlight: cellHighlight, featureSelection: geneSelection, obsSetSelection: cellSetSelection, obsSetColor: cellSetColor, obsColorEncoding: cellColorEncoding, additionalObsSets: additionalCellSets, embeddingObsSetPolygonsVisible: cellSetPolygonsVisible, embeddingObsSetLabelsVisible: cellSetLabelsVisible, embeddingObsSetLabelSize: cellSetLabelSize, embeddingObsRadius: cellRadiusFixed, embeddingObsRadiusMode: cellRadiusMode, embeddingObsOpacity: cellOpacityFixed, embeddingObsOpacityMode: cellOpacityMode, featureValueColormap: geneExpressionColormap, featureValueColormapRange: geneExpressionColormapRange, tooltipsVisible, sampleSetSelection: sampleSetSelectionFromCoordination, sampleSetColor, embeddingPointsVisible, embeddingContoursVisible, embeddingContoursFilled, embeddingContourPercentiles: contourPercentiles, contourColorEncoding, contourColor, featureAggregationStrategy, annotationStory, annotationShapes, annotationOverlayVisible, annotationSemanticZoom, annotationTransitionDuration, annotationEditable }, { setEmbeddingZoom: setZoom, setEmbeddingTargetX: setTargetX, setEmbeddingTargetY: setTargetY, setEmbeddingTargetZ: setTargetZ, setObsFilter: setCellFilter, setObsSetSelection: setCellSetSelection, setObsHighlight: setCellHighlight, setObsSetColor: setCellSetColor, setObsColorEncoding: setCellColorEncoding, setAdditionalObsSets: setAdditionalCellSets, setEmbeddingObsSetPolygonsVisible: setCellSetPolygonsVisible, setEmbeddingObsSetLabelsVisible: setCellSetLabelsVisible, setEmbeddingObsSetLabelSize: setCellSetLabelSize, setEmbeddingObsRadius: setCellRadiusFixed, setEmbeddingObsRadiusMode: setCellRadiusMode, setEmbeddingObsOpacity: setCellOpacityFixed, setEmbeddingObsOpacityMode: setCellOpacityMode, setFeatureValueColormap: setGeneExpressionColormap, setFeatureValueColormapRange: setGeneExpressionColormapRange, setTooltipsVisible, setEmbeddingPointsVisible, setEmbeddingContoursVisible, setEmbeddingContoursFilled, setEmbeddingContourPercentiles: setContourPercentiles, setContourColorEncoding, setFeatureAggregationStrategy, setAnnotationStory }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType$1.SCATTERPLOT], coordinationScopes, coordinationValues, uuid2);
229277
+ const { embeddingZoom: initialZoom, embeddingTargetX: initialTargetX, embeddingTargetY: initialTargetY } = useInitialCoordination(COMPONENT_COORDINATION_TYPES[ViewType$1.SCATTERPLOT], coordinationScopes, coordinationValues);
227867
229278
  const observationsLabel = observationsLabelOverride || obsType;
227868
229279
  const sampleSetSelection = sampleSetSelectionFromProps || sampleSetSelectionFromCoordination;
227869
229280
  const featureAggregationStrategyToUse = featureAggregationStrategy ?? DEFAULT_FEATURE_AGGREGATION_STRATEGY$3;
227870
229281
  const [width2, height2, deckRef] = useDeckCanvasSize();
227871
229282
  const title2 = titleOverride || `Scatterplot (${mapping})`;
229283
+ const [, annotationStoryStatus, annotationStoryUrls, annotationStoryError] = useAnnotationStoryData(loaders, dataset, false, { setAnnotationStory }, { annotationStory }, {});
227872
229284
  const [
227873
229285
  // eslint-disable-next-line no-unused-vars
227874
229286
  obsLabelsTypes,
@@ -227900,11 +229312,13 @@ function EmbeddingScatterplotSubscriber(props) {
227900
229312
  matrixIndicesError,
227901
229313
  featureLabelsError,
227902
229314
  sampleSetsError,
227903
- sampleEdgesError
229315
+ sampleEdgesError,
229316
+ annotationStoryError
227904
229317
  ];
227905
229318
  const isReady = useReady([
227906
229319
  obsEmbeddingStatus,
227907
229320
  obsSetsStatus,
229321
+ annotationStoryStatus,
227908
229322
  featureSelectionStatus,
227909
229323
  featureLabelsStatus,
227910
229324
  expandedFeatureLabelsStatus,
@@ -227918,7 +229332,8 @@ function EmbeddingScatterplotSubscriber(props) {
227918
229332
  matrixIndicesUrls,
227919
229333
  featureLabelsUrls,
227920
229334
  sampleSetsUrl,
227921
- sampleEdgesUrl
229335
+ sampleEdgesUrl,
229336
+ annotationStoryUrls
227922
229337
  ]);
227923
229338
  const [dynamicCellRadius, setDynamicCellRadius] = useState(cellRadiusFixed);
227924
229339
  const [isSelectionPending, setIsSelectionPending] = useState(false);
@@ -228195,7 +229610,13 @@ function EmbeddingScatterplotSubscriber(props) {
228195
229610
  embeddingPointsVisible,
228196
229611
  embeddingContoursVisible,
228197
229612
  circleInfo,
228198
- featureSelection: geneSelection
229613
+ featureSelection: geneSelection,
229614
+ // Annotation stuff
229615
+ annotationShapes,
229616
+ annotationOverlayVisible,
229617
+ annotationSemanticZoom,
229618
+ annotationTransitionDuration,
229619
+ annotationEditable
228199
229620
  }), tooltipsVisible && width2 && height2 ? jsx$1(ScatterplotTooltipSubscriber, { parentUuid: uuid2, obsHighlight: cellHighlight, width: width2, height: height2, getObsInfo: getObsInfo2, featureType, featureLabelsMap }) : null, jsx$1(Legend, {
228200
229621
  visible: true,
228201
229622
  theme,
@@ -243356,8 +244777,8 @@ async function getSingleSelectionStats2D({ loader: loader2, selection }) {
243356
244777
  const filteredSelection = filterSelection(loader2, selection);
243357
244778
  const raster = await data2.getRaster({ selection: filteredSelection });
243358
244779
  const selectionStats = getChannelStats(raster.data);
243359
- const { domain: domain2, contrastLimits: slider } = selectionStats;
243360
- return { domain: domain2, slider };
244780
+ const { domain: domain2, contrastLimits: slider, q1, q3 } = selectionStats;
244781
+ return { domain: domain2, slider, q1, q3 };
243361
244782
  }
243362
244783
  async function getSingleSelectionStats3D({ loader: loader2, selection }) {
243363
244784
  const lowResSource = loader2[loader2.length - 1];
@@ -243384,7 +244805,9 @@ async function getSingleSelectionStats3D({ loader: loader2, selection }) {
243384
244805
  slider: [
243385
244806
  Math.min(stats0.contrastLimits[0], statsMid.contrastLimits[0], statsTop.contrastLimits[0]),
243386
244807
  Math.max(stats0.contrastLimits[1], statsMid.contrastLimits[1], statsTop.contrastLimits[1])
243387
- ]
244808
+ ],
244809
+ q1: Math.min(stats0.q1[0], statsMid.q1[0], statsTop.q1[0]),
244810
+ q3: Math.max(stats0.q3[1], statsMid.q3[1], statsTop.q3[1])
243388
244811
  };
243389
244812
  }
243390
244813
  const getSingleSelectionStats = async ({ loader: loader2, selection, use3d }) => {
@@ -243395,7 +244818,9 @@ const getMultiSelectionStats = async ({ loader: loader2, selections, use3d }) =>
243395
244818
  const stats = await Promise.all(selections.map((selection) => getSingleSelectionStats({ loader: loader2, selection, use3d })));
243396
244819
  const domains = stats.map((stat) => stat.domain);
243397
244820
  const sliders = stats.map((stat) => stat.slider);
243398
- return { domains, sliders };
244821
+ const q1s = stats.map((stat) => stat.q1);
244822
+ const q3s = stats.map((stat) => stat.q3);
244823
+ return { domains, sliders, q1s, q3s };
243399
244824
  };
243400
244825
  function getPhysicalSizeScalingMatrix(loader2) {
243401
244826
  const { x: x2, y: y2, z: z2 } = loader2?.meta?.physicalSizes ?? {};
@@ -247639,6 +249064,7 @@ let Spatial$1 = class Spatial extends AbstractSpatialOrScatterplot {
247639
249064
  this.obsSegmentationsPolygonLayer = null;
247640
249065
  this.obsLocationsLayer = null;
247641
249066
  this.neighborhoodsLayer = null;
249067
+ this.annotationLayer = null;
247642
249068
  this.layerLoaderSelections = {};
247643
249069
  this.randomColorData = {
247644
249070
  data: new Uint8Array(2048 * 2048 * 3).map((_, j) => j < 4 ? 0 : Math.round(255 * Math.random())),
@@ -247660,6 +249086,7 @@ let Spatial$1 = class Spatial extends AbstractSpatialOrScatterplot {
247660
249086
  this.onUpdateNeighborhoodsData();
247661
249087
  this.onUpdateNeighborhoodsLayer();
247662
249088
  this.onUpdateImages();
249089
+ this.onUpdateAnnotationLayer();
247663
249090
  }
247664
249091
  createPolygonSegmentationsLayer(layerDef, hasExplicitPolygons) {
247665
249092
  const { stroked, visible, opacity: opacity2, radius: radius2 } = layerDef;
@@ -247991,7 +249418,7 @@ let Spatial$1 = class Spatial extends AbstractSpatialOrScatterplot {
247991
249418
  return [];
247992
249419
  }
247993
249420
  getLayers() {
247994
- const { imageLayers, obsSegmentationsPolygonLayer, neighborhoodsLayer, obsLocationsLayer, obsSegmentationsBitmaskLayers } = this;
249421
+ const { imageLayers, obsSegmentationsPolygonLayer, neighborhoodsLayer, obsLocationsLayer, obsSegmentationsBitmaskLayers, annotationLayer } = this;
247995
249422
  return [
247996
249423
  ...imageLayers,
247997
249424
  ...obsSegmentationsBitmaskLayers,
@@ -247999,7 +249426,8 @@ let Spatial$1 = class Spatial extends AbstractSpatialOrScatterplot {
247999
249426
  neighborhoodsLayer,
248000
249427
  obsLocationsLayer,
248001
249428
  this.createScaleBarLayer(),
248002
- this.createSelectionLayer()
249429
+ this.createSelectionLayer(),
249430
+ annotationLayer
248003
249431
  ];
248004
249432
  }
248005
249433
  /**
@@ -248106,6 +249534,25 @@ let Spatial$1 = class Spatial extends AbstractSpatialOrScatterplot {
248106
249534
  onUpdateImages() {
248107
249535
  this.imageLayers = this.createImageLayers();
248108
249536
  }
249537
+ createAnnotationLayer() {
249538
+ const { annotationShapes, annotationOverlayVisible, annotationSemanticZoom, annotationShapeSelection } = this.props;
249539
+ return new AnnotationLayer({
249540
+ id: "annotation-layer",
249541
+ data: annotationShapes,
249542
+ visible: annotationOverlayVisible,
249543
+ semanticZoom: annotationSemanticZoom,
249544
+ selectedShapeUid: annotationShapeSelection,
249545
+ viewType: ViewType$1.SPATIAL
249546
+ });
249547
+ }
249548
+ onUpdateAnnotationLayer() {
249549
+ const { annotationShapes } = this.props;
249550
+ if (annotationShapes) {
249551
+ this.annotationLayer = this.createAnnotationLayer();
249552
+ } else {
249553
+ this.annotationLayer = null;
249554
+ }
249555
+ }
248109
249556
  viewInfoDidUpdate() {
248110
249557
  const { obsCentroidsIndex, obsCentroids } = this.props;
248111
249558
  super.viewInfoDidUpdate(obsCentroidsIndex, obsCentroids, makeDefaultGetObsCoords$1);
@@ -248205,6 +249652,15 @@ let Spatial$1 = class Spatial extends AbstractSpatialOrScatterplot {
248205
249652
  this.onUpdateImages();
248206
249653
  forceUpdate = true;
248207
249654
  }
249655
+ if ([
249656
+ "annotationShapes",
249657
+ "annotationOverlayVisible",
249658
+ "annotationSemanticZoom",
249659
+ "annotationShapeSelection"
249660
+ ].some(shallowDiff2)) {
249661
+ this.onUpdateAnnotationLayer();
249662
+ forceUpdate = true;
249663
+ }
248208
249664
  if (forceUpdate) {
248209
249665
  this.forceUpdate();
248210
249666
  }
@@ -248304,7 +249760,8 @@ function SpatialSubscriber$1(props) {
248304
249760
  const loaders = useLoaders();
248305
249761
  const setComponentHover = useSetComponentHover();
248306
249762
  const setComponentViewInfo = useSetComponentViewInfo(uuid2);
248307
- const [{ dataset, obsType, featureType, featureValueType, spatialZoom: zoom2, spatialTargetX: targetX2, spatialTargetY: targetY2, spatialTargetZ: targetZ, spatialRotationX: rotationX, spatialRotationY: rotationY, spatialRotationZ: rotationZ, spatialRotationOrbit: rotationOrbit, spatialOrbitAxis: orbitAxis, spatialImageLayer: imageLayers, spatialSegmentationLayer: cellsLayer, spatialPointLayer: moleculesLayer, spatialNeighborhoodLayer: neighborhoodsLayer, obsFilter: cellFilter, obsHighlight: cellHighlight, moleculeHighlight, featureSelection: geneSelection, obsSetSelection: cellSetSelection, obsSetColor: cellSetColor, obsColorEncoding: cellColorEncoding, additionalObsSets: additionalCellSets, spatialAxisFixed, featureValueColormap: geneExpressionColormap, featureValueColormapRange: geneExpressionColormapRange, tooltipsVisible, photometricInterpretation: photometricInterpretationFromCoordination, featureAggregationStrategy }, { setSpatialZoom: setZoom, setSpatialTargetX: setTargetX, setSpatialTargetY: setTargetY, setSpatialTargetZ: setTargetZ, setSpatialRotationX: setRotationX, setSpatialRotationOrbit: setRotationOrbit, setSpatialOrbitAxis: setOrbitAxis, setSpatialImageLayer: setRasterLayers, setSpatialSegmentationLayer: setCellsLayer, setSpatialPointLayer: setMoleculesLayer, setSpatialNeighborhoodLayer: setNeighborhoodsLayer, setObsFilter: setCellFilter, setObsSetSelection: setCellSetSelection, setObsHighlight: setCellHighlight, setObsSetColor: setCellSetColor, setObsColorEncoding: setCellColorEncoding, setAdditionalObsSets: setAdditionalCellSets, setMoleculeHighlight, setSpatialAxisFixed, setFeatureValueColormap: setGeneExpressionColormap, setFeatureValueColormapRange: setGeneExpressionColormapRange, setTooltipsVisible, setFeatureAggregationStrategy }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType$1.SPATIAL], coordinationScopes);
249763
+ useAnnotationFrameCoordination(uuid2, coordinationScopes);
249764
+ const [{ dataset, obsType, featureType, featureValueType, spatialZoom: zoom2, spatialTargetX: targetX2, spatialTargetY: targetY2, spatialTargetZ: targetZ, spatialRotationX: rotationX, spatialRotationY: rotationY, spatialRotationZ: rotationZ, spatialRotationOrbit: rotationOrbit, spatialOrbitAxis: orbitAxis, spatialImageLayer: imageLayers, spatialSegmentationLayer: cellsLayer, spatialPointLayer: moleculesLayer, spatialNeighborhoodLayer: neighborhoodsLayer, obsFilter: cellFilter, obsHighlight: cellHighlight, moleculeHighlight, featureSelection: geneSelection, obsSetSelection: cellSetSelection, obsSetColor: cellSetColor, obsColorEncoding: cellColorEncoding, additionalObsSets: additionalCellSets, spatialAxisFixed, featureValueColormap: geneExpressionColormap, featureValueColormapRange: geneExpressionColormapRange, tooltipsVisible, photometricInterpretation: photometricInterpretationFromCoordination, featureAggregationStrategy, annotationStory, annotationShapes, annotationOverlayVisible, annotationSemanticZoom, annotationShapeSelection }, { setSpatialZoom: setZoom, setSpatialTargetX: setTargetX, setSpatialTargetY: setTargetY, setSpatialTargetZ: setTargetZ, setSpatialRotationX: setRotationX, setSpatialRotationOrbit: setRotationOrbit, setSpatialOrbitAxis: setOrbitAxis, setSpatialImageLayer: setRasterLayers, setSpatialSegmentationLayer: setCellsLayer, setSpatialPointLayer: setMoleculesLayer, setSpatialNeighborhoodLayer: setNeighborhoodsLayer, setObsFilter: setCellFilter, setObsSetSelection: setCellSetSelection, setObsHighlight: setCellHighlight, setObsSetColor: setCellSetColor, setObsColorEncoding: setCellColorEncoding, setAdditionalObsSets: setAdditionalCellSets, setMoleculeHighlight, setSpatialAxisFixed, setFeatureValueColormap: setGeneExpressionColormap, setFeatureValueColormapRange: setGeneExpressionColormapRange, setTooltipsVisible, setFeatureAggregationStrategy, setAnnotationStory }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType$1.SPATIAL], coordinationScopes);
248308
249765
  const { spatialZoom: initialZoom, spatialTargetX: initialTargetX, spatialTargetY: initialTargetY, spatialTargetZ: initialTargetZ } = useInitialCoordination(COMPONENT_COORDINATION_TYPES[ViewType$1.SPATIAL], coordinationScopes);
248309
249766
  const observationsLabel = observationsLabelOverride || obsType;
248310
249767
  const [{ imageLayerCallbacks, segmentationLayerCallbacks }] = useAuxiliaryCoordination(COMPONENT_COORDINATION_TYPES.layerController, coordinationScopes);
@@ -248341,6 +249798,7 @@ function SpatialSubscriber$1(props) {
248341
249798
  const [neighborhoods, neighborhoodsStatus, neighborhoodsUrls, neighborhoodsError] = useNeighborhoodsData(loaders, dataset, false, { setSpatialNeighborhoodLayer: setNeighborhoodsLayer }, { spatialNeighborhoodLayer: neighborhoodsLayer });
248342
249799
  const [{ featureLabelsMap: featureLabelsMapOrig }, featureLabelsStatus, featureLabelsUrls, featureLabelsError] = useFeatureLabelsData(loaders, dataset, false, {}, {}, { featureType });
248343
249800
  const [featureLabelsMap, expandedFeatureLabelsStatus] = useExpandedFeatureLabelsMap(featureType, featureLabelsMapOrig, { stripCuriePrefixes: true });
249801
+ const [, annotationStoryStatus, annotationStoryUrls, annotationStoryError] = useAnnotationStoryData(loaders, dataset, false, { setAnnotationStory }, { annotationStory }, {});
248344
249802
  const errors = [
248345
249803
  ...obsLabelsErrorsMulti,
248346
249804
  obsLocationsError,
@@ -248352,7 +249810,8 @@ function SpatialSubscriber$1(props) {
248352
249810
  matrixIndicesError,
248353
249811
  imageError,
248354
249812
  neighborhoodsError,
248355
- featureLabelsError
249813
+ featureLabelsError,
249814
+ annotationStoryError
248356
249815
  ];
248357
249816
  const photometricInterpretation = photometricInterpretationFromCoordination ?? instance2?.getPhotometricInterpretation();
248358
249817
  const isReady = useReady([
@@ -248366,7 +249825,8 @@ function SpatialSubscriber$1(props) {
248366
249825
  imageStatus,
248367
249826
  neighborhoodsStatus,
248368
249827
  featureLabelsStatus,
248369
- expandedFeatureLabelsStatus
249828
+ expandedFeatureLabelsStatus,
249829
+ annotationStoryStatus
248370
249830
  ]);
248371
249831
  const urls2 = useUrls([
248372
249832
  obsLocationsUrls,
@@ -248377,7 +249837,8 @@ function SpatialSubscriber$1(props) {
248377
249837
  matrixIndicesUrls,
248378
249838
  imageUrls,
248379
249839
  neighborhoodsUrls,
248380
- featureLabelsUrls
249840
+ featureLabelsUrls,
249841
+ annotationStoryUrls
248381
249842
  ]);
248382
249843
  const obsLocationsFeatureIndex = useMemo$1(() => {
248383
249844
  if (obsLocationsLabels) {
@@ -248616,7 +250077,7 @@ function SpatialSubscriber$1(props) {
248616
250077
  orbitAxis
248617
250078
  }, setViewState, originalViewState, imageLayerDefs: imageLayers, obsSegmentationsLayerDefs: cellsLayer, obsLocationsLayerDefs: moleculesLayer, neighborhoodLayerDefs: neighborhoodsLayer, obsLocationsIndex, obsSegmentationsIndex, obsLocations, obsLocationsLabels, obsLocationsFeatureIndex, hasSegmentations: hasSegmentationsData, obsSegmentations, obsSegmentationsType, obsCentroids, obsCentroidsIndex, cellFilter, cellSelection, cellHighlight, cellColors, neighborhoods, imageLayerLoaders: resolutionFilteredImageLayerLoaders, setCellFilter, setCellSelection: setCellSelectionProp, setCellHighlight, setHoverInfo, setMoleculeHighlight, setComponentHover: () => {
248618
250079
  setComponentHover(uuid2);
248619
- }, updateViewInfo: setComponentViewInfo, imageLayerCallbacks, segmentationLayerCallbacks, spatialAxisFixed, geneExpressionColormap, geneExpressionColormapRange, expressionData: shiftedExpressionDataForBitmask, cellColorEncoding, getExpressionValue, theme, useFullResolutionImage, photometricInterpretation }), tooltipsVisible && jsx$1(SpatialTooltipSubscriber$1, { parentUuid: uuid2, obsHighlight: cellHighlight || moleculeHighlight, width: width2, height: height2, getObsInfo: getTooltipObsInfo, useHoverInfoForTooltip, hoverData, hoverCoord, hoverMode, getObsIdFromHoverData, featureType, featureLabelsMap }), jsx$1(Legend, {
250080
+ }, updateViewInfo: setComponentViewInfo, imageLayerCallbacks, segmentationLayerCallbacks, spatialAxisFixed, geneExpressionColormap, geneExpressionColormapRange, expressionData: shiftedExpressionDataForBitmask, cellColorEncoding, getExpressionValue, theme, useFullResolutionImage, photometricInterpretation, annotationShapes, annotationOverlayVisible, annotationSemanticZoom, annotationShapeSelection }), tooltipsVisible && jsx$1(SpatialTooltipSubscriber$1, { parentUuid: uuid2, obsHighlight: cellHighlight || moleculeHighlight, width: width2, height: height2, getObsInfo: getTooltipObsInfo, useHoverInfoForTooltip, hoverData, hoverCoord, hoverMode, getObsIdFromHoverData, featureType, featureLabelsMap }), jsx$1(Legend, {
248620
250081
  visible: true,
248621
250082
  // Fix to dark theme due to black background of spatial plot.
248622
250083
  theme: "dark",
@@ -248864,6 +250325,7 @@ class Spatial2 extends AbstractSpatialOrScatterplot {
248864
250325
  this.obsSegmentationsLayers = [];
248865
250326
  this.obsSpotsLayers = [];
248866
250327
  this.obsPointsLayers = [];
250328
+ this.annotationLayer = null;
248867
250329
  this.obsPointsLabelsData = {};
248868
250330
  this.spotToMatrixIndexMap = {};
248869
250331
  this.spotColors = {};
@@ -248905,6 +250367,7 @@ class Spatial2 extends AbstractSpatialOrScatterplot {
248905
250367
  this.onUpdateAllSpotsExpressionData();
248906
250368
  this.onUpdateSpotsLayer();
248907
250369
  this.onUpdateImages();
250370
+ this.onUpdateAnnotationLayer();
248908
250371
  }
248909
250372
  createPolygonSegmentationLayer(layerScope, layerCoordination, channelScopes, channelCoordinations, layerObsSegmentations, layerFeatureValues) {
248910
250373
  const { theme, delegateHover } = this.props;
@@ -249643,7 +251106,8 @@ class Spatial2 extends AbstractSpatialOrScatterplot {
249643
251106
  imageLayers,
249644
251107
  obsSpotsLayers,
249645
251108
  obsPointsLayers,
249646
- obsSegmentationsLayers
251109
+ obsSegmentationsLayers,
251110
+ annotationLayer
249647
251111
  // neighborhoodsLayer,
249648
251112
  // obsLocationsLayer,
249649
251113
  } = this;
@@ -249655,7 +251119,8 @@ class Spatial2 extends AbstractSpatialOrScatterplot {
249655
251119
  // neighborhoodsLayer,
249656
251120
  // obsLocationsLayer,
249657
251121
  this.createScaleBarLayer(),
249658
- this.createSelectionLayer()
251122
+ this.createSelectionLayer(),
251123
+ annotationLayer
249659
251124
  ];
249660
251125
  }
249661
251126
  // onUpdate functions:
@@ -250011,6 +251476,25 @@ class Spatial2 extends AbstractSpatialOrScatterplot {
250011
251476
  onUpdateImages() {
250012
251477
  this.imageLayers = this.createImageLayers();
250013
251478
  }
251479
+ createAnnotationLayer() {
251480
+ const { annotationShapes, annotationOverlayVisible, annotationSemanticZoom, annotationShapeSelection } = this.props;
251481
+ return new AnnotationLayer({
251482
+ id: "annotation-layer",
251483
+ data: annotationShapes,
251484
+ visible: annotationOverlayVisible,
251485
+ semanticZoom: annotationSemanticZoom,
251486
+ selectedShapeUid: annotationShapeSelection,
251487
+ viewType: ViewType$1.SPATIAL_BETA
251488
+ });
251489
+ }
251490
+ onUpdateAnnotationLayer() {
251491
+ const { annotationShapes } = this.props;
251492
+ if (annotationShapes) {
251493
+ this.annotationLayer = this.createAnnotationLayer();
251494
+ } else {
251495
+ this.annotationLayer = null;
251496
+ }
251497
+ }
250014
251498
  viewInfoDidUpdate() {
250015
251499
  super.viewInfoDidUpdate(null, null, makeDefaultGetObsCoords);
250016
251500
  }
@@ -250208,6 +251692,15 @@ class Spatial2 extends AbstractSpatialOrScatterplot {
250208
251692
  this.onUpdateImages();
250209
251693
  forceUpdate = true;
250210
251694
  }
251695
+ if ([
251696
+ "annotationShapes",
251697
+ "annotationOverlayVisible",
251698
+ "annotationSemanticZoom",
251699
+ "annotationShapeSelection"
251700
+ ].some(shallowDiff2)) {
251701
+ this.onUpdateAnnotationLayer();
251702
+ forceUpdate = true;
251703
+ }
250211
251704
  if (forceUpdate) {
250212
251705
  this.forceUpdate();
250213
251706
  }
@@ -250347,16 +251840,16 @@ class ErrorBoundary extends React__default.Component {
250347
251840
  }
250348
251841
  }
250349
251842
  const LazySpatialThree = React__default.lazy(async () => {
250350
- const { SpatialWrapper: SpatialWrapper2 } = await import("./index-B8xbImKw.js").then((n3) => n3.a);
251843
+ const { SpatialWrapper: SpatialWrapper2 } = await import("./index-CfweEgJW.js").then((n3) => n3.a);
250351
251844
  return { default: SpatialWrapper2 };
250352
251845
  });
250353
251846
  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 }) }) }) }));
250354
251847
  const LazySpatialAccelerated = React__default.lazy(async () => {
250355
- const { SpatialWrapper: SpatialWrapper2 } = await import("./index-Bub8madF.js");
251848
+ const { SpatialWrapper: SpatialWrapper2 } = await import("./index-CNXTPnLt.js");
250356
251849
  return { default: SpatialWrapper2 };
250357
251850
  });
250358
251851
  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 }) }) }) }));
250359
- const useStyles$p = makeStyles()(() => ({
251852
+ const useStyles$q = makeStyles()(() => ({
250360
251853
  spatialBetaCameraLabel: {
250361
251854
  padding: "0px 0px 0px 16px"
250362
251855
  },
@@ -250367,7 +251860,7 @@ const useStyles$p = makeStyles()(() => ({
250367
251860
  function SpatialOptions(props) {
250368
251861
  const { spatialAxisFixed, setSpatialAxisFixed, use3d } = props;
250369
251862
  const toggleAxisId = $bdb11010cef70236$export$f680877a34711e37();
250370
- const { classes: classes2 } = useStyles$p();
251863
+ const { classes: classes2 } = useStyles$q();
250371
251864
  return jsx$1(OptionsContainer, { children: jsxs(TableRow, { children: [jsx$1(TableCell, { className: classes2.spatialBetaCameraLabel, variant: "head", scope: "row", children: jsx$1("label", { htmlFor: `spatial-camera-axis-${toggleAxisId}`, children: "Fix Camera Axis" }) }), jsx$1(TableCell, { className: classes2.spatialBetaToggleBox, variant: "body", children: jsx$1(Checkbox$1, { onClick: () => setSpatialAxisFixed(!spatialAxisFixed), disabled: !use3d, checked: Boolean(spatialAxisFixed), slotProps: { input: {
250372
251865
  "aria-label": "Fix or not fix spatial camera axis",
250373
251866
  id: `spatial-camera-axis-${toggleAxisId}`
@@ -250555,6 +252048,7 @@ function SpatialSubscriber(props) {
250555
252048
  const mergeCoordination = useMergeCoordination();
250556
252049
  const coordinationScopes = useCoordinationScopes(coordinationScopesRaw);
250557
252050
  const coordinationScopesBy = useCoordinationScopesBy(coordinationScopes, coordinationScopesByRaw);
252051
+ useAnnotationFrameCoordination(uuid2, coordinationScopes);
250558
252052
  const [{
250559
252053
  dataset,
250560
252054
  obsType,
@@ -250576,7 +252070,12 @@ function SpatialSubscriber(props) {
250576
252070
  additionalObsSets,
250577
252071
  obsSetColor,
250578
252072
  obsColorEncoding,
250579
- obsSetSelection
252073
+ obsSetSelection,
252074
+ annotationStory,
252075
+ annotationShapes,
252076
+ annotationOverlayVisible,
252077
+ annotationSemanticZoom,
252078
+ annotationShapeSelection
250580
252079
  }, {
250581
252080
  setSpatialZoom: setZoom,
250582
252081
  setSpatialTargetX: setTargetX,
@@ -250592,7 +252091,8 @@ function SpatialSubscriber(props) {
250592
252091
  setAdditionalObsSets,
250593
252092
  setObsSetColor,
250594
252093
  setObsColorEncoding,
250595
- setObsSetSelection
252094
+ setObsSetSelection,
252095
+ setAnnotationStory
250596
252096
  }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType$1.SPATIAL_BETA], coordinationScopes);
250597
252097
  const { spatialZoom: initialZoom, spatialTargetX: initialTargetX, spatialTargetY: initialTargetY, spatialTargetZ: initialTargetZ } = useInitialCoordination(COMPONENT_COORDINATION_TYPES[ViewType$1.SPATIAL_BETA], coordinationScopes);
250598
252098
  const [segmentationLayerScopes, segmentationChannelScopesByLayer] = useMultiCoordinationScopesSecondaryNonNull(CoordinationType$1.SEGMENTATION_CHANNEL, CoordinationType$1.SEGMENTATION_LAYER, coordinationScopes, coordinationScopesBy);
@@ -250735,6 +252235,7 @@ function SpatialSubscriber(props) {
250735
252235
  channelCoordination: segmentationChannelCoordination
250736
252236
  });
250737
252237
  const [imageData, imageDataStatus, imageUrls, imageDataErrors] = useMultiImages(coordinationScopes, coordinationScopesBy, loaders, dataset, mergeCoordination, uuid2);
252238
+ const [, annotationStoryStatus, annotationStoryUrls, annotationStoryError] = useAnnotationStoryData(loaders, dataset, false, { setAnnotationStory }, { annotationStory }, {});
250738
252239
  const errors = [
250739
252240
  ...obsPointsErrors,
250740
252241
  ...obsSpotsErrors,
@@ -250749,7 +252250,8 @@ function SpatialSubscriber(props) {
250749
252250
  ...pointMultiIndicesDataErrors,
250750
252251
  ...segmentationMultiFeatureSelectionErrors,
250751
252252
  ...segmentationMultiIndicesDataErrors,
250752
- ...obsSegmentationsLocationsDataErrors
252253
+ ...obsSegmentationsLocationsDataErrors,
252254
+ annotationStoryError
250753
252255
  ];
250754
252256
  const isReadyToComputeInitialViewState = useReady([
250755
252257
  obsPointsDataStatus,
@@ -250774,13 +252276,16 @@ function SpatialSubscriber(props) {
250774
252276
  segmentationMultiIndicesDataStatus,
250775
252277
  obsSegmentationsLocationsDataStatus,
250776
252278
  // Images
250777
- imageDataStatus
252279
+ imageDataStatus,
252280
+ // Annotations
252281
+ annotationStoryStatus
250778
252282
  ]);
250779
252283
  const urls2 = useUrls([
250780
252284
  Object.values(obsSpotsUrls || {}).flat(),
250781
252285
  Object.values(obsPointsUrls || {}).flat(),
250782
252286
  Object.values(obsSegmentationsUrls || {}).flat(),
250783
- Object.values(imageUrls || {}).flat()
252287
+ Object.values(imageUrls || {}).flat(),
252288
+ annotationStoryUrls
250784
252289
  // TODO: more urls
250785
252290
  // TODO: a bit of memoization
250786
252291
  ]);
@@ -251028,7 +252533,7 @@ function SpatialSubscriber(props) {
251028
252533
  target: [targetX2, targetY2, targetZ],
251029
252534
  rotationX,
251030
252535
  rotationOrbit
251031
- } : DEFAULT_VIEW_STATE, orbitAxis, spatialAxisFixed, setViewState: isValidViewState ? setViewState : SET_VIEW_STATE_NOOP, originalViewState, spatialRenderingMode, updateViewInfo: setComponentViewInfo, delegateHover, obsPoints: obsPointsData, pointLayerScopes, pointLayerCoordination, pointMultiObsLabels: pointMultiObsLabelsData, pointMatrixIndices: pointMultiIndicesData, obsSpots: obsSpotsData, spotLayerScopes, spotLayerCoordination, obsSpotsSets: obsSpotsSetsData, spotMatrixIndices: spotMultiIndicesData, spotMultiExpressionData: spotMultiExpressionNormDataAggregated || spotMultiExpressionNormData, segmentationLayerScopes, segmentationLayerCoordination, segmentationChannelScopesByLayer, segmentationChannelCoordination, obsSegmentations: obsSegmentationsData, obsSegmentationsLocations: obsSegmentationsLocationsData, obsSegmentationsSets: obsSegmentationsSetsData, segmentationMatrixIndices: segmentationMultiIndicesData, segmentationMultiExpressionData: segmentationMultiExpressionNormDataAggregated || segmentationMultiExpressionNormData, bitmaskValueIsIndex, images: imageData, imageLayerScopes, imageLayerCoordination, imageChannelScopesByLayer, imageChannelCoordination, setTiledPointsLoadingProgress }), !disableTooltip && jsx$1(SpatialTooltipSubscriber, {
252536
+ } : DEFAULT_VIEW_STATE, orbitAxis, spatialAxisFixed, setViewState: isValidViewState ? setViewState : SET_VIEW_STATE_NOOP, originalViewState, spatialRenderingMode, updateViewInfo: setComponentViewInfo, delegateHover, obsPoints: obsPointsData, pointLayerScopes, pointLayerCoordination, pointMultiObsLabels: pointMultiObsLabelsData, pointMatrixIndices: pointMultiIndicesData, obsSpots: obsSpotsData, spotLayerScopes, spotLayerCoordination, obsSpotsSets: obsSpotsSetsData, spotMatrixIndices: spotMultiIndicesData, spotMultiExpressionData: spotMultiExpressionNormDataAggregated || spotMultiExpressionNormData, segmentationLayerScopes, segmentationLayerCoordination, segmentationChannelScopesByLayer, segmentationChannelCoordination, obsSegmentations: obsSegmentationsData, obsSegmentationsLocations: obsSegmentationsLocationsData, obsSegmentationsSets: obsSegmentationsSetsData, segmentationMatrixIndices: segmentationMultiIndicesData, segmentationMultiExpressionData: segmentationMultiExpressionNormDataAggregated || segmentationMultiExpressionNormData, bitmaskValueIsIndex, images: imageData, imageLayerScopes, imageLayerCoordination, imageChannelScopesByLayer, imageChannelCoordination, setTiledPointsLoadingProgress, annotationShapes, annotationOverlayVisible, annotationSemanticZoom, annotationShapeSelection }), !disableTooltip && jsx$1(SpatialTooltipSubscriber, {
251032
252537
  parentUuid: uuid2,
251033
252538
  width: width2,
251034
252539
  height: height2,
@@ -258445,7 +259950,7 @@ _defineProperty$3(WindowScroller, "defaultProps", {
258445
259950
  serverHeight: 0,
258446
259951
  serverWidth: 0
258447
259952
  });
258448
- const useStyles$o = makeStyles()((theme) => ({
259953
+ const useStyles$p = makeStyles()((theme) => ({
258449
259954
  selectableTable: {
258450
259955
  flex: "1 1 auto",
258451
259956
  outline: "none"
@@ -258594,7 +260099,7 @@ function SelectableTable(props) {
258594
260099
  onChange(null);
258595
260100
  }
258596
260101
  }, [selectedRows, allowMultiple]);
258597
- const { classes: classes2 } = useStyles$o();
260102
+ const { classes: classes2 } = useStyles$p();
258598
260103
  const inputUuid = v4$1();
258599
260104
  const rowRenderer = ({ index: index2, style: style2 }) => (
258600
260105
  // eslint-disable-next-line jsx-a11y/interactive-supports-focus
@@ -258619,7 +260124,7 @@ const FEATURELIST_SORT_OPTIONS = [
258619
260124
  "original"
258620
260125
  ];
258621
260126
  const ALT_COLNAME = "Alternate ID";
258622
- const useStyles$n = makeStyles()(() => ({
260127
+ const useStyles$o = makeStyles()(() => ({
258623
260128
  searchBar: {
258624
260129
  marginTop: "10px",
258625
260130
  marginLeft: "10px",
@@ -258632,7 +260137,7 @@ const useStyles$n = makeStyles()(() => ({
258632
260137
  }));
258633
260138
  function FeatureList(props) {
258634
260139
  const { width: width2, height: height2, hasColorEncoding, geneList = [], featureLabelsMap, geneSelection = [], geneFilter = null, setGeneSelection, enableMultiSelect, showFeatureTable, featureListSort, featureListSortKey, hasFeatureLabels, primaryColumnName } = props;
258635
- const { classes: classes2 } = useStyles$n();
260140
+ const { classes: classes2 } = useStyles$o();
258636
260141
  const [searchTerm, setSearchTerm] = useState("");
258637
260142
  const selectableTableSortKey = featureListSortKey === "featureIndex" ? "key" : "name";
258638
260143
  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]);
@@ -258705,7 +260210,7 @@ function FeatureListOptions(props) {
258705
260210
  id: `feature-list-show-alternative-ids-${featureListId}`
258706
260211
  } } }) })] })] }) : null] });
258707
260212
  }
258708
- const useStyles$m = makeStyles()((theme) => ({
260213
+ const useStyles$n = makeStyles()((theme) => ({
258709
260214
  featureListContainer: {
258710
260215
  width: "100%",
258711
260216
  height: "100%",
@@ -258720,7 +260225,7 @@ function FeatureListSubscriber(props) {
258720
260225
  const loaders = useLoaders();
258721
260226
  const coordinationScopes = useCoordinationScopes(coordinationScopesRaw);
258722
260227
  const [width2, height2, containerRef] = useGridItemSize();
258723
- const { classes: classes2 } = useStyles$m();
260228
+ const { classes: classes2 } = useStyles$n();
258724
260229
  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);
258725
260230
  const variablesLabel = variablesLabelOverride || featureType;
258726
260231
  const title2 = titleOverride || `${capitalize$3(variablesLabel)} List`;
@@ -258855,7 +260360,7 @@ const useSelectionSliderStyles = makeStyles()(() => ({
258855
260360
  backgroundColor: "rgba(128, 128, 128, 0.7)"
258856
260361
  }
258857
260362
  }));
258858
- const useStyles$l = makeStyles()((theme) => ({
260363
+ const useStyles$m = makeStyles()((theme) => ({
258859
260364
  paletteContainer: {
258860
260365
  width: "70px",
258861
260366
  height: "40px",
@@ -258876,10 +260381,10 @@ const useStyles$l = makeStyles()((theme) => ({
258876
260381
  }
258877
260382
  }));
258878
260383
  const ColorPalette = ({ handleChange }) => {
258879
- const { classes: classes2 } = useStyles$l();
260384
+ const { classes: classes2 } = useStyles$m();
258880
260385
  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)) });
258881
260386
  };
258882
- const useStyles$k = makeStyles()(() => ({
260387
+ const useStyles$l = makeStyles()(() => ({
258883
260388
  menuButton: {
258884
260389
  backgroundColor: "transparent"
258885
260390
  },
@@ -258898,7 +260403,7 @@ function MuiSpan(props) {
258898
260403
  }
258899
260404
  function ChannelOptions$1({ handlePropertyChange, handleChannelRemove, handleIQRUpdate }) {
258900
260405
  const [open2, setOpen] = useState(false);
258901
- const { classes: classes2 } = useStyles$k();
260406
+ const { classes: classes2 } = useStyles$l();
258902
260407
  const handleColorSelect = (color2) => {
258903
260408
  handlePropertyChange("color", color2);
258904
260409
  };
@@ -259515,7 +261020,7 @@ function LayerController$1(props) {
259515
261020
  minWidth: "50%"
259516
261021
  } })] }), 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] })] });
259517
261022
  }
259518
- const useStyles$j = makeStyles()(() => ({
261023
+ const useStyles$k = makeStyles()(() => ({
259519
261024
  addButton: {
259520
261025
  marginTop: "10px",
259521
261026
  marginBottom: "10px",
@@ -259527,7 +261032,7 @@ function ImageAddIcon() {
259527
261032
  }
259528
261033
  function ImageAddButton({ imageOptions, handleImageAdd }) {
259529
261034
  const [open2, setOpen] = useState(false);
259530
- const { classes: classes2 } = useStyles$j();
261035
+ const { classes: classes2 } = useStyles$k();
259531
261036
  const handleAdd = (imgData) => {
259532
261037
  setOpen((prev2) => !prev2);
259533
261038
  handleImageAdd(imgData);
@@ -259889,7 +261394,7 @@ const channelControlCellSx = {
259889
261394
  }
259890
261395
  };
259891
261396
  const channelSpacerCellSx = { flexShrink: 0, minWidth: "44px" };
259892
- const useStyles$i = makeStyles()(() => ({
261397
+ const useStyles$j = makeStyles()(() => ({
259893
261398
  colorPickerPaper: {
259894
261399
  overflow: "hidden",
259895
261400
  "& > ul": {
@@ -259947,7 +261452,7 @@ function ChannelColorPickerMenu(props) {
259947
261452
  setColor2([rgb2.r, rgb2.g, rgb2.b]);
259948
261453
  }
259949
261454
  }
259950
- const { classes: classes2 } = useStyles$i();
261455
+ const { classes: classes2 } = useStyles$j();
259951
261456
  const currentColor = color2 ? colorArrayToString(color2) : colorArrayToString(getDefaultColor(theme));
259952
261457
  const isWritable = typeof setColor2 === "function";
259953
261458
  const derivedOpen = isWritable ? isStaticColor && visible ? open2 : false : false;
@@ -259955,7 +261460,7 @@ function ChannelColorPickerMenu(props) {
259955
261460
  [classes2.colorIconOutline]: theme !== "dark" && isEqual$3(color2, [255, 255, 255])
259956
261461
  }), 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 }) });
259957
261462
  }
259958
- const useStyles$h = makeStyles()(() => ({
261463
+ const useStyles$i = makeStyles()(() => ({
259959
261464
  layerTypeSpotIcon: {
259960
261465
  height: "100%",
259961
261466
  marginLeft: "1px",
@@ -259968,7 +261473,7 @@ const useStyles$h = makeStyles()(() => ({
259968
261473
  function SpotLayerEllipsisMenu(props) {
259969
261474
  const { strokeWidth, setStrokeWidth, filled, setFilled, featureSelection, obsColorEncoding, setObsColorEncoding, featureValueColormap, setFeatureValueColormap, featureValueColormapRange, setFeatureValueColormapRange, tooltipsVisible, setTooltipsVisible, tooltipCrosshairsVisible, setTooltipCrosshairsVisible, legendVisible, setLegendVisible } = props;
259970
261475
  const [open2, setOpen] = useState(false);
259971
- const { classes: classes2 } = useStyles$h();
261476
+ const { classes: classes2 } = useStyles$i();
259972
261477
  const { classes: selectClasses2 } = useSelectStyles();
259973
261478
  const { classes: menuClasses } = useEllipsisMenuStyles();
259974
261479
  const filledId = $bdb11010cef70236$export$f680877a34711e37();
@@ -259990,7 +261495,7 @@ function SpotLayerController(props) {
259990
261495
  const Visibility = useMemo$1(() => visibleSetting ? VisibilityIcon : VisibilityOffIcon, [visibleSetting]);
259991
261496
  const isStaticColor = obsColorEncoding === "spatialLayerColor";
259992
261497
  const isColormap = obsColorEncoding === "geneSelection";
259993
- const { classes: classes2 } = useStyles$h();
261498
+ const { classes: classes2 } = useStyles$i();
259994
261499
  const { classes: lcClasses } = useControllerSectionStyles();
259995
261500
  const { classes: menuClasses } = useEllipsisMenuStyles();
259996
261501
  const handleVisibleChange = useCallback(() => {
@@ -260000,7 +261505,7 @@ function SpotLayerController(props) {
260000
261505
  const handleOpacityChange = useCallback((e3, v) => setOpacity2(v), [setOpacity2]);
260001
261506
  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 }) })] }) }) });
260002
261507
  }
260003
- const useStyles$g = makeStyles()(() => ({
261508
+ const useStyles$h = makeStyles()(() => ({
260004
261509
  layerFeatureControllerGrid: {
260005
261510
  padding: "0",
260006
261511
  flexWrap: "nowrap"
@@ -260045,7 +261550,7 @@ function LayerPerFeatureController(props) {
260045
261550
  ]);
260046
261551
  }
260047
261552
  }, [featureName, featureColor, setFeatureColor, featureColorIndex]);
260048
- const { classes: classes2 } = useStyles$g();
261553
+ const { classes: classes2 } = useStyles$h();
260049
261554
  const { classes: lcClasses } = useControllerSectionStyles();
260050
261555
  const { classes: menuClasses } = useEllipsisMenuStyles();
260051
261556
  const [open2, setOpen] = useState(false);
@@ -260099,7 +261604,7 @@ function LayerPerFeatureController(props) {
260099
261604
  ]);
260100
261605
  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);
260101
261606
  }
260102
- const useStyles$f = makeStyles()(() => ({
261607
+ const useStyles$g = makeStyles()(() => ({
260103
261608
  pointLayerButton: {
260104
261609
  borderStyle: "dashed",
260105
261610
  marginTop: "10px",
@@ -260216,7 +261721,7 @@ function PointLayerController(props) {
260216
261721
  featureSelection,
260217
261722
  showStaticColor
260218
261723
  ]);
260219
- const { classes: classes2 } = useStyles$f();
261724
+ const { classes: classes2 } = useStyles$g();
260220
261725
  const { classes: lcClasses } = useControllerSectionStyles();
260221
261726
  const { classes: menuClasses } = useEllipsisMenuStyles();
260222
261727
  const handleVisibleChange = useCallback(() => {
@@ -260245,7 +261750,7 @@ function PointLayerController(props) {
260245
261750
  }, [featureSelection, obsColorEncoding, featureIndex, featureColor]);
260246
261751
  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", sx: channelRowContainerSx, children: [jsx$1(Grid$1, { size: 1, sx: channelControlCellSx, children: jsx$1(Button, { onClick: handleVisibleChange, className: menuClasses.imageLayerVisibleButton, "aria-label": "Toggle layer visibility", children: jsx$1(Visibility, {}) }) }), jsx$1(Grid$1, { size: 1, sx: channelControlCellSx, children: jsx$1(ChannelColorPickerMenu, { theme, color: color2, setColor: setColor2, palette, isStaticColor, isColormap, featureValueColormap, visible }) }), jsx$1(Grid$1, { size: 6, sx: channelSelectorCellSx, children: jsx$1(Typography, { className: menuClasses.imageLayerName, title: label2, children: label2 }) }), jsx$1(Grid$1, { size: 2, sx: channelSliderCellSx, 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, sx: channelControlCellSx, 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", sx: channelControlCellSx, 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] });
260247
261752
  }
260248
- const useStyles$e = makeStyles()(() => ({
261753
+ const useStyles$f = makeStyles()(() => ({
260249
261754
  // Cap the channel name's width; imageLayerName ellipsizes the overflow.
260250
261755
  channelName: {
260251
261756
  overflow: "hidden",
@@ -260264,7 +261769,7 @@ const useStyles$e = makeStyles()(() => ({
260264
261769
  function SegmentationChannelEllipsisMenu(props) {
260265
261770
  const { obsType, featureType, featureValueType, strokeWidth, setStrokeWidth, filled, setFilled, featureSelection, obsColorEncoding, setObsColorEncoding, featureValueColormap, setFeatureValueColormap, featureValueColormapRange, setFeatureValueColormapRange, tooltipsVisible, setTooltipsVisible, tooltipCrosshairsVisible, setTooltipCrosshairsVisible, legendVisible, setLegendVisible } = props;
260266
261771
  const [open2, setOpen] = useState(false);
260267
- const { classes: classes2 } = useStyles$e();
261772
+ const { classes: classes2 } = useStyles$f();
260268
261773
  const { classes: selectClasses2 } = useSelectStyles();
260269
261774
  const { classes: menuClasses } = useEllipsisMenuStyles();
260270
261775
  const filledId = $bdb11010cef70236$export$f680877a34711e37();
@@ -260283,7 +261788,7 @@ function SegmentationChannelController(props) {
260283
261788
  const Visibility = useMemo$1(() => visibleSetting ? VisibilityIcon : VisibilityOffIcon, [visibleSetting]);
260284
261789
  const isStaticColor = obsColorEncoding === "spatialChannelColor";
260285
261790
  const isColormap = obsColorEncoding === "geneSelection";
260286
- const { classes: classes2 } = useStyles$e();
261791
+ const { classes: classes2 } = useStyles$f();
260287
261792
  const { classes: lcClasses } = useControllerSectionStyles();
260288
261793
  const { classes: menuClasses } = useEllipsisMenuStyles();
260289
261794
  const handleVisibleChange = useCallback(() => {
@@ -261524,7 +263029,7 @@ function useBaseQuery(options, Observer, queryClient) {
261524
263029
  function useQuery(options, queryClient) {
261525
263030
  return useBaseQuery(options, QueryObserver);
261526
263031
  }
261527
- const useStyles$d = makeStyles()(() => ({
263032
+ const useStyles$e = makeStyles()(() => ({
261528
263033
  channelMenuButton: {
261529
263034
  backgroundColor: "transparent",
261530
263035
  padding: "3px 0",
@@ -261538,7 +263043,7 @@ const useStyles$d = makeStyles()(() => ({
261538
263043
  function ChannelOptions(props) {
261539
263044
  const { onRemove: onRemove2, onResetWindowUsingIQR, showValueExtent, setShowValueExtent, numResolutions, spatialMaxResolution, setSpatialMaxResolution } = props;
261540
263045
  const [open2, setOpen] = useState(false);
261541
- const { classes: classes2 } = useStyles$d();
263046
+ const { classes: classes2 } = useStyles$e();
261542
263047
  const { classes: selectClasses2 } = useSelectStyles();
261543
263048
  const { classes: menuClasses } = useEllipsisMenuStyles();
261544
263049
  function handleRemove() {
@@ -261551,7 +263056,7 @@ function ChannelOptions(props) {
261551
263056
  const domainTypeId = $bdb11010cef70236$export$f680877a34711e37();
261552
263057
  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" })] });
261553
263058
  }
261554
- const useStyles$c = makeStyles()(() => ({
263059
+ const useStyles$d = makeStyles()(() => ({
261555
263060
  channelSlider: {
261556
263061
  marginTop: "7px"
261557
263062
  }
@@ -261561,13 +263066,13 @@ function ChannelSlider(props) {
261561
263066
  const rgbColor = toRgbUIString(colormapOn, color2, theme);
261562
263067
  const dtype = image2?.getDtype();
261563
263068
  const fullDomain = dtype ? DOMAINS[dtype] : [0, 0];
261564
- const { classes: classes2 } = useStyles$c();
263069
+ const { classes: classes2 } = useStyles$d();
261565
263070
  useEffect(() => {
261566
- if (!window2 && !disabled && Array.isArray(minMaxDomain)) {
261567
- setWindow2(minMaxDomain);
263071
+ if (!window2 && !disabled && Array.isArray(minMaxDomain?.upperThreeQuarters)) {
263072
+ setWindow2(minMaxDomain?.upperThreeQuarters);
261568
263073
  }
261569
263074
  }, [minMaxDomain, window2, disabled]);
261570
- const [min2, max2] = (showValueExtent ? minMaxDomain : fullDomain) || [0, 0];
263075
+ const [min2, max2] = (showValueExtent ? minMaxDomain?.minMax : fullDomain) || [0, 0];
261571
263076
  const step = max2 - min2 < 500 && dtype?.startsWith("Float") ? (max2 - min2) / 500 : 1;
261572
263077
  const handleChangeDebounced = useCallback(debounce$5(setWindow2 || (() => {
261573
263078
  }), 3, { trailing: true }), [setWindow2]);
@@ -261576,7 +263081,7 @@ function ChannelSlider(props) {
261576
263081
  return `${labelPrefix} for ${rgbColor} colormap channel`;
261577
263082
  }, getAriaValueText: () => `Current colormap values: ${rgbColor}-${window2}`, min: min2, max: max2, step, orientation: "horizontal", className: classes2.channelSlider, style: { color: rgbColor }, disabled });
261578
263083
  }
261579
- const useStyles$b = makeStyles()(() => ({
263084
+ const useStyles$c = makeStyles()(() => ({
261580
263085
  visibilityCheckbox: {
261581
263086
  padding: "10px 0"
261582
263087
  }
@@ -261590,10 +263095,10 @@ function getCheckboxColor(colormapOn, color2, theme) {
261590
263095
  function ChannelVisibilityCheckbox(props) {
261591
263096
  const { color: color2, visible, setVisible, disabled, theme, colormapOn } = props;
261592
263097
  const rgbColor = getCheckboxColor(colormapOn, color2, theme);
261593
- const { classes: classes2 } = useStyles$b();
263098
+ const { classes: classes2 } = useStyles$c();
261594
263099
  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" } } });
261595
263100
  }
261596
- const useStyles$a = makeStyles()(() => ({
263101
+ const useStyles$b = makeStyles()(() => ({
261597
263102
  oneLineChannelSelect: {
261598
263103
  width: "90%",
261599
263104
  marginLeft: "5%",
@@ -261602,7 +263107,7 @@ const useStyles$a = makeStyles()(() => ({
261602
263107
  }));
261603
263108
  function ChannelSelectionDropdown(props) {
261604
263109
  const { featureIndex, channelsSortOrder, targetC, setTargetC, setWindow: setWindow2, disabled } = props;
261605
- const { classes: classes2 } = useStyles$a();
263110
+ const { classes: classes2 } = useStyles$b();
261606
263111
  const { classes: selectClasses2 } = useSelectStyles();
261607
263112
  function handleChange(event2) {
261608
263113
  setTargetC(event2.target.value === "" ? null : Number(event2.target.value));
@@ -261672,7 +263177,13 @@ function ImageChannelController(props) {
261672
263177
  use3d: ctx.queryKey[5]
261673
263178
  });
261674
263179
  const [newDomain] = stats.domains;
261675
- return newDomain;
263180
+ return {
263181
+ minMax: newDomain,
263182
+ // iqr: [stats.q1s[0], stats.q3s[0]],
263183
+ // This seems to work better than the IQR for the slider
263184
+ // upper bound.
263185
+ upperThreeQuarters: [stats.q1s[0], newDomain[1]]
263186
+ };
261676
263187
  },
261677
263188
  meta: { image: image2 }
261678
263189
  });
@@ -261680,7 +263191,7 @@ function ImageChannelController(props) {
261680
263191
  const disabled = minMaxQuery.isLoading;
261681
263192
  function handleResetWindowUsingIQR() {
261682
263193
  if (!disabled) {
261683
- setWindow2(minMaxDomain);
263194
+ setWindow2(minMaxDomain?.upperThreeQuarters);
261684
263195
  }
261685
263196
  }
261686
263197
  return jsxs(Grid$1, { container: true, direction: "row", justifyContent: "space-between", sx: channelRowContainerSx, children: [jsx$1(Grid$1, { size: 1, sx: channelControlCellSx, children: jsx$1(ChannelVisibilityCheckbox, { color: color2, setColor: setColor2, visible, setVisible, disabled: isLoading, theme, colormapOn }) }), jsx$1(Grid$1, { size: 1, sx: channelControlCellSx, children: jsx$1(ChannelColorPickerMenu, { color: color2, setColor: setColor2, visible, setVisible, disabled: isLoading, theme, isStaticColor: !colormapOn, palette: VIEWER_PALETTE }) }), jsx$1(Grid$1, { size: 6, sx: channelSelectorCellSx, children: jsx$1(ChannelSelectionDropdown, { featureIndex, targetC, setTargetC, setWindow: setWindow2, disabled: isLoading, channelsSortOrder }) }), jsx$1(Grid$1, { size: 3, sx: channelSliderCellSx, children: jsx$1(ChannelSlider, { image: image2, targetT, targetZ, targetC, window: window2, setWindow: setWindow2, disabled, color: color2, theme, colormapOn, showValueExtent, minMaxDomain }) }), jsx$1(Grid$1, { size: 1, sx: channelControlCellSx, children: jsx$1(ChannelOptions, { onRemove: onRemove2, showValueExtent, setShowValueExtent, onResetWindowUsingIQR: handleResetWindowUsingIQR, numResolutions, spatialMaxResolution, setSpatialMaxResolution }) })] });
@@ -261737,7 +263248,7 @@ function LoadingStatusIndicator(props) {
261737
263248
  }
261738
263249
  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 }) })] });
261739
263250
  }
261740
- const useStyles$9 = makeStyles()(() => ({
263251
+ const useStyles$a = makeStyles()(() => ({
261741
263252
  imageLayerButton: {
261742
263253
  borderStyle: "dashed",
261743
263254
  marginTop: "10px",
@@ -261880,7 +263391,7 @@ function ImageLayerController(props) {
261880
263391
  }, [visible, setVisible]);
261881
263392
  const handleOpacityChange = useCallback((e3, v) => setOpacity2(v), [setOpacity2]);
261882
263393
  const handleOpenChange = useCallback(() => setOpen((prev2) => !prev2), []);
261883
- const { classes: classes2 } = useStyles$9();
263394
+ const { classes: classes2 } = useStyles$a();
261884
263395
  const { classes: menuClasses } = useEllipsisMenuStyles();
261885
263396
  const { classes: controllerSectionClasses } = useControllerSectionStyles();
261886
263397
  const isMultiChannel = photometricInterpretation !== "RGB";
@@ -261892,7 +263403,7 @@ function ImageLayerController(props) {
261892
263403
  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);
261893
263404
  }), 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] }) });
261894
263405
  }
261895
- const useStyles$8 = makeStyles()((theme) => ({
263406
+ const useStyles$9 = makeStyles()((theme) => ({
261896
263407
  dimensionsIcon: {
261897
263408
  height: "40px",
261898
263409
  width: "40px",
@@ -261916,7 +263427,7 @@ const useStyles$8 = makeStyles()((theme) => ({
261916
263427
  function GlobalDimensionSlider(props) {
261917
263428
  const { label: label2, targetValue, setTargetValue, min: min2 = 0, max: max2 = 0, spatialRenderingMode = null, setSpatialRenderingMode = null, enable3d = true } = props;
261918
263429
  const { classes: lcClasses } = useControllerSectionStyles();
261919
- const { classes: classes2 } = useStyles$8();
263430
+ const { classes: classes2 } = useStyles$9();
261920
263431
  const isForZ = spatialRenderingMode !== null;
261921
263432
  const is3dMode = spatialRenderingMode === "3D";
261922
263433
  function handleRenderingModeChange(event2) {
@@ -261927,7 +263438,7 @@ function GlobalDimensionSlider(props) {
261927
263438
  }
261928
263439
  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", sx: channelRowContainerSx, children: [jsx$1(Grid$1, { size: 1, sx: channelControlCellSx, children: jsx$1(SvgDimensions, { className: classes2.dimensionsIcon }) }), jsx$1(Grid$1, { size: 1, sx: channelSelectorCellSx, children: jsx$1(Typography, { className: classes2.dimensionLabel, children: label2 }) }), jsx$1(Grid$1, { size: 8, sx: channelSliderCellSx, 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, sx: channelControlCellSx, children: isForZ && enable3d ? 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 })] }) }) });
261929
263440
  }
261930
- const useStyles$7 = makeStyles()(() => ({
263441
+ const useStyles$8 = makeStyles()(() => ({
261931
263442
  layerTypeCentroidSegmentationIcon: {
261932
263443
  height: "100%",
261933
263444
  marginLeft: "1px",
@@ -261977,7 +263488,7 @@ function CombinedEllipsisMenu(props) {
261977
263488
  }
261978
263489
  function SegmentationCentroidsController(props) {
261979
263490
  const { theme, segScope, segLayerCoordination, segChannelScopesByLayer, segChannelCoordination, pointScope, pointLayerCoordination } = props;
261980
- const { classes: classes2 } = useStyles$7();
263491
+ const { classes: classes2 } = useStyles$8();
261981
263492
  const { classes: lcClasses } = useControllerSectionStyles();
261982
263493
  const { classes: menuClasses } = useEllipsisMenuStyles();
261983
263494
  const channelScopes = segChannelScopesByLayer?.[segScope] ?? [];
@@ -262419,6 +263930,218 @@ function LinkControllerSubscriber(props) {
262419
263930
  } = props;
262420
263931
  return jsx$1(TitleInfo, { title: title2, closeButtonVisible, removeGridComponent, isScroll: true, theme, isReady: true, children: jsx$1(LinkController, { linkIDInit: linkID, authToken, linkEndpoint, websocketEndpoint, fileTypes, stores, coordinationTypes }) });
262421
263932
  }
263933
+ const useStyles$7 = makeStyles()((theme) => ({
263934
+ textSection: {
263935
+ padding: "6px 10px"
263936
+ },
263937
+ annotationMarkdown: {
263938
+ "& p, details, table": {
263939
+ fontSize: "80%",
263940
+ opacity: "0.8"
263941
+ },
263942
+ "& details": {
263943
+ marginBottom: "6px"
263944
+ },
263945
+ "& summary": {
263946
+ // TODO(monorepo): lighten color by 10%
263947
+ borderBottom: `1px solid ${theme.palette.primaryBackground}`,
263948
+ cursor: "pointer"
263949
+ }
263950
+ },
263951
+ navigation: {
263952
+ display: "flex",
263953
+ alignItems: "center",
263954
+ justifyContent: "space-between",
263955
+ padding: "0 4px",
263956
+ borderTop: `1px solid ${theme.palette.primaryBackground}`,
263957
+ borderBottom: `1px solid ${theme.palette.primaryBackground}`
263958
+ }
263959
+ }));
263960
+ function AnnotationText(props) {
263961
+ const { text: text2, textType } = props;
263962
+ const { classes: classes2 } = useStyles$7();
263963
+ if (!text2) {
263964
+ return null;
263965
+ }
263966
+ return jsx$1("div", { className: classes2.annotationMarkdown, children: textType === DescriptionType.MARKDOWN ? jsx$1(Markdown, { children: text2 }) : jsx$1("p", { children: text2 }) });
263967
+ }
263968
+ function AnnotationController(props) {
263969
+ const { story, frameIndex, setFrameIndex } = props;
263970
+ const { classes: classes2 } = useStyles$7();
263971
+ if (!story) {
263972
+ return jsx$1("div", { className: classes2.textSection, children: jsx$1(Typography, { variant: "body2", children: "No annotation story has been loaded." }) });
263973
+ }
263974
+ const frames = story.frames || [];
263975
+ const numFrames = frames.length;
263976
+ const hasFrameIndex = typeof frameIndex === "number";
263977
+ const currentFrame = hasFrameIndex ? frames[frameIndex] : null;
263978
+ const canGoBack = hasFrameIndex;
263979
+ const canGoForward = hasFrameIndex ? frameIndex < numFrames - 1 : numFrames > 0;
263980
+ function goBack() {
263981
+ setFrameIndex(frameIndex > 0 ? frameIndex - 1 : null);
263982
+ }
263983
+ function goForward() {
263984
+ setFrameIndex(hasFrameIndex ? frameIndex + 1 : 0);
263985
+ }
263986
+ const frameLabel = hasFrameIndex ? `Frame ${frameIndex + 1} of ${numFrames}` : `Overview (${numFrames} frames)`;
263987
+ return jsxs("div", { children: [jsxs("div", { className: classes2.textSection, children: [story.title ? jsx$1(Typography, { variant: "h6", children: story.title }) : null, jsx$1(AnnotationText, { text: story.description, textType: story.descriptionType })] }), jsxs("div", { className: classes2.navigation, children: [jsx$1(Button, { size: "small", onClick: goBack, disabled: !canGoBack, startIcon: jsx$1(ArrowLeft, {}), children: "Previous" }), jsx$1(Typography, { variant: "body2", children: frameLabel }), jsx$1(Button, { size: "small", onClick: goForward, disabled: !canGoForward, endIcon: jsx$1(ArrowRight, {}), children: "Next" })] }), currentFrame ? jsxs("div", { className: classes2.textSection, children: [currentFrame.title ? jsx$1(Typography, { variant: "subtitle1", children: currentFrame.title }) : null, jsx$1(AnnotationText, { text: currentFrame.description, textType: currentFrame.descriptionType })] }) : null, jsx$1(MenuList, { dense: true, "aria-label": "Annotation frames", children: frames.map((frame2, i2) => jsx$1(MenuItem, { selected: i2 === frameIndex, onClick: () => setFrameIndex(i2), children: jsx$1(ListItemText, { primary: `${i2 + 1}. ${frame2.title || frame2.uid}` }) }, frame2.uid)) })] });
263988
+ }
263989
+ function AnnotationControllerSubscriber(props) {
263990
+ const {
263991
+ uuid: uuid2,
263992
+ removeGridComponent,
263993
+ theme,
263994
+ title: title2 = "Annotation",
263995
+ closeButtonVisible,
263996
+ downloadButtonVisible,
263997
+ helpText = ViewHelpMapping.ANNOTATION_CONTROLLER,
263998
+ // TODO: importable/exportable
263999
+ // Note: areAnnotationsEditable is a prop passed down from
264000
+ // the ancestor <Vitessce/> or <VitS/> component.
264001
+ // TODO: use areAnnotationsEditable.
264002
+ // eslint-disable-next-line no-unused-vars
264003
+ areAnnotationsEditable
264004
+ } = props;
264005
+ const loaders = useLoaders();
264006
+ const [
264007
+ // eslint-disable-next-line no-unused-vars
264008
+ coordinationScopes,
264009
+ _coordinationScopesBy,
264010
+ coordinationValues
264011
+ ] = useViewMapping(uuid2);
264012
+ const [{ dataset, annotationStory, annotationFrameIndex }, { setAnnotationStory, setAnnotationFrameIndex }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType$1.ANNOTATION_CONTROLLER], coordinationScopes, coordinationValues, uuid2);
264013
+ const [, annotationStoryStatus, annotationStoryUrls, annotationStoryError] = useAnnotationStoryData(loaders, dataset, false, { setAnnotationStory }, { annotationStory }, {});
264014
+ const errors = [annotationStoryError];
264015
+ const isReady = useReady([annotationStoryStatus]);
264016
+ const urls2 = useUrls([annotationStoryUrls]);
264017
+ return jsx$1(TitleInfo, { title: title2, theme, closeButtonVisible, downloadButtonVisible, removeGridComponent, isScroll: true, isReady, urls: urls2, errors, helpText, withPadding: false, children: jsx$1(AnnotationController, { story: annotationStory, frameIndex: annotationFrameIndex, setFrameIndex: setAnnotationFrameIndex }) });
264018
+ }
264019
+ const NUM_GRID_COLUMNS = 12;
264020
+ const NUM_GRID_ROWS = 12;
264021
+ const DEFAULT_ANNOTATION_CONTROLLER_UID = "annotation-controller";
264022
+ const DEFAULT_ANNOTATION_CONTROLLER_WIDTH = 3;
264023
+ function hasAnnotationControllerView(config3) {
264024
+ return (config3?.layout || []).some((view) => view.component === ViewType$1.ANNOTATION_CONTROLLER);
264025
+ }
264026
+ function getUniqueViewUid(layout, baseUid) {
264027
+ const usedUids = new Set(layout.map((view) => view.uid).filter(Boolean));
264028
+ if (!usedUids.has(baseUid)) {
264029
+ return baseUid;
264030
+ }
264031
+ let i2 = 1;
264032
+ while (usedUids.has(`${baseUid}-${i2}`)) {
264033
+ i2 += 1;
264034
+ }
264035
+ return `${baseUid}-${i2}`;
264036
+ }
264037
+ function getExistingScope(layout, coordinationType) {
264038
+ const scope = layout.map((view) => view.coordinationScopes?.[coordinationType]).find((s2) => typeof s2 === "string");
264039
+ return scope;
264040
+ }
264041
+ function addAnnotationControllerView(config3, options = {}) {
264042
+ const { width: width2 = DEFAULT_ANNOTATION_CONTROLLER_WIDTH, uid: uid2 = DEFAULT_ANNOTATION_CONTROLLER_UID } = options;
264043
+ if (!Number.isInteger(width2) || width2 < 1 || width2 >= NUM_GRID_COLUMNS) {
264044
+ throw new Error(`The annotationController width must be a whole number between 1 and ${NUM_GRID_COLUMNS - 1}.`);
264045
+ }
264046
+ const layout = config3?.layout || [];
264047
+ const remainingColumns = NUM_GRID_COLUMNS - width2;
264048
+ const scaleX2 = (value2) => Math.round(Math.min(Math.max(value2, 0), NUM_GRID_COLUMNS) * remainingColumns / NUM_GRID_COLUMNS);
264049
+ const newLayout = layout.map((view) => {
264050
+ const x2 = scaleX2(view.x);
264051
+ const right2 = Math.max(scaleX2(view.x + view.w), x2 + 1);
264052
+ return {
264053
+ ...view,
264054
+ x: Math.min(x2, remainingColumns - 1),
264055
+ w: Math.min(right2, remainingColumns) - Math.min(x2, remainingColumns - 1)
264056
+ };
264057
+ });
264058
+ const coordinationScopes = Object.fromEntries([
264059
+ CoordinationType$1.ANNOTATION_STORY,
264060
+ CoordinationType$1.ANNOTATION_FRAME_INDEX,
264061
+ CoordinationType$1.ANNOTATION_OVERLAY_VISIBLE
264062
+ ].map((coordinationType) => [coordinationType, getExistingScope(layout, coordinationType)]).filter(([, scope]) => scope !== void 0));
264063
+ newLayout.push({
264064
+ uid: getUniqueViewUid(layout, uid2),
264065
+ component: ViewType$1.ANNOTATION_CONTROLLER,
264066
+ ...Object.keys(coordinationScopes).length > 0 ? { coordinationScopes } : {},
264067
+ x: remainingColumns,
264068
+ y: 0,
264069
+ w: width2,
264070
+ h: NUM_GRID_ROWS
264071
+ });
264072
+ return {
264073
+ ...config3,
264074
+ layout: newLayout
264075
+ };
264076
+ }
264077
+ function isAnyAnnotationEditing(config3) {
264078
+ const type2 = CoordinationType$1.ANNOTATION_EDITABLE;
264079
+ const scopeValues = Object.values(config3?.coordinationSpace?.[type2] || {});
264080
+ const viewValues = (config3?.layout || []).map((view) => view.coordinationValues?.[type2]);
264081
+ return [...scopeValues, ...viewValues].some((value2) => value2 === true);
264082
+ }
264083
+ function hasScopeMapping(view, coordinationSpace, coordinationType) {
264084
+ if (view.coordinationScopes?.[coordinationType] !== void 0) {
264085
+ return true;
264086
+ }
264087
+ const metaScopes = view.coordinationScopes?.[CoordinationType$1.META_COORDINATION_SCOPES];
264088
+ const metaScopesArr = Array.isArray(metaScopes) ? metaScopes : [metaScopes].filter(Boolean);
264089
+ const metaSpace = coordinationSpace?.[CoordinationType$1.META_COORDINATION_SCOPES] || {};
264090
+ return metaScopesArr.some((metaScope) => metaSpace[metaScope]?.[coordinationType] !== void 0);
264091
+ }
264092
+ function getUnusedScopeName(config3, coordinationType) {
264093
+ const usedScopes = /* @__PURE__ */ new Set([
264094
+ ...Object.keys(config3?.coordinationSpace?.[coordinationType] || {}),
264095
+ ...(config3?.layout || []).map((view) => view.coordinationScopes?.[coordinationType])
264096
+ ]);
264097
+ let i2 = 0;
264098
+ const toName = (n3) => n3 < 26 ? String.fromCharCode(65 + n3) : toName(Math.floor(n3 / 26) - 1) + String.fromCharCode(65 + n3 % 26);
264099
+ while (usedScopes.has(toName(i2))) {
264100
+ i2 += 1;
264101
+ }
264102
+ return toName(i2);
264103
+ }
264104
+ function setAnnotationEditing(config3, value2) {
264105
+ if (!hasAnnotationControllerView(config3)) {
264106
+ throw new Error("The view config does not contain an annotationController view. Call addAnnotationControllerView first.");
264107
+ }
264108
+ const type2 = CoordinationType$1.ANNOTATION_EDITABLE;
264109
+ const coordinationSpace = config3.coordinationSpace || {};
264110
+ const supportsEditable = (view) => view.component === ViewType$1.ANNOTATION_CONTROLLER || COMPONENT_COORDINATION_TYPES[view.component]?.includes(type2);
264111
+ const needsScope = (view) => supportsEditable(view) && view.coordinationValues?.[type2] === void 0 && !hasScopeMapping(view, coordinationSpace, type2);
264112
+ const existingControllerScope = config3.layout.filter((view) => view.component === ViewType$1.ANNOTATION_CONTROLLER).map((view) => view.coordinationScopes?.[type2]).find((scope) => typeof scope === "string");
264113
+ const newScope = config3.layout.some(needsScope) ? existingControllerScope ?? getUnusedScopeName(config3, type2) : void 0;
264114
+ const newLayout = config3.layout.map((view) => {
264115
+ if (needsScope(view)) {
264116
+ return {
264117
+ ...view,
264118
+ coordinationScopes: { ...view.coordinationScopes, [type2]: newScope }
264119
+ };
264120
+ }
264121
+ if (view.coordinationValues?.[type2] !== void 0) {
264122
+ return {
264123
+ ...view,
264124
+ coordinationValues: { ...view.coordinationValues, [type2]: value2 }
264125
+ };
264126
+ }
264127
+ return view;
264128
+ });
264129
+ const newScopeValues = Object.fromEntries([...Object.keys(coordinationSpace[type2] || {}), ...newScope ? [newScope] : []].map((scope) => [scope, value2]));
264130
+ return {
264131
+ ...config3,
264132
+ coordinationSpace: {
264133
+ ...coordinationSpace,
264134
+ [type2]: newScopeValues
264135
+ },
264136
+ layout: newLayout
264137
+ };
264138
+ }
264139
+ function enableAnnotationEditing(config3) {
264140
+ return setAnnotationEditing(config3, true);
264141
+ }
264142
+ function disableAnnotationEditing(config3) {
264143
+ return setAnnotationEditing(config3, false);
264144
+ }
262422
264145
  const useStyles$6 = makeStyles()(() => ({
262423
264146
  info: {
262424
264147
  // details
@@ -264230,7 +265953,7 @@ function HiglassGlobalStyles(props) {
264230
265953
  }
264231
265954
  register({ dataFetcher: ZarrMultivecDataFetcher_default, config: ZarrMultivecDataFetcher_default.config }, { pluginType: "dataFetcher" });
264232
265955
  const LazyHiGlassComponent = React__default.lazy(async () => {
264233
- const { HiGlassComponent } = await import("./higlass-BLYM2C4s.js");
265956
+ const { HiGlassComponent } = await import("./higlass-BzshVp46.js");
264234
265957
  return { default: HiGlassComponent };
264235
265958
  });
264236
265959
  const HG_SIZE = 800;
@@ -267187,7 +268910,7 @@ function NeuroglancerGlobalStyles(props) {
267187
268910
  const { classes: classes2 } = props;
267188
268911
  return jsxs(Fragment, { children: [jsx$1(GlobalStyles$3, { styles: globalNeuroglancerCss }), jsx$1(ScopedGlobalStyles, { styles: globalNeuroglancerStyles, parentClassName: classes2.neuroglancerWrapper })] });
267189
268912
  }
267190
- const LazyReactNeuroglancer = React__default.lazy(() => import("./ReactNeuroglancer-Cm_ERWOf.js"));
268913
+ const LazyReactNeuroglancer = React__default.lazy(() => import("./ReactNeuroglancer-Ch1xO_u5.js"));
267191
268914
  function createWorker() {
267192
268915
  const worker = new WorkerFactory$1();
267193
268916
  worker.AsyncComputationWorker = WorkerFactory;
@@ -321504,7 +323227,7 @@ const VEGA_THEMES = {
321504
323227
  };
321505
323228
  const DATASET_NAME = "table";
321506
323229
  function VegaGlobalStyles() {
321507
- const { theme } = useStyles$z();
323230
+ const { theme } = useStyles$A();
321508
323231
  return jsx$1(GlobalStyles$3, { styles: {
321509
323232
  "#vg-tooltip-element.vg-tooltip.custom-theme": {
321510
323233
  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)",
@@ -321538,7 +323261,7 @@ function renderTooltipContents(tooltipText) {
321538
323261
  }
321539
323262
  function VegaPlot(props) {
321540
323263
  const { spec: partialSpec, data: data2, getTooltipText, signalListeners, renderer = "svg", onNewView } = props;
321541
- const { classes: tooltipClasses2 } = useStyles$u();
323264
+ const { classes: tooltipClasses2 } = useStyles$v();
321542
323265
  const tooltipHandler = useMemo$1(() => {
321543
323266
  if (typeof getTooltipText === "function") {
321544
323267
  const tooltipConfig = {
@@ -324250,6 +325973,24 @@ class ObsSetsJsonLoader extends JsonLoader {
324250
325973
  }, url, coordinationValues));
324251
325974
  }
324252
325975
  }
325976
+ class AnnotationStoryJsonLoader extends JsonLoader {
325977
+ constructor(dataSource, params2) {
325978
+ super(dataSource, params2);
325979
+ this.schema = annotationStoryObj;
325980
+ }
325981
+ async load() {
325982
+ if (this.cachedResult) {
325983
+ return this.cachedResult;
325984
+ }
325985
+ const payload = await super.load();
325986
+ const { data: annotationStory, url } = payload;
325987
+ const coordinationValues = {
325988
+ annotationStory
325989
+ };
325990
+ this.cachedResult = new LoaderResult({ annotationStory }, url, coordinationValues);
325991
+ return this.cachedResult;
325992
+ }
325993
+ }
324253
325994
  class CellsJsonAsObsEmbeddingLoader extends JsonLoader {
324254
325995
  constructor(dataSource, params2) {
324255
325996
  super(dataSource, params2);
@@ -347039,7 +348780,8 @@ const baseViewTypes = [
347039
348780
  makeViewType(ViewType$1.COMPARATIVE_HEADING, ComparativeHeadingSubscriber),
347040
348781
  makeViewType(ViewType$1.SAMPLE_SET_PAIR_MANAGER, SampleSetPairManagerSubscriber),
347041
348782
  makeViewType(ViewType$1.OBS_SET_COMPOSITION_BAR_PLOT, CellSetCompositionBarPlotSubscriber),
347042
- makeViewType(ViewType$1.FEATURE_SET_ENRICHMENT_BAR_PLOT, FeatureSetEnrichmentBarPlotSubscriber)
348783
+ makeViewType(ViewType$1.FEATURE_SET_ENRICHMENT_BAR_PLOT, FeatureSetEnrichmentBarPlotSubscriber),
348784
+ makeViewType(ViewType$1.ANNOTATION_CONTROLLER, AnnotationControllerSubscriber)
347043
348785
  ];
347044
348786
  const baseFileTypes = [
347045
348787
  // All CSV file types
@@ -347056,6 +348798,7 @@ const baseFileTypes = [
347056
348798
  // All JSON file types
347057
348799
  makeFileType(FileType$1.OBS_SEGMENTATIONS_JSON, DataType$3.OBS_SEGMENTATIONS, ObsSegmentationsJsonLoader, JsonSource, z.null()),
347058
348800
  makeFileType(FileType$1.OBS_SETS_JSON, DataType$3.OBS_SETS, ObsSetsJsonLoader, JsonSource, z.null()),
348801
+ makeFileType(FileType$1.ANNOTATION_STORY_JSON, DataType$3.ANNOTATION_STORY, AnnotationStoryJsonLoader, JsonSource, z.null()),
347059
348802
  // All AnnData file types
347060
348803
  ...makeZarrFileTypes(FileType$1.OBS_SETS_ANNDATA_ZARR, DataType$3.OBS_SETS, ObsSetsAnndataLoader, AnnDataSource, obsSetsAnndataSchema),
347061
348804
  ...makeZarrFileTypes(FileType$1.OBS_EMBEDDING_ANNDATA_ZARR, DataType$3.OBS_EMBEDDING, ObsEmbeddingAnndataLoader, AnnDataSource, obsEmbeddingAnndataSchema),
@@ -347407,7 +349150,16 @@ const baseCoordinationTypes = [
347407
349150
  new PluginCoordinationType(CoordinationType$1.FEATURE_POINT_SIGNIFICANCE_THRESHOLD, 0.01, z.number().nullable()),
347408
349151
  new PluginCoordinationType(CoordinationType$1.FEATURE_LABEL_SIGNIFICANCE_THRESHOLD, 0.01, z.number().nullable()),
347409
349152
  new PluginCoordinationType(CoordinationType$1.FEATURE_POINT_FOLD_CHANGE_THRESHOLD, 1, z.number().nullable()),
347410
- new PluginCoordinationType(CoordinationType$1.FEATURE_LABEL_FOLD_CHANGE_THRESHOLD, 5, z.number().nullable())
349153
+ new PluginCoordinationType(CoordinationType$1.FEATURE_LABEL_FOLD_CHANGE_THRESHOLD, 5, z.number().nullable()),
349154
+ // Annotation frames
349155
+ new PluginCoordinationType(CoordinationType$1.ANNOTATION_STORY, null, annotationStoryObj.nullable()),
349156
+ new PluginCoordinationType(CoordinationType$1.ANNOTATION_SHAPES, null, annotationShapesArray.nullable()),
349157
+ new PluginCoordinationType(CoordinationType$1.ANNOTATION_FRAME_INDEX, null, z.number().nullable()),
349158
+ new PluginCoordinationType(CoordinationType$1.ANNOTATION_OVERLAY_VISIBLE, true, z.boolean()),
349159
+ new PluginCoordinationType(CoordinationType$1.ANNOTATION_TRANSITION_DURATION, 0, z.number()),
349160
+ // TODO: make this non-zero by default?
349161
+ new PluginCoordinationType(CoordinationType$1.ANNOTATION_SEMANTIC_ZOOM, true, z.boolean()),
349162
+ new PluginCoordinationType(CoordinationType$1.ANNOTATION_EDITABLE, false, z.boolean())
347411
349163
  ];
347412
349164
  const baseAsyncFunctions = [
347413
349165
  new PluginAsyncFunction(AsyncFunctionType.AUTOCOMPLETE_FEATURE, autocompleteFeature),
@@ -347515,6 +349267,11 @@ export {
347515
349267
  PluginJointFileType as a5,
347516
349268
  PluginAsyncFunction as a6,
347517
349269
  z as a7,
349270
+ hasAnnotationControllerView as a8,
349271
+ addAnnotationControllerView as a9,
349272
+ isAnyAnnotationEditing as aa,
349273
+ enableAnnotationEditing as ab,
349274
+ disableAnnotationEditing as ac,
347518
349275
  LercAddCompression as b,
347519
349276
  commonjsGlobal$1 as c,
347520
349277
  requireAssign as d,