# План: фильтрация файлов через `.lst` ## Цель Добавить в file picker режим manifest-фильтра: - пользователь выбирает папку целиком; - если в корне выбранной папки есть `obscure.lst`, он ограничивает набор файлов; - если `.lst` нет, сохраняется текущее поведение: выбираются все поддерживаемые файлы; - сам `obscure.lst` никогда не отправляется на обработку; - другие файлы с расширением `.lst` не являются manifest-файлами и не загружаются. ## Правила `obscure.lst` Одна строка — одно правило. - пустые строки игнорируются; - строки, начинающиеся с `#`, являются комментариями; - `!` в начале строки означает отрицательное правило; - пути относительны к выбранной папке; - абсолютные пути и сегменты `..` запрещены; - разделители `\` нормализуются в `/`; - весь путь сравнивается без учёта регистра; - несколько совпавших правил не создают дубли. Пример: ```text *.pdf reports/name*.docx !archive/** !**/*draft*.* ``` ## Glob-маски Поддержать общепринятые шаблоны: - `*` — любое количество символов, кроме `/`; - `?` — один символ, кроме `/`; - `**` — любое количество каталогов; - `[abc]` и `[a-z]` — классы и диапазоны символов; - `[!a]` и `[^a]` — отрицательные классы; - обычный путь без glob-символов — точное совпадение. Семантика: - `*.pdf` — PDF только в корне выбранной папки; - `reports/*.pdf` — PDF внутри `reports`; - `**/*.pdf` — PDF на любой глубине; - `name*.pdf` — файлы с префиксом `name` в корне; - `reports/name*.pdf` — файлы с префиксом `name` непосредственно в `reports`; - `reports/` — то же, что `reports/**`; - `a**b` — две звёздочки внутри сегмента работают как обычная `*`. Правило без `/` не применяется рекурсивно. Для поиска на любой глубине нужно явно использовать `**/`, например `**/*.pdf`. Положительные правила формируют набор кандидатов. Отрицательные правила удаляют совпавшие файлы из результата. ## Алгоритм 1. Получить все файлы выбранной папки и подпапок через `webkitdirectory`. 2. Нормализовать `webkitRelativePath`, убрав имя корневой выбранной папки. 3. Найти только `obscure.lst` в корне выбранной папки, без учёта регистра. Остальные `.lst` игнорировать. 4. Если `obscure.lst` нет, передать в текущую логику все поддерживаемые файлы. 5. Если `obscure.lst` найден: - прочитать UTF-8 и убрать BOM; - разобрать строки и скомпилировать glob в RegExp; - отбросить некорректные правила с предупреждением и номером строки; - выбрать поддерживаемые файлы, совпавшие хотя бы с одним положительным правилом; - удалить совпадения с отрицательными правилами; - удалить `.lst` из результата; - сохранить исходный порядок файлов и убрать дубли по нормализованному пути. 6. Передать итоговый список в существующий цикл построения таблицы без изменений. Если все положительные правила отсутствуют или некорректны, результат пустой. Автоматический fallback на все файлы запрещён. Если положительное правило не совпало ни с одним файлом, показать предупреждение, но продолжить обработку остальных правил. Неподдерживаемые файлы не добавлять. `obscure.lst` во вложенной папке не является manifest-фильтром и обрабатывается как обычный неподдерживаемый файл. Содержимое ZIP отдельно не фильтровать: ZIP выбирается как один файл и далее обрабатывается существующим механизмом раскрытия архивов. ## Нормализация и предупреждения Перед сопоставлением: - заменить `\` на `/`; - убрать ведущие `./`; - свернуть повторные `/`; - убрать имя корневой выбранной папки из `webkitRelativePath`; - отклонить абсолютные пути, сегменты `..` и пустые правила; - отклонить незакрытые классы `[` с указанием номера строки. Ошибки и предупреждения передавать через существующий `cfg.onError` или `statusEl`. Ошибка одного правила не должна останавливать остальные правила. Ошибка чтения или декодирования `obscure.lst` даёт предупреждение и пустой результат. ## Где менять код Алгоритм фильтра вынести в чистый модуль рядом с текущим picker, например: ```text upload/frontend/table/lst_filter.js ``` Точка подключения — `on_folder_change.js`, до существующего цикла построения дерева. DOM и upload-транспорт в модуль фильтра не добавлять. Сборочные `site/static/dist/*.js` изменять только штатной сборкой. ## Минимальные тесты Проверить: 1. Папка без `.lst` сохраняет старое поведение. 2. `obscure.lst` не попадает в результат, другие `.lst` также не загружаются. 3. `*.pdf`, `name*.pdf`, `?`, `**` и классы символов. 4. `*.pdf` совпадает только с файлом в корне, а `**/*.pdf` — также с `x.pdf` в корне и файлами в подпапках. 5. `docs/name*.pdf`, `docs/`, `a**b` и точные пути. 6. Положительные и отрицательные правила вместе. 7. Комментарии, пустые строки, BOM, CRLF и пробелы. 8. Регистр всего пути (`DOCS/A.PDF` совпадает с `docs/*.pdf`). 9. Кириллицу, пробелы, вложенные каталоги и обратные слеши (`docs\a.pdf`). 10. Дубли и перекрывающиеся маски. 11. Запрет `/etc/x`, `C:/x`, `../x`, `a/../b`. 12. Битую маску без падения всего picker. 13. Ошибка чтения `.lst` даёт пустой результат и предупреждение. 14. Правило без совпадений даёт предупреждение. 15. Только отрицательные или полностью битые правила дают пустой результат. 16. Другой `.lst` в корне не включает manifest-режим. 17. `obscure.lst` во вложенной папке не включает manifest-режим. 18. ZIP выбирается целиком, без фильтрации его внутреннего содержимого. ## Ограничения реализации - Не менять backend-контракт. - Не добавлять базу данных, отдельный сервис или новый формат конфигурации. - Не переписывать существующий picker. - Изменить минимальное число файлов. - Сначала реализовать алгоритм и тесты, затем обновить dist штатной сборкой.