v0.0.68: кнопка «Выбрать папку» — рекурсивный выбор папки с подпапками (webkitdirectory), относительный путь сохраняется
Deploy drhider / validate (push) Canceled after 0s
Deploy drhider / validate (push) Canceled after 0s
This commit is contained in:
@@ -104,6 +104,7 @@
|
||||
- **2026-08-24-interrupt-eta-design.md** — дизайн: TTL-фикс + прерывание с сохранением + оценка времени + UI.
|
||||
- **2026-08-24-interrupt-eta-implemented.md** — v0.0.63: реализовано прерывание с сохранением + ETA + UI-таблица.
|
||||
- **2026-08-24-time-tickers-estimates.md** — v0.0.64–0.0.65: тикер текущего файла на всех этапах, мгновенные оценки времени по файлам и суммарно.
|
||||
- **2026-08-24-folder-select-implemented.md** — v0.0.68: кнопка «Выбрать папку» (webkitdirectory, рекурсивно, относительный путь).
|
||||
- **2026-08-24-ui-locks-session-freeze.md** — v0.0.66: блокировки UI (выбор/удаление) на время работы, заморозка сессии + кнопка «Новая сессия».
|
||||
|
||||
## upload/ — загрузка файлов
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# v0.0.68 — Кнопка «Выбрать папку» (webkitdirectory, рекурсивно) (2026-08-24)
|
||||
|
||||
_ux-frontend. По плану `plans/2026-08-24-folder-select-plan.md`. Код: `site/templates/index.html`._
|
||||
|
||||
## Что сделано
|
||||
- Кнопка «📁 Выбрать папку» (`#folderBtn`) + скрытый `<input type="file" id="folderInput" webkitdirectory multiple>`.
|
||||
- Обработчик `change` `#folderInput`:
|
||||
- `if (busy) return` — во время загрузки/обработки выбор папки заблокирован.
|
||||
- Берёт `webkitRelativePath` (`TopFolder/Подпапка/file.pdf`), **отбрасывает верхнюю папку** → `rel`.
|
||||
- `.zip` → раскрывает `listZipFiles`, префикс пути папки к именам; при ошибке — zip как есть.
|
||||
- Документы (`.pdf .doc .docx .txt .md`) → `addFileWithDedup(new File([...], rel))`.
|
||||
- Прочее — пропускается. Индикатор «Разбираю папку…», после — «✅ Добавлено N файлов».
|
||||
- Дедуп/лимиты работают штатно (имя = относительный путь → подпапки не сливаются).
|
||||
- Поддержка: Chrome/Edge — полно, Firefox — частично, Safari — нет (best-effort).
|
||||
|
||||
## Проверка
|
||||
- `node --check` — OK; `py_compile` — OK.
|
||||
- Версия 0.0.67 → 0.0.68.
|
||||
+1
-1
@@ -21,7 +21,7 @@ if _sys_path_root not in sys.path:
|
||||
sys.path.insert(0, _sys_path_root)
|
||||
|
||||
# Версия приложения (меняется при изменениях)
|
||||
VERSION = "0.0.67"
|
||||
VERSION = "0.0.68"
|
||||
|
||||
|
||||
def setup_logging():
|
||||
|
||||
@@ -144,6 +144,8 @@
|
||||
</p>
|
||||
<div class="file-input-wrap">
|
||||
<input type="file" id="fileInput" multiple accept=".doc,.docx,.pdf,.txt,.zip">
|
||||
<button type="button" class="btn" id="folderBtn" onclick="document.getElementById('folderInput').click()">📁 Выбрать папку</button>
|
||||
<input type="file" id="folderInput" webkitdirectory multiple style="display:none">
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
@@ -200,6 +202,8 @@
|
||||
</div>
|
||||
<script>
|
||||
const fi = document.getElementById('fileInput');
|
||||
const folderInput = document.getElementById('folderInput');
|
||||
const DOC_EXTS = ['.pdf', '.doc', '.docx', '.txt', '.md']; // документы (из папки/архивов)
|
||||
const fl = document.getElementById('fileList');
|
||||
const fc = document.getElementById('fileCount');
|
||||
const ub = document.getElementById('uploadBtn');
|
||||
@@ -572,6 +576,49 @@ fi.addEventListener('change', async () => {
|
||||
rr();
|
||||
});
|
||||
|
||||
// ═══ Выбор целой папки (webkitdirectory): рекурсивно, относительный путь сохраняется ═══
|
||||
folderInput.addEventListener('change', async () => {
|
||||
if (busy) return; // во время загрузки/обработки менять список нельзя
|
||||
const incoming = Array.from(folderInput.files);
|
||||
if (!incoming.length) return;
|
||||
document.body.style.cursor = 'wait';
|
||||
st.className = 'status progress';
|
||||
st.textContent = 'Разбираю папку…';
|
||||
let added = 0;
|
||||
try {
|
||||
for (const f of incoming) {
|
||||
// webkitRelativePath: "TopFolder/Подпапка/file.pdf" — отбрасываем верхнюю папку
|
||||
const parts = (f.webkitRelativePath || f.name).split('/');
|
||||
const rel = parts.slice(1).join('/') || f.name;
|
||||
const low = rel.toLowerCase();
|
||||
const slashIdx = rel.lastIndexOf('/');
|
||||
const relDir = slashIdx >= 0 ? rel.slice(0, slashIdx) : '';
|
||||
if (low.endsWith('.zip')) {
|
||||
try {
|
||||
const nested = await listZipFiles(f);
|
||||
for (const nf of nested) {
|
||||
const nm = relDir ? relDir + '/' + nf.name : nf.name;
|
||||
addFileWithDedup(new File([nf], nm, { lastModified: nf.lastModified }));
|
||||
added++;
|
||||
}
|
||||
} catch (err) {
|
||||
addFileWithDedup(new File([f], rel, { lastModified: f.lastModified })); // zip как есть
|
||||
added++;
|
||||
}
|
||||
} else if (DOC_EXTS.some(e => low.endsWith(e))) {
|
||||
addFileWithDedup(new File([f], rel, { lastModified: f.lastModified }));
|
||||
added++;
|
||||
}
|
||||
// иначе — не документ, пропускаем
|
||||
}
|
||||
} finally {
|
||||
document.body.style.cursor = '';
|
||||
}
|
||||
folderInput.value = ''; // чтобы повторный выбор той же папки сработал
|
||||
rr();
|
||||
if (added) { st.className = 'status done'; st.textContent = '✅ Добавлено из папки: ' + added + ' файлов'; }
|
||||
});
|
||||
|
||||
function ss(idx, h) { const e = document.getElementById('st-' + idx); if (e) e.innerHTML = h; }
|
||||
|
||||
async function uploadFiles() {
|
||||
|
||||
Reference in New Issue
Block a user