@cytario/web 2.1.3 → 2.1.5

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 (202) hide show
  1. package/README.md +20 -20
  2. package/app/.server/auth/README.md +8 -8
  3. package/app/.server/auth/authMiddleware.ts +9 -25
  4. package/app/.server/auth/exchangeAuthCode.ts +2 -6
  5. package/app/.server/auth/getS3Client.ts +3 -13
  6. package/app/.server/auth/getSessionCredentials.ts +6 -20
  7. package/app/.server/auth/getUserInfo.ts +2 -6
  8. package/app/.server/auth/keycloakAdmin/client.ts +2 -9
  9. package/app/.server/auth/keycloakAdmin/groups.ts +9 -26
  10. package/app/.server/auth/keycloakAdmin/users.ts +7 -23
  11. package/app/.server/auth/oauthState.ts +4 -13
  12. package/app/.server/auth/redirectIfAuthenticated.ts +1 -3
  13. package/app/.server/auth/refreshAuthTokens.ts +5 -19
  14. package/app/.server/auth/sessionMiddleware.ts +1 -4
  15. package/app/.server/auth/sessionStorage.ts +1 -4
  16. package/app/.server/auth/verifyIdToken.ts +1 -3
  17. package/app/.server/auth/wellKnownEndpoints.ts +1 -4
  18. package/app/.server/db/redis.ts +5 -1
  19. package/app/.server/logging.ts +1 -4
  20. package/app/.server/requestDurationMiddleware.ts +1 -4
  21. package/app/components/.client/ImageViewer/README.md +5 -5
  22. package/app/components/.client/ImageViewer/components/ChannelsController/ChannelsController.tsx +7 -9
  23. package/app/components/.client/ImageViewer/components/ChannelsController/ChannelsControllerBrightfieldItem.tsx +1 -2
  24. package/app/components/.client/ImageViewer/components/ChannelsController/ChannelsControllerItem.tsx +2 -5
  25. package/app/components/.client/ImageViewer/components/ChannelsController/ChannelsControllerItemList.tsx +7 -15
  26. package/app/components/.client/ImageViewer/components/ChannelsController/ColorPicker/ColorPicker.tsx +2 -9
  27. package/app/components/.client/ImageViewer/components/ChannelsController/ColorPicker/ColorSwatch.tsx +1 -4
  28. package/app/components/.client/ImageViewer/components/ChannelsController/DomainSlider.tsx +1 -3
  29. package/app/components/.client/ImageViewer/components/ChannelsController/Histogram.tsx +16 -18
  30. package/app/components/.client/ImageViewer/components/ChannelsController/HistogramChannel.tsx +2 -8
  31. package/app/components/.client/ImageViewer/components/ChannelsController/MinMaxSettings.tsx +6 -15
  32. package/app/components/.client/ImageViewer/components/FeatureBar/FeatureBarDragHandle.tsx +1 -5
  33. package/app/components/.client/ImageViewer/components/FeatureBar/FeatureBarToggle.tsx +1 -5
  34. package/app/components/.client/ImageViewer/components/FeatureBar/FeatureItem.tsx +1 -5
  35. package/app/components/.client/ImageViewer/components/FeatureBar/Presets.tsx +3 -11
  36. package/app/components/.client/ImageViewer/components/FeatureBar/useFeatureBar.tsx +16 -25
  37. package/app/components/.client/ImageViewer/components/Image/Channels/useChannelsLayer.ts +7 -18
  38. package/app/components/.client/ImageViewer/components/Image/ImageContainer.tsx +1 -1
  39. package/app/components/.client/ImageViewer/components/Image/ImagePanel.tsx +6 -26
  40. package/app/components/.client/ImageViewer/components/Image/ImagePreview.tsx +2 -9
  41. package/app/components/.client/ImageViewer/components/Image/Overlays/AdditivePolygonLayer.tsx +1 -5
  42. package/app/components/.client/ImageViewer/components/Image/Overlays/AdditiveScatterplotLayer.tsx +1 -5
  43. package/app/components/.client/ImageViewer/components/Image/Overlays/OverlaysLayer.tsx +6 -24
  44. package/app/components/.client/ImageViewer/components/Image/Overlays/markerUniforms.ts +2 -5
  45. package/app/components/.client/ImageViewer/components/Image/Overlays/useOverlaysLayer.tsx +7 -21
  46. package/app/components/.client/ImageViewer/components/Image/useInitializeChannels.ts +1 -7
  47. package/app/components/.client/ImageViewer/components/Image/useResizeObserver.ts +1 -1
  48. package/app/components/.client/ImageViewer/components/Magnifier.tsx +5 -13
  49. package/app/components/.client/ImageViewer/components/Measurements/ActiveViewStatePreview.tsx +3 -8
  50. package/app/components/.client/ImageViewer/components/Measurements/CursorTick.tsx +2 -7
  51. package/app/components/.client/ImageViewer/components/Measurements/Ruler.tsx +1 -8
  52. package/app/components/.client/ImageViewer/components/Measurements/SlideCarrier.tsx +3 -13
  53. package/app/components/.client/ImageViewer/components/Measurements/Tick.tsx +1 -1
  54. package/app/components/.client/ImageViewer/components/Measurements/calculateViewStateToFit.ts +1 -1
  55. package/app/components/.client/ImageViewer/components/Measurements/useMeasurements.ts +9 -28
  56. package/app/components/.client/ImageViewer/components/OverlaysController/AddOverlay.tsx +4 -13
  57. package/app/components/.client/ImageViewer/components/OverlaysController/OverlayPicker.modal.tsx +1 -6
  58. package/app/components/.client/ImageViewer/components/OverlaysController/OverlaysController.Item.tsx +24 -54
  59. package/app/components/.client/ImageViewer/components/OverlaysController/OverlaysController.tsx +1 -3
  60. package/app/components/.client/ImageViewer/components/SplitViewToggle.tsx +1 -3
  61. package/app/components/.client/ImageViewer/components/ViewerHeader.tsx +1 -3
  62. package/app/components/.client/ImageViewer/state/decoders/decodeJPEG2000.d.ts +9 -11
  63. package/app/components/.client/ImageViewer/state/decoders/decodeJPEG2000.js +11 -11
  64. package/app/components/.client/ImageViewer/state/decoders/decoder.worker.js +49 -49
  65. package/app/components/.client/ImageViewer/state/decoders/genericDecoder.ts +76 -81
  66. package/app/components/.client/ImageViewer/state/decoders/jp2k-decoder.ts +9 -9
  67. package/app/components/.client/ImageViewer/state/decoders/lzwDecoder.ts +9 -9
  68. package/app/components/.client/ImageViewer/state/loaders/loadBioformatsZarrWithCredentials.ts +10 -22
  69. package/app/components/.client/ImageViewer/state/store/ViewerStoreContext.tsx +4 -18
  70. package/app/components/.client/ImageViewer/state/store/createViewerStore.ts +110 -194
  71. package/app/components/.client/ImageViewer/state/store/getInitialChannelsState.ts +2 -6
  72. package/app/components/.client/ImageViewer/state/store/selectors.ts +9 -9
  73. package/app/components/.client/ImageViewer/state/store/types.ts +3 -12
  74. package/app/components/.client/ImageViewer/state/transport/CredentialedHTTPStore.ts +1 -5
  75. package/app/components/.client/ImageViewer/state/transport/SigV4TiffClient.ts +2 -9
  76. package/app/components/.client/ImageViewer/utils/getSelectionStats.ts +1 -4
  77. package/app/components/.client/ImageViewer/utils/handleImageViewerHover.ts +1 -1
  78. package/app/components/.client/ImageViewer/utils/mapChannelConfigsToState.ts +2 -4
  79. package/app/components/.client/ImageViewer/utils/useTilesLoading.ts +3 -3
  80. package/app/components/AppHeader.tsx +1 -4
  81. package/app/components/Breadcrumbs/Breadcrumbs.tsx +4 -13
  82. package/app/components/Breadcrumbs/getCrumbs.tsx +1 -1
  83. package/app/components/ClientOnly.tsx +1 -1
  84. package/app/components/Container.tsx +3 -15
  85. package/app/components/DataGrid/ConvertOverlay.modal.tsx +1 -6
  86. package/app/components/DataGrid/DataGrid.tsx +7 -27
  87. package/app/components/DataGrid/WktSvg.tsx +2 -4
  88. package/app/components/DataGrid/getParquetSchema.ts +1 -4
  89. package/app/components/DescriptionList.tsx +1 -3
  90. package/app/components/DirectoryView/ConnectionMenu.tsx +8 -22
  91. package/app/components/DirectoryView/DirectoryView.tsx +10 -46
  92. package/app/components/DirectoryView/DirectoryViewGrid.tsx +16 -49
  93. package/app/components/DirectoryView/DirectoryViewTableConnection.tsx +1 -4
  94. package/app/components/DirectoryView/DirectoryViewTableDirectory.tsx +2 -7
  95. package/app/components/DirectoryView/DirectoryViewTree.tsx +5 -21
  96. package/app/components/DirectoryView/FilterBar.tsx +9 -48
  97. package/app/components/DirectoryView/buildDirectoryTree.ts +6 -25
  98. package/app/components/DirectoryView/filterNodes.ts +4 -11
  99. package/app/components/DirectoryView/modals/Cyberduck.modal.tsx +6 -15
  100. package/app/components/DirectoryView/modals/FileInfo.modal.tsx +1 -5
  101. package/app/components/DirectoryView/useLayoutStore.ts +5 -25
  102. package/app/components/GlobalSearch/GlobalSearch.tsx +1 -4
  103. package/app/components/GlobalSearch/SearchBar.tsx +1 -7
  104. package/app/components/GlobalSearch/Suggestions.tsx +0 -1
  105. package/app/components/ImageViewer/state/formatRegistry.ts +5 -18
  106. package/app/components/LavaLoader.tsx +4 -11
  107. package/app/components/Layout/Footer.tsx +1 -4
  108. package/app/components/Pills/ScopePill.tsx +2 -8
  109. package/app/components/Table/ColumnFilterInput.tsx +5 -20
  110. package/app/components/Table/ColumnResizeHandle.tsx +1 -5
  111. package/app/components/Table/ColumnSortButton.tsx +1 -5
  112. package/app/components/Table/SelectionFooter.tsx +3 -5
  113. package/app/components/Table/Table.tsx +5 -21
  114. package/app/components/Table/TableBodyRow.tsx +19 -31
  115. package/app/components/Table/TableHeaderRow.tsx +7 -28
  116. package/app/components/Table/TableMenu.tsx +4 -20
  117. package/app/components/Table/state/createTableStore.ts +3 -9
  118. package/app/components/Table/state/useTableStore.ts +1 -3
  119. package/app/components/Table/types.ts +4 -10
  120. package/app/components/Table/useColumnFilters.ts +1 -4
  121. package/app/components/Table/useColumnVisibility.ts +4 -11
  122. package/app/components/Table/useColumnWidths.ts +1 -3
  123. package/app/components/Table/useTableSorting.ts +4 -12
  124. package/app/components/Tooltip/Tooltip.tsx +4 -17
  125. package/app/components/Tooltip/TooltipSpan.tsx +5 -28
  126. package/app/components/Tooltip/useCopyToClipboard.ts +1 -3
  127. package/app/components/Tooltip/useMiddleEllipsis.ts +2 -7
  128. package/app/components/Tooltip/useOverflowDetection.ts +2 -5
  129. package/app/components/UserMenu.tsx +2 -9
  130. package/app/entry.server.tsx +9 -19
  131. package/app/hooks/useSearchParam.ts +2 -4
  132. package/app/lib/bootstrapPluginsCore.ts +4 -9
  133. package/app/root.tsx +4 -15
  134. package/app/routes/admin/assertAdminScope.ts +1 -3
  135. package/app/routes/admin/assertGroupPathsInScope.ts +3 -11
  136. package/app/routes/admin/assertGroupsInScope.ts +3 -11
  137. package/app/routes/admin/assertUsersInScope.ts +2 -8
  138. package/app/routes/admin/bulkInvite/bulkInvite.action.ts +2 -13
  139. package/app/routes/admin/bulkInvite/bulkInvite.form.tsx +18 -35
  140. package/app/routes/admin/createGroup/createGroup.action.ts +3 -10
  141. package/app/routes/admin/createGroup/createGroup.form.tsx +2 -9
  142. package/app/routes/admin/createGroup/createGroup.modal.tsx +1 -5
  143. package/app/routes/admin/inviteUser/inviteUser.action.ts +1 -4
  144. package/app/routes/admin/inviteUser/inviteUser.form.tsx +2 -8
  145. package/app/routes/admin/inviteUser/inviteUser.loader.ts +1 -4
  146. package/app/routes/admin/inviteUser/inviteUser.modal.tsx +3 -16
  147. package/app/routes/admin/updateUser/updateUser.form.tsx +4 -15
  148. package/app/routes/admin/updateUser/updateUser.modal.tsx +5 -23
  149. package/app/routes/admin/updateUser/userDetail.action.ts +2 -10
  150. package/app/routes/admin/users/BulkActions.tsx +15 -38
  151. package/app/routes/admin/users/bulkUsers.action.ts +2 -9
  152. package/app/routes/admin/users/bulkUsers.schema.ts +1 -6
  153. package/app/routes/admin/users/users.route.tsx +14 -63
  154. package/app/routes/api/cyberduck-profile.$name.ts +6 -2
  155. package/app/routes/auth/callback.route.tsx +8 -33
  156. package/app/routes/auth/login.route.tsx +8 -11
  157. package/app/routes/auth/logout.route.tsx +4 -14
  158. package/app/routes/config.route.tsx +1 -5
  159. package/app/routes/connections/connection.form.tsx +5 -14
  160. package/app/routes/connections/connection.schema.ts +4 -16
  161. package/app/routes/connections/connections.loader.ts +11 -23
  162. package/app/routes/connections/connections.route.tsx +1 -5
  163. package/app/routes/connections/connections.server.ts +1 -3
  164. package/app/routes/connections/createConnection.action.ts +2 -8
  165. package/app/routes/connections/createConnection.modal.tsx +3 -13
  166. package/app/routes/connections/deleteConnection.action.ts +1 -4
  167. package/app/routes/connections/updateConnection.action.ts +2 -8
  168. package/app/routes/connections/updateConnection.modal.tsx +4 -14
  169. package/app/routes/home/home.route.tsx +8 -33
  170. package/app/routes/layouts/ModalOutlet.tsx +6 -18
  171. package/app/routes/objects/objects.loader.ts +5 -19
  172. package/app/routes/objects/objects.route.tsx +11 -30
  173. package/app/routes/presign.route.tsx +5 -18
  174. package/app/routes/recent.route.tsx +1 -4
  175. package/app/routes/search.route.tsx +4 -17
  176. package/app/routes.ts +1 -4
  177. package/app/tailwind.css +17 -12
  178. package/app/types/cornerstone-codecs.d.ts +25 -29
  179. package/app/utils/connectionsStore/selectors.ts +2 -6
  180. package/app/utils/connectionsStore/useConnectionsStore.ts +2 -6
  181. package/app/utils/db/convertCsvToParquet.ts +1 -3
  182. package/app/utils/db/createDatabase.ts +1 -3
  183. package/app/utils/db/createSingleton.ts +1 -1
  184. package/app/utils/db/getBlobFromObjectNode.ts +3 -9
  185. package/app/utils/db/getGeomQuery.ts +1 -1
  186. package/app/utils/db/getMarkerInfoWasm.ts +1 -3
  187. package/app/utils/db/getTileBoundingBox.ts +1 -4
  188. package/app/utils/db/sqlQueries.ts +1 -4
  189. package/app/utils/fileType.ts +80 -10
  190. package/app/utils/filterObjects.ts +2 -5
  191. package/app/utils/localFilesStore/useFileStore.ts +7 -7
  192. package/app/utils/recentlyViewed.server.ts +1 -4
  193. package/app/utils/resourceId.ts +3 -11
  194. package/app/utils/s3Provider.ts +3 -7
  195. package/app/utils/signedFetch.ts +4 -13
  196. package/bin-src/codegen.ts +4 -1
  197. package/package.json +5 -1
  198. package/prisma/seed.ts +1 -2
  199. package/public/favicon/site.webmanifest +1 -1
  200. package/server.js +1 -4
  201. package/server.js.map +1 -1
  202. package/vite-plugins/cytario-plugins.ts +2 -8
