diff --git a/frontend/src/app/PanelManagerContext.tsx b/frontend/src/app/PanelManagerContext.tsx index af8123a..5198557 100644 --- a/frontend/src/app/PanelManagerContext.tsx +++ b/frontend/src/app/PanelManagerContext.tsx @@ -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 = ({ 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 ? { diff --git a/frontend/src/correspondents/CorrespondentsPanel.tsx b/frontend/src/correspondents/CorrespondentsPanel.tsx index 91bf53b..80e7ce6 100644 --- a/frontend/src/correspondents/CorrespondentsPanel.tsx +++ b/frontend/src/correspondents/CorrespondentsPanel.tsx @@ -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 ( diff --git a/frontend/src/desktop/hooks/usePreviewMetadata.ts b/frontend/src/desktop/hooks/usePreviewMetadata.ts index 6cba541..4dd8ff2 100644 --- a/frontend/src/desktop/hooks/usePreviewMetadata.ts +++ b/frontend/src/desktop/hooks/usePreviewMetadata.ts @@ -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 | null = (asset?.metadata as Partial | 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 | 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 | null) || null; } // If still no dimensions, mark as failed so we don't try again diff --git a/frontend/src/desktop/tags/useDeskTagInteractions.ts b/frontend/src/desktop/tags/useDeskTagInteractions.ts index 27ac140..539254f 100644 --- a/frontend/src/desktop/tags/useDeskTagInteractions.ts +++ b/frontend/src/desktop/tags/useDeskTagInteractions.ts @@ -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'; diff --git a/frontend/src/documents/DocumentThumbnailImage.tsx b/frontend/src/documents/DocumentThumbnailImage.tsx index 73649c4..fdaa282 100644 --- a/frontend/src/documents/DocumentThumbnailImage.tsx +++ b/frontend/src/documents/DocumentThumbnailImage.tsx @@ -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'); diff --git a/frontend/src/documents/SelectionAssignmentMenu.tsx b/frontend/src/documents/SelectionAssignmentMenu.tsx index 59ec9be..edec50f 100644 --- a/frontend/src/documents/SelectionAssignmentMenu.tsx +++ b/frontend/src/documents/SelectionAssignmentMenu.tsx @@ -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, diff --git a/frontend/src/documents/documentSummary.ts b/frontend/src/documents/documentSummary.ts index 648c3ca..5393288 100644 --- a/frontend/src/documents/documentSummary.ts +++ b/frontend/src/documents/documentSummary.ts @@ -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 = (entries?: Array | 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(doc.tags); const correspondents = sanitizeArray(doc.correspondents); diff --git a/frontend/src/hooks/documents/useDocumentDragHandlers.ts b/frontend/src/hooks/documents/useDocumentDragHandlers.ts index 7e17986..32be931 100644 --- a/frontend/src/hooks/documents/useDocumentDragHandlers.ts +++ b/frontend/src/hooks/documents/useDocumentDragHandlers.ts @@ -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); diff --git a/frontend/src/hooks/documents/useDocumentTagging.ts b/frontend/src/hooks/documents/useDocumentTagging.ts index d01968c..7dedd6f 100644 --- a/frontend/src/hooks/documents/useDocumentTagging.ts +++ b/frontend/src/hooks/documents/useDocumentTagging.ts @@ -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( diff --git a/frontend/src/preview/PdfViewer.tsx b/frontend/src/preview/PdfViewer.tsx index 1beec7a..e4730ad 100644 --- a/frontend/src/preview/PdfViewer.tsx +++ b/frontend/src/preview/PdfViewer.tsx @@ -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]); diff --git a/frontend/src/settings/sections/CapabilitySetsSection.tsx b/frontend/src/settings/sections/CapabilitySetsSection.tsx index f88174c..479a4ef 100644 --- a/frontend/src/settings/sections/CapabilitySetsSection.tsx +++ b/frontend/src/settings/sections/CapabilitySetsSection.tsx @@ -161,7 +161,7 @@ const CapabilitySetsSection: React.FC = ({ 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 = ({ : '—'} {isSystem ? 'Yes' : 'No'} - {Number.isFinite(set.cap_version) ? Number(set.cap_version) : '—'} + {set.cap_version != null ? Number(set.cap_version) : '—'} {isSystem ? ( System set diff --git a/frontend/src/types/assets.ts b/frontend/src/types/assets.ts index 9e637bc..18d16a2 100644 --- a/frontend/src/types/assets.ts +++ b/frontend/src/types/assets.ts @@ -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 | null; + metadata?: ThumbnailMetadata | Record | null; [key: string]: unknown; } diff --git a/frontend/src/types/documents.ts b/frontend/src/types/documents.ts index f5a8f06..5094997 100644 --- a/frontend/src/types/documents.ts +++ b/frontend/src/types/documents.ts @@ -40,7 +40,7 @@ export interface Correspondent { export interface DocumentVersion { assets?: Record | Asset[] | null; metadata?: Record & { page_count?: number } | null; - size_bytes?: number | string | null; + size_bytes?: number | null; checksum?: string | null; download?: Download | null; [key: string]: unknown; diff --git a/frontend/src/ui/icons.tsx b/frontend/src/ui/icons.tsx index 51c7a43..2fc6ebc 100644 --- a/frontend/src/ui/icons.tsx +++ b/frontend/src/ui/icons.tsx @@ -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 ( 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; diff --git a/frontend/src/utils/format.ts b/frontend/src/utils/format.ts index 556b244..e7be327 100644 --- a/frontend/src/utils/format.ts +++ b/frontend/src/utils/format.ts @@ -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]}`; };