Под капотом SEO-анализатора: история разработки

Любой полезный инструмент начинается с боли. Эта статья — честная история о том, как боль от рутинной SEO-проверки текстов превратилась в полноценное Flutter-приложение. Мы пройдем весь путь: от наивного прототипа и фидбэка коллег до борьбы с зависанием UI и неожиданных открытий о работе буфера обмена.

Глава 1: Прототип v0.1 — Боль, страдания и Ctrl+F

Все началось с простой задачи: проверить, соответствует ли текст копирайтера техническому заданию (ТЗ). Каждый раз это был один и тот же ритуал: 1. Скопировать текст в один сервис для подсчета символов. 2. Вручную (Ctrl+F) искать каждое ключевое слово, считая вхождения. 3. Пытаться понять, все ли темы из структуры ТЗ раскрыты в подзаголовках. Это было долго, нудно и чревато ошибками. Так родилась идея: создать инструмент, который бы делал все это автоматически.

Первый прототип SEO-анализатора

Глава 2: Первый фидбэк и первые проблемы

Первая версия была собрана «на коленке»: два поля TextField, кнопка «Проверить» и простейшая логика text.contains(keyword). Проблемы обнаружились сразу, как только я показал инструмент коллегам: • «Он не находит ключи, которые точно есть!» — String.contains() не понимал падежей. «Купить машину» и «купить машина» для него были разными фразами. • «В ТЗ куча всего, а ты проверяешь только ключи» — парсер ТЗ был слишком примитивен и не умел извлекать структуру, LSI-слова, мета-теги. • «В объем текста попадает всё подряд» — алгоритм не мог правильно «отрезать» конец секции и забирал в «Объем» половину ТЗ. Стало ясно, что наивный подход провалился. Нужна была настоящая «математика».

Глава 3: Эволюция — от строк к семантике

Алгоритмы вместо Contains(). Простой поиск был заменен на более надежный, вдохновленный классическими алгоритмами вроде Бойера-Мура... Главным прорывом стала борьба с морфологией. На помощь пришел алгоритм стемминга. Теперь перед анализом и текст, и ключи проходили «очистку», превращаясь в набор основ слов.

Схема работы стемминга

Парсер ТЗ 2.0. Парсер был полностью переписан. Теперь алгоритм искал стартовый маркер, начинал собирать строки и останавливался, как только встречал следующий известный маркер или несколько пустых строк подряд.

Схема парсера ТЗ

Глава 4: Фидбэк Лены и рождение v1.0

После всех улучшений инструмент стал гораздо умнее. Настало время для серьезного тест-драйва. Я отдал его на проверку коллеге Лене Мельниковой (@oh_laalaa). Ее фидбэк стал катализатором для превращения прототипа в настоящий рабочий инструмент.

Проблема №1: «Текст — это не просто буквы!» Решение: Интеграция flutter_quill. Этот Rich Text редактор, хранящий документ в формате Delta, стал сердцем новой версии.

Проблема №2: «А где проверка орфографии?» Решение: Интеграция с API Яндекс.Спеллера.

Проблема №3: «Считает не так, как Миратекст!» Решение: Глубокое погружение в математику. Алгоритм был допилен, чтобы учитывать «занятые» диапазоны.

Глава 5: Главный враг — зависание интерфейса (UI Freeze)

При нажатии на кнопку «Запустить анализ» приложение замирало на 5-10 секунд. Причина — блокировка UI-потока.

Блокировка UI потока

Решение: Изоляты и compute(). Вся логика анализа была вынесена в отдельную глобальную функцию и запущена через compute(), что освободило основной поток и сделало анимацию плавной.

Использование изолятов для разгрузки UI потока

Но даже после этого лоадер «дергался». Финальным штрихом стало добавление Future.delayed перед запуском тяжелых операций.

Глава 6: Битва за UX и фиаско с RTF

Последней нерешенной задачей оставалась вставка из Google Docs. Отладка показала, что Google Docs не кладет в буфер обмена ничего, кроме простого текста!

Элегантный поворот: функция «Распознать структуру». Так родилась идея кнопки «🪄 Распознать структуру», которая запускает эвристический алгоритм и применяет форматирование по правилам: короткая строка без точки — заголовок, строка с • или * — пункт списка.

Результат работы функции Распознать структуру

Заключение: итерации, компромиссы и фокус на пользователе

Путь создания этого инструмента — классический пример итеративной разработки. От неработающего прототипа и критики коллег, через решение фундаментальных проблем производительности, до столкновения с ограничениями внешнего мира и поиска элегантных обходных путей. Ключевой вывод: идеального решения не существует. Но можно, шаг за шагом, приближаться к нему, постоянно держа в фокусе главную цель — решить «боль» пользователя максимально простым и эффективным способом.