@@ -22,11 +22,7 @@ import { createMigrate } from "~/utils/persistMigration";
22
22
 
23
23
  type PersistedViewerState = Pick<
24
24
  ViewerStore,
25
- | "selectedChannelId"
26
- | "imagePanelIndex"
27
- | "imagePanels"
28
- | "layersStates"
29
- | "viewStateActive"
25
+ "selectedChannelId" | "imagePanelIndex" | "imagePanels" | "layersStates" | "viewStateActive"
30
26
  >;
31
27
 
32
28
  const VIEWER_FALLBACK_STATE: PersistedViewerState = {
@@ -88,15 +84,11 @@ export const createViewerStore = (id: string) =>
88
84
  state.error = error;
89
85
  },
90
86
  false,
91
- "setError"
87
+ "setError",
92
88
  ),
93
89
 
94
90
  setCursorPosition: (cursorPosition) =>
95
- set(
96
- (state) => ({ ...state, cursorPosition }),
97
- false,
98
- "setCursorPosition"
99
- ),
91
+ set((state) => ({ ...state, cursorPosition }), false, "setCursorPosition"),
100
92
 
101
93
  setViewStatePreview: (viewStatePreview: ViewState) =>
102
94
  set(
@@ -104,7 +96,7 @@ export const createViewerStore = (id: string) =>
104
96
  state.viewStatePreview = viewStatePreview;
105
97
  },
