From 11716259a1229a3cf7831d4727c88b1344873e71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Sat, 5 Sep 2026 13:27:35 +0300 Subject: [PATCH 1/2] 1 --- .gitignore | 19 +++++++++++++ PLAN-file-picker.md | 62 +++++++++++++++++++++++++++++++++++++++++++ PLAN-upload-layers.md | 23 +++++++--------- 3 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 PLAN-file-picker.md diff --git a/.gitignore b/.gitignore index 5b24833..8c39840 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ diff --git a/PLAN-file-picker.md b/PLAN-file-picker.md new file mode 100644 index 0000000..8363868 --- /dev/null +++ b/PLAN-file-picker.md @@ -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. diff --git a/PLAN-upload-layers.md b/PLAN-upload-layers.md index 09dd10f..3044385 100644 --- a/PLAN-upload-layers.md +++ b/PLAN-upload-layers.md @@ -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). --- From d0128392e3d5c168a15a879d757224468f3984b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Sun, 6 Sep 2026 14:09:22 +0300 Subject: [PATCH 2/2] feat: add upload platform --- PLAN-file-picker.md | 62 +++++++++++++++++++++++++++++++++++++++++++++ upload-platform | 1 + 2 files changed, 63 insertions(+) create mode 100644 PLAN-file-picker.md create mode 160000 upload-platform diff --git a/PLAN-file-picker.md b/PLAN-file-picker.md new file mode 100644 index 0000000..8363868 --- /dev/null +++ b/PLAN-file-picker.md @@ -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. diff --git a/upload-platform b/upload-platform new file mode 160000 index 0000000..09016b2 --- /dev/null +++ b/upload-platform @@ -0,0 +1 @@ +Subproject commit 09016b22e81fab38556b0fbc2f80f0eb97941ac9