Feat: Live thumbnails for office files
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m26s

This commit is contained in:
Elijah 2026-05-25 14:43:04 -07:00
parent b80abdbefb
commit 80d60d540a
10 changed files with 244 additions and 106 deletions

View file

@ -1,6 +1,7 @@
import React, { useState, useEffect, useRef } from 'react';
import { Plus, Pencil, Trash2, Pin, FileText, LayoutTemplate, FileSpreadsheet } from 'lucide-react';
import api from '@/lib/api';
import ThumbnailImage from './ThumbnailImage';
interface FileItem {
name: string;
@ -11,6 +12,7 @@ interface FileItem {
is_pinned: boolean;
is_trashed: boolean;
mod_time: string;
checksum?: string;
}
interface OfficeHomeProps {
@ -63,6 +65,7 @@ export default function OfficeHome({ type, onFileSelect, onCreateBlank, onPinCha
const [renaming, setRenaming] = useState<string | null>(null); // path of file being renamed
const [renameValue, setRenameValue] = useState('');
const renameInputRef = useRef<HTMLInputElement>(null);
const [downloadToken, setDownloadToken] = useState('');
const ext = type === 'docs' ? '.docx' : type === 'slides' ? '.pptx' : '.xlsx';
const fileTypeParam = type === 'docs' ? 'docx' : type === 'slides' ? 'pptx' : 'xlsx';
@ -118,6 +121,7 @@ export default function OfficeHome({ type, onFileSelect, onCreateBlank, onPinCha
useEffect(() => {
fetchFiles();
api.createDownloadToken().then(setDownloadToken).catch(console.error);
}, [type]);
// Close context menu on click elsewhere
@ -232,7 +236,13 @@ export default function OfficeHome({ type, onFileSelect, onCreateBlank, onPinCha
className={`w-full ${type === 'docs' ? 'aspect-[1/1.4]' : 'aspect-[1.4/1]'} rounded-lg border hover:border-blue-500 transition-colors flex items-center justify-center shadow-sm overflow-hidden relative`}
style={{ backgroundColor: 'var(--color-bg-elevated)', borderColor: 'var(--color-border)' }}
>
<Icon className="w-16 h-16 text-3xl opacity-30 group-hover:opacity-60 transition-opacity" />
{file.checksum ? (
<div className="absolute inset-0 w-full h-full flex items-center justify-center z-0">
<ThumbnailImage checksum={file.checksum} fallbackIcon={<Icon className="w-16 h-16 text-3xl opacity-30 group-hover:opacity-60 transition-opacity" />} downloadToken={downloadToken} />
</div>
) : (
<Icon className="w-16 h-16 text-3xl opacity-30 group-hover:opacity-60 transition-opacity" />
)}
</button>
<div className="flex items-start gap-2 min-w-0">
<Icon className="w-4 h-4 flex-shrink-0 mt-0.5 text-[10px]" />