Files
drhider/History/plans/2026-08-24-folder-select-plan.md
T

6.0 KiB
Raw Blame History

План для Flash: выбор целой папки рекурсивно (v0.0.68)

Самодостаточный план. Файл: site/templates/index.html (весь фронт — ванильный JS, без сборки). Текущая версия 0.0.67 → поднять до 0.0.68site/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 () => {...}) — текущий обработчик: для .ziplistZipFiles(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();
      • Если .zipconst 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/обработчик выбора папки.