64 lines
6.0 KiB
Markdown
64 lines
6.0 KiB
Markdown
# План для Flash: выбор целой папки рекурсивно (v0.0.68)
|
||
|
||
> Самодостаточный план. Файл: `site/templates/index.html` (весь фронт — ванильный JS, без сборки).
|
||
> Текущая версия 0.0.67 → поднять до **0.0.68** (в `site/app.py` `VERSION`).
|
||
|
||
## Задача
|
||
Кнопка «📁 Выбрать папку»: юзер выбирает папку → в таблицу рекурсивно добавляются ВСЕ файлы
|
||
(включая вложенные подпапки), как сейчас раскрываются ZIP. **Относительный путь сохраняется**
|
||
(без имени верхней выбранной папки): `Подпапка/Внутри/report.pdf`.
|
||
|
||
## Что уже есть (ориентиры в index.html)
|
||
- `<input type="file" id="fileInput" multiple accept=".doc,.docx,.pdf,.txt,.zip">` в `.file-input-wrap`.
|
||
- `const fi = document.getElementById('fileInput');` (в начале `<script>`).
|
||
- `fi.addEventListener('change', async () => {...})` — текущий обработчик: для `.zip` → `listZipFiles(f)` (раскрывает архив, возвращает `File[]`), иначе `addFileWithDedup(f)`; в конце пересобирает `DataTransfer`, `rr()`.
|
||
- `addFileWithDedup(file)` — дедуп по `file.name` + `file.size`, лимиты `MAX_FILE_BYTES`/`MAX_SESSION_BYTES`, `overNames`.
|
||
- `listZipFiles(file)` — рекурсивно раскрывает `.zip` (в т.ч. вложенные), фильтрует только документы
|
||
`['.pdf','.doc','.docx','.txt','.md']`, возвращает `File[]` (имена = пути внутри архива).
|
||
- `busy` — флаг «идёт загрузка/обработка»; есть `setBusy(b)`, guard'ы в `change` и `rm`.
|
||
- Footer: `<div class="footer-bar">` содержит `fileCount`, `cancelBtn`, `newSessionBtn`, `uploadBtn`.
|
||
|
||
## Реализация
|
||
|
||
### 1. HTML — кнопка и скрытый input
|
||
- В `.file-input-wrap` (рядом с кнопкой «Choose File», т.е. `#fileInput`) добавить кнопку:
|
||
`<button type="button" class="btn" id="folderBtn">📁 Выбрать папку</button>`
|
||
- Добавить скрытый input (вне/внутри формы без разницы):
|
||
`<input type="file" id="folderInput" webkitdirectory style="display:none">`
|
||
- `folderBtn.onclick = () => folderInput.click();`
|
||
|
||
### 2. JS — ссылки и обработчик
|
||
- `const folderInput = document.getElementById('folderInput');`
|
||
- `const DOC_EXTS = ['.pdf', '.doc', '.docx', '.txt', '.md'];` (можно переиспользовать из listZipFiles — там это локальная константа `allowedExt`; продублировать на верхнем уровне или вынести).
|
||
- `folderInput.addEventListener('change', async () => { ... })`:
|
||
1. `if (busy) return;`
|
||
2. `const files = Array.from(folderInput.files);` — у каждого есть `file.webkitRelativePath` (вида `TopFolder/Подпапка/report.pdf`).
|
||
3. Для каждого файла:
|
||
- Определить `rel = file.webkitRelativePath.split('/')` и **отбросить первый сегмент** (верхняя папка): `rel = rel.slice(1).join('/')`.
|
||
- `const low = rel.toLowerCase();`
|
||
- Если `.zip` → `const nested = await listZipFiles(file);` затем для каждого `nf` создать
|
||
`new File([nf], rel_dir + '/' + nf.name, {lastModified: nf.lastModified})`, где
|
||
`rel_dir = rel.slice(0, rel.lastIndexOf('/'))` (папка, в которой лежал zip; если в корне — пусто),
|
||
и `addFileWithDedup(that)`. Обернуть в try/catch: при ошибке раскрытия добавить сам `.zip` как есть (по аналогии с текущим обработчиком).
|
||
- Иначе если `DOC_EXTS.some(e => low.endsWith(e))` → `addFileWithDedup(new File([file], rel, {lastModified: file.lastModified}))`.
|
||
- Иначе — пропустить (не документ).
|
||
4. В конце — показать индикатор «Разбираю папку…» на время обработки (как с ZIP: `document.body.style.cursor='wait'` + `st`), затем `rr()`.
|
||
- Показать индикатор до цикла, сбросить в `finally` (как текущий change-обработчик).
|
||
|
||
### 3. Важные детали
|
||
- **Дедуп/лимиты**: `addFileWithDedup` уже работает по `file.name`; так как имя = относительный путь,
|
||
файлы из разных подпапок не сливаются. Лимиты 50МБ/500МБ применяются автоматически.
|
||
- **Имя в таблице и в выходном ZIP** = относительный путь (с `/`). Это ожидаемо.
|
||
- **`webkitdirectory`**: поддерживается Chrome/Edge (полно), Firefox (частично), Safari — нет.
|
||
Best-effort, ничего не ломается в неподдерживаемых браузерах (кнопка просто не откроет выбор папки).
|
||
- **Guard `busy`**: во время загрузки/обработки выбор папки не должен работать (как и обычный input).
|
||
|
||
### 4. Версия и проверка
|
||
- `site/app.py`: `VERSION = "0.0.68"`.
|
||
- Проверки: `python3 -m py_compile site/app.py`; `node --check` на извлечённом `<script>` (как обычно).
|
||
- Тест вручную/браузером: папка с подпапками + вложенный zip + кириллические имена → все файлы в таблице
|
||
с относительными путями, дедуп/лимиты работают.
|
||
|
||
## Не трогать
|
||
- Логику обфускации, SSE, сессии, лимиты — только добавить кнопку/input/обработчик выбора папки.
|