@cytario/web 2.1.4 → 2.1.6

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
@@ -8,16 +8,12 @@ import { getSelectionStats } from "../../utils/getSelectionStats";
8
8
 
9
9
  /** Returns the RGB color for a channel, falling back to OVERLAY_COLORS if metadata lacks a Color. */
10
10
  const getInitialColor = (channels: Channel[], index: number): RGB => {
11
- const colorRaw =
12
- channels[index]?.Color ?? OVERLAY_COLORS[index % OVERLAY_COLORS.length];
11
+ const colorRaw = channels[index]?.Color ?? OVERLAY_COLORS[index % OVERLAY_COLORS.length];
13
12
  return colorRaw.slice(0, 3) as RGB;
14
13
  };
15
14
 
16
15
  /** Builds the initial channel configs (color, domain, contrast limits) from OME-TIFF metadata. */
17
- export const getInitialChannelsState = async (
18
- metadata: Image,
19
- loader: Loader,
20
- ) => {
16
+ export const getInitialChannelsState = async (metadata: Image, loader: Loader) => {
21
17
  const channels = metadata.Pixels.Channels as Channel[];
22
18
 
23
19
  const selection = { c: 0, x: 0, y: 0, z: 0, t: 0 };
@@ -14,7 +14,12 @@ const EMPTY_ARRAY: readonly string[] = Object.freeze([]);
14
14
  // Zustand uses Object.is for equality — returning a new object on every call
15
15
  // causes infinite re-render loops.
16
16
  let _bfGroupCache: { ids: readonly string[]; result: BrightfieldGroup | null } | null = null;
17
- let _bfSelectedCache: { r: ChannelConfig; g: ChannelConfig; b: ChannelConfig; result: ChannelConfig } | null = null;
17
+ let _bfSelectedCache: {
18
+ r: ChannelConfig;
19
+ g: ChannelConfig;
20
+ b: ChannelConfig;
21
+ result: ChannelConfig;
22
+ } | null = null;
18
23
  export const select = {
19
24
  id: (state: ViewerStore) => state.id,
20
25
  error: (state: ViewerStore) => state.error,
@@ -54,10 +59,8 @@ export const select = {
54
59
  setCursorPosition: (state: ViewerStore) => state.setCursorPosition,
55
60
 
56
61
  /* Channels State Management */
57
- setActiveChannelsStateIndex: (state: ViewerStore) =>
58
- state.setActiveChannelsStateIndex,
59
- activeChannelsStateIndex: (state: ViewerStore) =>
60
- state.imagePanels[state.imagePanelIndex],
62
+ setActiveChannelsStateIndex: (state: ViewerStore) => state.setActiveChannelsStateIndex,
63
+ activeChannelsStateIndex: (state: ViewerStore) => state.imagePanels[state.imagePanelIndex],
61
64
 
62
65
  /* Layers */
63
66
  layersState: (state: ViewerStore) => {
@@ -112,10 +115,7 @@ export const select = {
112
115
 
113
116
  /* Channels > Selected */
114
117
  selectedChannelId: (state: ViewerStore) =>
115
- state.selectedChannelId as
116
- | keyof ChannelsStateColumns
117
- | typeof BRIGHTFIELD_GROUP_ID
118
- | null,
118
+ state.selectedChannelId as keyof ChannelsStateColumns | typeof BRIGHTFIELD_GROUP_ID | null,
119
119
  setSelectedChannelId: (state: ViewerStore) => state.setSelectedChannelId,
120
120
  selectedChannel: (state: ViewerStore): ChannelConfig | null => {
121
121
  const selectedChannelId = select.selectedChannelId(state);
@@ -55,9 +55,7 @@ export interface BrightfieldGroup {
55
55
  }
56
56
 
57
57
  /** Detects brightfield R/G/B channels by name from UltiStacker output. */
58
- export const detectBrightfieldGroup = (
59
- channelIds: readonly string[],
60
- ): BrightfieldGroup | null => {
58
+ export const detectBrightfieldGroup = (channelIds: readonly string[]): BrightfieldGroup | null => {
61
59
  const red = channelIds.find((id) => id.toLowerCase() === "red");
62
60
  const green = channelIds.find((id) => id.toLowerCase() === "green");
63
61
  const blue = channelIds.find((id) => id.toLowerCase() === "blue");
@@ -140,16 +138,9 @@ interface ViewerStoreActions {
140
138
  setContrastLimits: (contrastLimits: ByteDomain) => void;
141
139
  resetContrastLimits: () => void;
142
140
 
143
- setChannelVisibility: (
144
- key: keyof ChannelsStateColumns,
145
- isVisible: boolean
146
- ) => void;
141
+ setChannelVisibility: (key: keyof ChannelsStateColumns, isVisible: boolean) => void;
147
142
 
148
- setMarkerVisibility: (
149
- fileName: string,
150
- markerName: string,
151
- isVisible: boolean
152
- ) => void;
143
+ setMarkerVisibility: (fileName: string, markerName: string, isVisible: boolean) => void;
153
144
 
154
145
  setChannelColor: (key: keyof ChannelsState, color: RGBA) => void;
155
146
  setMarkerColor: (fileName: string, markerName: string, color: RGBA) => void;
@@ -13,11 +13,7 @@ export class CredentialedHTTPStore {
13
13
  private signedFetch: SignedFetch;
14
14
  private extraHeaders: Record<string, string>;
15
15
 
16
- constructor(
17
- url: string,
18
- signedFetch: SignedFetch,
19
- extraHeaders?: Record<string, string>,
20
- ) {
16
+ constructor(url: string, signedFetch: SignedFetch, extraHeaders?: Record<string, string>) {
21
17
  this.baseUrl = new URL(url.endsWith("/") ? url : url + "/");
22
18
  this.signedFetch = signedFetch;
23
19
  this.extraHeaders = extraHeaders ?? {};
@@ -15,20 +15,13 @@ export class SigV4TiffClient {
15
15
  private signedFetch: SignedFetch;
16
16
  private extraHeaders: Record<string, string>;
17
17
 
18
- constructor(
19
- url: string,
20
- signedFetch: SignedFetch,
21
- extraHeaders?: Record<string, string>,
22
- ) {
18
+ constructor(url: string, signedFetch: SignedFetch, extraHeaders?: Record<string, string>) {
23
19
  this.url = url;
24
20
  this.signedFetch = signedFetch;
25
21
  this.extraHeaders = extraHeaders ?? {};
26
22
  }
27
23
 
28
- async request({
29
- headers,
30
- signal,
31
- }: { headers?: HeadersInit; signal?: AbortSignal } = {}) {
24
+ async request({ headers, signal }: { headers?: HeadersInit; signal?: AbortSignal } = {}) {
32
25
  const response = await this.signedFetch(this.url, {
33
26
  headers: {
34
27
  ...this.extraHeaders,
@@ -32,10 +32,7 @@ export async function getSelectionStats({
32
32
  const sortedPixels = [...pixels].sort((a, b) => a - b);
33
33
  // dtype is structurally `string` in @cytario/plugin-api; one of the
34
34
  // canonical PixelType values is guaranteed at runtime.
35
- const histogram = getHistogram(
36
- sortedPixels,
37
- getDtypeBitDepth(data.dtype as SupportedDtype),
38
- );
35
+ const histogram = getHistogram(sortedPixels, getDtypeBitDepth(data.dtype as SupportedDtype));
39
36
  const domain = getDomain(sortedPixels);
40
37
  const contrastLimits = getContrastLimits(sortedPixels);
41
38
 
@@ -3,7 +3,7 @@
3
3
  // import { PickingInfo } from "@deck.gl/core";
4
4
 
5
5
  export const handleImageViewerHover = (
6
- { tile, coordinate, sourceLayer: layer }: any // PickingInfo
6
+ { tile, coordinate, sourceLayer: layer }: any, // PickingInfo
7
7
  ) => {
8
8
  let hoverData;
9
9
  // Tiled layer needs a custom layerZoomScale.
@@ -1,8 +1,6 @@
1
1
  import { ChannelsStateColumns, ChannelsState } from "../state/store/types";
2
2
 
3
- export const mapChannelConfigsToState = (
4
- state: ChannelsState
5
- ): ChannelsStateColumns => {
3
+ export const mapChannelConfigsToState = (state: ChannelsState): ChannelsStateColumns => {
6
4
  return Object.entries(state).reduce<ChannelsStateColumns>(
7
5
  (acc, [id, config]) => {
8
6
  if (!config.isVisible) return acc;
@@ -25,6 +23,6 @@ export const mapChannelConfigsToState = (
25
23
  domains: [],
26
24
  selections: [],
27
25
  histograms: [],
28
- }
26
+ },
29
27
  );
30
28
  };
@@ -2,7 +2,7 @@ import { useRef, useCallback } from "react";
2
2
 
3
3
  export const useTilesLoading = (
4
4
  imagePanelId: number,
5
- setIsTilesLoading: (imagePanelId: number, count: number) => void
5
+ setIsTilesLoading: (imagePanelId: number, count: number) => void,
6
6
  ) => {
7
7
  const loadingSet = useRef(new Set<string>());
8
8
 
@@ -12,7 +12,7 @@ export const useTilesLoading = (
12
12
  loadingSet.current.add(id);
13
13
  setIsTilesLoading(imagePanelId, loadingSet.current.size);
14
14
  },
15
- [imagePanelId, setIsTilesLoading]
15
+ [imagePanelId, setIsTilesLoading],
16
16
  );
17
17
 
18
18
  const finishTile = useCallback(
@@ -20,7 +20,7 @@ export const useTilesLoading = (
20
20
  loadingSet.current.delete(id);
21
21
  setIsTilesLoading(imagePanelId, loadingSet.current.size);
22
22
  },
23
- [imagePanelId, setIsTilesLoading]
23
+ [imagePanelId, setIsTilesLoading],
24
24
  );
25
25
 
26
26
  return { loadTile, finishTile };
@@ -53,10 +53,7 @@ export function AppHeader() {
53
53
  <div className="h-full flex-none flex gap-2 p-2 items-center">
54
54
  <GlobalSearch />
55
55
  {data?.accountSettingsUrl && data.user && (
56
- <UserMenu
57
- user={data.user}
58
- accountSettingsUrl={data.accountSettingsUrl}
59
- />
56
+ <UserMenu user={data.user} accountSettingsUrl={data.accountSettingsUrl} />
60
57
  )}
61
58
  </div>
62
59
  </header>
@@ -1,9 +1,5 @@
1
- import {
2
- Breadcrumbs as DesignBreadcrumbs,
3
- type BreadcrumbItem,
4
- } from "@cytario/design";
5
- import { Link , UIMatch, useMatches } from "react-router";
6
-
1
+ import { Breadcrumbs as DesignBreadcrumbs, type BreadcrumbItem } from "@cytario/design";
2
+ import { Link, UIMatch, useMatches } from "react-router";
7
3
 
8
4
  import { Logo } from "../Logo";
9
5
 
@@ -21,9 +17,7 @@ type BreadcrumbMatch = UIMatch<
21
17
 
22
18
  export function Breadcrumbs() {
23
19
  const matches = useMatches() as BreadcrumbMatch[];
24
- const filteredMatches = matches.filter(
25
- (match) => match.handle && match.handle.breadcrumb
26
- );
20
+ const filteredMatches = matches.filter((match) => match.handle && match.handle.breadcrumb);
27
21
 
28
22
  const crumbs = filteredMatches.flatMap((match) => {
29
23
  const result = match.handle.breadcrumb(match);
@@ -47,10 +41,7 @@ export function Breadcrumbs() {
47
41
  </Link>
48
42
  )}
49
43
  {items.length > 0 && (
50
- <DesignBreadcrumbs
51
- items={items}
52
- className="flex items-center overflow-hidden"
53
- />
44
+ <DesignBreadcrumbs items={items} className="flex items-center overflow-hidden" />
54
45
  )}
55
46
  </div>
56
47
  );
@@ -10,7 +10,7 @@ export interface CrumbsOptions {
10
10
  export const getCrumbs = (
11
11
  basePath: string,
12
12
  segments: string[],
13
- options?: CrumbsOptions
13
+ options?: CrumbsOptions,
14
14
  ): BreadcrumbData[] => {
15
15
  const { dataConnectionName, dataConnectionPath } = options ?? {};
16
16
 
@@ -6,7 +6,7 @@ export const ClientOnly = ({ children }: { children: React.ReactNode }) => {
6
6
  const isClient = useSyncExternalStore(
7
7
  emptySubscribe,
8
8
  () => true,
9
- () => false
9
+ () => false,
10
10
  );
11
11
 
12
12
  if (!isClient) {
@@ -18,18 +18,8 @@ export function Section({ children, className, flush }: SectionProps) {
18
18
  );
19
19
  }
20
20
 
21
- export const Container = ({
22
- children,
23
- wide,
24
- }: {
25
- children: ReactNode;
26
- wide?: boolean;
27
- }) => {
28
- return (
29
- <div className={wide ? "mx-auto px-4" : "container mx-auto px-4"}>
30
- {children}
31
- </div>
32
- );
21
+ export const Container = ({ children, wide }: { children: ReactNode; wide?: boolean }) => {
22
+ return <div className={wide ? "mx-auto px-4" : "container mx-auto px-4"}>{children}</div>;
33
23
  };
34
24
 
35
25
  export function SectionHeader({
@@ -48,9 +38,7 @@ export function SectionHeader({
48
38
  {name && <H2 className="grow">{name}</H2>}
49
39
  {children}
50
40
  </div>
51
- {secondaryActions && (
52
- <div className="flex items-center gap-2">{secondaryActions}</div>
53
- )}
41
+ {secondaryActions && <div className="flex items-center gap-2">{secondaryActions}</div>}
54
42
  </header>
55
43
  </Container>
56
44
  );
@@ -4,12 +4,7 @@ import { RouteModal } from "~/components/RouteModal";
4
4
  /** CSV to Parquet conversion modal. */
5
5
  export default function ConvertOverlayModal({ onClose }: { onClose: () => void }) {
6
6
  return (
7
- <RouteModal
8
- title="Convert CSV to Parquet"
9
- onClose={onClose}
10
- size="lg"
11
- isDismissable={false}
12
- >
7
+ <RouteModal title="Convert CSV to Parquet" onClose={onClose} size="lg" isDismissable={false}>
13
8
  <AddOverlay callback={onClose} query="csv" />
14
9
  </RouteModal>
15
10
  );
@@ -36,9 +36,7 @@ export const DataGrid = ({ resourceId }: { resourceId: string }) => {
36
36
 
37
37
  const containerRef = useRef<HTMLDivElement>(null);
38
38
  const { connectionName } = parseResourceId(resourceId);
39
- const connectionConfig = useConnectionsStore(
40
- select.connectionConfig(connectionName),
41
- );
39
+ const connectionConfig = useConnectionsStore(select.connectionConfig(connectionName));
42
40
 
43
41
  // Initial data fetch
44
42
  useEffect(() => {
@@ -77,12 +75,7 @@ export const DataGrid = ({ resourceId }: { resourceId: string }) => {
77
75
  } finally {
78
76
  setIsFetchingMore(false);
79
77
  }
80
- }, [
81
- resourceId,
82
- rows.length,
83
- isFetchingMore,
84
- hasMore,
85
- ]);
78
+ }, [resourceId, rows.length, isFetchingMore, hasMore]);
86
79
 
87
80
  const columnHelper = createColumnHelper<Record<string, unknown>>();
88
81
 
@@ -161,26 +154,17 @@ export const DataGrid = ({ resourceId }: { resourceId: string }) => {
161
154
  <table className="min-w-full border-collapse text-sm">
162
155
  <thead className="bg-gray-100 dark:bg-slate-800 sticky top-0 z-10">
163
156
  {table.getHeaderGroups().map((headerGroup) => (
164
- <tr
165
- key={headerGroup.id}
166
- className="grid"
167
- style={{ gridTemplateColumns }}
168
- >
157
+ <tr key={headerGroup.id} className="grid" style={{ gridTemplateColumns }}>
169
158
  {headerGroup.headers.map((header) => (
170
159
  <th
171
160
  key={header.id}
172
161
  className={`border-b border-gray-200 dark:border-slate-700 px-4 py-2 font-semibold ${
173
- RIGHT_ALIGNED_COLUMNS.has(header.id)
174
- ? "text-right"
175
- : "text-left"
162
+ RIGHT_ALIGNED_COLUMNS.has(header.id) ? "text-right" : "text-left"
176
163
  }`}
177
164
  >
178
165
  {header.isPlaceholder
179
166
  ? null
180
- : flexRender(
181
- header.column.columnDef.header,
182
- header.getContext(),
183
- )}
167
+ : flexRender(header.column.columnDef.header, header.getContext())}
184
168
  </th>
185
169
  ))}
186
170
  </tr>
@@ -208,9 +192,7 @@ export const DataGrid = ({ resourceId }: { resourceId: string }) => {
208
192
  <td
209
193
  key={cell.id}
210
194
  className={`border-b border-gray-100 dark:border-slate-700 tabular-nums px-4 flex items-center ${
211
- RIGHT_ALIGNED_COLUMNS.has(cell.column.id)
212
- ? "justify-end"
213
- : ""
195
+ RIGHT_ALIGNED_COLUMNS.has(cell.column.id) ? "justify-end" : ""
214
196
  }`}
215
197
  >
216
198
  {flexRender(cell.column.columnDef.cell, cell.getContext())}
@@ -221,9 +203,7 @@ export const DataGrid = ({ resourceId }: { resourceId: string }) => {
221
203
  })}
222
204
  </tbody>
223
205
  </table>
224
- {isFetchingMore && (
225
- <div className="p-2 text-center text-gray-500">Loading more...</div>
226
- )}
206
+ {isFetchingMore && <div className="p-2 text-center text-gray-500">Loading more...</div>}
227
207
  </div>
228
208
  );
229
209
  };
@@ -87,15 +87,13 @@ function parseWkt(wkt: string): Point[][] {
87
87
 
88
88
  if (geometry.type === "Polygon") {
89
89
  // Polygon: [[[x, y], [x, y], ...]]
90
- return (coords as number[][][]).map((ring) =>
91
- ring.map(([x, y]) => ({ x, y }))
92
- );
90
+ return (coords as number[][][]).map((ring) => ring.map(([x, y]) => ({ x, y })));
93
91
  }
94
92
 
95
93
  if (geometry.type === "MultiPolygon") {
96
94
  // MultiPolygon: [[[[x, y], ...]], [[[x, y], ...]]]
97
95
  return (coords as number[][][][]).flatMap((polygon) =>
98
- polygon.map((ring) => ring.map(([x, y]) => ({ x, y })))
96
+ polygon.map((ring) => ring.map(([x, y]) => ({ x, y }))),
99
97
  );
100
98
  }
101
99
 
@@ -2,7 +2,6 @@ import { getFileType, getReadFunction } from "./fileReader";
2
2
  import { createDatabase } from "../../utils/db/createDatabase";
3
3
  import { resolveResourceId } from "~/utils/connectionsStore/selectors";
4
4
 
5
-
6
5
  export interface ParquetColumn {
7
6
  name: string;
8
7
  type: string;
@@ -12,9 +11,7 @@ export interface ParquetColumn {
12
11
  * Fetch the schema (column names and types) from a data file on S3.
13
12
  * Supports: parquet, csv, json
14
13
  */
15
- export async function getParquetSchema(
16
- resourceId: string,
17
- ): Promise<ParquetColumn[]> {
14
+ export async function getParquetSchema(resourceId: string): Promise<ParquetColumn[]> {
18
15
  const { credentials, connectionConfig, s3Uri } = resolveResourceId(resourceId);
19
16
  const connection = await createDatabase(resourceId, credentials, connectionConfig);
20
17
  const fileType = getFileType(resourceId);
@@ -16,9 +16,7 @@ const DescriptionList = ({ data, className }: DescriptionListProps<any>) => {
16
16
  <React.Fragment key={key}>
17
17
  <dt className="font-bold text-sm w-32 truncate">{key}</dt>
18
18
  <dd>
19
- <code className="text-slate-700 px-2 py-1 bg-slate-50">
20
- {String(value)}
21
- </code>
19
+ <code className="text-slate-700 px-2 py-1 bg-slate-50">{String(value)}</code>
22
20
  </dd>
23
21
  </React.Fragment>
24
22
  ))}
@@ -20,18 +20,12 @@ export function ConnectionMenu({ connectionName }: ConnectionMenuProps) {
20
20
  const focusReturnRef = useRef<HTMLElement | null>(null);
21
21
  const { openModal } = useModal();
22
22
 
23
- const connectionConfig = useConnectionsStore(
24
- select.connectionConfig(connectionName),
25
- );
23
+ const connectionConfig = useConnectionsStore(select.connectionConfig(connectionName));
26
24
 
27
- const rootData = useRouteLoaderData("root") as
28
- | { user?: UserProfile }
29
- | undefined;
25
+ const rootData = useRouteLoaderData("root") as { user?: UserProfile } | undefined;
30
26
  const user = rootData?.user;
31
27
  const userCanModify =
32
- user && connectionConfig
33
- ? canModify(user, connectionConfig.ownerScope)
34
- : false;
28
+ user && connectionConfig ? canModify(user, connectionConfig.ownerScope) : false;
35
29
 
36
30
  return (
37
31
  <>
@@ -50,9 +44,7 @@ export function ConnectionMenu({ connectionName }: ConnectionMenuProps) {
50
44
  <MenuItem
51
45
  id="edit"
52
46
  icon={Pencil}
53
- onAction={() =>
54
- openModal("edit-connection", { nodeName: connectionName })
55
- }
47
+ onAction={() => openModal("edit-connection", { nodeName: connectionName })}
56
48
  >
57
49
  Edit
58
50
  </MenuItem>
@@ -63,8 +55,7 @@ export function ConnectionMenu({ connectionName }: ConnectionMenuProps) {
63
55
  isDanger
64
56
  textValue="Delete connection"
65
57
  onAction={() => {
66
- focusReturnRef.current =
67
- document.activeElement as HTMLElement | null;
58
+ focusReturnRef.current = document.activeElement as HTMLElement | null;
68
59
  setConfirmOpen(true);
69
60
  }}
70
61
  >
@@ -83,12 +74,7 @@ export function ConnectionMenu({ connectionName }: ConnectionMenuProps) {
83
74
  />
84
75
  </Menu>
85
76
 
86
- <Form
87
- method="delete"
88
- action="/connections"
89
- ref={formRef}
90
- className="hidden"
91
- >
77
+ <Form method="delete" action="/connections" ref={formRef} className="hidden">
92
78
  <input type="hidden" name="connectionName" value={connectionName} />
93
79
  </Form>
94
80
 
@@ -103,8 +89,8 @@ export function ConnectionMenu({ connectionName }: ConnectionMenuProps) {
103
89
  confirmLabel="Remove"
104
90
  >
105
91
  <p>
106
- This will remove <strong>{connectionName}</strong> and its associated
107
- recents and pins. The underlying storage is not affected.
92
+ This will remove <strong>{connectionName}</strong> and its associated recents and pins.
93
+ The underlying storage is not affected.
108
94
  </p>
109
95
  </ConfirmDialog>
110
96
  </>
@@ -3,21 +3,11 @@ import { useMemo } from "react";
3
3
 
4
4
  import { TreeNode } from "./buildDirectoryTree";
5
5
  import { DirectoryViewGrid } from "./DirectoryViewGrid";
6
- import {
7
- DirectoryViewTableConnection,
8
- connectionColumns,
9
- } from "./DirectoryViewTableConnection";
10
- import {
11
- DirectoryViewTableDirectory,
12
- fileColumns,
13
- } from "./DirectoryViewTableDirectory";
6
+ import { DirectoryViewTableConnection, connectionColumns } from "./DirectoryViewTableConnection";
7
+ import { DirectoryViewTableDirectory, fileColumns } from "./DirectoryViewTableDirectory";
14
8
  import { DirectoryViewTree } from "./DirectoryViewTree";
15
9
  import { FilterBar } from "./FilterBar";
16
- import {
17
- filterHiddenNodes,
18
- filterNodes,
19
- getNodeAccessors,
20
- } from "./filterNodes";
10
+ import { filterHiddenNodes, filterNodes, getNodeAccessors } from "./filterNodes";
21
11
  import { type ViewMode, useLayoutStore } from "./useLayoutStore";
22
12
  import { Container, Section, SectionHeader } from "~/components/Container";
23
13
  import { useColumnFilters } from "~/components/Table/useColumnFilters";
@@ -73,14 +63,7 @@ export function DirectoryView({
73
63
  );
74
64
 
75
65
  const filteredNodes = useMemo(
76
- () =>
77
- filterNodes(
78
- visibleNodes,
79
- columnFilters,
80
- columns,
81
- kind,
82
- connections,
83
- ),
66
+ () => filterNodes(visibleNodes, columnFilters, columns, kind, connections),
84
67
  [visibleNodes, columnFilters, columns, kind, connections],
85
68
  );
86
69
 
@@ -109,8 +92,7 @@ export function DirectoryView({
109
92
  // view at prefix level is an anti-pattern anyway — proper tree-based
110
93
  // navigation belongs in a global sidebar per C-56
111
94
  // (https://app.plane.so/cytario/browse/C-56/).
112
- const nameFilter =
113
- (columnFilters.find((f) => f.id === "name")?.value as string) ?? "";
95
+ const nameFilter = (columnFilters.find((f) => f.id === "name")?.value as string) ?? "";
114
96
 
115
97
  return (
116
98
  <Section flush={flush}>
@@ -120,37 +102,19 @@ export function DirectoryView({
120
102
 
121
103
  {showFilters && viewMode !== "list" && (
122
104
  <Container>
123
- <FilterBar
124
- columns={columns}
125
- tableId={kind}
126
- dynamicOptions={dynamicOptions}
127
- />
105
+ <FilterBar columns={columns} tableId={kind} dynamicOptions={dynamicOptions} />
128
106
  </Container>
129
107
  )}
130
108
 
131
109
  <Container>
132
110
  {isTree ? (
133
- <DirectoryViewTree
134
- nodes={visibleNodes}
135
- searchTerm={nameFilter}
136
- kind={kind}
137
- />
111
+ <DirectoryViewTree nodes={visibleNodes} searchTerm={nameFilter} kind={kind} />
138
112
  ) : isGrid ? (
139
- <DirectoryViewGrid
140
- nodes={filteredNodes}
141
- viewMode={viewMode}
142
- kind={kind}
143
- />
113
+ <DirectoryViewGrid nodes={filteredNodes} viewMode={viewMode} kind={kind} />
144
114
  ) : kind === "connections" ? (
145
- <DirectoryViewTableConnection
146
- nodes={filteredNodes}
147
- showFilters={showFilters}
148
- />
115
+ <DirectoryViewTableConnection nodes={filteredNodes} showFilters={showFilters} />
149
116
  ) : (
150
- <DirectoryViewTableDirectory
151
- nodes={filteredNodes}
152
- showFilters={showFilters}
153
- />
117
+ <DirectoryViewTableDirectory nodes={filteredNodes} showFilters={showFilters} />
154
118
  )}
155
119
  </Container>
156
120
  </Section>