This commit is contained in:
2025-12-04 17:20:45 +01:00
parent 834d4f641b
commit d06fc133e7
16 changed files with 44 additions and 69 deletions
+3 -13
View File
@@ -78,9 +78,6 @@ const persistWidth = (panel: PanelKey, value: number): void => {
};
const applyPanelWidthToRoot = (panel: PanelKey, width: number, active: boolean): void => {
if (!Number.isFinite(width)) {
return;
}
const varName = panel === 'sidebar' ? '--sidebar-width' : '--detail-panel-width';
const resolvedValue = panel === 'detail' && !active ? '0px' : `${width}px`;
document.documentElement.style.setProperty(varName, resolvedValue);
@@ -170,9 +167,7 @@ export const PanelManagerProvider: React.FC<PanelManagerProviderProps> = ({ chil
const setPanelWidth = useCallback(
(panel: PanelKey, width: number, { commit = true, log = true }: SetPanelWidthOptions = {}) => {
const clamped = clampPanelWidth(panel, width);
if (!Number.isFinite(clamped)) {
return panelWidthsRef.current[panel];
}
if (panel === 'sidebar') {
setSidebarWidthState((prev) => (prev === clamped ? prev : clamped));
} else {
@@ -348,10 +343,7 @@ export const usePanelResizeBindings = (
event.preventDefault();
event.stopPropagation();
const rect = panelRef.current.getBoundingClientRect();
const startWidth = Number.isFinite(rect?.width) ? rect.width : getPanelWidth(panel);
if (!Number.isFinite(startWidth)) {
return;
}
const startWidth = rect?.width ?? getPanelWidth(panel);
const pointerId = event.pointerId ?? 'mouse';
const startX = event.clientX;
startPanelResize(panel);
@@ -398,9 +390,7 @@ export const usePanelResizeBindings = (
],
);
const panelStyle = enabled && Number.isFinite(liveWidth)
? { width: `${liveWidth}px` }
: undefined;
const panelStyle = enabled ? { width: `${liveWidth}px` } : undefined;
const handleProps = enabled
? {
@@ -111,11 +111,7 @@ function CorrespondentsPanel({
);
const renderUsage = useCallback((correspondent: Correspondent) => {
const count = correspondent.usage_count;
if (count == null || !Number.isFinite(count)) {
return '0';
}
return count.toString();
return correspondent.usage_count;
}, []);
return (
@@ -1,8 +1,7 @@
import { useEffect, useState, useRef } from 'react';
import type { DocumentId } from '../../types/identifiers';
import type { Document } from '../../types/documents';
import type { Asset } from '../../types/assets';
import type { Asset, ThumbnailMetadata } from '../../types/assets';
interface PreviewMetadataEntry {
docId: DocumentId;
@@ -40,13 +39,13 @@ const usePreviewMetadata = (
const resolveAsset = (type: string) => getDocumentAsset?.(doc, type) ?? null;
let asset = resolveAsset('thumbnail');
let metadata = (asset?.metadata as { width?: number; height?: number } | null) || null;
let metadata: Partial<ThumbnailMetadata> | null = (asset?.metadata as Partial<ThumbnailMetadata> | null) || null;
const hasDimensions = (meta: { width?: number | string; height?: number | string } | null) =>
Number.isFinite(Number(meta?.width)) &&
Number.isFinite(Number(meta?.height)) &&
Number(meta.width) > 0 &&
Number(meta.height) > 0;
const hasDimensions = (meta: Partial<ThumbnailMetadata> | null): meta is ThumbnailMetadata =>
typeof meta?.width === 'number' &&
typeof meta?.height === 'number' &&
meta.width > 0 &&
meta.height > 0;
if (!hasDimensions(metadata) && ensureAssetUrl && docId && asset?.id) {
// Skip if we already failed for this doc to avoid infinite loops
@@ -55,7 +54,7 @@ const usePreviewMetadata = (
const ensured = await ensureAssetUrl(doc.id, asset);
if (ensured) {
asset = ensured;
metadata = (asset?.metadata as { width?: number; height?: number } | null) || null;
metadata = (asset?.metadata as Partial<ThumbnailMetadata> | null) || null;
}
// If still no dimensions, mark as failed so we don't try again
@@ -23,10 +23,8 @@ const createDragPreview = (node: EventTarget | null, clientX: number, clientY: n
return null;
}
const rect = node.getBoundingClientRect();
const safeClientX = Number.isFinite(clientX) ? clientX : rect.left + rect.width / 2;
const safeClientY = Number.isFinite(clientY) ? clientY : rect.top + rect.height / 2;
const offsetX = Math.min(Math.max(safeClientX - rect.left, 0), rect.width);
const offsetY = Math.min(Math.max(safeClientY - rect.top, 0), rect.height);
const offsetX = Math.min(Math.max(clientX - rect.left, 0), rect.width);
const offsetY = Math.min(Math.max(clientY - rect.top, 0), rect.height);
const clone = node.cloneNode(true) as HTMLElement;
clone.style.position = 'absolute';
clone.style.top = '-9999px';
@@ -67,9 +67,8 @@ const useLazyVisibility = (
const getPageCount = (doc?: Document | null) => {
const count = doc?.current_version?.metadata?.page_count;
return Number.isFinite(count) ? Number(count) : null;
const getPageCount = (doc?: Document | null): number | null => {
return doc?.current_version?.metadata?.page_count;
};
type Asset = AssetManagerAsset;
@@ -140,7 +139,7 @@ const DocumentThumbnailImage = ({
}, [document, ensureAssetUrl, getDocumentAsset, isVisible, thumbnailAsset]);
const pageCount = getPageCount(document);
const showMultiPageBadge = Number.isFinite(pageCount) && pageCount > 1;
const showMultiPageBadge = pageCount !== null && pageCount > 1;
const innerClasses = ['document-thumbnail-inner'];
if (showMultiPageBadge) {
innerClasses.push('document-thumbnail-inner--multipage');
@@ -67,8 +67,8 @@ const normalizeItems = (items?: SelectionAssignmentMenuItem[]): NormalizedSelect
: item.state === 'partial'
? 'partial'
: 'none';
const numericCount = Number.isFinite(item.count) ? Number(item.count) : null;
const numericTotal = Number.isFinite(item.total) ? Number(item.total) : null;
const numericCount = item.count ?? null;
const numericTotal = item.total ?? null;
return {
id: item.id ?? trimmedLabel,
label: trimmedLabel,
+4 -4
View File
@@ -25,7 +25,7 @@ const coercePageCount = (metadata?: { page_count?: number | string | null } | nu
return null;
}
const parsed = Number.parseInt(String(raw), 10);
return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
return parsed >= 0 ? parsed : null;
};
const sanitizeArray = <T>(entries?: Array<T | null> | null): T[] =>
@@ -47,11 +47,11 @@ export const describeDocumentSummary = (document?: Document | null, options: Des
return formatDateTime(value) || '—';
};
const doc = document ?? ({} as Document);
const sizeBytes = Number(doc.current_version?.size_bytes);
const sizeLabel = Number.isFinite(sizeBytes) && sizeBytes > 0 ? formatFileSize(sizeBytes) : '—';
const sizeBytes = doc.current_version?.size_bytes ?? null;
const sizeLabel = sizeBytes !== null && sizeBytes > 0 ? formatFileSize(sizeBytes) : '—';
const metadata = doc.current_version?.metadata || null;
const pageCount = coercePageCount(metadata);
const pageCountLabel = Number.isFinite(pageCount) ? String(pageCount) : '—';
const pageCountLabel = pageCount !== null ? String(pageCount) : '—';
const folderLabel = doc.folder_id == null ? DEFAULT_FOLDER_NAME : `Folder ${doc.folder_id}`;
const tags = sanitizeArray<DocumentTag>(doc.tags);
const correspondents = sanitizeArray<DocumentCorrespondent>(doc.correspondents);
@@ -145,7 +145,7 @@ const useDocumentDragHandlers = ({
let thumbWidth = size;
let thumbHeight = size;
if (Number.isFinite(aspectRatio) && aspectRatio > 0) {
if (aspectRatio > 0) {
if (aspectRatio >= 1) {
thumbWidth = size;
thumbHeight = Math.max(size / aspectRatio, size * 0.5);
@@ -267,7 +267,7 @@ const useDocumentTagging = ({
},
);
const payload = 'data' in response ? response.data : response;
const queued = Number.isFinite(payload?.queued)
const queued = payload?.queued != null
? Number(payload.queued)
: targetIds.length;
setStatusMessage(
+1 -10
View File
@@ -519,7 +519,7 @@ const PdfViewer = ({ src, title, className, viewportRef }: PdfViewerProps): JSX.
return;
}
const pageNumber = Number(attr);
if (!Number.isFinite(pageNumber) || pageNumber <= 0) {
if (!(pageNumber > 0)) {
return;
}
handlePageVisibilityChange(pageNumber, entry.isIntersecting);
@@ -833,15 +833,6 @@ function PdfPageCanvas({
const safePageHeight = Math.max(1, descriptor.height);
const widthScale = safeViewportWidth / safePageWidth;
const heightScale = safeViewportHeight / safePageHeight;
if (!Number.isFinite(widthScale) && Number.isFinite(heightScale)) {
return 'height';
}
if (!Number.isFinite(heightScale) && Number.isFinite(widthScale)) {
return 'width';
}
if (!Number.isFinite(widthScale) && !Number.isFinite(heightScale)) {
return 'width';
}
return widthScale <= heightScale ? 'width' : 'height';
}, [descriptor.height, descriptor.width, viewMode, viewportHeight, viewportWidth]);
@@ -161,7 +161,7 @@ const CapabilitySetsSection: React.FC<CapabilitySetsSectionProps> = ({
label: set.label || set.slug || set.id,
capabilities: Array.isArray(set.capabilities) ? set.capabilities : [],
isSystem: Boolean(set?.is_system),
version: Number.isFinite(set?.cap_version) ? Number(set.cap_version) : null,
version: set?.cap_version != null ? Number(set.cap_version) : null,
})),
[capabilitySets],
);
@@ -492,7 +492,7 @@ const CapabilitySetsSection: React.FC<CapabilitySetsSectionProps> = ({
: '—'}
</td>
<td>{isSystem ? 'Yes' : 'No'}</td>
<td>{Number.isFinite(set.cap_version) ? Number(set.cap_version) : '—'}</td>
<td>{set.cap_version != null ? Number(set.cap_version) : '—'}</td>
<td className="settings-table__actions">
{isSystem ? (
<span className="settings-status">System set</span>
+6 -1
View File
@@ -1,10 +1,15 @@
import type { Identifier } from './identifiers';
import type { Download } from './common';
export interface ThumbnailMetadata {
width: number;
height: number;
}
export interface Asset {
id?: Identifier;
asset_type?: string;
download?: Download | null;
metadata?: Record<string, unknown> | null;
metadata?: ThumbnailMetadata | Record<string, unknown> | null;
[key: string]: unknown;
}
+1 -1
View File
@@ -40,7 +40,7 @@ export interface Correspondent {
export interface DocumentVersion {
assets?: Record<string, Asset> | Asset[] | null;
metadata?: Record<string, unknown> & { page_count?: number } | null;
size_bytes?: number | string | null;
size_bytes?: number | null;
checksum?: string | null;
download?: Download | null;
[key: string]: unknown;
+2 -2
View File
@@ -123,11 +123,11 @@ export const CorrespondentIcon = createIcon(IconUserFilled, { baseClass: 'icon i
// Custom icons that need special handling
export const FolderIcon: TablerIconComponent = ({ className, size = 16, title, ...rest }) => {
const dimensionProps = Number.isFinite(size) ? { width: Number(size), height: Number(size) } : {};
return (
<FolderSvg
className={composeClassName('folder-icon', className)}
{...dimensionProps}
width={size}
height={size}
role={title ? 'img' : 'presentation'}
aria-hidden={title ? undefined : true}
focusable="false"
+2 -2
View File
@@ -33,8 +33,8 @@ const formatStyle = (metrics: FloatingMenuMetrics | null): FloatingMenuStyle =>
top: metrics.top,
left: metrics.left,
};
if (Number.isFinite(metrics.minWidth)) {
style['--floating-min-width'] = `${Math.max(metrics.minWidth ?? 0, 0)}px`;
if (metrics.minWidth != null) {
style['--floating-min-width'] = `${Math.max(metrics.minWidth, 0)}px`;
}
if (metrics.width) {
style.width = metrics.width;
+5 -8
View File
@@ -1,17 +1,14 @@
export const formatFileSize = (value: number | string): string => {
const bytes = Number(value);
if (!Number.isFinite(bytes) || bytes <= 0) {
return '0 B';
}
export const formatFileSize = (bytes: number): string => {
const sign = bytes < 0 ? '-' : '';
const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'];
let index = 0;
let amount = bytes;
let amount = Math.abs(bytes);
while (amount >= 1024 && index < units.length - 1) {
amount /= 1024;
index += 1;
}
return `${amount.toFixed(2)} ${units[index]}`;
const decimals = index === 0 ? 0 : 2;
return `${sign}${amount.toFixed(decimals)} ${units[index]}`;
};