Files
photoai/src/shared/constants.ts
T
koriweb 72c41ae834 Add NextGen library: index DB, thumbnails, AI culling, and CLIP search
Builds the "indexed library" foundation and first intelligent features on
top of the organizer (sql.js index, non-destructive in-place indexing).

Phase 0 — Library index:
- sql.js (WASM SQLite) index DB; contentHash-keyed assets, resumable indexing
  (skip by path+mtime), batch persistence (chosen over native better-sqlite3
  which fails to build on Node 24 / Python 3.12)
- Library folders (in place, non-destructive) + background indexer w/ progress
- Thumbnails generated in the AI worker (canvas->webp), cached in userData;
  served via photoai-media://thumb by hash; thumbnail grid w/ pagination

Phase 1 — AI quality assessment & culling:
- Focus (Laplacian variance), exposure (histogram), eyes-open (face-api EAR)
  computed in one analyze pass alongside the thumbnail
- Culling filters (candidate/rejected) + quality badges
- Adjustable thresholds (live SQL re-classification from stored raw scores,
  no re-analysis) + manual star rating (0-5) and color labels (usermeta)

Phase 2 — CLIP natural-language / similarity search:
- @huggingface/transformers (WASM/WebGPU, no native build)
- CLIP image/text embeddings (lazy-loaded); Korean queries auto-translated
  via opus-mt-ko-en into the English CLIP
- Embeddings stored as SQLite BLOBs; "build search index" batch w/ progress;
  brute-force cosine search; new Search tab
- Note: models download from HF Hub on first use; fully-offline ORT-wasm
  packaging and KO search-accuracy tuning are follow-ups

Tabs added (Organize / Library / Search). All typecheck/tests(12)/build green;
boot smoke verified across phases.

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

121 lines
3.8 KiB
TypeScript

// 전 프로세스 공유 상수
/** 처리 대상 이미지 확장자 (소문자, 점 포함) */
export const SUPPORTED_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.webp'] as const
/** 처리 대상 영상 확장자 (소문자, 점 포함) */
export const SUPPORTED_VIDEO_EXTENSIONS = [
'.mp4',
'.mov',
'.avi',
'.mkv',
'.webm',
'.m4v'
] as const
/** 미검출/인식실패 사진이 들어가는 폴더명 (언어 중립 — 영어 모드에서 한글 노출 방지) */
export const UNMATCHED_FOLDER = 'Unsorted'
/** 영상 파일이 들어가는 폴더명 (얼굴인식 없이 날짜 기준 이동) */
export const MOVIE_FOLDER = 'Movie'
/** 로컬 참조 이미지를 UI 창에 안전하게 표시하기 위한 커스텀 프로토콜 스킴 */
export const MEDIA_SCHEME = 'photoai-media'
/** 로그 폴더명 (출력 루트 하위) */
export const LOG_FOLDER = '_PhotoAI_logs'
/** 프로필 영속화 파일명 (userData 하위) */
export const PROFILE_STORE_FILE = 'profiles.json'
/** 앱 설정 영속화 파일명 (userData 하위) */
export const SETTINGS_FILE = 'settings.json'
/** 프리셋(인물 라이브러리) 영속화 파일명 (userData 하위, 로컬 전용) */
export const PRESET_STORE_FILE = 'presets.json'
/** 최대 프로필 인원 (PRD) */
export const MAX_PROFILES = 3
/** 최대 프리셋(저장 인물) 수 */
export const MAX_PRESETS = 5
/** 기본 잡 옵션 */
export const DEFAULT_JOB_OPTIONS = {
matchThreshold: 0.5,
concurrency: 3,
detector: 'ssd' as const
}
/** 추론 시 이미지 장변 최대 픽셀 (다운스케일 기준) */
export const MAX_IMAGE_DIMENSION = 1024
/** 썸네일 장변 픽셀 */
export const THUMBNAIL_SIZE = 256
/** 품질 분석 시 이미지 장변 픽셀 (초점/노출/얼굴 계산용) */
export const ANALYZE_SIZE = 512
/** 품질 판정 기본 임계값 (Phase 1) */
export const QUALITY_THRESHOLDS = {
/** 라플라시안 분산이 이 값 미만이면 흐림 (512px 기준) */
focus: 60,
/** 노출 점수(0~1)가 이 값 미만이면 노출 불량 */
exposure: 0.35,
/** EAR(눈 종횡비)이 이 값 미만이면 눈 감음 */
eyes: 0.18
}
/** IPC 채널명 */
export const IPC = {
// UI → Main (invoke)
PROFILES_LIST: 'profiles:list',
PROFILES_UPSERT: 'profiles:upsert',
PROFILES_REMOVE: 'profiles:remove',
PROFILES_ADD_REFERENCE: 'profiles:addReference',
PROFILES_ADD_REFERENCE_DATA: 'profiles:addReferenceData',
PROFILES_REMOVE_REFERENCE: 'profiles:removeReference',
PROFILES_APPLY_PRESET: 'profiles:applyPreset',
// 프리셋(인물 라이브러리)
PRESETS_LIST: 'presets:list',
PRESETS_SAVE_FROM: 'presets:saveFrom',
PRESETS_REMOVE: 'presets:remove',
DIALOG_PICK_SOURCE: 'dialog:pickSource',
DIALOG_PICK_OUTPUT: 'dialog:pickOutput',
DIALOG_PICK_IMAGES: 'dialog:pickImages',
JOB_RUN: 'job:run',
JOB_CANCEL: 'job:cancel',
// 설정
SETTINGS_GET: 'settings:get',
SETTINGS_SET: 'settings:set',
SETTINGS_CHANGED: 'settings:changed',
// 라이브러리 / 색인 (Phase 0)
LIBRARY_LIST: 'library:list',
LIBRARY_ADD: 'library:add',
LIBRARY_REMOVE: 'library:remove',
INDEX_RUN: 'index:run',
INDEX_CANCEL: 'index:cancel',
INDEX_PROGRESS: 'index:progress',
INDEX_DONE: 'index:done',
INDEX_ASSETS: 'index:assets',
INDEX_SET_RATING: 'index:setRating',
INDEX_SET_LABEL: 'index:setLabel',
// 검색 (Phase 2)
SEARCH_BUILD: 'search:build',
SEARCH_CANCEL: 'search:cancel',
SEARCH_STATUS: 'search:status',
SEARCH_QUERY: 'search:query',
SEARCH_PROGRESS: 'search:progress',
SEARCH_DONE: 'search:done',
// Main → UI (send)
JOB_PROGRESS: 'job:progress',
JOB_FILE_PROCESSED: 'job:fileProcessed',
JOB_DONE: 'job:done',
JOB_ERROR: 'job:error',
// Main ↔ Inference
INFER_READY: 'infer:ready',
INFER_DETECT: 'infer:detect',
INFER_DESCRIBE: 'infer:describe',
INFER_INIT: 'infer:init'
} as const