106
98
  false,
107
- "setViewStatePreview"
99
+ "setViewStatePreview",
108
100
  ),
109
101
 
110
102
  setViewStateActive: (viewStateActive: ViewState) =>
@@ -115,7 +107,7 @@ export const createViewerStore = (id: string) =>
115
107
  state.viewStateActive.maxZoom = 2;
116
108
  },
117
109
  false,
118
- "setViewStateActive"
110
+ "setViewStateActive",
119
111
  ),
120
112
 
121
113
  setIsViewerLoading: (isViewerLoading: boolean) =>
@@ -124,7 +116,7 @@ export const createViewerStore = (id: string) =>
124
116
  state.isViewerLoading = isViewerLoading;
125
117
  },
126
118
  false,
127
- "setIsViewerLoading"
119
+ "setIsViewerLoading",
128
120
  ),
129
121
 
130
122
  setIsChannelsLoading: (imagePanelId: number, count: number) =>
@@ -137,7 +129,7 @@ export const createViewerStore = (id: string) =>
137
129
  }
138
130
  },
139
131
  false,
140
- "setIsChannelsLoading"
132
+ "setIsChannelsLoading",
141
133
  ),
142
134
 
143
135
  setIsOverlaysLoading: (imagePanelId: number, count: number) =>
@@ -150,7 +142,7 @@ export const createViewerStore = (id: string) =>
150
142
  }
