# 2026-09-22 20:40 — HEIC, конструктор «Картинки → PDF», белые фоны UI **Сделано:** - **HEIC в /compress.** Раньше `.HEIC` проходил фильтр, но падал «Ошибка при сжатии»: prebuilt libvips в sharp декодирует только AV1-HEIF (AVIF), а не HEVC. Добавлен `heic-decode` (libheif на WASM, без нативных зависимостей), общий хелпер `lib/image.js`. В режиме «Оригинал» HEIC/BMP → JPEG с расширением `.jpg`; формат источника берётся из `sharp.metadata()`, а не из MIME браузера. - **Конструктор «Картинки → PDF» в /pdf** (старая вкладка была нерабочей: фронт слал поле `files`, бэкенд ждал `images`, и попасть туда можно было только после загрузки PDF): форматы JPG/PNG/WebP/HEIC/AVIF/TIFF/GIF/BMP; drag&drop-порядок, стрелки, сортировка (имя с natural-order/дата/размер), реверс, поворот по одной и всех, повтор страниц, удаление; размер листа (по картинке/A4/A3/A5/Letter), ориентация, поля, качество, имя файла; прогресс загрузки через XHR. Вход — кнопкой на шаге загрузки или броском одних картинок в основную зону. Превью HEIC/TIFF — серверные миниатюры (`POST /pdf/thumb`). - **Попутные фиксы /pdf:** потерянная обёртка `
` — из-за лишнего `
` `stepConfig` закрывался рано, «Выполнить» и панели торчали на шаге загрузки; `pdfIcon` был недоступен из inline-`onerror` (вынесен в `window`); SVG больше не принимается. - **Белый фон (`--surface-800`)** у зон drag&drop, неактивных табов, кнопок-опций и карточек на страницах compress, pdf, video, converter, formatter, sanitizer, parser. Активные табы — светло-голубая заливка поверх белого (`linear-gradient(...), var(--surface-800)`). - **Всё на проде** (VPS 195.140.146.84, контейнер `wadevelop`), прод-файлы байт-в-байт = `HEAD`. `heic-decode@2.1.0` установлен в контейнере (`npm install` в `/app`, node_modules на томе). - **Git:** два коммита запушены в Gitea (rpi4): `dabc26d` — незакоммиченная работа сессии 2026-09-05 (owner-реестр, лимиты, crash-guard ffmpeg/gs, водяной знак); `c65b3f5` — эта сессия. **Изменённые / созданные файлы:** - [lib/image.js](../../lib/image.js) — **новый**: `isHeic()` (по ftyp-бренду + расширению), `openImage()` (HEIC через heic-decode → raw в sharp; непрозрачная альфа отбрасывается, иначе всё уходит в PNG) - [routes/compress.js](../../routes/compress.js) — `openImage()`, общий `encodeImage()` вместо двух копий if-цепочек, `sourceFormatOf(metadata)` - [routes/pdf.js](../../routes/pdf.js) — новый `/fromImages` (опции в JSON-поле `options`, страницы `[{f, r}]`, повторы встраиваются один раз, нетронутые JPEG/PNG — байт-в-байт), `/thumb` (свой лимитер 300/мин, стоит ДО общего `router.use(limiter)`), `saveResult(..., displayName)` + `owner.displayName` в скачивании, сообщение о лимите файлов, фильтр без SVG - [public/pdf.html](../../public/pdf.html) — блок `#stepImages` (CSS `.ip-*`, JS `ip*`), панель водяного знака, белые фоны - [public/compress.html](../../public/compress.html) — `accept` с `.heic`, превью без битой картинки, белые summary/список - [public/video.html](../../public/video.html), [converter](../../public/converter.html), [formatter](../../public/formatter.html), [sanitizer](../../public/sanitizer.html), [parser](../../public/parser.html) — белые фоны - [package.json](../../package.json) — `heic-decode` **Решения:** - HEIC декодируем через `heic-decode` (WASM), а не пересобираем sharp/libvips с libde265: не нужен свой libvips в образе, работает на `node:20-slim`. - Страницы PDF в режиме «По картинке»: 1 px = 1 pt (как было), длинная сторона ≤ 5080 pt. - Миниатюры HEIC/TIFF делает сервер: браузеры, кроме Safari, их не рисуют. - Табы `httpclient` (внутри белой панели), `regex` (уже белые), `md` (подчёркнутые) не трогали; редакторы (`svgeditor`, `editor`) — полноэкранные холсты, не трогали. **Открытые вопросы / следующие шаги:** - [ ] Тёмная тема после смены фонов на `--surface-800` визуально не проверена (эмуляция в headless не сработала) — глянуть руками. - [ ] В самом браузере под учёткой прод-страницы не открывались (они за авторизацией) — проверено сквозными запросами внутри контейнера и headless-тестом локальной копии. - [ ] Серверные бэкапы этой сессии можно удалить, когда всё устоится: `find /opt/sites/www/wadevelop.ru -name '*.bak-20260922-*'`. - [ ] Идея: смешанная сборка «PDF + картинки» в одном документе (сейчас конструктор — только картинки). **Как продолжить (для чистой сессии):** - Контекст: [CLAUDE.md](../../CLAUDE.md), скилл `/deploy-wadevelop`. Перед правкой сверяй md5 файла с VPS: `python3 C:/Users/tream/.claude/ssh_cmd.py root@195.140.146.84 "md5sum /opt/sites/www/wadevelop.ru/"`. - ⚠️ Грабли Windows: Python в текстовом режиме пишет CRLF — править файлы через `open(p,'rb'/'wb')` или Edit-инструментом, иначе диф раздувается и хэши с продом расходятся (в репо — LF). - ⚠️ Заливка через `ssh_cmd.run` + base64 в аргументе упирается в «Argument list too long» на больших файлах (pdf.html ~100 КБ) — лить чанками по ~60 КБ; `ssh_cmd.run` вызывает `sys.exit`, поэтому на несколько файлов — отдельный вызов на каждый (или ловить `SystemExit`). - Локальный стенд без MySQL: мини-express с фейковой сессией (`app.use((q,r,n)=>{q.session={user:{id:1}};n()})` + `app.use('/pdf', require('./routes/pdf.js'))` + `express.static('public')`), UI — headless Chrome через `puppeteer-core` (`executablePath: 'C:/Program Files/Google/Chrome/Application/chrome.exe'`). - Проверка на проде без логина: временный express-стенд внутри контейнера (`docker exec -w /app wadevelop node -e "..."` на порту 3998). **Ссылки:** https://wadevelop.ru/compress · https://wadevelop.ru/pdf · Gitea `http://192.168.31.200:3002/treamz/wa-dev-tools`