Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,36 @@ jobs:
- name: Lint React packages (react-hooks / react-compiler rules)
run: pnpm lint:react

biome-lint:
runs-on: ubuntu-latest
permissions:
contents: read
# Biome lint/format gate over the library source (packages/*/src). The error
# backlog there has been cleared, so `biome ci` fails on any new error.
# noExplicitAny is configured as a non-blocking warning. Demo, scripts, and
# tests are intentionally out of scope (still carry findings), so this gate is
# narrower than `pnpm lint` (biome check .).
steps:
- uses: actions/checkout@v5
with:
persist-credentials: false

- name: Setup pnpm
uses: pnpm/action-setup@v5
with:
run_install: false

- uses: actions/setup-node@v5
with:
node-version: '24.14.1'
cache: 'pnpm'

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Biome lint/format (packages/*/src)
run: pnpm lint:biome

test:
runs-on: ubuntu-latest
steps:
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"test:proxy": "node scripts/cors-proxy.js",
"validate:datasets:js": "node scripts/validate-datasets-js.js",
"lint": "biome check .",
"lint:biome": "biome ci packages/*/src",
"lint:react": "eslint \"packages/react/src/**/*.{ts,tsx}\" \"packages/vis/src/**/*.{ts,tsx}\"",
"format": "biome format --write .",
"format:check": "biome format .",
Expand Down
2 changes: 1 addition & 1 deletion packages/avivatorish/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
"default": "./dist/index.js"
}
},
"files": [
Expand Down
21 changes: 10 additions & 11 deletions packages/avivatorish/src/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
import { useEffect } from 'react';
// import { useDropzone as useReactDropzone } from 'react-dropzone';
import { shallow } from 'zustand/shallow';
// eslint-disable-next-line camelcase
import { unstable_batchedUpdates } from 'react-dom';

// import { useDropzone as useReactDropzone } from 'react-dropzone';
import { shallow } from 'zustand/shallow';
import { COLOR_PALLETE, FILL_PIXEL_VALUE } from './constants';
import {
useChannelsStoreApi,
useImageSettingsStore,
useImageSettingsStoreApi,
useLoader,
useMetadata,
useViewerStore,
useViewerStoreApi,
useChannelsStoreApi,
useImageSettingsStoreApi,
// type PixelSource,
} from './state';
import {
buildDefaultSelection,
guessRgb,
getMultiSelectionStats,
createLoader,
getBoundingCube,
getMultiSelectionStats,
guessRgb,
isInterleaved,
resolveRasterSource,
} from './utils';
import { COLOR_PALLETE, FILL_PIXEL_VALUE } from './constants';
import { createLoader } from './utils';

export const useImage = (source?: { description: string; urlOrFile: string }, history?: any) => {

Check warning on line 27 in packages/avivatorish/src/hooks.ts

View workflow job for this annotation

GitHub Actions / biome-lint

lint/suspicious/noExplicitAny

Unexpected any. Specify a different type.
const [use3d, toggleUse3d, toggleIsOffsetsSnackbarOn] = useViewerStore(
(store) => [store.use3d, store.toggleUse3d, store.toggleIsOffsetsSnackbarOn],
shallow
Expand Down Expand Up @@ -54,10 +53,10 @@
loaderErrorSnackbar: { on: true, message },
})
);
//@ts-ignore flagging so we can get back to this
// TODO: flagging so we can get back to this
let nextMeta: any;

Check warning on line 57 in packages/avivatorish/src/hooks.ts

View workflow job for this annotation

GitHub Actions / biome-lint

lint/suspicious/noExplicitAny

Unexpected any. Specify a different type.
//@ts-ignore flagging so we can get back to this
// TODO: flagging so we can get back to this
let nextLoader: any; //PixelSource | PixelSource[] | null = null;

Check warning on line 59 in packages/avivatorish/src/hooks.ts

View workflow job for this annotation

GitHub Actions / biome-lint

lint/suspicious/noExplicitAny