151
143
  },
152
144
  false,
153
- "setIsOverlaysLoading"
145
+ "setIsOverlaysLoading",
154
146
  ),
155
147
 
156
148
  setMetadata: (metadata) =>
@@ -159,7 +151,7 @@ export const createViewerStore = (id: string) =>
159
151
  state.metadata = metadata;
160
152
  },
161
153
  false,
162
- "setMetadata"
154
+ "setMetadata",
163
155
  ),
164
156
 
165
157
  setLoader: (loader) =>
@@ -168,7 +160,7 @@ export const createViewerStore = (id: string) =>
168
160
  state.loader = loader;
169
161
  },
170
162
  false,
171
- "setLoader"
163
+ "setLoader",
172
164
  ),
173
165
 
174
166
  setSelectedChannelId: (selectedChannelId) =>
@@ -177,7 +169,7 @@ export const createViewerStore = (id: string) =>
177
169
  state.selectedChannelId = selectedChannelId;
178
170
  },
179
171
  false,
180
- "setSelectedChannelId"
172
+ "setSelectedChannelId",
181
173
  ),
182
174
 
183
175
  setActiveImagePanelId: (imagePanelIndex) =>
@@ -186,7 +178,7 @@ export const createViewerStore = (id: string) =>
186
178
  state.imagePanelIndex = imagePanelIndex;
187
179
  },
188
180
  false,
189
- "setActiveImagePanelId"
181
+ "setActiveImagePanelId",
190
182
  ),
191
183
 
192
184
  addImagePanel: () =>
@@ -197,13 +189,12 @@ export const createViewerStore = (id: string) =>
197
189
 
198
190
  // If we don't have enough layersStates, duplicate the last one
199
191
  while (state.layersStates.length < state.imagePanels.length) {
200
- const lastLayersState =
201
- state.layersStates[state.layersStates.length - 1];
192
+ const lastLayersState = state.layersStates[state.layersStates.length - 1];
202
193
  state.layersStates.push({ ...lastLayersState });
203
194
  }
204
195
  },
205
196
  false,
206
- "addImagePanel"
197
+ "addImagePanel",
207
198
  ),
208
199
 
209
200
  addChannelsState: async () => {
@@ -217,18 +208,17 @@ export const createViewerStore = (id: string) =>
217
208
  setTimeout(() => {
218
209
  reject(
219
210
  new Error(
220
- "Worker initialization timeout after 10 seconds. The decoder worker may not be loaded correctly."
221
- )
211
+ "Worker initialization timeout after 10 seconds. The decoder worker may not be loaded correctly.",
212
+ ),
222
213
  );
223
214
  }, 10000);
224
215
  });
225
216
 
226
217
  // Race between actual initialization and timeout
