mirror of
https://github.com/Ku6epXBOCTuK/easy-png-tools.git
synced 2026-11-13 17:08:20 +00:00
271 lines
26 KiB
Markdown
271 lines
26 KiB
Markdown
# Backlog
|
||
|
||
> Статус: **active**. Открытые задачи. Подробные планы, разбор решений и сырые
|
||
> находки — `docs/plan-seo.md`, `docs/plan-platform.md`,
|
||
> `docs/plan-fields-audit-fixes.md`, `docs/tools-audit.md`, `docs/decisions.md`.
|
||
>
|
||
> Все пункты — чекбоксы; нумерация «Идея N» в тексте сквозная по документу и
|
||
> сохраняется ради перекрёстных ссылок («идея 4 зависит от идеи 3»). Оценки
|
||
> (S/M/L) — оценка труда, не приоритет.
|
||
|
||
## Pipeline
|
||
|
||
- [ ] названия инструментов — в pipeline инструмент называется «обрезать png»,
|
||
но он работает с любыми картинками, наверное надо убрать; в заголовке
|
||
страницы — возможно, надо указать все форматы (связано с SEO)
|
||
- [ ] кнопка «добавить инструмент» в pipeline — обдумать селектор с нормальным
|
||
поиском, фильтром возможных инструментов и т.д.
|
||
- [ ] посмотреть реализацию flip animation svelte для анимации переключения
|
||
режима aligned\normal
|
||
- [ ] переделать визуал toggle компонента (которым режим переключается)
|
||
- [ ] **Идея 1. Заменить инструмент шага без удаления** — оценка M. В заголовке
|
||
карточки шага кнопка «поменять инструмент»: заменяет инструмент на месте
|
||
(любой шаг, не только первый), остальные шаги и их параметры сохраняются,
|
||
вход перечитывается. Открытый вопрос: что делать с несовместимыми входами
|
||
(text-source ↔ file).
|
||
- [ ] **Идея 3. Несколько цепочек: сохранение и загрузка** — оценка M.
|
||
Именованные цепочки в localStorage (список, создать/переименовать/
|
||
удалить), быстрое переключение. Формат экспортного JSON расширить
|
||
опциональным полем имени; обратной совместимости со старыми выгрузками не
|
||
делаем — формат не устоялся: он появится вместе с фазой 11
|
||
`docs/roadmap.md` и до её старта может измениться. Плюс отдельная страница
|
||
со списком всех цепочек: удалить, переименовать, добавить новую,
|
||
«использовать» (переход на страницу инструмента с этой цепочкой). На
|
||
странице — упрощённый вид: только список инструментов цепочки, без
|
||
реального выполнения и параметров.
|
||
- [ ] **Идея 4. Batch-обработка архивов выбранным chain** — оценка L. Вход —
|
||
zip-архив картинок (распаковка в браузере), прогон текущего или выбранного
|
||
сохранённого chain по каждому файлу, сборка результата обратно в zip для
|
||
скачивания. Зависит от идеи 3 (выбор цепочки) и от экспортного формата
|
||
pipeline JSON; прогресс и ошибки — пофайлово.
|
||
- [ ] **Идея 15. Набор файлов в пайплайне: 1 → many не должен быть
|
||
терминальным** — оценка L. **Сейчас** `execute` принимает один
|
||
`source: PixelImage | undefined` и возвращает один `ToolResult`, а
|
||
`SchemaToolView` раскладывает вывод в `result` / `fileResult` /
|
||
`textResult` по `resultKind` — поэтому шаг с `result: "files"` упирается в
|
||
конец цепочки. **Направление:** следующий инструмент применяется **ко всем
|
||
картинкам** предыдущего результата (fan-out), а many → 1 (вход = архив или
|
||
несколько файлов) — обратная сторона того же контракта. Терминальными
|
||
остаются только результаты, которые не картинка (text, verdict). Что
|
||
обдумать: - где проходит граница: один вызов `run(ctx)` на файл (имя части
|
||
склеивается в `FileResult.files`) или батч целиком на стороне исполнителя;
|
||
`executor/protocol.ts` `files` уже сериализуются, но при батче нужен
|
||
прогресс и отмена; - частичные ошибки: один битый файл не должен ронять
|
||
весь шаг — нужен per-file отчёт, а не один `ToolError` на шаг; - имена
|
||
частей на каждом шаге и Download для набора (сейчас zip по `page.slug`,
|
||
`SchemaToolView.download()`); - обратная сторона: распаковка zip в
|
||
браузере, порядок файлов, поведение many → 1 → many в одной цепочке.
|
||
Зависит от идеи 10 (остальные потребители 1 → many) и идеи 4 (batch по
|
||
архиву), `docs/roadmap.md` фаза 8 «batch-страница».
|
||
|
||
## Инструменты
|
||
|
||
- [ ] remove-alpha-channel - дубль инструмента с выбором цвета на замену, что с
|
||
такими делать вообще? оставить как seo? можно ли под капотом использовать
|
||
другой инструмент с хардкодед настройками?
|
||
- [ ] qr code to text, text to qr code
|
||
- [ ] bar code to text, text to bar code
|
||
- [ ] нужен инструмент для удаления фона по выбранной точке - удалять внутри
|
||
выделенной области
|
||
- [ ] сделать конвертацию в смайлики\награды для твитча (6 размеров или выбор
|
||
какой размер - 112\56\28px или 72\36\18)
|
||
- [ ] сделать несколько проходное сжатие png для получения необходимого размера
|
||
файлов, посмотреть реализацию в
|
||
[twitch-emote-resizer](https://github.com/tma02/twitch-emote-resizer)
|
||
- [ ] Font Style - обдумать выбор шрифта (google fonts???) как организовать
|
||
шрифты с кириллицей? сделать галочку "только с кириллицей"??? (решение о
|
||
наборе и лицензиях: `docs/plan-platform.md` §4 — только OFL/Apache с
|
||
паспортом, семейства с кириллицей)
|
||
- [ ] Свести convert-страницы (`convert-png-to-jpg`, `convert-png-to-webp`,
|
||
`png-to-bmp`) на один инструмент `convert` — зависит от S1f
|
||
(`docs/plan-seo.md` §4). Мешает не только `quality` (он живёт в схеме
|
||
инструмента), а сама модель: `output` с mime/ext — свойство `Tool`, а
|
||
`PageStep` умеет только `{ id, params? }`, поэтому страницы на общий `id`
|
||
получат одинаковый формат вывода. Нужно научить шаг переопределять формат
|
||
вывода
|
||
- [ ] **Идея 6. Конвертация формата — НЕ отдельный инструмент, а выбор в кнопке
|
||
Download** — оценка M/L. Решение и продуктовое обоснование:
|
||
`docs/plan-seo.md` §0 и задача **S1f**; почему селектор не заменяет
|
||
существующие convert-страницы — `docs/plan-seo.md` §5. Механическая часть
|
||
уже есть: `Tool.output` (`OutputFormat`: mime/ext/qualityParamId) и
|
||
кодирование по нему в `SchemaToolView.download()` —
|
||
`docs/architecture.md`, раздел 3. Что осталось сверх S1f: - экспорт с
|
||
лимитом размера → «сжатие до целевого КБ»: для lossy это quality в
|
||
селекторе формата, для PNG — бинарный поиск по k из квантования. Нужна ли
|
||
отдельная страница под этот интент — открытый вопрос с вордстатом, имя не
|
||
выбрано (`docs/plan-seo.md` §5). Проработать вместе с достройкой
|
||
download-флоу.
|
||
- [ ] **Идея 7. Страница `watermark-image-png` (водяной знак картинкой)** —
|
||
оценка M. Нужна overlay-механика: второй источник изображения
|
||
(getOverlay/store), параметры scale/opacity/position/margin/aspect.
|
||
Решается вместе с вопросом, как второй источник картинки вписывается в
|
||
schema-driven preview (`SchemaToolView`).
|
||
- [ ] **Идея 8. `png-info` (детальная информация о PNG)** — оценка M/L.
|
||
Отдельный райз: exif-теги, редактирование, структурированный вывод; нужен
|
||
свой отдельный случай в UI, не «ещё один text/verdict». Возможно нужна
|
||
вообще отдельная страница, несовместимая с другими инструментами: 1) она
|
||
работает не с изображением, а с метаданными из файла, 2) она не может быть
|
||
частью цепочки, т.к. результат не является изображением, 3) если надо
|
||
изменить теги, никакого форматирования и изменения в изображение вносить
|
||
не надо, только в метаинформацию.
|
||
- [ ] **Идея 9. Region-инструменты** (censor/erase/pixelate-area/blur-area/
|
||
sharpen-area/reverse-area) — ждут UI выделения области на превью. Вне
|
||
очереди.
|
||
- [ ] **Идея 10. Мультифайловые инструменты-потребители** (gif-to-frames,
|
||
separate-colors) — механизм «результат = набор файлов» (1 → many) уже есть
|
||
(`docs/architecture.md`, раздел 7), первый потребитель —
|
||
`split-into-parts-png`. Остальные подключаются по мере нужды.
|
||
- [ ] **Идея 11. Анимационные** (slow-reveal/fade/scrolling) — выход не PNG;
|
||
отдельное решение о формате.
|
||
- [ ] **Идея 12. HARD-серия** (glitch, barcode, signature-extract,
|
||
handwritten-digital).
|
||
- [ ] **Идея 14. Отображение маски для инструментов (`runMask`)** — придумать и
|
||
реализовать процесс показа маски результата. **Контекст:** удалённый UI-v1
|
||
умел показывать ч/б превью-маску «что будет вырезано» отдельным полем; при
|
||
переходе на единый `run(ctx)` этого поля не осталось — executor и
|
||
schema-превью зовут `run()` напрямую, маска не возвращается. **Идея 1:**
|
||
дать каждому `run` возможность вернуть (или инструмент — объявить)
|
||
`runMask` — ч/б изображение маски изменений либо иной подход (отдельное
|
||
поле `mask` + функция маски, второй результат, overlay). Оценка — на
|
||
усмотрение при проработке (S/M). Пока живём без неё. **Идея 2:** каждый
|
||
`run` возвращает два изображения — для отображения (показывает маску или
|
||
сам результат) и для передачи в следующий инструмент (всегда результат).
|
||
Оценка — на усмотрение при проработке (S/M). Пока живём без неё. Первые
|
||
кандидаты на маску: `remove-color-from-png`, `remove-background-png`.
|
||
|
||
## UI и качество
|
||
|
||
- [ ] split to parts - посмотреть вариант с toggle component - может будет
|
||
удобнее чем sectioned buttons
|
||
- [ ] неудобно настраивать ползунками, некоторые инструменты удобнее настроить
|
||
через drag-n-drop. звучит сложно, но надо придумать процесс
|
||
- [ ] fav: текущий ui слишком дерганный — при клике на звёздочку элементы
|
||
мгновенно пересортировываются и под мышкой оказывается другой элемент.
|
||
Нужна или анимация, или механизм отложенного обновления позиций
|
||
- [ ] просмотр картинки в оригинальном размере, и возможно с зумом отдельных
|
||
областей, чтобы просмотреть мелкие артефакты, выбор разных подложек -
|
||
черная\белая\цветные\серая для проверки прозрачных картинок
|
||
- [ ] обдумать объединение пар `inputMode + source` и `resultKind + result` в
|
||
единую структуру, позволит упростить некоторые вещи - например
|
||
`sourceValue` \ `resultValue` в `SchemaPreview.svelte`
|
||
- [ ] Рассмотреть необходимость всех этих пропсов в `SchemaPreview.svelte` они
|
||
дублируют друг друга `ontextsource`, `onrendertext`, `oncopytext`,
|
||
`ondownloadtxt`, `ondownload`,
|
||
- [ ] resize keep aspect - работает, но немного не очевидное поведение при
|
||
прямоугольной картинке и выборе размера через быстрые кнопки (например
|
||
256х256). Воспроизведение поведения: keep aspect включено, загружаем
|
||
прямоугольную картинку 832 × 1232, картинка рескейлится до 173 × 256,
|
||
выключаем keep aspect, картинка рескейлится до 256 × 256 (с искажением
|
||
пропорций), включаем keep aspect - картинка рескейлится до размера 256 ×
|
||
379
|
||
- [ ] Переделать внешний вид инструментов без настроек, например Remove alpha
|
||
channel PNG "remove-alpha-channel-png". Сейчас выглядит слишком пустым.
|
||
- [ ] Пересмотреть поля - Plate, Position, Font Style - сейчас второй label
|
||
- [ ] поле ввода текста - сделать по умолчанию нормального размера, а не одну
|
||
строку для svg - форматирование или подсветку синтаксиса??? для bin\hex -
|
||
форматирование по колонкам??? для hex\bytes\rgba - пример в placeholder
|
||
- [ ] прикрутить кастомный color picker, добавить прозрачность (range?)
|
||
- [ ] обновить страницу kit или сделать storybook
|
||
- [ ] `PreviewTile`: высота плитки задана только `min-height: clamp(...)`,
|
||
поэтому она не следует за пропорциями результата — широкий результат
|
||
уезжает в узкую полосу, а высокий упирается в `max-height: 100vh`. Нужно,
|
||
чтобы соотношение сторон выбиралось по результату в диапазоне 16:9…9:16
|
||
(`web/src/lib/components/schema/PreviewTile.svelte`).
|
||
- [ ] Радиусы бордеров: `--radius-m` ненулевой и применён ко всем бордерам
|
||
(`app.css`, `SchemaToolView.svelte`). Решить, где скругления реально есть
|
||
в дизайне, а где должен быть 0
|
||
- [ ] Component-тесты `SchemaToolView`/`SchemaPreview` требуют отдельного
|
||
решения: node Vitest не имеет DOM/canvas, а e2e уже покрывает поведение.
|
||
- [ ] **Идея 13. Контраст текста на акцентных кнопках — частично закрыт**. Тона
|
||
`--color-main`/`--color-accent` подняты под текущий порог
|
||
`palette.test.ts`; targeted-тест проходит. Light-тема всё ещё ниже AA 4.5
|
||
для обычного текста, а `--color-danger` в light-теме на границе: после
|
||
завершения редизайна вернуть порог 4.5 и перепроверить оба токена.
|
||
- [ ] **Идея 16. Главная должна отличаться от каталога** — оценка S. Сейчас
|
||
`src/routes/+page.svelte` и `src/routes/list-tools/+page.svelte` побайтово
|
||
идентичны (совпадают script, шаблон и стили), при этом `/list-tools` —
|
||
рабочий маршрут: на него ссылается крошка в `+layout.svelte` и он открыт
|
||
e2e-сценариями `catalog.spec.ts`, `i18n.spec.ts`, `navigation.spec.ts`.
|
||
Замысел: главная — крупнее и с более простым поиском, без категорий;
|
||
каталог остаётся группировкой с фильтрами. Детектор дублирования разметки
|
||
сознательно не вводим — `docs/decisions.md`, раздел 1, поэтому расхождение
|
||
ловится на ревью.
|
||
|
||
## Тесты и lint
|
||
|
||
- [ ] `tseslint.config(...)` deprecated: сигнатура
|
||
`(...configs: InfiniteDepthConfigWithExtends[]): ConfigArray` помечена
|
||
устаревшей, нужен переход на `defineConfig` из `eslint/config` (ESLint
|
||
core, а не `typescript-eslint`) — `web/eslint.config.js`.
|
||
- [ ] удалить мёртвые `tools.*.params` из root i18n после проверки потребителей;
|
||
до этого действует pattern-based policy-исключение
|
||
(`web/eslint-plugins/README.md`, раздел `dict-consistency`)
|
||
- [ ] e2e: `i18n.spec.ts` и `text-and-verdicts.spec.ts` дублируют локаторы из
|
||
`helpers/page.ts` вместо вызова — правило «спека зовёт helpers»
|
||
(`docs/testing-strategy.md`, раздел 5)
|
||
- [ ] Пересмотреть тесты — убрать точные совпадения текстов и любые селекторы,
|
||
привязанные к разметке или svelte-классам (сейчас остались:
|
||
`tool-flows.spec.ts`, `schema-tool-strings.test.ts`)
|
||
- [ ] можно ли в тестах получать i18n ключ вместо текста? можно ли написать lint
|
||
правило такое (запретить toContain без i18n)
|
||
- [ ] чистка под новые конвенции комментариев и тестов: правила
|
||
`conventions/ascii-only` (тесты — ASCII), `conventions/comments-english`
|
||
(комментарии — английский), `conventions/comment-format` (≤5 строк на
|
||
блок, ≤15% файла) уже работают в warn-режиме, но в `lint:all` отключены
|
||
через override в `web/scripts/lint-all.mjs` (baseline ≠ 0). Критерий
|
||
закрытия: ноль срабатываний по всем трём правилам, overrides удалены,
|
||
правила переведены с warn на error
|
||
- [ ] убрать в тестах все нестандартные символы `→`, длинные тире, русский язык
|
||
и т.д. — часть чистки под `conventions/ascii-only` (см. выше)
|
||
- [ ] комментарии на английском — часть чистки под
|
||
`conventions/comments-english` (см. выше)
|
||
|
||
## SEO/GEO
|
||
|
||
- [ ] переименование бренда + домен: чек-лист в локальном файле
|
||
`plan-domain.local.md` (в git не входит, в клоне отсутствует; подробности
|
||
и порядок — `docs/plan-seo.md` §6, R1/R2)
|
||
- [ ] снять `noindex` со страниц инструментов, когда будут sitemap, canonical и
|
||
контент (S1h; блокирует индексацию всего сайта) — `docs/plan-seo.md` §4
|
||
- [ ] форматные страницы по конкурентным слагам (`crop-jpeg`, …) — только по
|
||
вордстату с уникальным контентом; редиректы только для опечаток и
|
||
переименований уже опубликованных страниц; клонов с canonical не делать
|
||
(`docs/plan-seo.md` §5)
|
||
- [ ] sitemap.xml из реестра + `Sitemap:` в robots.txt (S1b/S1c)
|
||
- [ ] Seo.svelte: per-tool OG-картинки (S1e) — у каждой страницы инструмента
|
||
своя превью-карточка 1200×630 для соцсетей/мессенджеров: имя инструмента,
|
||
бренд, иконка. Сейчас все страницы ссылаются на общую
|
||
`static/og/default.png` (генерирует `pnpm --dir web gen:og` —
|
||
Playwright-скриншот HTML-карточки в `web/scripts/gen-og.mjs`). Что делать:
|
||
расширить gen-og.mjs — проход по реестру страниц, карточка с названием
|
||
инструмента в `static/og/[slug].png`, в `Seo.svelte` подставлять og-URL по
|
||
slug страницы. Критерий приёмки: у `/tools/flip-png` в og:image своя
|
||
картинка с названием инструмента, файл лежит в `static/og/`
|
||
- [ ] контент страницы инструмента: seo-блок в словарях, how-to/FAQ/related (S2)
|
||
- [ ] JSON-LD (SoftwareApplication/FAQPage/Breadcrumb) + llms.txt (S3)
|
||
- [ ] дистрибуция: закрепить репо, alternative.to, Product Hunt, Reddit/HN (S4c)
|
||
- [ ] GSC: подключить домен, отправить sitemap, мониторинг индексации (S4a)
|
||
- [ ] GA4: cookieless-конфигурация, отключить рекламные audiences и signals,
|
||
`anonymize_ip` — сейчас в `web/src/routes/+layout.svelte` голый
|
||
`gtag('config')` (S4b)
|
||
|
||
## Платформа (API/PWA)
|
||
|
||
- [ ] Vercel: переезд хостинга с GitHub Pages + глобальная статистика
|
||
популярности инструментов через Vercel KV (serverless-функция: INCR при
|
||
открытии инструмента, чтение топа для скоринга поиска).
|
||
- [ ] PWA: manifest + service worker, оффлайн-режим, установка (после S1)
|
||
- [ ] API: спайк серверного ядра (нативный Rust vs edge-wasm), ключи, free tier
|
||
(после wasm-фаз 5–7)
|
||
- [ ] Шрифты: лицензирование (F1–F4) и DOM-free рендер (F5–F6) — до фазы 8
|
||
(`docs/plan-platform.md` §4)
|
||
|
||
## Переезд
|
||
|
||
- [ ] множественные числа (часть/части/частей; «N параметров», счётчики) —
|
||
функция `tPlural(path, n)` и форма `PluralForms` (обязательное `other`,
|
||
опциональные `one`/`few`/`many`); первые потребители — `part`/`parts` в
|
||
подписи результата и счётчики параметров. **Критерий приёмки:** у ключа с
|
||
plurals есть все формы, для которых нужен перевод, `other` никогда не
|
||
пустой, число подставляется в текст без склейки
|