feat: implement Files page with persistent storage and version tracking

Three-panel file manager (nav, list, details) modeled after Stirling-PDF.
- Backend: user_files table, /api/v1/files/* CRUD routes, file storage
  helpers, thumbnail generation via Sharp, recursive CTE version chains
- Frontend: FilesNav, FileList, FileDetails, FileUploadArea components,
  Zustand store, mobile layout with bottom sheet
- Integration: tool-factory auto-saves results as new versions when
  fileId is provided, "Open File" loads file into tool processing flow
- Search, bulk select/delete/download, version badges, tool chain tags
This commit is contained in:
Siddharth Kumar Sah
2026-03-26 01:10:51 +08:00
parent 926b52d330
commit 62fbb5484c
24 changed files with 771 additions and 470 deletions
+6 -17
View File
@@ -1,14 +1,7 @@
import type { LucideIcon } from "lucide-react";
import { FolderOpen, Grid3x3, HelpCircle, LayoutGrid, Settings, Workflow } from "lucide-react";
import { Link, useLocation } from "react-router-dom";
import { cn } from "@/lib/utils";
import {
LayoutGrid,
Workflow,
HelpCircle,
Settings,
Grid3x3,
FolderOpen,
} from "lucide-react";
import type { LucideIcon } from "lucide-react";
interface SidebarItem {
icon: LucideIcon;
@@ -45,7 +38,7 @@ export function Sidebar({ onSettingsClick, onHelpClick, expanded = false }: Side
"flex items-center gap-3 px-4 py-2.5 rounded-lg cursor-pointer transition-colors",
isActive
? "bg-primary text-primary-foreground"
: "text-muted-foreground hover:bg-muted hover:text-foreground"
: "text-muted-foreground hover:bg-muted hover:text-foreground",
)}
>
<item.icon className="h-5 w-5" />
@@ -57,7 +50,7 @@ export function Sidebar({ onSettingsClick, onHelpClick, expanded = false }: Side
"flex flex-col items-center gap-1 p-2 rounded-lg cursor-pointer transition-colors",
isActive
? "bg-primary text-primary-foreground"
: "text-muted-foreground hover:bg-muted hover:text-foreground"
: "text-muted-foreground hover:bg-muted hover:text-foreground",
)}
>
<item.icon className="h-6 w-6" />
@@ -89,9 +82,7 @@ export function Sidebar({ onSettingsClick, onHelpClick, expanded = false }: Side
if (expanded) {
return (
<div className="flex flex-col p-3 gap-1">
{topItems.map((item) =>
renderItem(item, location.pathname === item.href)
)}
{topItems.map((item) => renderItem(item, location.pathname === item.href))}
<div className="border-t border-border my-2" />
{bottomItems.map((item) => renderItem(item, false))}
</div>
@@ -101,9 +92,7 @@ export function Sidebar({ onSettingsClick, onHelpClick, expanded = false }: Side
return (
<aside className="flex flex-col items-center w-16 bg-sidebar border-r border-border py-3 gap-1 shrink-0">
<div className="flex flex-col gap-1 flex-1">
{topItems.map((item) =>
renderItem(item, location.pathname === item.href)
)}
{topItems.map((item) => renderItem(item, location.pathname === item.href))}
</div>
<div className="border-t border-border w-10 my-2" />
<div className="flex flex-col gap-1">