227
- const { channelsState, channelIds, firstChannelKey } =
228
- await Promise.race([
229
- getInitialChannelsState(state.metadata, state.loader),
230
- timeoutPromise,
231
- ]);
218
+ const { channelsState, channelIds, firstChannelKey } = await Promise.race([
219
+ getInitialChannelsState(state.metadata, state.loader),
220
+ timeoutPromise,
221
+ ]);
232
222
 
233
223
  return set(
234
224
  (state) => {
@@ -249,47 +239,36 @@ export const createViewerStore = (id: string) =>
249
239
  ];
250
240
  },
251
241
  false,
252
- "addChannelsStateInitial"
242
+ "addChannelsStateInitial",
253
243
  );
254
244
  } catch (error) {
255
- console.error(
256
- "[createViewerStore] addChannelsState - FAILED:",
257
- error
258
- );
245
+ console.error("[createViewerStore] addChannelsState - FAILED:", error);
259
246
  // Set error state so the UI can show an error message instead of hanging
260
247
  set(
261
248
  (state) => {
262
- state.error =
263
- error instanceof Error
264
- ? error
265
- : new Error(String(error));
249
+ state.error = error instanceof Error ? error : new Error(String(error));
266
250
  },
267
251
  false,
268
- "addChannelsStateError"
252
+ "addChannelsStateError",
269
253
  );
270
254
  return;
271
255
  }
272
256
  }
273
257
 
274
- const activeImagePanelIndex =
275
- state.imagePanels[state.imagePanelIndex];
258
+ const activeImagePanelIndex = state.imagePanels[state.imagePanelIndex];
276
259
 
277
260
  return set(
278
261
  (draft) => {
279
- draft.imagePanels = draft.imagePanels.map(
280
- (imagePanelIndex, index) => {
281
- if (index === draft.imagePanelIndex) {
282
- return draft.layersStates.length;
283
- }
284
- return imagePanelIndex;
262
+ draft.imagePanels = draft.imagePanels.map((imagePanelIndex, index) => {
263
+ if (index === draft.imagePanelIndex) {
264
+ return draft.layersStates.length;
285
265
  }
286
- );
287
- draft.layersStates.push(
288
- castDraft(state.layersStates[activeImagePanelIndex])
289
- );
266
+ return imagePanelIndex;
267
+ });
268
+ draft.layersStates.push(castDraft(state.layersStates[activeImagePanelIndex]));
290
269
  },
291
270
  false,
292
- "addChannelsStateDuplicate"
271
+ "addChannelsStateDuplicate",
293
272
  );
294
273
  },
295
274
 
@@ -297,14 +276,12 @@ export const createViewerStore = (id: string) =>
297
276
  set(
298
277
  (state) => {
299
278
  state.imagePanels = state.imagePanels.map((imagePanelIndex) =>
300
- Math.min(imagePanelIndex, state.layersStates.length - 2)
301
- );
302
- state.layersStates = state.layersStates.filter(
303
- (_, index) => index !== i
279
+ Math.min(imagePanelIndex, state.layersStates.length - 2),
304
280
  );
281
+ state.layersStates = state.layersStates.filter((_, index) => index !== i);
305
282
  },
306
283
  false,
307
- "removeChannelsState"
284
+ "removeChannelsState",
308
285
  ),
309
286
 
310
287
  setActiveChannelsStateIndex: (channelsStateIndex: number) =>
@@ -312,22 +289,19 @@ export const createViewerStore = (id: string) =>
312
289
  (state) => {
313
290
  // If we don't have enough layersStates, duplicate the last one
314
291
  while (state.layersStates.length < channelsStateIndex + 1) {
315
- const lastLayersState =
316
- state.layersStates[state.layersStates.length - 1];
292
+ const lastLayersState = state.layersStates[state.layersStates.length - 1];
317
293
  state.layersStates.push({ ...lastLayersState });
318
294
  }
319
295
 
320
- state.imagePanels = state.imagePanels.map(
321
- (imagePanel, index) => {
322
- if (index === state.imagePanelIndex) {
323
- return channelsStateIndex;
324
- }
325
- return imagePanel;
296
+ state.imagePanels = state.imagePanels.map((imagePanel, index) => {
297
+ if (index === state.imagePanelIndex) {
298
+ return channelsStateIndex;
326
299
  }
327
- );
300
+ return imagePanel;
301
+ });
328
302
  },
329
303
  false,
330
- "setActiveChannelsStateIndex"
304
+ "setActiveChannelsStateIndex",
331
305
  ),
332
306
 
333
307
  removeImagePanel: (imagePanelIndex) =>
@@ -335,20 +309,18 @@ export const createViewerStore = (id: string) =>
335
309
  (state) => {
336
310
  state.imagePanelIndex = imagePanelIndex - 1;
337
311
  state.imagePanels = state.imagePanels.filter(
338
- (_, index) => index !== imagePanelIndex
312
+ (_, index) => index !== imagePanelIndex,
339
313
  );
340
314
  },
341
315
  false,
342
- "removeImagePanel"
316
+ "removeImagePanel",
343
317
  ),
344
318
 
345
319
  setContrastLimits: (contrastLimits) =>
346
320
  set(
347
321
  (state) => {
348
- const activeChannelsStateIndex =
349
- state.imagePanels[state.imagePanelIndex];
350
- const layerState =
351
- state.layersStates[activeChannelsStateIndex];
322
+ const activeChannelsStateIndex = state.imagePanels[state.imagePanelIndex];
323
+ const layerState = state.layersStates[activeChannelsStateIndex];
352
324
  if (!layerState) return;
353
325
 
354
326
  if (state.selectedChannelId === BRIGHTFIELD_GROUP_ID) {
@@ -360,24 +332,21 @@ export const createViewerStore = (id: string) =>
360
332
  }
361
333
  }
362
334
  } else {
363
- const key =
364
- state.selectedChannelId as keyof ChannelsStateColumns;
335
+ const key = state.selectedChannelId as keyof ChannelsStateColumns;
365
336
  if (layerState.channels[key]) {
366
337
  layerState.channels[key].contrastLimits = contrastLimits;
367
338
  }
368
339
  }
