AGENTS.md 내용 업데이트
This commit is contained in:
@@ -31,9 +31,11 @@ src/
|
|||||||
│ ├── index.js # Main process entry point; app.setName(), BrowserWindow, registerAll()
|
│ ├── index.js # Main process entry point; app.setName(), BrowserWindow, registerAll()
|
||||||
│ └── ipc/
|
│ └── ipc/
|
||||||
│ ├── index.js # registerAll(mainWindow) — delegates to folder and files modules
|
│ ├── index.js # registerAll(mainWindow) — delegates to folder and files modules
|
||||||
|
│ ├── file-utils.js # Shared main-process file helpers: image listing/data URLs,
|
||||||
|
│ │ # exists check, EXDEV-safe move/copy fallback
|
||||||
│ ├── folder.js # select-folder, list-directories, get/set-last-folder,
|
│ ├── folder.js # select-folder, list-directories, get/set-last-folder,
|
||||||
│ │ # get/set-preview-on, get-home-dir, reveal-folder
|
│ │ # get/set-preview-on, get-home-dir, reveal-folder
|
||||||
│ └── files.js # list-images, read-image, rename-files,
|
│ └── files.js # list-images, read-image, rename-files, stage/restore-delete,
|
||||||
│ # check-files-exist, move-files
|
│ # check-files-exist, move-files
|
||||||
├── preload/
|
├── preload/
|
||||||
│ └── index.js # Bridges IPC to window.api via contextBridge (isolated context)
|
│ └── index.js # Bridges IPC to window.api via contextBridge (isolated context)
|
||||||
@@ -41,14 +43,25 @@ src/
|
|||||||
├── index.html # App shell; dark theme, CSS variables in :root, modal HTML
|
├── index.html # App shell; dark theme, CSS variables in :root, modal HTML
|
||||||
├── style.css # All CSS
|
├── style.css # All CSS
|
||||||
├── state.js # Shared mutable state: files[], currentFolder, selectedIdxs, sortMode
|
├── state.js # Shared mutable state: files[], currentFolder, selectedIdxs, sortMode
|
||||||
├── renderer.js # Entry point: init, loadFolder, sortFiles, undo/redo, keyboard shortcuts
|
├── renderer.js # Entry point: DOM wiring, init order, preview-name updates
|
||||||
|
├── folder-controller.js # Folder open/load, directory tree sync, sort mode
|
||||||
|
├── history-controller.js # Undo/redo UI flow, delete undo/redo filesystem calls
|
||||||
|
├── keyboard.js # Global app shortcuts; disabled while a modal is open
|
||||||
|
├── modal.js # Common modal open/close, ESC/Enter, focus trap
|
||||||
├── ui.js # showLoading, hideLoading, setStatus, showToast
|
├── ui.js # showLoading, hideLoading, setStatus, showToast
|
||||||
├── grid.js # renderGrid, createCard, card UI update functions, showEmptyGrid
|
├── grid.js # renderGrid, createCard, card UI update functions, showEmptyGrid
|
||||||
├── rename.js # doRename + rename-conflict modal; resets history after rename
|
├── rename.js # doRename + confirm/conflict modals; resets history after rename
|
||||||
├── move.js # handleFolderDrop + move modals + _doMoveFiles; resets history after move
|
├── move.js # handleFolderDrop + move modals + _doMoveFiles; resets history after move
|
||||||
├── history.js # Undo/redo stack (max 50 entries); tracks file order + affected paths
|
├── delete.js # Delete modal + staged-delete execution; delete is undoable
|
||||||
|
├── history.js # Undo/redo stack (max 50 entries); reorder/delete entries
|
||||||
|
├── utils/
|
||||||
|
│ ├── dom.js # Small DOM helpers such as renderTextList()
|
||||||
|
│ └── file-names.js # Extension parsing, digit clamping, sequential name formatting
|
||||||
└── handlers/
|
└── handlers/
|
||||||
├── drag-drop.js # Card reorder (single/multi) + drag-to-folder in sidebar
|
├── drag-drop.js # Card reorder (single/multi) + drag-to-folder in sidebar
|
||||||
|
├── drag/
|
||||||
|
│ ├── auto-scroll.js # Drag auto-scroll helper
|
||||||
|
│ └── ghost.js # Drag ghost UI helper
|
||||||
├── file-explorer.js # Sidebar directory tree (lazy-load, expand/collapse)
|
├── file-explorer.js # Sidebar directory tree (lazy-load, expand/collapse)
|
||||||
├── selection.js # Rubber-band, Shift-click, Cmd+click, resetAnchor
|
├── selection.js # Rubber-band, Shift-click, Cmd+click, resetAnchor
|
||||||
├── pinch-zoom.js # Ctrl+scroll to resize thumbnails (CSS --thumb-size, 150–400px)
|
├── pinch-zoom.js # Ctrl+scroll to resize thumbnails (CSS --thumb-size, 150–400px)
|
||||||
@@ -67,6 +80,8 @@ Defined in `src/main/ipc/`, bridged via `src/preload/index.js` as `window.api`:
|
|||||||
| `rename-files({folderPath, files, prefix, digits})` | invoke | Two-phase rename: temp names (crypto.randomUUID) first, then final names |
|
| `rename-files({folderPath, files, prefix, digits})` | invoke | Two-phase rename: temp names (crypto.randomUUID) first, then final names |
|
||||||
| `check-files-exist(folderPath, names[])` | invoke | Returns subset of names that already exist in folderPath |
|
| `check-files-exist(folderPath, names[])` | invoke | Returns subset of names that already exist in folderPath |
|
||||||
| `move-files({targetFolder, files[]})` | invoke | Moves files; each file may have optional `newName`; handles cross-device (EXDEV) |
|
| `move-files({targetFolder, files[]})` | invoke | Moves files; each file may have optional `newName`; handles cross-device (EXDEV) |
|
||||||
|
| `stage-delete-files(paths[])` | invoke | Moves files to userData deleted_staging for undoable deletion; handles cross-device (EXDEV) |
|
||||||
|
| `restore-staged-files(entries[])` | invoke | Restores staged delete entries back to their original paths |
|
||||||
| `list-directories(folderPath)` | invoke | Returns `{name, path, hasChildren}[]` subdirs, or `null` on access error |
|
| `list-directories(folderPath)` | invoke | Returns `{name, path, hasChildren}[]` subdirs, or `null` on access error |
|
||||||
| `get-home-dir` | invoke | Returns `os.homedir()` |
|
| `get-home-dir` | invoke | Returns `os.homedir()` |
|
||||||
| `get-last-folder` / `set-last-folder` | invoke | Persists last opened folder in userData config.json |
|
| `get-last-folder` / `set-last-folder` | invoke | Persists last opened folder in userData config.json |
|
||||||
@@ -80,11 +95,16 @@ Uses ES module imports (`type="module"`). No framework.
|
|||||||
|
|
||||||
**Dependency direction** (no cycles):
|
**Dependency direction** (no cycles):
|
||||||
```
|
```
|
||||||
renderer.js → rename.js, move.js → grid.js → handlers/*
|
renderer.js → folder-controller.js ─┬→ grid.js → handlers/*
|
||||||
↘ ↘ ↗
|
→ history-controller.js ├→ history.js
|
||||||
history.js ui.js
|
→ keyboard.js └→ ui.js
|
||||||
|
→ rename.js / move.js / delete.js
|
||||||
|
→ modal.js / utils/*
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Keep filesystem work in `src/main/`; renderer modules use `window.api` only.
|
||||||
|
Prefer narrow utility modules (`utils/file-names.js`, `utils/dom.js`) over broad catch-all helpers.
|
||||||
|
|
||||||
**State** (`src/renderer/state.js`):
|
**State** (`src/renderer/state.js`):
|
||||||
- `files[]` — ordered array of `{name, path, mtime}`; order determines rename sequence
|
- `files[]` — ordered array of `{name, path, mtime}`; order determines rename sequence
|
||||||
- `selectedIdxs` — `Set<number>` of selected card indices
|
- `selectedIdxs` — `Set<number>` of selected card indices
|
||||||
@@ -98,13 +118,26 @@ renderer.js → rename.js, move.js → grid.js → handlers/*
|
|||||||
- `Cmd+Z` — Undo (되돌리기)
|
- `Cmd+Z` — Undo (되돌리기)
|
||||||
- `Cmd+Shift+Z` — Redo (다시하기)
|
- `Cmd+Shift+Z` — Redo (다시하기)
|
||||||
- `P` — 미리보기 on/off 토글
|
- `P` — 미리보기 on/off 토글
|
||||||
|
- `Backspace` / `Delete` — 선택 파일 삭제 팝업
|
||||||
|
|
||||||
|
Global shortcuts are registered in `keyboard.js`. They do not fire while any `.modal-overlay` is open.
|
||||||
|
|
||||||
|
**Modals** (`modal.js` + modal HTML in `index.html`):
|
||||||
|
- All modal cancel/close buttons use `.modal-close` and `btn-ghost`.
|
||||||
|
- All modal primary buttons use `.modal-action`, `btn-success`, and the text `실행`.
|
||||||
|
- `Escape` triggers the active modal's `.modal-close`; `Enter` triggers its `.modal-action`.
|
||||||
|
- `Tab` / `Shift+Tab` is trapped inside the active modal.
|
||||||
|
- Modal titles use `.modal-titlebar`; warning/danger variants are `.modal-titlebar-warning` and `.modal-titlebar-danger`.
|
||||||
|
- Rename confirm uses the same modal for selected-only and all-image rename. The visible title switches between "선택된 이미지 저장" and "전체 이미지 저장"; selected-only mode also shows "선택된 파일들만 저장합니다".
|
||||||
|
|
||||||
**Undo / Redo** (`history.js`):
|
**Undo / Redo** (`history.js`):
|
||||||
- 파일 순서 변경(드래그, 정렬)만 추적; 파일시스템 변경(rename, move)은 히스토리 리셋
|
- 파일 순서 변경(드래그, 정렬)과 staged delete를 추적; rename, move는 파일 경로가 바뀌므로 히스토리 리셋
|
||||||
- 각 엔트리: `{ files: [...], affectedPaths: Set | null }`
|
- Reorder 엔트리: `{ type: 'reorder', files: [...], affectedPaths: Set | null }`
|
||||||
|
- Delete 엔트리: `{ type: 'delete', files: [...], deletedEntries: [...] }`
|
||||||
- `affectedPaths`: undo/redo 실행 후 해당 파일들을 선택 상태로 복원하는 데 사용
|
- `affectedPaths`: undo/redo 실행 후 해당 파일들을 선택 상태로 복원하는 데 사용
|
||||||
- `setOnUpdate(fn)`: push/reset/undo/redo 시 자동 호출되는 콜백 등록 (버튼 상태 갱신용)
|
- `setOnUpdate(fn)`: push/reset/undo/redo 시 자동 호출되는 콜백 등록 (버튼 상태 갱신용)
|
||||||
- 최대 50개 엔트리 유지
|
- 최대 50개 엔트리 유지
|
||||||
|
- Undo/redo orchestration lives in `history-controller.js`; actual stack operations stay in `history.js`.
|
||||||
|
|
||||||
**Card selection** (`selection.js`):
|
**Card selection** (`selection.js`):
|
||||||
- Click — 단독 선택 (재클릭 시 해제)
|
- Click — 단독 선택 (재클릭 시 해제)
|
||||||
@@ -122,6 +155,7 @@ renderer.js → rename.js, move.js → grid.js → handlers/*
|
|||||||
- 사이드바 폴더로 드래그: 선택된 파일들을 해당 폴더로 이동
|
- 사이드바 폴더로 드래그: 선택된 파일들을 해당 폴더로 이동
|
||||||
- 파일명 충돌 시 rename-and-move 모달 표시
|
- 파일명 충돌 시 rename-and-move 모달 표시
|
||||||
- 드래그 중 `.tree-row.drop-target` 클래스로 대상 폴더 하이라이트
|
- 드래그 중 `.tree-row.drop-target` 클래스로 대상 폴더 하이라이트
|
||||||
|
- Drag auto-scroll and drag ghost UI are extracted to `handlers/drag/auto-scroll.js` and `handlers/drag/ghost.js`.
|
||||||
|
|
||||||
**Rename** (`rename.js`):
|
**Rename** (`rename.js`):
|
||||||
- 선택된 파일만 이름 변경 (선택 없으면 전체)
|
- 선택된 파일만 이름 변경 (선택 없으면 전체)
|
||||||
@@ -134,13 +168,19 @@ renderer.js → rename.js, move.js → grid.js → handlers/*
|
|||||||
- 새 이름도 충돌이면 에러 모달
|
- 새 이름도 충돌이면 에러 모달
|
||||||
- 완료 후 history 리셋
|
- 완료 후 history 리셋
|
||||||
|
|
||||||
|
**Delete** (`delete.js`):
|
||||||
|
- `Backspace` / `Delete` or card delete button opens the delete modal.
|
||||||
|
- If a clicked card is part of the current selection, all selected files are deleted; otherwise only that card is deleted.
|
||||||
|
- Main process stages deleted files under `app.getPath('userData')/deleted_staging/<timestamp>/`.
|
||||||
|
- Delete is undoable/redoable via `history-controller.js` + `stage-delete-files` / `restore-staged-files`.
|
||||||
|
|
||||||
**Thumbnail loading:** `IntersectionObserver` with `rootMargin: '400px 0px'` — lazy load as cards enter viewport. `entry.target.isConnected` 체크로 폴더 전환 중 스탈 카드 방지. Drag reorder remaps card elements via `card.dataset.filepath` to avoid re-fetching.
|
**Thumbnail loading:** `IntersectionObserver` with `rootMargin: '400px 0px'` — lazy load as cards enter viewport. `entry.target.isConnected` 체크로 폴더 전환 중 스탈 카드 방지. Drag reorder remaps card elements via `card.dataset.filepath` to avoid re-fetching.
|
||||||
|
|
||||||
**Hover preview** (`image-preview.js`): 1초 지연 후 전체 크기 팝업. 썸네일 캐시 우선 사용. 상태는 config.json에 영속.
|
**Hover preview** (`image-preview.js`): 1초 지연 후 전체 크기 팝업. 썸네일 캐시 우선 사용. 상태는 config.json에 영속.
|
||||||
|
|
||||||
**Sidebar tree** (`file-explorer.js`): Lazy-load 자식 폴더. 접근 불가 폴더는 `null` 반환 → "접근 불가" 표시. 행 클릭 시 항상 펼침, 화살표 클릭 시 토글.
|
**Sidebar tree** (`file-explorer.js`): Lazy-load 자식 폴더. 접근 불가 폴더는 `null` 반환 → "접근 불가" 표시. 행 클릭 시 항상 펼침, 화살표 클릭 시 토글.
|
||||||
|
|
||||||
**Styling:** CSS는 `src/renderer/style.css`에 집중. Dark theme, CSS variables in `:root`. macOS traffic-light은 `header`의 `-webkit-app-region: drag`.
|
**Styling:** CSS는 `src/renderer/style.css`에 집중. Dark theme, CSS variables in `:root`. macOS traffic-light은 `header`의 `-webkit-app-region: drag`. Avoid inline styles in `index.html`; use small semantic classes such as `.format-arrow`, `.empty-state-hint`, and modal title variants.
|
||||||
|
|
||||||
**App icon:** 소스는 `assets/icon.png` (2048×2048). `assets/icon.icns`는 10개 해상도(16px~1024px@2x)를 포함하며 electron-builder가 사용. `app.setName('Photo Renamer')`로 개발 모드 메뉴바 이름도 지정.
|
**App icon:** 소스는 `assets/icon.png` (2048×2048). `assets/icon.icns`는 10개 해상도(16px~1024px@2x)를 포함하며 electron-builder가 사용. `app.setName('Photo Renamer')`로 개발 모드 메뉴바 이름도 지정.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user