Unexpected any. Specify a different type.
if (Array.isArray(newLoader)) {
const arr = newLoader as Array<{ metadata?: unknown; data?: unknown }>;
if (arr.length > 1) {
Expand Down
10 changes: 5 additions & 5 deletions packages/avivatorish/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
export * from './state';
export * from './hooks';
export * from './utils';
export * from './channelStats';
export * from './constants';
export * from './omeZarrMultiscales';
export * from './hooks';
export * from './layerChannelState';
export * from './channelStats';
export * from './omeZarrMultiscales';
export * from './state';
export * from './useChannelSelectionStats';
export * from './utils';
17 changes: 6 additions & 11 deletions packages/avivatorish/src/layerChannelState.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { useCallback, useEffect, useMemo, useRef } from 'react';
import { createStore } from 'zustand';
import { useStore } from 'zustand';
import { clampVivSelectionsToAxes } from './utils';
import { createStore, useStore } from 'zustand';
import { COLOR_PALLETE, MAX_CHANNELS } from './constants';
import { clampVivSelectionsToAxes } from './utils';

export type LayerChannelSelection = Partial<{ z: number; c: number; t: number }>;

Expand Down Expand Up @@ -44,8 +43,7 @@ type AxisSizes = Partial<Record<'z' | 'c' | 't', number>>;
* replacement for `JSON.stringify`-based `channelConfigKey` in the host).
*/
export function serializeChannelConfig(config: LayerChannelConfig): string {
const selections =
config.selections?.map((s) => [s.z ?? null, s.c ?? null, s.t ?? null]) ?? null;
const selections = config.selections?.map((s) => [s.z ?? null, s.c ?? null, s.t ?? null]) ?? null;
return JSON.stringify({
channelIds: config.channelIds ?? null,
colors: config.colors ?? null,
Expand Down Expand Up @@ -208,6 +206,7 @@ export function useLayerChannelState({
const selectionAxisSizes = defaults?.selectionAxisSizes;
const lastEmittedRef = useRef<LayerChannelConfig | null>(null);

// biome-ignore lint/correctness/useExhaustiveDependencies: hydrate effect handles config/defaults updates; only layerId should re-seed the store.
const mergedInitial = useMemo(
() => mergeLayerChannelState(config, defaults, layerId),
// eslint-disable-next-line react-hooks/exhaustive-deps -- hydrate effect handles config updates
Expand Down Expand Up @@ -269,11 +268,7 @@ export function useLayerChannelState({
channelsVisible: [...current.channelsVisible, true],
selections: [
...current.selections,
mergeSelectionRow(
undefined,
current.selections[0] ?? fillSel,
selectionAxisSizes
),
mergeSelectionRow(undefined, current.selections[0] ?? fillSel, selectionAxisSizes),
],
};
storeRef.current.setState(nextMerged);
Expand All @@ -285,7 +280,7 @@ export function useLayerChannelState({
const current = storeRef.current.getState();
if (current.channelCount <= 1 || index < 0 || index >= current.channelCount) return;

const splice = <T,>(arr: T[]) => arr.filter((_, i) => i !== index);
const splice = <T>(arr: T[]) => arr.filter((_, i) => i !== index);
const nextMerged: MergedLayerChannelState = {
channelCount: current.channelCount - 1,
channelIds: splice(current.channelIds),
Expand Down
6 changes: 4 additions & 2 deletions packages/avivatorish/src/state.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type PropsWithChildren, createContext, useContext } from 'react';
import type { loadOmeTiff, loadOmeZarr } from '@hms-dbmi/viv';
import { createContext, type PropsWithChildren, useContext } from 'react';
import { createStore } from 'zustand';
import { useStoreWithEqualityFn } from 'zustand/traditional';
import type { EqFn, Selector, ZustandStore } from './zustandTypes';
Expand Down Expand Up @@ -32,7 +32,7 @@
// I don't know why we suddenly needed to add TState... typescript complaining otherwise
set: SetFunctionType<TState>
): TogglesReturnType<TDefaults> {
const toggles: any = {};

Check warning on line 35 in packages/avivatorish/src/state.tsx

View workflow job for this annotation

GitHub Actions / biome-lint

lint/suspicious/noExplicitAny

Unexpected any. Specify a different type.
for (const [k, v] of Object.entries(defaults)) {
if (typeof v === 'boolean') {
toggles[`toggle${capitalize(k)}`] = () =>
Expand All @@ -50,7 +50,7 @@
colors: [number, number, number][];
domains: [number, number][];
selections: { z: number; c: number; t: number }[];
loader: any; //TBD

Check warning on line 53 in packages/avivatorish/src/state.tsx

View workflow job for this annotation

GitHub Actions / biome-lint

lint/suspicious/noExplicitAny

Unexpected any. Specify a different type.
image: number;
ids: string[];
// props for VivContrastExtension
Expand Down Expand Up @@ -114,10 +114,10 @@
useLens: false,
useColormap: false,
globalSelection: { z: 0, t: 0 },
channelOptions: [] as any[],

Check warning on line 117 in packages/avivatorish/src/state.tsx

View workflow job for this annotation

GitHub Actions / biome-lint

lint/suspicious/noExplicitAny

Unexpected any. Specify a different type.
/** type is WIP */
metadata: null as Metadata | null,
viewState: null as any,

Check warning on line 120 in packages/avivatorish/src/state.tsx

View workflow job for this annotation

GitHub Actions / biome-lint

lint/suspicious/noExplicitAny

Unexpected any. Specify a different type.
source: undefined as { urlOrFile: string; description: string } | undefined,
pyramidResolution: 0,
};
Expand Down Expand Up @@ -268,7 +268,7 @@
return { viewerStore, channelsStore, imageSettingsStore } as VivContextType;
}

const VivContext = createContext<VivContextType>(null as any);

Check warning on line 271 in packages/avivatorish/src/state.tsx

View workflow job for this annotation

GitHub Actions / biome-lint

lint/suspicious/noExplicitAny

Unexpected any. Specify a different type.
/**
* This implictly assumes that we are in a context where there is a chart, and that we
* can access it with `useChart()`. That design decision may be revisited (and it means
Expand Down Expand Up @@ -330,7 +330,7 @@
// at least, that was true for the first sample I looked at...
// and at least that allows us to remove some ts-expect-error
Pixels: {
Channels: Array<{ Name: string; SamplesPerPixel: number; Color?: any }>;

Check warning on line 333 in packages/avivatorish/src/state.tsx

View workflow job for this annotation

GitHub Actions / biome-lint

lint/suspicious/noExplicitAny

Unexpected any. Specify a different type.
//! I don't think we actually do see these on OME-ZARR
PhysicalSizeX?: number;
PhysicalSizeXUnit?: string;
Expand All @@ -340,10 +340,12 @@
//export type Metadata = TiffPreviewProps["metadata"];
export const useMetadata = (): Metadata | undefined | null => {
try {
// biome-ignore lint/correctness/useHookAtTopLevel: intentionally called outside a Viv context; the try/catch is the guard (see catch note).
const image = useChannelsStore((store) => store.image);
// biome-ignore lint/correctness/useHookAtTopLevel: intentionally called outside a Viv context; the try/catch is the guard (see catch note).
const metadata = useViewerStore((store) => store.metadata);
return Array.isArray(metadata) ? metadata[image] : metadata;
} catch (e) {
} catch (_e) {
// we now sometimes call this hook outside of a Viv context, so this is expected.
// console.error("no metadata", e);
}
Expand Down
4 changes: 3 additions & 1 deletion packages/avivatorish/src/useChannelSelectionStats.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
import { selectionStatsKey } from './channelStats';
import type { LayerChannelSelection } from './layerChannelState';
import type { SelectionRaster } from './utils';
import { getSingleSelectionStats } from './utils';
import { selectionStatsKey } from './channelStats';

export type ChannelSelectionEntry = {
domain: [number, number];
Expand Down Expand Up @@ -68,6 +68,7 @@ export function useChannelSelectionStats({
// Sync projection of the cache into result state, with fallback for unloaded
// channels. Runs before paint so consumers never see a flash of stale data
// when inputs change and cache has a hit.
// biome-ignore lint/correctness/useExhaustiveDependencies: deps are intentional stable-key strings (channelIdsKey/selectionsKey/...); see eslint-disable at the deps array.
useLayoutEffect(() => {
const cache = statsCacheRef.current;
const statsByChannelId = new Map<string, ChannelSelectionEntry>();
Expand All @@ -93,6 +94,7 @@ export function useChannelSelectionStats({
}, [channelIdsKey, selectionsKey, fallbackDomainsKey, loader]);

// Async fetch loop for selection keys that differ from the last completed set.
// biome-ignore lint/correctness/useExhaustiveDependencies: deps are intentional stable-key strings (channelIdsKey/selectionsKey/...); see eslint-disable at the deps array.
useEffect(() => {
if (!loader) return;

Expand Down
65 changes: 41 additions & 24 deletions packages/avivatorish/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
import { useState, useEffect } from 'react';
import { fromBlob, fromUrl } from 'geotiff';
import { Matrix4 } from '@math.gl/core';

import {
loadOmeZarr,
AdditiveColormap3DExtensions,
ColorPalette3DExtensions,
getChannelStats,
loadOmeZarr,
RENDERING_MODES,
ColorPalette3DExtensions,
AdditiveColormap3DExtensions,
} from '@hms-dbmi/viv';
import { Matrix4 } from '@math.gl/core';
import { fromBlob, fromUrl } from 'geotiff';
import { useEffect, useState } from 'react';

import { GLOBAL_SLIDER_DIMENSION_FIELDS } from './constants';
import type { OME_TIFF, PixelSource } from './state';

const MAX_CHANNELS_FOR_SNACKBAR_WARNING = 40;
const _MAX_CHANNELS_FOR_SNACKBAR_WARNING = 40;

function isArray(value: unknown): value is unknown[] {
function _isArray(value: unknown): value is unknown[] {
return Array.isArray(value) as boolean;
}

/**
* Guesses whether string URL or File is for an OME-TIFF image.
* @param {string | File} urlOrFile
*/
function isOmeTiff(urlOrFile: string | File) {
function _isOmeTiff(urlOrFile: string | File) {
if (Array.isArray(urlOrFile)) return false; // local Zarr is array of File Objects
const name = typeof urlOrFile === 'string' ? urlOrFile : urlOrFile.name;
return name.includes('ome.tiff') || name.includes('ome.tif') || name.includes('.companion.ome');
Expand All @@ -46,7 +45,7 @@
/**
* Guesses whether string URL or File is one or multiple standard TIFF images.
*/
function isMultiTiff(urlOrFiles: UrlOrFiles) {
function _isMultiTiff(urlOrFiles: UrlOrFiles) {
const filenames = getMultiTiffFilenames(urlOrFiles);
for (const filename of filenames) {
const lowerCaseName = filename.toLowerCase();
Expand Down Expand Up @@ -84,9 +83,9 @@
/**
* Guesses whether string URL or File is one or multiple standard TIFF images.
*/
async function generateMultiTiffSources(urlOrFiles: UrlOrFiles) {
async function _generateMultiTiffSources(urlOrFiles: UrlOrFiles) {
const multiTiffFiles = await generateMultiTiffFileArray(urlOrFiles);
const sources: [VivSelection[], any][] = [];

Check warning on line 88 in packages/avivatorish/src/utils.ts

View workflow job for this annotation

GitHub Actions / biome-lint

lint/suspicious/noExplicitAny

Unexpected any. Specify a different type.
let c = 0;
for (const tiffFile of multiTiffFiles) {
const selections: VivSelection[] = [];
Expand All @@ -108,7 +107,7 @@
}
/** todo - clarify types / zarr... */
type OmeTiffImage = OME_TIFF;
async function getTotalImageCount(sources: OmeTiffImage[]) {
async function _getTotalImageCount(sources: OmeTiffImage[]) {
const firstOmeTiffImage = sources[0];
const firstPixelSource = firstOmeTiffImage.data[0];
//@ts-expect-error - firstPixelSource._indexer is a private method
Expand Down Expand Up @@ -136,11 +135,11 @@
return numImagesPerResolution * levels;
}

function isZodError(e: unknown): e is Error & { issues: unknown } {
function _isZodError(e: unknown): e is Error & { issues: unknown } {
return e instanceof Error && 'issues' in e;
}

async function fetchSingleFileOmeTiffOffsets(url: string) {
async function _fetchSingleFileOmeTiffOffsets(url: string) {
// No offsets for multifile OME-TIFFs
if (url.includes('companion.ome')) {
return undefined;
Expand All @@ -160,7 +159,7 @@
*/
export async function createLoader(
urlOrFile: UrlOrFiles,
handleOffsetsNotFound: (arg0: boolean) => void,
_handleOffsetsNotFound: (arg0: boolean) => void,
handleLoaderError: (msg: string | null) => void
) {
// If the loader fails to load, handle the error (show an error snackbar).
Expand Down Expand Up @@ -253,7 +252,10 @@
export function buildDefaultSelection({
labels,
shape,
}: { labels: string[]; shape: number[] }): VivSelection[] {
}: {
labels: string[];
shape: number[];
}): VivSelection[] {
const selection: VivSelection[] = [];

const dimensions = zip(labels, shape).map(([name, size]) => ({
Expand Down Expand Up @@ -400,7 +402,10 @@
} {
return narrowLimits(stats.domain) && narrowLimits(stats.contrastLimits);
}
function clampVivSelectionToSource(selection: VivSelection, source: RasterSourceLike): VivSelection {
function clampVivSelectionToSource(
selection: VivSelection,
source: RasterSourceLike
): VivSelection {
return (clampVivSelectionsToAxes(
[selection],
getVivSelectionAxisSizes(source.labels, source.shape)
Expand Down Expand Up @@ -525,7 +530,10 @@
export async function getSingleSelectionStats3D({
loader,
selection,
}: { loader: LOADER; selection: VivSelection }) {
}: {
loader: LOADER;
selection: VivSelection;
}) {
const lowResSource = getRasterSource(loader);
const { shape, labels } = lowResSource;
const sizeZ = getVivSelectionAxisSizes(labels, shape).z;
Expand Down Expand Up @@ -620,7 +628,7 @@
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(
a
) ||
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(
a.substr(0, 4)
)
)
Expand Down Expand Up @@ -666,16 +674,25 @@

function getAxisSize(shape: number[], labels: string[], axis: 'x' | 'y' | 'z') {
const index = labels.findIndex((label) => label.toLowerCase() === axis);
return index >= 0 ? shape[index] ?? 0 : 0;
return index >= 0 ? (shape[index] ?? 0) : 0;
}

export function getBoundingCube(loader: PixelSource | RasterSourceLike) {
const source = getRasterSource(loader);
const { shape, labels } = source;
const physicalSizeScalingMatrix = getPhysicalSizeScalingMatrix(loader);
const xSlice: [number, number] = [0, physicalSizeScalingMatrix[0] * getAxisSize(shape, labels, 'x')];
const ySlice: [number, number] = [0, physicalSizeScalingMatrix[5] * getAxisSize(shape, labels, 'y')];
const zSlice: [number, number] = [0, physicalSizeScalingMatrix[10] * getAxisSize(shape, labels, 'z')];
const xSlice: [number, number] = [
0,
physicalSizeScalingMatrix[0] * getAxisSize(shape, labels, 'x'),
];
const ySlice: [number, number] = [
0,
physicalSizeScalingMatrix[5] * getAxisSize(shape, labels, 'y'),
];
const zSlice: [number, number] = [
0,
physicalSizeScalingMatrix[10] * getAxisSize(shape, labels, 'z'),
];
return [xSlice, ySlice, zSlice];
}

Expand Down
2 changes: 1 addition & 1 deletion packages/avivatorish/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"skipLibCheck": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"lib": ["ES2022", "DOM", "DOM.Iterable"],
// Cross-package types resolve via node_modules to each package's built dist/*.d.ts
// (topological build order guarantees deps are built first). Matches core's pattern.
"types": ["react", "react-dom"]
Expand Down
Loading