369
340
  },
370
341
  false,
371
- "setContrastLimits"
342
+ "setContrastLimits",
372
343
  ),
373
344
 
374
345
  resetContrastLimits: () =>
375
346
  set(
376
347
  (state) => {
377
- const activeChannelsStateIndex =
378
- state.imagePanels[state.imagePanelIndex];
379
- const layerState =
380
- state.layersStates[activeChannelsStateIndex];
348
+ const activeChannelsStateIndex = state.imagePanels[state.imagePanelIndex];
349
+ const layerState = state.layersStates[activeChannelsStateIndex];
381
350
  if (!layerState) return;
382
351
 
383
352
  if (state.selectedChannelId === BRIGHTFIELD_GROUP_ID) {
@@ -386,22 +355,19 @@ export const createViewerStore = (id: string) =>
386
355
  for (const key of [group.red, group.green, group.blue]) {
387
356
  const channel = layerState.channels[key];
388
357
  if (channel) {
389
- channel.contrastLimits =
390
- channel.contrastLimitsInitial as ByteDomain;
358
+ channel.contrastLimits = channel.contrastLimitsInitial as ByteDomain;
391
359
  }
392
360
  }
393
361
  } else {
394
- const key =
395
- state.selectedChannelId as keyof ChannelsStateColumns;
362
+ const key = state.selectedChannelId as keyof ChannelsStateColumns;
396
363
  const channel = layerState.channels[key];
397
364
  if (channel) {
398
- channel.contrastLimits =
399
- channel.contrastLimitsInitial as ByteDomain;
365
+ channel.contrastLimits = channel.contrastLimitsInitial as ByteDomain;
400
366
  }
401
367
  }
402
368
  },
403
369
  false,
404
- "resetContrastLimits"
370
+ "resetContrastLimits",
405
371
  ),
406
372
 
407
373
  /**
@@ -409,18 +375,13 @@ export const createViewerStore = (id: string) =>
409
375
  * If the channel is not initialized, it loads stats and initializes it before setting visibility.
410
376
  * Handles BRIGHTFIELD_GROUP_ID by toggling all R/G/B channels together.
411
377
  */
412
- setChannelVisibility: async (
413
- key: keyof ChannelsState,
414
- isVisible: boolean
415
- ) => {
378
+ setChannelVisibility: async (key: keyof ChannelsState, isVisible: boolean) => {
416
379
  const state = get();
417
380
 
418
381
  if (!state.loader || state.imagePanelIndex < 0) return;
419
382
 
420
- const activeChannelsStateIndex =
421
- state.imagePanels[state.imagePanelIndex];
422
- const layerState =
423
- state.layersStates[activeChannelsStateIndex];
383
+ const activeChannelsStateIndex = state.imagePanels[state.imagePanelIndex];
384
+ const layerState = state.layersStates[activeChannelsStateIndex];
424
385
 
425
386
  // Brightfield group: toggle all 3 channels
426
387
  if (key === BRIGHTFIELD_GROUP_ID) {
@@ -429,21 +390,17 @@ export const createViewerStore = (id: string) =>
429
390
  const keys = [group.red, group.green, group.blue];
430
391
 
431
392
  // Initialize any uninitialized channels in parallel
432
- const uninitialized = keys.filter(
433
- (k) => !layerState.channels[k]?.isInitialized
434
- );
393
+ const uninitialized = keys.filter((k) => !layerState.channels[k]?.isInitialized);
435
394
 
436
395
  if (uninitialized.length > 0) {
437
396
  set(
438
397
  (state) => {
439
398
  for (const k of uninitialized) {
440
- state.layersStates[activeChannelsStateIndex].channels[
441
- k
442
- ].isLoading = true;
399
+ state.layersStates[activeChannelsStateIndex].channels[k].isLoading = true;
443
400
  }
444
401
  },
445
402
  false,
446
- "setBrightfieldVisibility/stats/request"
403
+ "setBrightfieldVisibility/stats/request",
447
404
  );
448
405
 
449
406
  try {
@@ -452,14 +409,13 @@ export const createViewerStore = (id: string) =>
452
409
  getSelectionStats({
453
410
  loader: state.loader!,
454
411
  selection: layerState.channels[k].selection,
455
- }).then((stats) => ({ key: k, ...stats }))
456
- )
412
+ }).then((stats) => ({ key: k, ...stats })),
413
+ ),
457
414
  );
458
415
 
459
416
  return set(
460
417
  (state) => {
461
- const ls =
462
- state.layersStates[activeChannelsStateIndex];
418
+ const ls = state.layersStates[activeChannelsStateIndex];
463
419
  for (const { key: k, domain, histogram } of results) {
464
420
  const channel = ls.channels[k];
465
421
  channel.isInitialized = true;
@@ -475,34 +431,32 @@ export const createViewerStore = (id: string) =>
475
431
  }
476
432
  },
477
433
  false,
478
- "setBrightfieldVisibility/stats/success"
434
+ "setBrightfieldVisibility/stats/success",
479
435
  );
