리펙토링 - 수정한 작업 최적화
This commit is contained in:
@@ -35,7 +35,8 @@ src/
|
||||
│ ├── file-utils.js # Pure helpers: listImages, readImageAsDataUrl, fileExists,
|
||||
│ │ # moveFileWithCopyFallback (EXDEV-safe cross-device move)
|
||||
│ ├── folder.js # select-folder, list-directories, get/set-last-folder,
|
||||
│ │ # get/set-preview-on, get/set-sidebar-width, get/set-thumb-size,
|
||||
│ │ # get/set-preview-on, get/set-sidebar-width,
|
||||
│ │ # get/set-file-explorer-visible, get/set-thumb-size,
|
||||
│ │ # get-home-dir, reveal-folder
|
||||
│ └── files.js # list-images, read-image, rename-files, stage/restore-delete,
|
||||
│ # check-files-exist, move-files
|
||||
@@ -90,6 +91,7 @@ Defined in `src/main/ipc/`, bridged via `src/preload/index.js` as `window.api`:
|
||||
| `get-last-folder` / `set-last-folder` | invoke | Persists last opened folder in userData config.json |
|
||||
| `get-preview-on` / `set-preview-on` | invoke | Persists hover preview toggle state in userData config.json |
|
||||
| `get-sidebar-width` / `set-sidebar-width` | invoke | Persists sidebar width in userData config.json |
|
||||
| `get-file-explorer-visible` / `set-file-explorer-visible` | invoke | Persists file explorer visibility in userData config.json; default is visible |
|
||||
| `get-thumb-size` / `set-thumb-size` | invoke | Persists thumbnail/card size in userData config.json |
|
||||
| `reveal-folder` | invoke | Opens folder in Finder via `shell.openPath` |
|
||||
| `fullscreen-change` | push (main→renderer) | Sent on `enter-full-screen` / `leave-full-screen` window events |
|
||||
@@ -123,6 +125,7 @@ Prefer narrow utility modules (`utils/file-names.js`, `utils/dom.js`) over broad
|
||||
- `Cmd+Z` — Undo (되돌리기)
|
||||
- `Cmd+Shift+Z` — Redo (다시하기)
|
||||
- `Space` — 미리보기 on/off 토글
|
||||
- `D` — 파일 탐색기 표시/숨김 토글
|
||||
- `Backspace` / `Delete` — 선택 파일 삭제 팝업
|
||||
- `ArrowLeft` / `ArrowRight` / `ArrowUp` / `ArrowDown` — 해당 방향의 카드를 키보드 타겟으로 이동하고 단독 선택
|
||||
- `Home` / `End` — 첫 카드 / 마지막 카드를 키보드 타겟으로 이동하고 단독 선택 (`grid-container` 최상단 / 최하단으로 스크롤)
|
||||
@@ -172,6 +175,7 @@ Global shortcuts are registered in `keyboard.js`. They do not fire while any `.m
|
||||
- 선택된 파일만 이름 변경 (선택 없으면 전체)
|
||||
- 파일명 포맷은 `input-prefix` + 자리순번 + `input-postfix` + 확장자이다. 구분자 `_`는 자동 삽입하지 않고 입력값 자체에 포함한다.
|
||||
- `input-prefix`의 HTML 기본값은 `photo_`, `input-postfix`의 HTML 기본값은 `_`이다. 접미사를 비우면 접두사와 자리순번만으로 저장한다.
|
||||
- 폴더 로드 시 이미지 파일 유무와 관계없이 폴더명 뒤에 `_`를 붙여 `input-prefix`로 설정한다.
|
||||
- 상단 파일명 preview, 카드별 새 이름 라벨, rename 확인 목록은 모두 `utils/file-names.js`의 `getNameFormat()`과 `buildSequentialName()`을 사용해 같은 규칙을 공유한다.
|
||||
- 변경될 이름이 미선택 파일과 충돌하면 충돌 목록 모달 표시
|
||||
- 완료 후 history 리셋 (rename은 파일 경로가 바뀌므로 undo 대상 아님)
|
||||
@@ -192,7 +196,9 @@ Global shortcuts are registered in `keyboard.js`. They do not fire while any `.m
|
||||
|
||||
**Hover preview** (`image-preview.js`): 활성화 상태에서 카드 hover 시 대기 없이 전체 크기 팝업을 표시한다. 썸네일 캐시 우선 사용. 상태는 config.json에 영속. `Space`로 preview를 켤 때 커서가 카드 위에 있으면 즉시 팝업을 표시하고, 끌 때는 열린 팝업을 즉시 닫는다.
|
||||
|
||||
**Sidebar tree** (`file-explorer.js`): Lazy-load 자식 폴더. 접근 불가 폴더는 `null` 반환 → "접근 불가" 표시. 행 클릭 시 항상 펼침, 화살표 클릭 시 토글. 폴더 로드 후 `folder-controller.js`가 폴더명 뒤에 `_`를 붙여 `input-prefix`로 설정한다.
|
||||
**Workspace / file explorer:** `index.html` uses semantic `section.app-workspace` above the `aside` and `main`. The top `workspace-nav` contains the file explorer visibility toggle, folder open/rename actions, preview/undo/redo controls, and sort group. The file explorer visibility toggle uses the fixed text `🗂️ (D)`, persists as `fileExplorerVisible`, and hides both `aside` and `#sidebar-resizer` via `.file-explorer-hidden`.
|
||||
|
||||
**Sidebar tree** (`file-explorer.js`): Lazy-load 자식 폴더. 접근 불가 폴더는 `null` 반환 → "접근 불가" 표시. 행 클릭 시 항상 펼침, 화살표 클릭 시 토글.
|
||||
|
||||
**Thumbnail size** (`pinch-zoom.js`): Ctrl+scroll changes CSS `--thumb-size` in the 150–400px range. The latest size is persisted as `thumbSize` in config.json and restored on startup without showing the size hint.
|
||||
|
||||
@@ -202,4 +208,4 @@ Global shortcuts are registered in `keyboard.js`. They do not fire while any `.m
|
||||
|
||||
**Build output:** `electron-builder` targets DMG for arm64 and x64.
|
||||
|
||||
**Config persistence:** `app.getPath('userData')/config.json`에 `lastFolder`, `previewOn`, `sidebarWidth`, `thumbSize`, `windowState` 저장. `windowState` includes fullscreen status and normal window bounds (`x`, `y`, `width`, `height`) and is restored on startup if the saved bounds overlap an available display.
|
||||
**Config persistence:** `app.getPath('userData')/config.json`에 `lastFolder`, `previewOn`, `sidebarWidth`, `fileExplorerVisible`, `thumbSize`, `windowState` 저장. `previewOn` and `fileExplorerVisible` default to `true`. `windowState` includes fullscreen status and normal window bounds (`x`, `y`, `width`, `height`) and is restored on startup if the saved bounds overlap an available display.
|
||||
|
||||
@@ -5,9 +5,11 @@ macOS용 Electron 데스크톱 앱입니다. 폴더 안의 이미지 파일을
|
||||
## 주요 기능
|
||||
|
||||
- 이미지 폴더 열기 및 사이드바 폴더 탐색
|
||||
- 파일 탐색기 표시/숨김 토글 및 상태 저장
|
||||
- 썸네일 그리드 표시와 지연 로딩
|
||||
- 드래그 앤 드롭으로 카드 순서 변경
|
||||
- 선택 파일 또는 전체 파일의 순차 이름 변경
|
||||
- 폴더 선택 시 이미지 유무와 관계없이 폴더명을 파일명 prefix로 자동 적용
|
||||
- 사이드바 폴더로 파일 이동, 파일명 충돌 시 새 이름으로 이동
|
||||
- 삭제 파일 staging 및 Undo/Redo
|
||||
- Hover 이미지 미리보기 토글
|
||||
@@ -57,6 +59,7 @@ npm run dist
|
||||
| `Cmd+Z` | Undo |
|
||||
| `Cmd+Shift+Z` | Redo |
|
||||
| `Space` | Hover 미리보기 on/off |
|
||||
| `D` | 파일 탐색기 표시/숨김 |
|
||||
| `Backspace` / `Delete` | 선택 파일 삭제 |
|
||||
| `ArrowLeft` / `ArrowRight` / `ArrowUp` / `ArrowDown` | 해당 방향 카드로 이동 및 단독 선택 |
|
||||
| `Home` / `End` | 첫 카드 / 마지막 카드로 이동 및 그리드 최상단 / 최하단 스크롤 |
|
||||
@@ -82,6 +85,6 @@ Electron 보안 설정은 `contextIsolation: true`, `nodeIntegration: false`를
|
||||
## 개발 메모
|
||||
|
||||
- 테스트와 린트 스크립트는 현재 구성되어 있지 않습니다.
|
||||
- 설정값은 `app.getPath('userData')/config.json`에 저장됩니다.
|
||||
- 설정값은 `app.getPath('userData')/config.json`에 저장됩니다. 최근 폴더, Hover 미리보기, 사이드바 폭, 파일 탐색기 표시 상태, 썸네일 크기, 창 위치/크기를 포함합니다.
|
||||
- 삭제 Undo를 위해 삭제된 파일은 `userData/deleted_staging/` 아래에 임시 보관됩니다.
|
||||
- 자세한 코드 작업 규칙과 아키텍처 메모는 `AGENTS.md`를 참고하세요.
|
||||
|
||||
@@ -43,6 +43,7 @@ export function createFolderController({
|
||||
dirPath.textContent = folder;
|
||||
await window.api.setLastFolder(folder);
|
||||
await fileExplorer.setSelectedPath(folder);
|
||||
updateFolderPrefix(folder);
|
||||
|
||||
showLoading('이미지 목록 불러오는 중...');
|
||||
let list = [];
|
||||
@@ -56,6 +57,7 @@ export function createFolderController({
|
||||
state.selectedIdxs.clear();
|
||||
showEmptyGrid();
|
||||
btnRename.disabled = true;
|
||||
updatePreview();
|
||||
setStatus('이미지 파일 없음', false);
|
||||
log('folder', 'loadFolder empty', { folder });
|
||||
return;
|
||||
@@ -68,9 +70,6 @@ export function createFolderController({
|
||||
updateSortButtons();
|
||||
setStatus(`${state.files.length}개 파일 로드됨`, true);
|
||||
|
||||
const folderName = folder.replace(/\\/g, '/').split('/').pop();
|
||||
if (folderName) inputPrefix.value = `${folderName}_`;
|
||||
|
||||
renderGrid();
|
||||
updatePreview();
|
||||
btnRename.disabled = false;
|
||||
@@ -104,6 +103,11 @@ export function createFolderController({
|
||||
btnSortDate.classList.toggle('active', state.sortMode === 'date');
|
||||
}
|
||||
|
||||
function updateFolderPrefix(folder) {
|
||||
const folderName = folder.replace(/\\/g, '/').split('/').pop();
|
||||
if (folderName) inputPrefix.value = `${folderName}_`;
|
||||
}
|
||||
|
||||
return {
|
||||
loadFolder,
|
||||
openFolder,
|
||||
|
||||
@@ -175,12 +175,9 @@ main {
|
||||
font-family: inherit; font-size: 13px; font-weight: 600;
|
||||
cursor: pointer; transition: all .15s;
|
||||
}
|
||||
.btn-primary { background: var(--accent); color: #fff; }
|
||||
.btn-primary:hover { background: #9180ff; transform: translateY(-1px); }
|
||||
.btn-ghost { background: transparent; color: var(--text); border: 1px solid var(--border); }
|
||||
.btn-ghost:hover { background: var(--card); }
|
||||
.btn-ghost:disabled { color: var(--muted); border-color: var(--border); opacity: .4; cursor: not-allowed; }
|
||||
.btn-ghost.active { border-color: var(--accent); color: var(--accent); }
|
||||
.btn-info { background: #0d2535; color: var(--info); border: 1px solid #1a4060; }
|
||||
.btn-info:hover { background: #132e42; }
|
||||
.btn-info:disabled { opacity: .4; cursor: not-allowed; }
|
||||
|
||||
Reference in New Issue
Block a user