feat: settings panel + multi-folder scanning
Settings panel (⚙ in header): - Watch Folders: add/remove paths, toggle enabled; worker polls every 30s and calls watcher.add/unwatch without restart - Ingestion: thumb/preview size and quality, applied per-file at ingest time - Duplicate Detection: configurable Hamming distance threshold - Danger Zone: clear all dismissed duplicate pairs Multi-folder scanning: - watch_folders table replaces single PHOTO_DIR env var - PHOTO_DIR env var seeds the first entry on startup - settings table with defaults for all tunable values - Worker reads settings from DB on each drain cycle so changes are live Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
8c5c36857f
commit
d18c855571
8 files changed
+494
-73
No files matched your search
+23
-4
@@ -5,6 +5,7 @@ import Lightbox from './components/Lightbox.jsx';
|
||||
import FilterSidebar from './components/FilterSidebar.jsx';
|
||||
import IngestBadge from './components/IngestBadge.jsx';
|
||||
import DuplicatesView from './components/DuplicatesView.jsx';
|
||||
import SettingsPanel from './components/SettingsPanel.jsx';
|
||||
|
||||
import { lazy, Suspense } from 'react';
|
||||
const MapView = lazy(() => import('./components/MapView.jsx'));
|
||||
@@ -24,8 +25,9 @@ export default function App() {
|
||||
const [filters, setFilters] = useState(EMPTY_FILTERS);
|
||||
const [stats, setStats] = useState(null);
|
||||
|
||||
const [lbPhotos, setLbPhotos] = useState(null);
|
||||
const [lbIndex, setLbIndex] = useState(null);
|
||||
const [lbPhotos, setLbPhotos] = useState(null);
|
||||
const [lbIndex, setLbIndex] = useState(null);
|
||||
const [showSettings, setShowSettings] = useState(false);
|
||||
|
||||
const loadingRef = useRef(false);
|
||||
const sentinelRef = useRef(null);
|
||||
@@ -138,9 +140,23 @@ export default function App() {
|
||||
</Tab>
|
||||
</nav>
|
||||
|
||||
{/* Ingest progress — right side */}
|
||||
<div style={{ flex: 1, display: 'flex', justifyContent: 'flex-end' }}>
|
||||
{/* Right side: ingest badge + gear */}
|
||||
<div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: 10 }}>
|
||||
<IngestBadge onScanComplete={handleScanComplete} />
|
||||
<button
|
||||
onClick={() => setShowSettings(s => !s)}
|
||||
title="Settings"
|
||||
style={{
|
||||
background: showSettings ? '#1e2a3a' : 'transparent',
|
||||
border: 'none', borderRadius: 6,
|
||||
color: showSettings ? '#7ab8f5' : '#555',
|
||||
cursor: 'pointer', fontSize: 16,
|
||||
width: 32, height: 32,
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
⚙
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -217,6 +233,9 @@ export default function App() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Settings panel */}
|
||||
{showSettings && <SettingsPanel onClose={() => setShowSettings(false)} />}
|
||||
|
||||
{/* Lightbox */}
|
||||
{lbPhotos !== null && (
|
||||
<Lightbox
|
||||
|
||||
Reference in new issue
Block a user