From 7f837ead71e61778b869e6cad5dc99068e50a2a2 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 8 Feb 2026 14:39:35 +0000 Subject: [PATCH] feat: add sidebar, characters page, and equipments page with filter - Add `react-router-dom` for client-side routing. - Add `shadcn` components: `Sidebar`, `Dialog`, `Button`, `ScrollArea`, `Avatar`, `Badge`, `Card`, `Sheet`, `Tooltip`, `Separator`, `Input`, `Label`. - Implement `AppSidebar` with navigation to Characters and Equipments. - Implement `Characters` page displaying character avatars. - Implement `Equipments` page with: - Horizontal scrollable category filter (multi-select). - Equipment grid layout. - "Extract Equipment" button opening a `Dialog` (refactored from `Extractor` component, maintaining original UI design). - Added support for pasting images directly into the Extractor Dialog. - Update `Equipment` API and Service to support filtering by multiple categories. - Add database index to `Equipment` table on `[category, subCategory]`. - Refactor `App.tsx` and `Layout.tsx` to use the new routing and sidebar structure. - Remove legacy `Stash` and `Extractor` components. --- src/ui/components/ExtractorDialog.tsx | 32 ++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/ui/components/ExtractorDialog.tsx b/src/ui/components/ExtractorDialog.tsx index fe09adc..e35a098 100644 --- a/src/ui/components/ExtractorDialog.tsx +++ b/src/ui/components/ExtractorDialog.tsx @@ -18,6 +18,8 @@ interface ExtractorDialogProps { onSuccess: (assets: ExtractedAsset[]) => void } +import { useEffect } from 'react' + export const ExtractorDialog: React.FC = ({ open, onOpenChange, @@ -30,17 +32,35 @@ export const ExtractorDialog: React.FC = ({ const [error, setError] = useState(null) const [results, setResults] = useState([]) + const processFile = (f: File) => { + setFile(f) + setPreview(URL.createObjectURL(f)) + setResults([]) + setError(null) + setStatus('idle') + } + const handleFileChange = (e: React.ChangeEvent) => { if (e.target.files && e.target.files[0]) { - const f = e.target.files[0] - setFile(f) - setPreview(URL.createObjectURL(f)) - setResults([]) - setError(null) - setStatus('idle') + processFile(e.target.files[0]) } } + useEffect(() => { + const handlePaste = (e: ClipboardEvent) => { + if (!open) return + if (e.clipboardData && e.clipboardData.files.length > 0) { + const f = e.clipboardData.files[0] + if (f.type.startsWith('image/')) { + processFile(f) + } + } + } + + window.addEventListener('paste', handlePaste) + return () => window.removeEventListener('paste', handlePaste) + }, [open]) + const handleExtract = async () => { if (!file) return