+19
@@ -1,9 +1,28 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
venv/
|
||||
.venv/
|
||||
env/
|
||||
ENV/
|
||||
instance/
|
||||
.flaskenv
|
||||
.pytest_cache/
|
||||
.mypy_cache/
|
||||
.ruff_cache/
|
||||
.coverage
|
||||
coverage.xml
|
||||
htmlcov/
|
||||
*.log
|
||||
build/
|
||||
dist/
|
||||
*.egg-info/
|
||||
# TMP/
|
||||
# about1500.md
|
||||
*.har
|
||||
chrome-net-export-log.json
|
||||
TSTFILES/
|
||||
files/
|
||||
upload-platform/
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# ПЛАН: компонент выбора файлов/папок/архивов → файловое дерево
|
||||
|
||||
> Для Flash. Vanilla JS, БЕЗ React и сборщика (как drhider). Новое — только если проще.
|
||||
> Переиспользуемый компонент (слой 1 из `PLAN-upload-layers.md`).
|
||||
|
||||
## Решения (фиксировано)
|
||||
|
||||
| Вопрос | Решение | Почему |
|
||||
|---|---|---|
|
||||
| Стек | **vanilla JS**, один модуль | drhider без сборщика; React избыточен |
|
||||
| ZIP | **`fflate`** (`unzipSync`/`unzip` + `filter`) | заменяет ручной `parseZip` (~200 строк); 8 kB; filter по расширению ДО распаковки |
|
||||
| Выбор папки | `webkitdirectory` (оставить) | уже в drhider, рекурсивно |
|
||||
| Дерево | **свой лёгкий** рендер (ленивый + делегирование) | jsTree старые, react — нет |
|
||||
| Единственная новая зависимость | `fflate` (CDN `unpkg`/`jsdelivr`) | оправдано и проще |
|
||||
|
||||
## Модель узла
|
||||
```
|
||||
node = { type:'dir'|'zip'|'file', name, path, size, file, children, collapsed }
|
||||
```
|
||||
- `path` для file — полный относительный путь: `"Папка/архив.zip/Внутри/файл.txt"`.
|
||||
- Дубль = `path`+`size` → пропуск; имя то же, размер другой → суффикс `_2`.
|
||||
|
||||
## API компонента
|
||||
```
|
||||
createFilePicker({ container, allowedExt, maxFileBytes, onSelectionChange }) -> {
|
||||
pickFiles(), pickFolder(),
|
||||
getFlatFiles() -> [{path,name,size,file}], // в слой закачки
|
||||
getOverNames(), clear(), setStatus(path, html)
|
||||
}
|
||||
```
|
||||
|
||||
## Сборка дерева
|
||||
1. **Папка**: `webkitRelativePath="Top/…"` → отбросить `Top` → вставить по сегментам.
|
||||
2. **Файлы**: в корень; `.zip` → `fflate.unzip` (рекурсивно по вложенным `.zip`), узел `zip` + дети.
|
||||
3. **Фильтр**: лист принимается только если расширение ∈ `allowedExt`; `.zip` — ради раскрытия.
|
||||
|
||||
## Рендер (сотни файлов)
|
||||
- Свёрнутый `dir`/`zip` **не рендерит детей** (ленивый).
|
||||
- Плоский список «видимых строк» одним проходом (или `DocumentFragment`).
|
||||
- Клики — **делегирование** (один обработчик на контейнер по `data-path`), не сотни слушателей.
|
||||
- `getFlatFiles()` — обход дерева, только листья-файлы.
|
||||
|
||||
## Шаги (для Flash)
|
||||
1. Подключить `fflate` (CDN). Написать обёртку `zip.js`: `listZip(f) -> [{path,name,size,file}]` с `filter` по `allowedExt`.
|
||||
2. Модель узла + вставка по сегментам пути.
|
||||
3. Сборка из `folderInput` (webkitRelativePath) и `fileInput` (раскрытие zip рекурсивно).
|
||||
4. Дедуп (`path`+`size`, суффикс `_2`).
|
||||
5. Рендер дерева: collapse, ленивый рендер, делегирование кликов.
|
||||
6. Фильтр `allowedExt` + лимит + статус «пропущен (лимит)».
|
||||
7. `getFlatFiles()` → слой закачки.
|
||||
|
||||
## Чек-лист приёмки
|
||||
- [ ] дерево папок/архивов сворачивается/разворачивается;
|
||||
- [ ] путь файла из архива виден (`Папка/архив.zip/…`);
|
||||
- [ ] дубли исключены, коллизия имени → `_2`;
|
||||
- [ ] `allowedExt` — параметр (фильтр и в zip, и в папке);
|
||||
- [ ] сотни файлов не тормозят (ленивый рендер + делегирование);
|
||||
- [ ] `getFlatFiles()` отдаёт только учитываемые файлы.
|
||||
|
||||
## Не делать (не оправдано)
|
||||
- React / сборщик / jQuery-деревья (jsTree, Fancytree).
|
||||
- Виртуализация `react-arborist` — только если появятся тысячи узлов и будет React.
|
||||
+10
-13
@@ -16,6 +16,9 @@
|
||||
(для этого короткий `README.md` внутри модуля).
|
||||
4. **НЕ поломать текущий рабочий код drhider** — это рефакторинг 1:1 (extract, не rewrite).
|
||||
5. **Файлы мелкие** — каждая функция в отдельном файле.
|
||||
6. **Vanilla JS, без React/сборщика**. ZIP раскрытие — на **`fflate`** (вместо ручного парсера):
|
||||
проще и надёжнее (`unzip` + `filter` по расширению до распаковки). Дерево файлов
|
||||
(сворачивание по папкам/архивам) — отдельный план `PLAN-file-picker.md`.
|
||||
|
||||
---
|
||||
|
||||
@@ -63,12 +66,8 @@ upload/
|
||||
config.example.json # слой 0: образец конфига
|
||||
|
||||
frontend/
|
||||
zip/ # распаковка ZIP (чистые функции, без DOM)
|
||||
dos_to_ms.js # dosToMs(date, time)
|
||||
decode_zip_name.js # decodeZipName(bytes, isUtf8)
|
||||
inflate_raw.js # inflateRaw(bytes)
|
||||
parse_zip.js # parseZip(buf)
|
||||
list_zip_files.js # listZipFiles(file) — рекурсия по вложенным zip
|
||||
zip/ # распаковка ZIP (чистые функции, без DOM) — на базе fflate
|
||||
list_zip_files.js # listZipFiles(file, allowedExt) — fflate.unzip + filter + рекурсия
|
||||
|
||||
table/ # слой 1: выбор файлов
|
||||
fs.js # fs(b) — формат размера
|
||||
@@ -124,11 +123,7 @@ upload/
|
||||
|
||||
| Функция в drhider | → файл модуля | Что меняется при переносе |
|
||||
|---|---|---|
|
||||
| `dosToMs` | `zip/dos_to_ms.js` | ничего (чистая) |
|
||||
| `decodeZipName` | `zip/decode_zip_name.js` | ничего |
|
||||
| `inflateRaw` | `zip/inflate_raw.js` | ничего |
|
||||
| `parseZip` | `zip/parse_zip.js` | ничего |
|
||||
| `listZipFiles` | `zip/list_zip_files.js` | `allowedExt` → параметр (сейчас захардкожен внутри) |
|
||||
| `listZipFiles` (+ вложенные `parseZip`/`inflateRaw`/`decodeZipName`/`dosToMs`) | `zip/list_zip_files.js` | **заменяется на `fflate`** (`unzip` + `filter` по `allowedExt` + рекурсия по вложенным zip); ручной парсер НЕ переносим |
|
||||
| `fs` | `table/fs.js` | ничего |
|
||||
| `fmtSec` | `table/fmt_sec.js` | ничего |
|
||||
| `esc` | `table/esc.js` | ничего |
|
||||
@@ -234,8 +229,9 @@ POST /api/upload_refs {session?, files:[{name,size,url}]}
|
||||
|
||||
Строго последовательно, чтобы не сломать прод:
|
||||
|
||||
1. **`zip/*`** — вынести 5 чистых функций, написать юнит-тест (кириллица, вложенный zip,
|
||||
не-doc расширения). Самый изолированный кусок — безопасен.
|
||||
1. **`zip/*`** — подключить `fflate` (CDN) и написать `listZipFiles(file, allowedExt)` на
|
||||
`fflate.unzip` с `filter` (не распаковывать не-doc) + рекурсия по вложенным zip.
|
||||
Юнит-тест: кириллица, вложенный zip, не-doc расширения.
|
||||
2. **`table/*`** (слой 1) — вынести функции таблицы/выбора, состояние `sf`/`fileMeta`/`overNames`
|
||||
увести внутрь `init_upload_table.js`.
|
||||
3. **`upload/*`** (слой 2 фронт) — PUT + `upload_refs`, параметризовать `vmUploadUrl`.
|
||||
@@ -270,6 +266,7 @@ POST /api/upload_refs {session?, files:[{name,size,url}]}
|
||||
импортировать `session` (create/add/get).
|
||||
4. **Какой конфиг задать** — один блок `config.example.json` с пояснением каждого поля.
|
||||
5. **Что НЕ трогать** — слой 3 (логика приложения у каждого своя).
|
||||
6. **Дерево файлов** (сворачивание по папкам/архивам) — см. `PLAN-file-picker.md` (слой 1, vanilla + fflate).
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user