@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
@@ -15,9 +15,7 @@ import { toastBridge } from "~/toast-bridge";
15
15
  import { isPointMode } from "~/utils/db/getGeomQuery";
16
16
  import { getTileDataWasm } from "~/utils/db/getTileDataWasm";
17
17
 
18
- type SetTooltip = (
19
- tooltip: { content: ReactNode; x: number; y: number } | null
20
- ) => void;
18
+ type SetTooltip = (tooltip: { content: ReactNode; x: number; y: number } | null) => void;
21
19
 
22
20
  interface OverlaysLayerProps {
23
21
  resourceId: string;
@@ -37,10 +35,7 @@ interface OverlaysLayerProps {
37
35
  const MarkerLabel = ({ color, name }: { color: string; name: string }) => {
38
36
  return (
39
37
  <div key={name} className="flex items-center gap-2">
40
- <div
41
- className="w-4 h-4 rounded-full"
42
- style={{ backgroundColor: color }}
43
- />
38
+ <div className="w-4 h-4 rounded-full" style={{ backgroundColor: color }} />
44
39
  {name}
45
40
  </div>
46
41
  );
@@ -62,29 +57,20 @@ export const OverlaysLayer = ({
62
57
  }: OverlaysLayerProps) => {
63
58
  const markerPrefix = "marker_positive_";
64
59
 
65
- const getTileData = async ({
66
- id,
67
- index,
68
- }: TileLoadProps): Promise<Table | null> => {
60
+ const getTileData = async ({ id, index }: TileLoadProps): Promise<Table | null> => {
69
61
  loadTile(id);
70
62
 
71
63
  try {
72
64
  // Get ALL marker column names (not just enabled ones)
73
65
  const allMarkerKeys = Object.keys(fileMarkers);
74
66
 
75
- const data = await getTileDataWasm(
76
- resourceId,
77
- index,
78
- allMarkerKeys,
79
- );
67
+ const data = await getTileDataWasm(resourceId, index, allMarkerKeys);
80
68
 
81
69
  return data;
82
70
  } catch (error) {
83
71
  toastBridge.emit({
84
72
  variant: "error",
85
- message: `Error fetching tile data: ${
86
- (error as Error).message ?? error
87
- }`,
73
+ message: `Error fetching tile data: ${(error as Error).message ?? error}`,
88
74
  });
89
75
  console.error("Error fetching tile data:", error);
90
76
  return null;
@@ -243,11 +229,7 @@ export const OverlaysLayer = ({
243
229
  const fullBitmask = new Float32Array(numRows);
244
230
  let outputIndex = 0;
245
231
 
246
- for (
247
- let chunkIdx = 0;
248
- chunkIdx < bitmaskCol.data.length;
249
- chunkIdx++
250
- ) {
232
+ for (let chunkIdx = 0; chunkIdx < bitmaskCol.data.length; chunkIdx++) {
251
233
  const chunk = bitmaskCol.data[chunkIdx];
252
234
  const values = chunk.values as Float32Array;
253
235
  const chunkLength = chunk.length;
@@ -32,10 +32,7 @@ export type MarkerProps = {
32
32
 
33
33
  export interface MarkerLayerProps {
34
34
  markerProps?: MarkerProps;
35
- getMarkerMask?: (
36
- d: unknown,
37
- info: { index: number; data: unknown; target: unknown[] }
38
- ) => number; // Returns a 32-bit bitmask
35
+ getMarkerMask?: (d: unknown, info: { index: number; data: unknown; target: unknown[] }) => number; // Returns a 32-bit bitmask
39
36
  }
40
37
 
41
38
  // Export the ShaderModule with uniform types
@@ -75,7 +72,7 @@ export const markerUniforms = {
75
72
  */
76
73
  export function createMarkerProps(
77
74
  fileMarkers: Record<string, { color: RGBA }>,
78
- opacity: number
75
+ opacity: number,
79
76
  ): MarkerProps {
80
77
  const keys = Object.keys(fileMarkers);
81
78
 
@@ -6,37 +6,25 @@ import { select } from "../../../state/store/selectors";
6
6
  import { useViewerStore } from "../../../state/store/ViewerStoreContext";
7
7
  import { useTilesLoading } from "../../../utils/useTilesLoading";
8
8
 
9
- type SetTooltip = (
10
- tooltip: { content: ReactNode; x: number; y: number } | null
11
- ) => void;
9
+ type SetTooltip = (tooltip: { content: ReactNode; x: number; y: number } | null) => void;
12
10
 
13
11
  /**
14
12
  * Hook to create overlays layers for the image viewer.
15
13
  */
16
- export const useOverlaysLayers = (
17
- imagePanelId: number,
18
- setTooltip?: SetTooltip
19
- ) => {
14
+ export const useOverlaysLayers = (imagePanelId: number, setTooltip?: SetTooltip) => {
20
15
  const layersStates = useViewerStore(select.layersStates);
21
16
  const metadata = useViewerStore(select.metadata);
22
17
  const minZoom = useViewerStore(select.minZoom);
23
18
  const maxZoom = useViewerStore(select.maxZoom);
24
- const panelLayersStateIndex = useViewerStore((state) => state.imagePanels)[
25
- imagePanelId
26
- ];
19
+ const panelLayersStateIndex = useViewerStore((state) => state.imagePanels)[imagePanelId];
27
20
  const setIsOverlaysLoading = useViewerStore(select.setIsOverlaysLoading);
28
- const { loadTile, finishTile } = useTilesLoading(
29
- imagePanelId,
30
- setIsOverlaysLoading
31
- );
21
+ const { loadTile, finishTile } = useTilesLoading(imagePanelId, setIsOverlaysLoading);
32
22
 
33
23
  const imageWidth = metadata?.Pixels?.SizeX ?? 0;
34
24
  const imageHeight = metadata?.Pixels?.SizeY ?? 0;
35
25
 
36
- const fillOpacity =
37
- layersStates[panelLayersStateIndex]?.overlaysFillOpacity ?? 0.8;
38
- const showCellOutline =
39
- layersStates[panelLayersStateIndex]?.showCellOutline ?? true;
26
+ const fillOpacity = layersStates[panelLayersStateIndex]?.overlaysFillOpacity ?? 0.8;
27
+ const showCellOutline = layersStates[panelLayersStateIndex]?.showCellOutline ?? true;
40
28
 
41
29
  const overlaysLayers = useMemo(() => {
42
30
  const layersState = layersStates[panelLayersStateIndex];
@@ -47,9 +35,7 @@ export const useOverlaysLayers = (
47
35
  return Object.keys(overlayState).map((resourceId) => {
48
36
  const fileMarkers = overlayState[resourceId];
49
37
 
50
- const enabledMarkers = Object.keys(fileMarkers).filter(
51
- (key) => fileMarkers[key].isVisible
52
- );
38
+ const enabledMarkers = Object.keys(fileMarkers).filter((key) => fileMarkers[key].isVisible);
53
39
 
54
40
  // Build marker props directly from fileMarkers
55
41
  const markerProps = createMarkerProps(fileMarkers, fillOpacity);
@@ -22,11 +22,5 @@ export const useInitializeChannels = (viewPort: ViewPort) => {
22
22
  isInitialized.current = true;
23
23
  addChannelsState();
24
24
  }
25
- }, [
26
- metadata,
27
- viewPort,
28
- channelsState,
29
- addChannelsState,
30
- setChannelVisibility,
31
- ]);
25
+ }, [metadata, viewPort, channelsState, addChannelsState, setChannelVisibility]);
32
26
  };
@@ -13,7 +13,7 @@ export function useDebouncedValue<T>(value: T, delay: number): T {
13
13
 
14
14
  export const useResizeObserver = (
15
15
  ref: React.RefObject<HTMLElement | null>,
16
- resetOnResize: boolean
16
+ resetOnResize: boolean,
17
17
  ) => {
18
18
  const [viewPort, setViewPort] = useState({ width: 0, height: 0 });
19
19
  const debouncedViewPort = useDebouncedValue(viewPort, 100);
@@ -5,15 +5,11 @@ import { type ViewerStore, type ViewState } from "../state/store/types";
5
5
 
6
6
  const MAGNIFICATION_PRESETS = [5, 10, 20, 40, 80] as const;
7
7
 
8
- export const zoomFromMagnification = (
9
- magnification: number,
10
- objectivePower = 20,
11
- ): number => Math.log2(magnification / objectivePower);
8
+ export const zoomFromMagnification = (magnification: number, objectivePower = 20): number =>
9
+ Math.log2(magnification / objectivePower);
12
10
 
13
- export const magnificationFromZoom = (
14
- zoom: number,
15
- objectivePower = 20,
16
- ): number => objectivePower * Math.pow(2, zoom);
11
+ export const magnificationFromZoom = (zoom: number, objectivePower = 20): number =>
12
+ objectivePower * Math.pow(2, zoom);
17
13
 
18
14
  export const Magnifier = ({
19
15
  metadata,
@@ -42,11 +38,7 @@ export const Magnifier = ({
42
38
  setViewState={setViewStateActive}
43
39
  />
44
40
 
45
- <SegmentedControl
46
- selectionMode="none"
47
- size="sm"
48
- aria-label="Magnification presets"
49
- >
41
+ <SegmentedControl selectionMode="none" size="sm" aria-label="Magnification presets">
50
42
  {MAGNIFICATION_PRESETS.map((mag) => (
51
43
  <SegmentedControlItem
52
44
  key={mag}
@@ -11,18 +11,13 @@ export function ActiveViewStatePreview() {
11
11
  const measurementsPreview = useMeasurements(viewStatePreview);
12
12
 
13
13
  const { width, height, x, y } = useMemo(() => {
14
- const scaleFactor =
15
- 2 ** (measurementsPreview.zoom - measurementsActive.zoom);
14
+ const scaleFactor = 2 ** (measurementsPreview.zoom - measurementsActive.zoom);
16
15
 
17
16
  return {
18
17
  width: measurementsActive.viewPortWidth * scaleFactor,
19
18
  height: measurementsActive.viewPortHeight * scaleFactor,
20
- x:
21
- measurementsPreview.screenOffsetLeft -
22
- measurementsActive.screenOffsetLeft * scaleFactor,
23
- y:
24
- measurementsPreview.screenOffsetTop -
25
- measurementsActive.screenOffsetTop * scaleFactor,
19
+ x: measurementsPreview.screenOffsetLeft - measurementsActive.screenOffsetLeft * scaleFactor,
20
+ y: measurementsPreview.screenOffsetTop - measurementsActive.screenOffsetTop * scaleFactor,
26
21
  };
27
22
  }, [
28
23
  measurementsActive.viewPortWidth,
@@ -20,10 +20,7 @@ export const CursorTick = ({ vertical }: { vertical?: boolean }) => {
20
20
 
21
21
  // Convert absolute pixels to metric units
22
22
  const unit = metadata.Pixels.PhysicalSizeXUnit;
23
- const absoluteToMetric = absoluteToMetricFactory(
24
- metadata.Pixels.PhysicalSizeX ?? 1,
25
- unit
26
- );
23
+ const absoluteToMetric = absoluteToMetricFactory(metadata.Pixels.PhysicalSizeX ?? 1, unit);
27
24
 
28
25
  // Calculate metric position
29
26
  const absoluteX = screenPixelsToAbsolutePixels(x - screenOffsetLeft);
@@ -32,7 +29,5 @@ export const CursorTick = ({ vertical }: { vertical?: boolean }) => {
32
29
  const metricY = absoluteToMetric(absoluteY);
33
30
  const cursorOffset = vertical ? metricY : metricX;
34
31
 
35
- return (
36
- <Tick number={cursorOffset} offset={vertical ? y : x} vertical={vertical} />
37
- );
32
+ return <Tick number={cursorOffset} offset={vertical ? y : x} vertical={vertical} />;
38
33
  };
@@ -47,14 +47,7 @@ export const Ruler = ({
47
47
  const isMajor = i % interval === 0;
48
48
  const label = isMajor ? i : undefined;
49
49
 
50
- return (
51
- <Tick
52
- key={i}
53
- number={label}
54
- offset={offset + i * one_mm}
55
- vertical={vertical}
56
- />
57
- );
50
+ return <Tick key={i} number={label} offset={offset + i * one_mm} vertical={vertical} />;
58
51
  })}
59
52
 
60
53
  <CursorTick vertical={vertical} />
@@ -31,19 +31,9 @@ export function SlideCarrier() {
31
31
  );
32
32
  }
33
33
 
34
- const Size = ({
35
- value,
36
- vertical = false,
37
- }: {
38
- value: number;
39
- vertical?: boolean;
40
- }) => {
41
- const {
42
- imageWidthScreen,
43
- imageHeightScreen,
44
- screenOffsetLeft,
45
- screenOffsetTop,
46
- } = useMeasurements();
34
+ const Size = ({ value, vertical = false }: { value: number; vertical?: boolean }) => {
35
+ const { imageWidthScreen, imageHeightScreen, screenOffsetLeft, screenOffsetTop } =
36
+ useMeasurements();
47
37
 
48
38
  const transform = vertical
49
39
  ? `translate(${screenOffsetLeft}px, ${screenOffsetTop - 18}px) rotate(90deg)`
@@ -17,7 +17,7 @@ export const Tick = ({ number, offset, vertical = false }: TickProps) => {
17
17
  "border-l border-l-[var(--color-text-secondary)]",
18
18
  isMajor ? "h-4" : "h-2",
19
19
  vertical ? "rotate-90" : "",
20
- vertical ? "bottom-0" : "top-0"
20
+ vertical ? "bottom-0" : "top-0",
21
21
  );
22
22
 
23
23
  const adjustedOffset = Math.floor(offset) - (vertical ? 2 : 3);
@@ -8,7 +8,7 @@ interface CalculateViewStateOptions {
8
8
  export function calculateViewStateToFit(
9
9
  metadata: Image,
10
10
  viewport: ViewPort,
11
- options?: CalculateViewStateOptions
11
+ options?: CalculateViewStateOptions,
12
12
  ): ViewState {
13
13
  const { SizeX, SizeY, PhysicalSizeX, PhysicalSizeY } = metadata.Pixels;
14
14
 
@@ -55,15 +55,11 @@ const measurementsDataInitial: UseMeasurementsData = {
55
55
  target: [0, 0],
56
56
  };
57
57
 
58
- const absolutePixelsToScreenPixels = (n = 0, zoom: number) =>
59
- n / (1 / 2 ** zoom);
58
+ const absolutePixelsToScreenPixels = (n = 0, zoom: number) => n / (1 / 2 ** zoom);
60
59
 
61
- const screenPixelsToAbsolutePixels = (n = 0, zoom: number) =>
62
- n * (1 / 2 ** zoom);
60
+ const screenPixelsToAbsolutePixels = (n = 0, zoom: number) => n * (1 / 2 ** zoom);
63
61
 
64
- export const useMeasurements = (
65
- viewStateOptional?: ViewState | null,
66
- ): UseMeasurementsData => {
62
+ export const useMeasurements = (viewStateOptional?: ViewState | null): UseMeasurementsData => {
67
63
  const viewStateActive = useViewerStore(select.viewStateActive);
68
64
  const viewState = viewStateOptional ?? viewStateActive;
69
65
  const metadata = useViewerStore(select.metadata);
@@ -85,37 +81,22 @@ export const useMeasurements = (
85
81
  unit,
86
82
  );
87
83
 
88
- const metricToAbsolute = metricToAbsoluteFactory(
89
- metadata.Pixels.PhysicalSizeX ?? 1,
90
- unit,
91
- );
84
+ const metricToAbsolute = metricToAbsoluteFactory(metadata.Pixels.PhysicalSizeX ?? 1, unit);
92
85
 
93
86
  const widthTotalMm = absoluteToMetric(metadata.Pixels.SizeX);
94
87
  const heightTotalMm = absoluteToMetric(metadata.Pixels.SizeY);
95
88
 
96
- const imageWidthScreen = absolutePixelsToScreenPixels(
97
- metadata.Pixels.SizeX,
98
- zoom,
99
- );
100
- const imageHeightScreen = absolutePixelsToScreenPixels(
101
- metadata.Pixels.SizeY,
102
- zoom,
103
- );
89
+ const imageWidthScreen = absolutePixelsToScreenPixels(metadata.Pixels.SizeX, zoom);
90
+ const imageHeightScreen = absolutePixelsToScreenPixels(metadata.Pixels.SizeY, zoom);
104
91
 
105
92
  const screenOffsetLeft = -target[0] / (1 / 2 ** zoom) + viewPortWidth / 2;
106
93
  const screenOffsetRight = viewPortWidth - screenOffsetLeft;
107
94
  const screenOffsetTop = -target[1] / (1 / 2 ** zoom) + viewPortHeight / 2;
108
95
  const screenOffsetBottom = viewPortHeight - screenOffsetTop;
109
96
 
110
- const metricOffsetLeft = absoluteToMetric(
111
- screenPixelsToAbsolutePixels(-screenOffsetLeft, zoom),
112
- );
113
- const metricOffsetTop = absoluteToMetric(
114
- screenPixelsToAbsolutePixels(-screenOffsetTop, zoom),
115
- );
116
- const metricOffsetRight = absoluteToMetric(
117
- screenPixelsToAbsolutePixels(screenOffsetRight, zoom),
118
- );
97
+ const metricOffsetLeft = absoluteToMetric(screenPixelsToAbsolutePixels(-screenOffsetLeft, zoom));
98
+ const metricOffsetTop = absoluteToMetric(screenPixelsToAbsolutePixels(-screenOffsetTop, zoom));
99
+ const metricOffsetRight = absoluteToMetric(screenPixelsToAbsolutePixels(screenOffsetRight, zoom));
119
100
  const metricOffsetBottom = absoluteToMetric(
120
101
  screenPixelsToAbsolutePixels(screenOffsetBottom, zoom),
121
102
  );
@@ -2,10 +2,7 @@ import { Button, Input, Tree, useToast } from "@cytario/design";
2
2
  import { useCallback, useEffect, useMemo, useState } from "react";
3
3
  import { useFetcher } from "react-router";
4
4
 
5
- import {
6
- type TreeNode,
7
- findNodeById,
8
- } from "~/components/DirectoryView/buildDirectoryTree";
5
+ import { type TreeNode, findNodeById } from "~/components/DirectoryView/buildDirectoryTree";
9
6
  import { LavaLoader } from "~/components/LavaLoader";
10
7
  import { SearchRouteLoaderResponse } from "~/routes/search.route";
11
8
  import { convertCsvToParquet } from "~/utils/db/convertCsvToParquet";
@@ -32,10 +29,7 @@ export function AddOverlay({ callback, query, onOverlayAdd }: AddOverlayProps) {
32
29
  // eslint-disable-next-line react-hooks/exhaustive-deps -- fetcher object ref changes every render; only re-run on state transitions
33
30
  }, [objectsFetcher.state, objectsFetcher.data, searchString]);
34
31
 
35
- const nodes = useMemo(
36
- () => objectsFetcher.data?.nodes ?? [],
37
- [objectsFetcher.data?.nodes],
38
- );
32
+ const nodes = useMemo(() => objectsFetcher.data?.nodes ?? [], [objectsFetcher.data?.nodes]);
39
33
  const isLoading = objectsFetcher.state === "loading";
40
34
 
41
35
  // Selection state: single file selection
@@ -57,8 +51,7 @@ export function AddOverlay({ callback, query, onOverlayAdd }: AddOverlayProps) {
57
51
  const [searchTerm, setSearchTerm] = useState("");
58
52
 
59
53
  const searchMatch = useCallback(
60
- (node: { name: string }, term: string) =>
61
- node.name.toLowerCase().includes(term.toLowerCase()),
54
+ (node: { name: string }, term: string) => node.name.toLowerCase().includes(term.toLowerCase()),
62
55
  [],
63
56
  );
64
57
 
@@ -131,9 +124,7 @@ export function AddOverlay({ callback, query, onOverlayAdd }: AddOverlayProps) {
131
124
  searchMatch={searchMatch}
132
125
  />
133
126
  </div>
134
- <p className="truncate text-xs text-(--color-text-tertiary)">
135
- {hoveredPath ?? "…"}
136
- </p>
127
+ <p className="truncate text-xs text-(--color-text-tertiary)">{hoveredPath ?? "…"}</p>
137
128
  </>
138
129
  )}
139
130
 
@@ -8,12 +8,7 @@ export function LoadOverlayModal({ onClose }: { onClose: () => void }) {
8
8
  const addOverlaysState = useViewerStore(select.addOverlaysState);
9
9
 
10
10
  return (
11
- <RouteModal
12
- title="Select Overlay File"
13
- onClose={onClose}
14
- size="lg"
15
- isDismissable={false}
16
- >
11
+ <RouteModal title="Select Overlay File" onClose={onClose} size="lg" isDismissable={false}>
17
12
  <AddOverlay callback={onClose} query="parquet" onOverlayAdd={addOverlaysState} />
18
13
  </RouteModal>
19
14
  );
@@ -1,8 +1,4 @@
1
- import {
2
- IconButton,
3
- IconButtonLink,
4
- useToast,
5
- } from "@cytario/design";
1
+ import { IconButton, IconButtonLink, useToast } from "@cytario/design";
6
2
  import { ExternalLink, X } from "lucide-react";
7
3
  import { useEffect, useState } from "react";
8
4
  import { RadioGroup } from "react-aria-components";
@@ -36,13 +32,9 @@ export const OverlaysControllerItem = ({
36
32
  const { toast } = useToast();
37
33
 
38
34
  // Get file download progress from the file store
39
- const fileProgress = useFileStore(
40
- (state) => state.files[resourceId]?.progress,
41
- );
35
+ const fileProgress = useFileStore((state) => state.files[resourceId]?.progress);
42
36
 
43
- const cx = twMerge(
44
- "flex flex-col px-3",
45
- );
37
+ const cx = twMerge("flex flex-col px-3");
46
38
 
47
39
  const [isOpen, setIsOpen] = useState(true);
48
40
  const [isLoading, setIsLoading] = useState(false);
@@ -57,9 +49,7 @@ export const OverlaysControllerItem = ({
57
49
  );
58
50
 
59
51
  const { connectionName } = parseResourceId(resourceId);
60
- const connectionConfig = useConnectionsStore(
61
- connectionsSelect.connectionConfig(connectionName),
62
- );
52
+ const connectionConfig = useConnectionsStore(connectionsSelect.connectionConfig(connectionName));
63
53
 
64
54
  // Fetch markers on mount if not already loaded
65
55
  useEffect(() => {
@@ -90,14 +80,7 @@ export const OverlaysControllerItem = ({
90
80
  };
91
81
 
92
82
  fetchMarkers();
93
- }, [
94
- hasMarkers,
95
- resourceId,
96
- connectionConfig,
97
- updateOverlaysState,
98
- toast,
99
- fileName,
100
- ]);
83
+ }, [hasMarkers, resourceId, connectionConfig, updateOverlaysState, toast, fileName]);
101
84
 
102
85
  return (
103
86
  <div className="flex flex-col">
@@ -125,9 +108,7 @@ export const OverlaysControllerItem = ({
125
108
  variant="ghost"
126
109
  size="sm"
127
110
  onPress={() => {
128
- const confirmation = confirm(
129
- `Are you sure you want to remove overlay "${fileName}"?`,
130
- );
111
+ const confirmation = confirm(`Are you sure you want to remove overlay "${fileName}"?`);
131
112
  if (confirmation) removeOverlaysState(resourceId);
132
113
  }}
133
114
  />
@@ -140,38 +121,27 @@ export const OverlaysControllerItem = ({
140
121
  <div className="flex flex-col items-center justify-center gap-2 p-4">
141
122
  <LavaLoader />
142
123
  {fileProgress && fileProgress.percentage < 100 && (
143
- <div className="text-sm">
144
- Downloading: {Math.round(fileProgress.percentage)}%
145
- </div>
124
+ <div className="text-sm">Downloading: {Math.round(fileProgress.percentage)}%</div>
146
125
  )}
147
126
  </div>
148
127
  ) : hasMarkers ? (
149
- Object.entries(overlayState).map(
150
- ([markerName, { color, count, isVisible }]) => {
151
- return (
152
- <ChannelsControllerItem
153
- key={markerName}
154
- name={
155
- markerName.replace(
156
- "marker_positive_",
157
- "",
158
- ) as keyof ChannelsStateColumns
159
- }
160
- color={color}
161
- isVisible={isVisible}
162
- isLoading={false}
163
- pixelValue={count}
164
- maxDomain={maxDomain}
165
- toggleChannelVisibility={() =>
166
- setMarkerVisibility(resourceId, markerName, !isVisible)
167
- }
168
- onColorChange={(color) =>
169
- setMarkerColor(resourceId, markerName, color)
170
- }
171
- />
172
- );
173
- },
174
- )
128
+ Object.entries(overlayState).map(([markerName, { color, count, isVisible }]) => {
129
+ return (
130
+ <ChannelsControllerItem
131
+ key={markerName}
132
+ name={markerName.replace("marker_positive_", "") as keyof ChannelsStateColumns}
133
+ color={color}
134
+ isVisible={isVisible}
135
+ isLoading={false}
136
+ pixelValue={count}
137
+ maxDomain={maxDomain}
138
+ toggleChannelVisibility={() =>
139
+ setMarkerVisibility(resourceId, markerName, !isVisible)
140
+ }
141
+ onColorChange={(color) => setMarkerColor(resourceId, markerName, color)}
142
+ />
143
+ );
144
+ })
175
145
  ) : (
176
146
  <div className="p-4 text-sm text-[var(--color-text-secondary)]">
177
147
  No markers found in this overlay
@@ -66,9 +66,7 @@ export const OverlaysController = () => {
66
66
  )}
67
67
  </footer>
68
68
 
69
- {isOpen && (
70
- <LoadOverlayModal onClose={() => setIsOpen(false)} />
71
- )}
69
+ {isOpen && <LoadOverlayModal onClose={() => setIsOpen(false)} />}
72
70
  </FeatureItem>
73
71
  );
74
72
  };
@@ -17,9 +17,7 @@ export const SplitViewToggle = () => {
17
17
  const isSplitViewEnabled = imagePanels.length > 1;
18
18
 
19
19
  return (
20
- <Tooltip
21
- content={isSplitViewEnabled ? "Disable Split View" : "Enable Split View"}
22
- >
20
+ <Tooltip content={isSplitViewEnabled ? "Disable Split View" : "Enable Split View"}>
23
21
  <Button
24
22
  onPress={() => {
25
23
  if (imagePanels.length === 1) {
@@ -22,9 +22,7 @@ export const ViewerHeader = ({
22
22
 
23
23
  useEffect(() => {
24
24
  if (children) {
25
- setHeaderSlot(
26
- children({ metadata, viewStateActive, setViewStateActive })
27
- );
25
+ setHeaderSlot(children({ metadata, viewStateActive, setViewStateActive }));
28
26
  }
29
27
 
30
28
  return () => {
@@ -1,15 +1,15 @@
1
1
  export interface FrameInfo {
2
- width: number;
3
- height: number;
4
- componentCount: number;
5
- bitsPerSample: number;
6
- isSigned: boolean;
7
- isReversible: boolean;
2
+ width: number;
3
+ height: number;
4
+ componentCount: number;
5
+ bitsPerSample: number;
6
+ isSigned: boolean;
7
+ isReversible: boolean;
8
8
  }
9
9
 
10
10
  export interface DecodedImage {
11
- info: FrameInfo;
12
- pixels: Uint8Array;
11
+ info: FrameInfo;
12
+ pixels: Uint8Array;
13
13
  }
14
14
 
15
15
  /**
@@ -25,9 +25,7 @@ export function loadJp2KDecoder(decodeConfig?: unknown): Promise<void>;
25
25
  * @returns Promise resolving to decoded image data with frame info and pixels
26
26
  * @throws Error if the compressed frame is invalid or decoding fails
27
27
  */
28
- export function decodeJPEG2000(
29
- compressedImageFrame: Uint8Array,
30
- ): Promise<DecodedImage>;
28
+ export function decodeJPEG2000(compressedImageFrame: Uint8Array): Promise<DecodedImage>;
31
29
 
32
30
  /**
33
31
  * Cleanup function to release decoder resources