-
- {tool.name}
-
+ {tool.name}
{tool.versionStatus === "alpha" && (
{/* we can add more translations for different badges in future, like beta, etc. */}
diff --git a/frontend/src/core/contexts/IndexedDBContext.tsx b/frontend/src/core/contexts/IndexedDBContext.tsx
index 6aabd7d47c..ca8702917b 100644
--- a/frontend/src/core/contexts/IndexedDBContext.tsx
+++ b/frontend/src/core/contexts/IndexedDBContext.tsx
@@ -92,9 +92,10 @@ export function IndexedDBProvider({ children }: IndexedDBProviderProps) {
fileId: FileId,
existingThumbnail?: string,
): Promise => {
- // Use existing thumbnail or generate new one if none provided
- const thumbnail =
+ // Use existing thumbnail or generate new one if none provided (empty string = no raster thumbnail)
+ const generated =
existingThumbnail || (await generateThumbnailForFile(file));
+ const thumbnail = generated || undefined;
// History is handled via direct fileStorage calls, not here
const stirlingFile = createStirlingFile(file, fileId);
diff --git a/frontend/src/core/hooks/useFileThumbnail.ts b/frontend/src/core/hooks/useFileThumbnail.ts
new file mode 100644
index 0000000000..e564142c7c
--- /dev/null
+++ b/frontend/src/core/hooks/useFileThumbnail.ts
@@ -0,0 +1,19 @@
+import { StirlingFileStub } from "@app/types/fileContext";
+import { useIndexedDBThumbnail } from "@app/hooks/useIndexedDBThumbnail";
+
+export function useFileThumbnail(
+ fileStub: StirlingFileStub | null | undefined,
+): {
+ isEncrypted: boolean;
+ thumbnail: string | null;
+ isGenerating: boolean;
+} {
+ const isEncrypted = Boolean(fileStub?.processedFile?.isEncrypted);
+ const { thumbnail: indexedDBThumb, isGenerating } = useIndexedDBThumbnail(
+ isEncrypted ? null : fileStub,
+ );
+ const thumbnail = isEncrypted
+ ? null
+ : fileStub?.thumbnailUrl || indexedDBThumb || null;
+ return { isEncrypted, thumbnail, isGenerating };
+}
diff --git a/frontend/src/core/hooks/useUrlSync.ts b/frontend/src/core/hooks/useUrlSync.ts
index 27ae833a70..ec4ede495d 100644
--- a/frontend/src/core/hooks/useUrlSync.ts
+++ b/frontend/src/core/hooks/useUrlSync.ts
@@ -60,15 +60,14 @@ export function useNavigationUrlSync(
firePixel(currentPath);
const route = parseToolRoute(registry);
- if (route.toolId !== selectedTool) {
- if (route.toolId) {
+ if (route.toolId) {
+ // URL specifies a tool — navigate to it (URL takes precedence over startup view preference)
+ if (route.toolId !== selectedTool) {
checkPremiumAndSelect(route.toolId);
- } else if (selectedTool !== null) {
- // Only clear selection if we actually had a tool selected
- // Don't clear on initial load when selectedTool starts as null
- clearToolSelection();
}
}
+ // When the URL is the home path (no tool), leave selectedTool untouched so that
+ // the startup view preference (defaultStartupView) is respected.
hasInitialized.current = true;
}, [checkPremiumAndSelect, config, enableSync, registry, selectedTool]); // Include dependencies
diff --git a/frontend/src/core/styles/theme.css b/frontend/src/core/styles/theme.css
index a2ac2f9474..43400fcfa5 100644
--- a/frontend/src/core/styles/theme.css
+++ b/frontend/src/core/styles/theme.css
@@ -228,7 +228,7 @@
--icon-inactive-color: #ffffff;
/* New theme colors for text and icons */
- --tools-text-and-icon-color: #6b7280;
+ --tools-text-and-icon-color: var(--text-primary);
/* Tool picker sticky header variables (light mode) */
--tool-header-bg: #dbefff;
@@ -621,7 +621,7 @@
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.4);
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.4);
- --tools-text-and-icon-color: #9ca3af;
+ --tools-text-and-icon-color: var(--text-primary);
/* Tool picker sticky header variables (dark mode) */
--tool-header-bg: #2a2f36;
diff --git a/frontend/src/core/utils/fileUtils.ts b/frontend/src/core/utils/fileUtils.ts
index 57a09d7bb8..6a256a9073 100644
--- a/frontend/src/core/utils/fileUtils.ts
+++ b/frontend/src/core/utils/fileUtils.ts
@@ -107,7 +107,7 @@ export type NonPdfFileType =
| "html"
| "unknown";
-const IMAGE_EXTENSIONS = new Set([
+export const IMAGE_EXTENSIONS = new Set([
"png",
"jpg",
"jpeg",
@@ -124,6 +124,17 @@ const TEXT_EXTENSIONS = new Set(["txt"]);
const MARKDOWN_EXTENSIONS = new Set(["md", "markdown"]);
const HTML_EXTENSIONS = new Set(["html", "htm"]);
+/** All file extensions that the built-in viewer can render. */
+export const VIEWER_SUPPORTED_EXTENSIONS: string[] = [
+ "pdf",
+ ...IMAGE_EXTENSIONS,
+ ...CSV_EXTENSIONS,
+ ...JSON_EXTENSIONS,
+ ...TEXT_EXTENSIONS,
+ ...MARKDOWN_EXTENSIONS,
+ ...HTML_EXTENSIONS,
+];
+
/**
* Detects the non-PDF file type category for viewer routing.
* Returns 'unknown' for PDFs or unrecognized formats.
diff --git a/frontend/src/core/utils/thumbnailUtils.ts b/frontend/src/core/utils/thumbnailUtils.ts
index 36ec904521..11ba6ae56b 100644
--- a/frontend/src/core/utils/thumbnailUtils.ts
+++ b/frontend/src/core/utils/thumbnailUtils.ts
@@ -16,16 +16,6 @@ export interface ThumbnailWithMetadata {
isEncrypted?: boolean;
}
-interface ColorScheme {
- bgTop: string;
- bgBottom: string;
- border: string;
- icon: string;
- badge: string;
- textPrimary: string;
- textSecondary: string;
-}
-
/**
* Calculate thumbnail scale based on file size (modern 2024 scaling)
*/
@@ -38,495 +28,6 @@ export function calculateScaleFromFileSize(fileSize: number): number {
return 0.3; // Still usable quality, not tiny
}
-/**
- * Generate encrypted PDF thumbnail with lock icon
- */
-function generateEncryptedPDFThumbnail(file: File): string {
- const canvas = document.createElement("canvas");
- canvas.width = 120;
- canvas.height = 150;
- const ctx = canvas.getContext("2d")!;
-
- // Use PDF color scheme but with encrypted styling
- const colorScheme = getFileTypeColorScheme("PDF");
-
- // Create gradient background
- const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
- gradient.addColorStop(0, colorScheme.bgTop);
- gradient.addColorStop(1, colorScheme.bgBottom);
-
- // Rounded rectangle background
- drawRoundedRect(ctx, 8, 8, canvas.width - 16, canvas.height - 16, 8);
- ctx.fillStyle = gradient;
- ctx.fill();
-
- // Border with dashed pattern for encrypted indicator
- ctx.strokeStyle = colorScheme.border;
- ctx.lineWidth = 2;
- ctx.setLineDash([4, 4]);
- ctx.stroke();
- ctx.setLineDash([]); // Reset dash pattern
-
- // Large lock icon as main element
- drawLargeLockIcon(ctx, canvas.width / 2, canvas.height / 2 - 10, colorScheme);
-
- // "PDF" text under the lock
- ctx.font =
- 'bold 14px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
- ctx.fillStyle = colorScheme.icon;
- ctx.textAlign = "center";
- ctx.fillText("PDF", canvas.width / 2, canvas.height / 2 + 35);
-
- // File size with subtle styling
- const sizeText = formatFileSize(file.size);
- ctx.font =
- '11px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
- ctx.fillStyle = colorScheme.textSecondary;
- ctx.textAlign = "center";
- ctx.fillText(sizeText, canvas.width / 2, canvas.height - 15);
-
- return canvas.toDataURL();
-}
-
-/**
- * Generate modern placeholder thumbnail with file extension
- */
-function generatePlaceholderThumbnail(file: File): string {
- const canvas = document.createElement("canvas");
- canvas.width = 120;
- canvas.height = 150;
- const ctx = canvas.getContext("2d")!;
-
- // Get file extension for color theming
- const extension = file.name.split(".").pop()?.toUpperCase() || "FILE";
- const colorScheme = getFileTypeColorScheme(extension);
-
- // Create gradient background
- const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
- gradient.addColorStop(0, colorScheme.bgTop);
- gradient.addColorStop(1, colorScheme.bgBottom);
-
- // Rounded rectangle background
- drawRoundedRect(ctx, 8, 8, canvas.width - 16, canvas.height - 16, 8);
- ctx.fillStyle = gradient;
- ctx.fill();
-
- // Subtle shadow/border
- ctx.strokeStyle = colorScheme.border;
- ctx.lineWidth = 1.5;
- ctx.stroke();
-
- // Modern document icon
- drawModernDocumentIcon(ctx, canvas.width / 2, 45, colorScheme.icon);
-
- // Extension badge
- drawExtensionBadge(
- ctx,
- canvas.width / 2,
- canvas.height / 2 + 15,
- extension,
- colorScheme,
- );
-
- // File size with subtle styling
- const sizeText = formatFileSize(file.size);
- ctx.font =
- '11px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
- ctx.fillStyle = colorScheme.textSecondary;
- ctx.textAlign = "center";
- ctx.fillText(sizeText, canvas.width / 2, canvas.height - 15);
-
- return canvas.toDataURL();
-}
-
-/**
- * Get color scheme based on file extension
- */
-function getFileTypeColorScheme(extension: string): ColorScheme {
- const schemes: Record = {
- // Documents
- PDF: {
- bgTop: "#FF6B6B20",
- bgBottom: "#FF6B6B10",
- border: "#FF6B6B40",
- icon: "#FF6B6B",
- badge: "#FF6B6B",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- DOC: {
- bgTop: "#4ECDC420",
- bgBottom: "#4ECDC410",
- border: "#4ECDC440",
- icon: "#4ECDC4",
- badge: "#4ECDC4",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- DOCX: {
- bgTop: "#4ECDC420",
- bgBottom: "#4ECDC410",
- border: "#4ECDC440",
- icon: "#4ECDC4",
- badge: "#4ECDC4",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- ODT: {
- bgTop: "#4ECDC420",
- bgBottom: "#4ECDC410",
- border: "#4ECDC440",
- icon: "#4ECDC4",
- badge: "#4ECDC4",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- TXT: {
- bgTop: "#95A5A620",
- bgBottom: "#95A5A610",
- border: "#95A5A640",
- icon: "#95A5A6",
- badge: "#95A5A6",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- RTF: {
- bgTop: "#95A5A620",
- bgBottom: "#95A5A610",
- border: "#95A5A640",
- icon: "#95A5A6",
- badge: "#95A5A6",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
-
- // Spreadsheets
- XLS: {
- bgTop: "#2ECC7120",
- bgBottom: "#2ECC7110",
- border: "#2ECC7140",
- icon: "#2ECC71",
- badge: "#2ECC71",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- XLSX: {
- bgTop: "#2ECC7120",
- bgBottom: "#2ECC7110",
- border: "#2ECC7140",
- icon: "#2ECC71",
- badge: "#2ECC71",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- ODS: {
- bgTop: "#2ECC7120",
- bgBottom: "#2ECC7110",
- border: "#2ECC7140",
- icon: "#2ECC71",
- badge: "#2ECC71",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- CSV: {
- bgTop: "#2ECC7120",
- bgBottom: "#2ECC7110",
- border: "#2ECC7140",
- icon: "#2ECC71",
- badge: "#2ECC71",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
-
- // Presentations
- PPT: {
- bgTop: "#E67E2220",
- bgBottom: "#E67E2210",
- border: "#E67E2240",
- icon: "#E67E22",
- badge: "#E67E22",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- PPTX: {
- bgTop: "#E67E2220",
- bgBottom: "#E67E2210",
- border: "#E67E2240",
- icon: "#E67E22",
- badge: "#E67E22",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- ODP: {
- bgTop: "#E67E2220",
- bgBottom: "#E67E2210",
- border: "#E67E2240",
- icon: "#E67E22",
- badge: "#E67E22",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
-
- // Images
- JPG: {
- bgTop: "#FF9F4320",
- bgBottom: "#FF9F4310",
- border: "#FF9F4340",
- icon: "#FF9F43",
- badge: "#FF9F43",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- JPEG: {
- bgTop: "#FF9F4320",
- bgBottom: "#FF9F4310",
- border: "#FF9F4340",
- icon: "#FF9F43",
- badge: "#FF9F43",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- PNG: {
- bgTop: "#FF9F4320",
- bgBottom: "#FF9F4310",
- border: "#FF9F4340",
- icon: "#FF9F43",
- badge: "#FF9F43",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- GIF: {
- bgTop: "#FF9F4320",
- bgBottom: "#FF9F4310",
- border: "#FF9F4340",
- icon: "#FF9F43",
- badge: "#FF9F43",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- BMP: {
- bgTop: "#FF9F4320",
- bgBottom: "#FF9F4310",
- border: "#FF9F4340",
- icon: "#FF9F43",
- badge: "#FF9F43",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- TIFF: {
- bgTop: "#FF9F4320",
- bgBottom: "#FF9F4310",
- border: "#FF9F4340",
- icon: "#FF9F43",
- badge: "#FF9F43",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- WEBP: {
- bgTop: "#FF9F4320",
- bgBottom: "#FF9F4310",
- border: "#FF9F4340",
- icon: "#FF9F43",
- badge: "#FF9F43",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- SVG: {
- bgTop: "#FF9F4320",
- bgBottom: "#FF9F4310",
- border: "#FF9F4340",
- icon: "#FF9F43",
- badge: "#FF9F43",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
-
- // Web
- HTML: {
- bgTop: "#FD79A820",
- bgBottom: "#FD79A810",
- border: "#FD79A840",
- icon: "#FD79A8",
- badge: "#FD79A8",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- XML: {
- bgTop: "#FD79A820",
- bgBottom: "#FD79A810",
- border: "#FD79A840",
- icon: "#FD79A8",
- badge: "#FD79A8",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
-
- // Text/Markup
- MD: {
- bgTop: "#6C5CE720",
- bgBottom: "#6C5CE710",
- border: "#6C5CE740",
- icon: "#6C5CE7",
- badge: "#6C5CE7",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
-
- // Email
- EML: {
- bgTop: "#A29BFE20",
- bgBottom: "#A29BFE10",
- border: "#A29BFE40",
- icon: "#A29BFE",
- badge: "#A29BFE",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
-
- // Archives
- ZIP: {
- bgTop: "#9B59B620",
- bgBottom: "#9B59B610",
- border: "#9B59B640",
- icon: "#9B59B6",
- badge: "#9B59B6",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- RAR: {
- bgTop: "#9B59B620",
- bgBottom: "#9B59B610",
- border: "#9B59B640",
- icon: "#9B59B6",
- badge: "#9B59B6",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- "7Z": {
- bgTop: "#9B59B620",
- bgBottom: "#9B59B610",
- border: "#9B59B640",
- icon: "#9B59B6",
- badge: "#9B59B6",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
-
- // Default
- DEFAULT: {
- bgTop: "#74B9FF20",
- bgBottom: "#74B9FF10",
- border: "#74B9FF40",
- icon: "#74B9FF",
- badge: "#74B9FF",
- textPrimary: "#FFFFFF",
- textSecondary: "#666666",
- },
- };
-
- return schemes[extension] || schemes["DEFAULT"];
-}
-
-/**
- * Draw rounded rectangle
- */
-function drawRoundedRect(
- ctx: CanvasRenderingContext2D,
- x: number,
- y: number,
- width: number,
- height: number,
- radius: number,
-) {
- ctx.beginPath();
- ctx.moveTo(x + radius, y);
- ctx.lineTo(x + width - radius, y);
- ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
- ctx.lineTo(x + width, y + height - radius);
- ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
- ctx.lineTo(x + radius, y + height);
- ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
- ctx.lineTo(x, y + radius);
- ctx.quadraticCurveTo(x, y, x + radius, y);
- ctx.closePath();
-}
-
-/**
- * Draw modern document icon
- */
-function drawModernDocumentIcon(
- ctx: CanvasRenderingContext2D,
- centerX: number,
- centerY: number,
- color: string,
-) {
- const size = 24;
- ctx.fillStyle = color;
- ctx.strokeStyle = color;
- ctx.lineWidth = 2;
-
- // Document body
- drawRoundedRect(
- ctx,
- centerX - size / 2,
- centerY - size / 2,
- size,
- size * 1.2,
- 3,
- );
- ctx.fill();
-
- // Folded corner
- ctx.beginPath();
- ctx.moveTo(centerX + size / 2 - 6, centerY - size / 2);
- ctx.lineTo(centerX + size / 2, centerY - size / 2 + 6);
- ctx.lineTo(centerX + size / 2 - 6, centerY - size / 2 + 6);
- ctx.closePath();
- ctx.fillStyle = "#FFFFFF40";
- ctx.fill();
-}
-
-/**
- * Draw large lock icon for encrypted PDFs
- */
-function drawLargeLockIcon(
- ctx: CanvasRenderingContext2D,
- centerX: number,
- centerY: number,
- colorScheme: ColorScheme,
-) {
- const size = 48;
- ctx.fillStyle = colorScheme.icon;
- ctx.strokeStyle = colorScheme.icon;
- ctx.lineWidth = 3;
-
- // Lock body (rectangle)
- const bodyWidth = size;
- const bodyHeight = size * 0.75;
- const bodyX = centerX - bodyWidth / 2;
- const bodyY = centerY - bodyHeight / 4;
-
- drawRoundedRect(ctx, bodyX, bodyY, bodyWidth, bodyHeight, 4);
- ctx.fill();
-
- // Lock shackle (semicircle)
- const shackleRadius = size * 0.32;
- const shackleY = centerY - size * 0.25;
-
- ctx.beginPath();
- ctx.arc(centerX, shackleY, shackleRadius, Math.PI, 2 * Math.PI);
- ctx.stroke();
-
- // Keyhole
- const keyholeX = centerX;
- const keyholeY = bodyY + bodyHeight * 0.4;
- ctx.fillStyle = colorScheme.textPrimary;
- ctx.beginPath();
- ctx.arc(keyholeX, keyholeY, 4, 0, 2 * Math.PI);
- ctx.fill();
- ctx.fillRect(keyholeX - 2, keyholeY, 4, 8);
-}
-
/** PDFium error code 4 = password required (encrypted PDF). */
const PDFIUM_ERR_PASSWORD = 4;
@@ -611,53 +112,8 @@ async function renderPdfThumbnailPdfium(
}
}
-/**
- * Draw extension badge
- */
-function drawExtensionBadge(
- ctx: CanvasRenderingContext2D,
- centerX: number,
- centerY: number,
- extension: string,
- colorScheme: ColorScheme,
-) {
- const badgeWidth = Math.max(extension.length * 8 + 16, 40);
- const badgeHeight = 22;
-
- // Badge background
- drawRoundedRect(
- ctx,
- centerX - badgeWidth / 2,
- centerY - badgeHeight / 2,
- badgeWidth,
- badgeHeight,
- 11,
- );
- ctx.fillStyle = colorScheme.badge;
- ctx.fill();
-
- // Badge text
- ctx.font =
- 'bold 11px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
- ctx.fillStyle = colorScheme.textPrimary;
- ctx.textAlign = "center";
- ctx.fillText(extension, centerX, centerY + 4);
-}
-
-/**
- * Format file size for display
- */
-function formatFileSize(bytes: number): string {
- if (bytes === 0) return "0 B";
- const k = 1024;
- const sizes = ["B", "KB", "MB", "GB"];
- const i = Math.floor(Math.log(bytes) / Math.log(k));
- return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i];
-}
-
async function generatePDFThumbnail(
arrayBuffer: ArrayBuffer,
- file: File,
scale: number,
): Promise {
const result = await renderPdfThumbnailPdfium(
@@ -667,7 +123,7 @@ async function generatePDFThumbnail(
false,
);
if (result.isEncrypted) {
- return generateEncryptedPDFThumbnail(file);
+ return "";
}
return result.thumbnail;
}
@@ -676,13 +132,14 @@ async function generatePDFThumbnail(
* Generate thumbnail for any file type - always returns a thumbnail (placeholder if needed)
*/
export async function generateThumbnailForFile(file: File): Promise {
- // Skip very large files
+ // Very large PDFs skip thumbnail generation — SVG icon shown in UI instead
if (file.size >= 100 * 1024 * 1024) {
- return generatePlaceholderThumbnail(file);
+ return "";
}
// Handle image files - convert to data URL for persistence
if (file.type.startsWith("image/")) {
+ if (file.size >= 100 * 1024 * 1024) return "";
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result as string);
@@ -701,26 +158,23 @@ export async function generateThumbnailForFile(file: File): Promise {
const arrayBuffer = await chunk.arrayBuffer();
try {
- return await generatePDFThumbnail(arrayBuffer, file, scale);
+ return await generatePDFThumbnail(arrayBuffer, scale);
} catch {
// PDFium needs the xref table at the end of the file, so the 2MB
// chunk can fail to open for PDFs larger than that. Retry with the
- // full buffer before falling back to a placeholder.
+ // full buffer before falling back to an empty thumbnail.
try {
const fullArrayBuffer = await file.arrayBuffer();
- return await generatePDFThumbnail(fullArrayBuffer, file, scale);
+ return await generatePDFThumbnail(fullArrayBuffer, scale);
} catch (error) {
- console.warn(
- `PDF processing failed for ${file.name} - using placeholder:`,
- error,
- );
- return generatePlaceholderThumbnail(file);
+ console.warn(`PDF processing failed for ${file.name}:`, error);
+ return "";
}
}
}
- // All other files get placeholder
- return generatePlaceholderThumbnail(file);
+ // Non-PDF, non-image files use scalable SVG icons in the UI — no raster thumbnail needed
+ return "";
}
/**
@@ -752,7 +206,7 @@ export async function generateThumbnailWithMetadata(
if (result.isEncrypted) {
return {
- thumbnail: generateEncryptedPDFThumbnail(file),
+ thumbnail: "",
pageCount: 1,
isEncrypted: true,
};
@@ -765,7 +219,6 @@ export async function generateThumbnailWithMetadata(
pageDimensions: result.pageDimensions,
};
} catch {
- const thumbnail = generatePlaceholderThumbnail(file);
- return { thumbnail, pageCount: 1 };
+ return { thumbnail: "", pageCount: 1 };
}
}
diff --git a/frontend/src/proprietary/components/shared/UpgradeBanner.tsx b/frontend/src/proprietary/components/shared/UpgradeBanner.tsx
index d0ee1612e2..649416f205 100644
--- a/frontend/src/proprietary/components/shared/UpgradeBanner.tsx
+++ b/frontend/src/proprietary/components/shared/UpgradeBanner.tsx
@@ -360,7 +360,8 @@ const UpgradeBanner: React.FC = () => {
closeIconColor="#fff"
buttonVariant="white"
buttonColor="blue"
- minHeight={64}
+ minHeight={48}
+ compact
/>
)}
{renderUrgentBanner()}