koriweb 3e73967c7b darktable-inspired reskin + metadata/collections, map, easy mode, select/export
UI overhaul to a darktable tone-and-manner and a set of features adapted from
darktable's proven patterns (reimplemented in our Electron/TS stack; no GPL code).

Design reskin:
- Dark neutral-gray palette + amber accent, flat/squared corners, no card shadows,
  compact darktable-style top bar (logo + pipe-separated view tabs), denser 15px base
- Done via design tokens (Tailwind slate/brand/radius/shadow remap) — minimal churn

Metadata & collections (Phase A/B):
- exifr now captures GPS + camera; asset table ALTER-migrated (gpsLat/gpsLon/camera,
  metaVersion backfill on re-index)
- Collection facet bar (year timeline / camera / color-label) filters the grid

Map & relation finder (Phase C):
- Leaflet + online OSM map tab; geotagged photos as markers
- relationService: related photos by place (GPS<1km) + time (+/-2d) + CLIP similarity

Easy mode (Phase D):
- easyMode setting (menu / onboarding); scales the whole UI (rem) + bigger thumbnails
  + large icon nav with plain labels (4050 accessibility)

Library usability:
- Video thumbnails (representative frame capture in the inference worker)
- Media filter (All / Photos / Videos) to separate them
- Clearer culling labels ("Good shots" / "To cull") + explanation tooltip
- Multi-select tiles -> Export selected to a folder (copy, best-cut extraction) and
  Delete to Recycle Bin (shell.trashItem) behind a confirm dialog
- ONNX Runtime wasm bundled locally (offline) via copy-ort-wasm + asarUnpack

Docs: DARKTABLE_REVIEW (feasibility + roadmap A->D). All typecheck/tests/build green;
boot smoke verified each phase.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 19:22:19 +09:00

AI Photo Organizer

얼굴 인식 + 촬영일(EXIF) 기준으로 사진을 로컬에서 자동 정리하는 Electron 데스크톱 앱. 클라우드 업로드 없이 내 PC 안에서만 동작한다.

기획·설계 문서: docs/PRD.md · docs/DECISIONS.md · docs/ARCHITECTURE.md

동작 방식

  1. 인물 프로필(최대 3명)과 참조 얼굴 사진을 등록한다.
  2. 정리할 폴더(소스)와 결과 폴더(출력)를 고른다.
  3. [정리 시작] → 각 사진을 스캔해
    • 얼굴이 매칭되면 출력/<인물>/YYYY/MM/이동(2·3순위 인물에게는 복사)
    • 매칭 인물이 없으면 출력/Unsorted/YYYY/MM/ 로 이동
    • 영상 파일(.mp4 .mov .avi .mkv .webm .m4v)은 얼굴인식 없이 출력/Movie/YYYY/MM/ 로 이동
    • EXIF 촬영일이 없으면 파일 수정일로 대체

데이터 안전: 이동은 복사 → 무결성 검증 → 원본 삭제 순서로 수행하고, 파일명 충돌 시 _1, _2 로 자동 리네임한다(덮어쓰기 없음).

기술 스택

Electron 33 · electron-vite · React 18 + TypeScript · Zustand · Tailwind · @vladmandic/face-api (WebGL) · exifr · electron-builder

3개 프로세스로 분리된 구조:

  • Main (Node): 스캔 / EXIF / 파일 이동·복사 / 오케스트레이션
  • UI Renderer (React): 화면 — 무거운 연산 없음
  • Inference Renderer (숨김 창): face-api 얼굴 인식 전담

개발

npm install
npm run models:download   # 최초 1회 — 모델 가중치 받기
npm run dev               # 개발 실행 (HMR)

검증 / 빌드

npm run typecheck         # 타입체크 (node + web)
npm test                  # 순수 로직 단위 테스트 (Vitest)
npm run dist:all          # Windows(nsis) + macOS(dmg) 인스톨러 빌드

macOS 타깃은 macOS 호스트에서 빌드해야 코드사이닝/공증이 가능하다(미서명 dmg는 어디서나 생성 가능).

트러블슈팅

  • TypeError: Cannot read properties of undefined (reading 'whenReady') 로 부팅 즉시 크래시하면, 환경에 ELECTRON_RUN_AS_NODE=1 이 설정된 것이다. 이 변수가 있으면 Electron 바이너리가 일반 Node로 동작해 electron.appundefined 가 된다. 실행 전 변수를 해제하라:
    • PowerShell: Remove-Item Env:\ELECTRON_RUN_AS_NODE
    • bash: unset ELECTRON_RUN_AS_NODE

폴더 구조

src/
  main/        Main 프로세스 (scanner, exif, fileOps, orchestrator, ...)
  preload/     contextBridge (UI용 index, 추론창용 inference)
  renderer/    React UI
  inference/   숨김 추론 창 (faceEngine, imageLoader)
  shared/      공유 타입/상수
models/        face-api 가중치 (download 스크립트로 채움)
tests/         Vitest 단위 테스트
docs/          기획·설계 문서
S
Description
사진정리 프로그램
Readme 421 KiB
Languages
TypeScript 97%
JavaScript 2.2%
CSS 0.6%
HTML 0.2%