@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.
- package/README.md +20 -20
- package/app/.server/auth/README.md +8 -8
- package/app/.server/auth/authMiddleware.ts +9 -25
- package/app/.server/auth/exchangeAuthCode.ts +2 -6
- package/app/.server/auth/getS3Client.ts +3 -13
- package/app/.server/auth/getSessionCredentials.ts +6 -20
- package/app/.server/auth/getUserInfo.ts +2 -6
- package/app/.server/auth/keycloakAdmin/client.ts +2 -9
- package/app/.server/auth/keycloakAdmin/groups.ts +9 -26
- package/app/.server/auth/keycloakAdmin/users.ts +7 -23
- package/app/.server/auth/oauthState.ts +4 -13
- package/app/.server/auth/redirectIfAuthenticated.ts +1 -3
- package/app/.server/auth/refreshAuthTokens.ts +5 -19
- package/app/.server/auth/sessionMiddleware.ts +1 -4
- package/app/.server/auth/sessionStorage.ts +1 -4
- package/app/.server/auth/verifyIdToken.ts +1 -3
- package/app/.server/auth/wellKnownEndpoints.ts +1 -4
- package/app/.server/db/redis.ts +5 -1
- package/app/.server/logging.ts +1 -4
- package/app/.server/requestDurationMiddleware.ts +1 -4
- package/app/components/.client/ImageViewer/README.md +5 -5
- package/app/components/.client/ImageViewer/components/ChannelsController/ChannelsController.tsx +7 -9
- package/app/components/.client/ImageViewer/components/ChannelsController/ChannelsControllerBrightfieldItem.tsx +1 -2
- package/app/components/.client/ImageViewer/components/ChannelsController/ChannelsControllerItem.tsx +2 -5
- package/app/components/.client/ImageViewer/components/ChannelsController/ChannelsControllerItemList.tsx +7 -15
- package/app/components/.client/ImageViewer/components/ChannelsController/ColorPicker/ColorPicker.tsx +2 -9
- package/app/components/.client/ImageViewer/components/ChannelsController/ColorPicker/ColorSwatch.tsx +1 -4
- package/app/components/.client/ImageViewer/components/ChannelsController/DomainSlider.tsx +1 -3
- package/app/components/.client/ImageViewer/components/ChannelsController/Histogram.tsx +16 -18
- package/app/components/.client/ImageViewer/components/ChannelsController/HistogramChannel.tsx +2 -8
- package/app/components/.client/ImageViewer/components/ChannelsController/MinMaxSettings.tsx +6 -15
- package/app/components/.client/ImageViewer/components/FeatureBar/FeatureBarDragHandle.tsx +1 -5
- package/app/components/.client/ImageViewer/components/FeatureBar/FeatureBarToggle.tsx +1 -5
- package/app/components/.client/ImageViewer/components/FeatureBar/FeatureItem.tsx +1 -5
- package/app/components/.client/ImageViewer/components/FeatureBar/Presets.tsx +3 -11
- package/app/components/.client/ImageViewer/components/FeatureBar/useFeatureBar.tsx +16 -25
- package/app/components/.client/ImageViewer/components/Image/Channels/useChannelsLayer.ts +7 -18
- package/app/components/.client/ImageViewer/components/Image/ImageContainer.tsx +1 -1
- package/app/components/.client/ImageViewer/components/Image/ImagePanel.tsx +6 -26
- package/app/components/.client/ImageViewer/components/Image/ImagePreview.tsx +2 -9
- package/app/components/.client/ImageViewer/components/Image/Overlays/AdditivePolygonLayer.tsx +1 -5
- package/app/components/.client/ImageViewer/components/Image/Overlays/AdditiveScatterplotLayer.tsx +1 -5
- package/app/components/.client/ImageViewer/components/Image/Overlays/OverlaysLayer.tsx +6 -24
- package/app/components/.client/ImageViewer/components/Image/Overlays/markerUniforms.ts +2 -5
- package/app/components/.client/ImageViewer/components/Image/Overlays/useOverlaysLayer.tsx +7 -21
- package/app/components/.client/ImageViewer/components/Image/useInitializeChannels.ts +1 -7
- package/app/components/.client/ImageViewer/components/Image/useResizeObserver.ts +1 -1
- package/app/components/.client/ImageViewer/components/Magnifier.tsx +5 -13
- package/app/components/.client/ImageViewer/components/Measurements/ActiveViewStatePreview.tsx +3 -8
- package/app/components/.client/ImageViewer/components/Measurements/CursorTick.tsx +2 -7
- package/app/components/.client/ImageViewer/components/Measurements/Ruler.tsx +1 -8
- package/app/components/.client/ImageViewer/components/Measurements/SlideCarrier.tsx +3 -13
- package/app/components/.client/ImageViewer/components/Measurements/Tick.tsx +1 -1
- package/app/components/.client/ImageViewer/components/Measurements/calculateViewStateToFit.ts +1 -1
- package/app/components/.client/ImageViewer/components/Measurements/useMeasurements.ts +9 -28
- package/app/components/.client/ImageViewer/components/OverlaysController/AddOverlay.tsx +4 -13
- package/app/components/.client/ImageViewer/components/OverlaysController/OverlayPicker.modal.tsx +1 -6
- package/app/components/.client/ImageViewer/components/OverlaysController/OverlaysController.Item.tsx +24 -54
- package/app/components/.client/ImageViewer/components/OverlaysController/OverlaysController.tsx +1 -3
- package/app/components/.client/ImageViewer/components/SplitViewToggle.tsx +1 -3
- package/app/components/.client/ImageViewer/components/ViewerHeader.tsx +1 -3
- package/app/components/.client/ImageViewer/state/decoders/decodeJPEG2000.d.ts +9 -11
- package/app/components/.client/ImageViewer/state/decoders/decodeJPEG2000.js +11 -11
- package/app/components/.client/ImageViewer/state/decoders/decoder.worker.js +49 -49
- package/app/components/.client/ImageViewer/state/decoders/genericDecoder.ts +76 -81
- package/app/components/.client/ImageViewer/state/decoders/jp2k-decoder.ts +9 -9
- package/app/components/.client/ImageViewer/state/decoders/lzwDecoder.ts +9 -9
- package/app/components/.client/ImageViewer/state/loaders/loadBioformatsZarrWithCredentials.ts +10 -22
- package/app/components/.client/ImageViewer/state/store/ViewerStoreContext.tsx +4 -18
- package/app/components/.client/ImageViewer/state/store/createViewerStore.ts +110 -194
- package/app/components/.client/ImageViewer/state/store/getInitialChannelsState.ts +2 -6
- package/app/components/.client/ImageViewer/state/store/selectors.ts +9 -9
- package/app/components/.client/ImageViewer/state/store/types.ts +3 -12
- package/app/components/.client/ImageViewer/state/transport/CredentialedHTTPStore.ts +1 -5
- package/app/components/.client/ImageViewer/state/transport/SigV4TiffClient.ts +2 -9
- package/app/components/.client/ImageViewer/utils/getSelectionStats.ts +1 -4
- package/app/components/.client/ImageViewer/utils/handleImageViewerHover.ts +1 -1
- package/app/components/.client/ImageViewer/utils/mapChannelConfigsToState.ts +2 -4
- package/app/components/.client/ImageViewer/utils/useTilesLoading.ts +3 -3
- package/app/components/AppHeader.tsx +1 -4
- package/app/components/Breadcrumbs/Breadcrumbs.tsx +4 -13
- package/app/components/Breadcrumbs/getCrumbs.tsx +1 -1
- package/app/components/ClientOnly.tsx +1 -1
- package/app/components/Container.tsx +3 -15
- package/app/components/DataGrid/ConvertOverlay.modal.tsx +1 -6
- package/app/components/DataGrid/DataGrid.tsx +7 -27
- package/app/components/DataGrid/WktSvg.tsx +2 -4
- package/app/components/DataGrid/getParquetSchema.ts +1 -4
- package/app/components/DescriptionList.tsx +1 -3
- package/app/components/DirectoryView/ConnectionMenu.tsx +8 -22
- package/app/components/DirectoryView/DirectoryView.tsx +10 -46
- package/app/components/DirectoryView/DirectoryViewGrid.tsx +16 -49
- package/app/components/DirectoryView/DirectoryViewTableConnection.tsx +1 -4
- package/app/components/DirectoryView/DirectoryViewTableDirectory.tsx +2 -7
- package/app/components/DirectoryView/DirectoryViewTree.tsx +5 -21
- package/app/components/DirectoryView/FilterBar.tsx +9 -48
- package/app/components/DirectoryView/buildDirectoryTree.ts +6 -25
- package/app/components/DirectoryView/filterNodes.ts +4 -11
- package/app/components/DirectoryView/modals/Cyberduck.modal.tsx +6 -15
- package/app/components/DirectoryView/modals/FileInfo.modal.tsx +1 -5
- package/app/components/DirectoryView/useLayoutStore.ts +5 -25
- package/app/components/GlobalSearch/GlobalSearch.tsx +1 -4
- package/app/components/GlobalSearch/SearchBar.tsx +1 -7
- package/app/components/GlobalSearch/Suggestions.tsx +0 -1
- package/app/components/ImageViewer/state/formatRegistry.ts +5 -18
- package/app/components/LavaLoader.tsx +4 -11
- package/app/components/Layout/Footer.tsx +1 -4
- package/app/components/Pills/ScopePill.tsx +2 -8
- package/app/components/Table/ColumnFilterInput.tsx +5 -20
- package/app/components/Table/ColumnResizeHandle.tsx +1 -5
- package/app/components/Table/ColumnSortButton.tsx +1 -5
- package/app/components/Table/SelectionFooter.tsx +3 -5
- package/app/components/Table/Table.tsx +5 -21
- package/app/components/Table/TableBodyRow.tsx +19 -31
- package/app/components/Table/TableHeaderRow.tsx +7 -28
- package/app/components/Table/TableMenu.tsx +4 -20
- package/app/components/Table/state/createTableStore.ts +3 -9
- package/app/components/Table/state/useTableStore.ts +1 -3
- package/app/components/Table/types.ts +4 -10
- package/app/components/Table/useColumnFilters.ts +1 -4
- package/app/components/Table/useColumnVisibility.ts +4 -11
- package/app/components/Table/useColumnWidths.ts +1 -3
- package/app/components/Table/useTableSorting.ts +4 -12
- package/app/components/Tooltip/Tooltip.tsx +4 -17
- package/app/components/Tooltip/TooltipSpan.tsx +5 -28
- package/app/components/Tooltip/useCopyToClipboard.ts +1 -3
- package/app/components/Tooltip/useMiddleEllipsis.ts +2 -7
- package/app/components/Tooltip/useOverflowDetection.ts +2 -5
- package/app/components/UserMenu.tsx +2 -9
- package/app/entry.server.tsx +9 -19
- package/app/hooks/useSearchParam.ts +2 -4
- package/app/lib/bootstrapPluginsCore.ts +4 -9
- package/app/root.tsx +4 -15
- package/app/routes/admin/assertAdminScope.ts +1 -3
- package/app/routes/admin/assertGroupPathsInScope.ts +3 -11
- package/app/routes/admin/assertGroupsInScope.ts +3 -11
- package/app/routes/admin/assertUsersInScope.ts +2 -8
- package/app/routes/admin/bulkInvite/bulkInvite.action.ts +2 -13
- package/app/routes/admin/bulkInvite/bulkInvite.form.tsx +18 -35
- package/app/routes/admin/createGroup/createGroup.action.ts +3 -10
- package/app/routes/admin/createGroup/createGroup.form.tsx +2 -9
- package/app/routes/admin/createGroup/createGroup.modal.tsx +1 -5
- package/app/routes/admin/inviteUser/inviteUser.action.ts +1 -4
- package/app/routes/admin/inviteUser/inviteUser.form.tsx +2 -8
- package/app/routes/admin/inviteUser/inviteUser.loader.ts +1 -4
- package/app/routes/admin/inviteUser/inviteUser.modal.tsx +3 -16
- package/app/routes/admin/updateUser/updateUser.form.tsx +4 -15
- package/app/routes/admin/updateUser/updateUser.modal.tsx +5 -23
- package/app/routes/admin/updateUser/userDetail.action.ts +2 -10
- package/app/routes/admin/users/BulkActions.tsx +15 -38
- package/app/routes/admin/users/bulkUsers.action.ts +2 -9
- package/app/routes/admin/users/bulkUsers.schema.ts +1 -6
- package/app/routes/admin/users/users.route.tsx +14 -63
- package/app/routes/api/cyberduck-profile.$name.ts +6 -2
- package/app/routes/auth/callback.route.tsx +8 -33
- package/app/routes/auth/login.route.tsx +8 -11
- package/app/routes/auth/logout.route.tsx +4 -14
- package/app/routes/config.route.tsx +1 -5
- package/app/routes/connections/connection.form.tsx +5 -14
- package/app/routes/connections/connection.schema.ts +4 -16
- package/app/routes/connections/connections.loader.ts +11 -23
- package/app/routes/connections/connections.route.tsx +1 -5
- package/app/routes/connections/connections.server.ts +1 -3
- package/app/routes/connections/createConnection.action.ts +2 -8
- package/app/routes/connections/createConnection.modal.tsx +3 -13
- package/app/routes/connections/deleteConnection.action.ts +1 -4
- package/app/routes/connections/updateConnection.action.ts +2 -8
- package/app/routes/connections/updateConnection.modal.tsx +4 -14
- package/app/routes/home/home.route.tsx +8 -33
- package/app/routes/layouts/ModalOutlet.tsx +6 -18
- package/app/routes/objects/objects.loader.ts +5 -19
- package/app/routes/objects/objects.route.tsx +11 -30
- package/app/routes/presign.route.tsx +5 -18
- package/app/routes/recent.route.tsx +1 -4
- package/app/routes/search.route.tsx +4 -17
- package/app/routes.ts +1 -4
- package/app/tailwind.css +17 -12
- package/app/types/cornerstone-codecs.d.ts +25 -29
- package/app/utils/connectionsStore/selectors.ts +2 -6
- package/app/utils/connectionsStore/useConnectionsStore.ts +2 -6
- package/app/utils/db/convertCsvToParquet.ts +1 -3
- package/app/utils/db/createDatabase.ts +1 -3
- package/app/utils/db/createSingleton.ts +1 -1
- package/app/utils/db/getBlobFromObjectNode.ts +3 -9
- package/app/utils/db/getGeomQuery.ts +1 -1
- package/app/utils/db/getMarkerInfoWasm.ts +1 -3
- package/app/utils/db/getTileBoundingBox.ts +1 -4
- package/app/utils/db/sqlQueries.ts +1 -4
- package/app/utils/fileType.ts +80 -10
- package/app/utils/filterObjects.ts +2 -5
- package/app/utils/localFilesStore/useFileStore.ts +7 -7
- package/app/utils/recentlyViewed.server.ts +1 -4
- package/app/utils/resourceId.ts +3 -11
- package/app/utils/s3Provider.ts +3 -7
- package/app/utils/signedFetch.ts +4 -13
- package/bin-src/codegen.ts +4 -1
- package/package.json +5 -1
- package/prisma/seed.ts +1 -2
- package/public/favicon/site.webmanifest +1 -1
- package/server.js +1 -4
- package/server.js.map +1 -1
- 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
|
-
|
|
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
|
|
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
|
-
|
|
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}
|
package/app/components/.client/ImageViewer/components/Measurements/ActiveViewStatePreview.tsx
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
36
|
-
|
|
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);
|
package/app/components/.client/ImageViewer/components/Measurements/calculateViewStateToFit.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
package/app/components/.client/ImageViewer/components/OverlaysController/OverlayPicker.modal.tsx
CHANGED
|
@@ -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
|
);
|
package/app/components/.client/ImageViewer/components/OverlaysController/OverlaysController.Item.tsx
CHANGED
|
@@ -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
|
-
(
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
|
@@ -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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
12
|
-
|
|
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
|