480
436
  } catch {
481
437
  return set(
482
438
  (state) => {
483
- const ls =
484
- state.layersStates[activeChannelsStateIndex];
439
+ const ls = state.layersStates[activeChannelsStateIndex];
485
440
  for (const k of uninitialized) {
486
441
  ls.channels[k].isLoading = false;
487
442
  ls.channels[k].isVisible = false;
488
443
  }
489
444
  },
490
445
  false,
491
- "setBrightfieldVisibility/stats/error"
446
+ "setBrightfieldVisibility/stats/error",
492
447
  );
493
448
  }
494
449
  }
495
450
 
496
451
  return set(
497
452
  (state) => {
498
- const ls =
499
- state.layersStates[activeChannelsStateIndex];
453
+ const ls = state.layersStates[activeChannelsStateIndex];
500
454
  for (const k of keys) {
501
455
  ls.channels[k].isVisible = isVisible;
502
456
  }
503
457
  },
504
458
  false,
505
- "setBrightfieldVisibility"
459
+ "setBrightfieldVisibility",
506
460
  );
507
461
  }
508
462
 
@@ -512,27 +466,21 @@ export const createViewerStore = (id: string) =>
512
466
  if (!activeChannelsStateConfig.isInitialized) {
513
467
  set(
514
468
  (state) => {
515
- state.layersStates[activeChannelsStateIndex].channels[
516
- key
517
- ].isLoading = true;
469
+ state.layersStates[activeChannelsStateIndex].channels[key].isLoading = true;
518
470
  },
519
471
  false,
520
- "setChannelVisibility/stats/request"
472
+ "setChannelVisibility/stats/request",
521
473
  );
522
474
 
523
475
  try {
524
- const { domain, contrastLimits, histogram } =
525
- await getSelectionStats({
526
- loader: state.loader,
527
- selection: activeChannelsStateConfig.selection,
528
- });
476
+ const { domain, contrastLimits, histogram } = await getSelectionStats({
477
+ loader: state.loader,
478
+ selection: activeChannelsStateConfig.selection,
479
+ });
529
480
 
530
481
  return set(
531
482
  (state) => {
532
- const channel =
533
- state.layersStates[activeChannelsStateIndex].channels[
534
- key
535
- ];
483
+ const channel = state.layersStates[activeChannelsStateIndex].channels[key];
536
484
  channel.isInitialized = true;
537
485
  channel.isLoading = false;
538
486
  channel.domain = castDraft(domain);
@@ -542,96 +490,76 @@ export const createViewerStore = (id: string) =>
542
490
  channel.isVisible = isVisible;
543
491
  },
544
492
  false,
545
- "setChannelVisibility/stats/success"
493
+ "setChannelVisibility/stats/success",
546
494
  );
547
495
  } catch {
548
496
  return set(
549
497
  (state) => {
550
- const channel =
551
- state.layersStates[activeChannelsStateIndex].channels[
552
- key
553
- ];
498
+ const channel = state.layersStates[activeChannelsStateIndex].channels[key];
554
499
  channel.isLoading = false;
555
500
  channel.isVisible = false;
556
501
  },
557
502
  false,
558
- "setChannelVisibility/stats/error"
503
+ "setChannelVisibility/stats/error",
559
504
  );
560
505
  }
561
506
  }
562
507
 
563
508
  set(
564
509
  (state) => {
565
- state.layersStates[activeChannelsStateIndex].channels[
566
- key
567
- ].isVisible = isVisible;
510
+ state.layersStates[activeChannelsStateIndex].channels[key].isVisible = isVisible;
568
511
  },
569
512
  false,
570
- "setChannelVisibility"
513
+ "setChannelVisibility",
571
514
  );
572
515
  },
573
516
 
574
- setMarkerVisibility: (
575
- fileName: string,
576
- markerName: string,
577
- isVisible: boolean
578
- ) =>
517
+ setMarkerVisibility: (fileName: string, markerName: string, isVisible: boolean) =>
579
518
  set(
580
519
  (state) => {
581
- const activeImagePanelIndex =
582
- state.imagePanels[state.imagePanelIndex];
583
- const overlays =
584
- state.layersStates[activeImagePanelIndex]?.overlays;
520
+ const activeImagePanelIndex = state.imagePanels[state.imagePanelIndex];
521
+ const overlays = state.layersStates[activeImagePanelIndex]?.overlays;
585
522
 
586
523
  if (overlays?.[fileName]?.[markerName]) {
587
524
  overlays[fileName][markerName].isVisible = isVisible;
588
525
  }
589
526
  },
590
527
  false,
591
- "setMarkerVisibility"
528
+ "setMarkerVisibility",
592
529
  ),
593
530
 
594
531
  setChannelColor: (key: keyof ChannelsState, color: RGBA) =>
595
532
  set(
596
533
  (state) => {
597
- const activeChannelsStateIndex =
598
- state.imagePanels[state.imagePanelIndex];
599
- const channel =
600
- state.layersStates[activeChannelsStateIndex]?.channels[key];
534
+ const activeChannelsStateIndex = state.imagePanels[state.imagePanelIndex];
535
+ const channel = state.layersStates[activeChannelsStateIndex]?.channels[key];
601
536
 
602
537
  if (channel) {
603
538
  channel.color = color.slice(0, 3) as RGB;
604
539
  }
605
540
  },
606
541
  false,
607
- "setChannelColor"
542
+ "setChannelColor",
608
543
  ),
609
544
 
