docs: план для Flash — кнопка «Выбрать папку» (webkitdirectory, рекурсивно, относительный путь)
This commit is contained in:
@@ -35,6 +35,7 @@
|
||||
- **2026-07-12-v3-done.md** — итоги v3: переход на Markdown-пайплайн + универсальное LLM-обнаружение.
|
||||
- **2026-07-12-v3.1-plan.md** — план v3.1: пофайловая обработка + прогресс в таблице (решение таймаута).
|
||||
- **2026-08-24-implementation-plan-for-flash.md** — план для агента-кодера (Flash): TTL-фикс, трекинг файлов, прерывание, ETA, UI.
|
||||
- **2026-08-24-folder-select-plan.md** — план для Flash: кнопка «Выбрать папку» (webkitdirectory, рекурсивно, относительный путь сохраняется).
|
||||
|
||||
## sonnet/ — обсуждения с Sonnet (вопросы и ответы)
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# План для 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/обработчик выбора папки.
|
||||
Reference in New Issue
Block a user