610
- setMarkerColor: (
611
- fileName: string,
612
- markerName: string,
613
- color: RGBA
614
- ) =>
545
+ setMarkerColor: (fileName: string, markerName: string, color: RGBA) =>
615
546
  set(
616
547
  (state) => {
617
- const activeImagePanelIndex =
618
- state.imagePanels[state.imagePanelIndex];
619
- const overlays =
620
- state.layersStates[activeImagePanelIndex]?.overlays;
548
+ const activeImagePanelIndex = state.imagePanels[state.imagePanelIndex];
549
+ const overlays = state.layersStates[activeImagePanelIndex]?.overlays;
621
550
 
622
551
  if (overlays?.[fileName]?.[markerName]) {
623
552
  overlays[fileName][markerName].color = color;
624
553
  }
625
554
  },
626
555
  false,
627
- "setMarkerColor"
556
+ "setMarkerColor",
628
557
  ),
629
558
 
630
559
  addOverlaysState: (overlaysState: OverlaysState) =>
631
560
  set(
632
561
  (state) => {
633
- const activeImagePanelIndex =
634
- state.imagePanels[state.imagePanelIndex];
562
+ const activeImagePanelIndex = state.imagePanels[state.imagePanelIndex];
635
563
  const layerState = state.layersStates[activeImagePanelIndex];
636
564
 
637
565
  if (layerState) {
@@ -639,17 +567,13 @@ export const createViewerStore = (id: string) =>
639
567
  }
640
568
  },
641
569
  false,
642
- "addOverlaysState"
570
+ "addOverlaysState",
643
571
  ),
644
572
 
645
- updateOverlaysState: (
646
- overlayId: string,
647
- overlayState: OverlayState
648
- ) =>
573
+ updateOverlaysState: (overlayId: string, overlayState: OverlayState) =>
649
574
  set(
650
575
  (state) => {
651
- const activeImagePanelIndex =
652
- state.imagePanels[state.imagePanelIndex];
576
+ const activeImagePanelIndex = state.imagePanels[state.imagePanelIndex];
653
577
  const layerState = state.layersStates[activeImagePanelIndex];
654
578
 
655
579
  if (layerState?.overlays[overlayId]) {
@@ -657,30 +581,27 @@ export const createViewerStore = (id: string) =>
657
581
  }
658
582
  },
659
583
  false,
660
- "updateOverlaysState"
584
+ "updateOverlaysState",
661
585
  ),
662
586
 
663
587
  removeOverlaysState: (overlaysStateId: string) =>
664
588
  set(
665
589
  (state) => {
666
- const activeImagePanelIndex =
667
- state.imagePanels[state.imagePanelIndex];
668
- const overlays =
669
- state.layersStates[activeImagePanelIndex]?.overlays;
590
+ const activeImagePanelIndex = state.imagePanels[state.imagePanelIndex];
591
+ const overlays = state.layersStates[activeImagePanelIndex]?.overlays;
670
592
 
671
593
  if (overlays) {
672
594
  delete overlays[overlaysStateId];
673
595
  }
674
596
  },
675
597
  false,
676
- "removeOverlaysState"
598
+ "removeOverlaysState",
677
599
  ),
678
600
 
679
601
  setOverlaysFillOpacity: (fillOpacity: number) =>
680
602
  set(
681
603
  (state) => {
682
- const activeImagePanelIndex =
683
- state.imagePanels[state.imagePanelIndex];
604
+ const activeImagePanelIndex = state.imagePanels[state.imagePanelIndex];
684
605
  const layerState = state.layersStates[activeImagePanelIndex];
685
606
 
686
607
  if (layerState) {
@@ -688,14 +609,13 @@ export const createViewerStore = (id: string) =>
688
609
  }
689
610
  },
690
611
  false,
691
- "setOverlaysFillOpacity"
612
+ "setOverlaysFillOpacity",
692
613
  ),
693
614
 
694
615
  setChannelsOpacity: (channelsOpacity: number) =>
695
616
  set(
696
617
  (state) => {
697
- const activeImagePanelIndex =
698
- state.imagePanels[state.imagePanelIndex];
618
+ const activeImagePanelIndex = state.imagePanels[state.imagePanelIndex];
699
619
  const layerState = state.layersStates[activeImagePanelIndex];
700
620
 
701
621
  if (layerState) {
@@ -703,14 +623,13 @@ export const createViewerStore = (id: string) =>
703
623
  }
704
624
  },
705
625
  false,
706
- "setChannelsOpacity"
626
+ "setChannelsOpacity",
707
627
  ),
708
628
 
709
629
  setShowCellOutline: (showCellOutline: boolean) =>
710
630
  set(
711
631
  (state) => {
712
- const activeImagePanelIndex =
713
- state.imagePanels[state.imagePanelIndex];
632
+ const activeImagePanelIndex = state.imagePanels[state.imagePanelIndex];
714
633
  const layerState = state.layersStates[activeImagePanelIndex];
715
634
 
716
635
  if (layerState) {
@@ -718,13 +637,13 @@ export const createViewerStore = (id: string) =>
718
637
  }
719
638
  },
720
639
  false,
721
- "setShowCellOutline"
640
+ "setShowCellOutline",
722
641
  ),
723
642
  }),
724
643
  {
725
644
  name: "ViewerStore-" + id,
726
- }
727
- )
645
+ },
646
+ ),
728
647
  ),
729
648
  {
730
649
  name: "ViewerStore-" + id,
@@ -766,12 +685,9 @@ export const createViewerStore = (id: string) =>
766
685
  }),
767
686
  onRehydrateStorage: () => (_state, error) => {
768
687
  if (error) {
769
- console.error(
770
- `[ViewerStore-${id}] Rehydration failed:`,
771
- error,
772
- );
688
+ console.error(`[ViewerStore-${id}] Rehydration failed:`, error);
773
689
  }
774
690
  },
775
- }
776
- )
691
+ },
692
+ ),
777
693
  );