Repository navigation
TaleEngine v2.4.0 Beta
Четыре жалобы из одного разбора: на телефоне разваливалась строка действий под
репликой, а десяток вложений выталкивал «Отправить» за край экрана; закреплённые
чаты путались с обычными, а список чатов обновлялся только по F5; в длинных чатах
модель теряла начало истории. Последнее потребовало новой архитектуры памяти.
Исправлено — строка действий под репликой на телефоне
Пагинация свайпов, время, бейдж модели и до девяти кнопок переносились как
придётся: три-четыре неровные строки, кнопки наезжали друг на друга.
- Строка разделена на две. Сверху — метаданные одной строкой без переноса:
сжимается только имя модели (с многоточием), свайпы и время сохраняют размер. - Снизу — кнопки одной горизонтально прокручиваемой полосой (
nowrap,
overflow-x: auto,gap: 6px). На виду четыре главных — 📋 копировать,
✎ править, ↻ перегенерировать (только у последнего ответа), 🗑 удалить — и «⋯»;
ответ, переход к началу, продолжение, 🎨 и ответвление ⑂ убраны за «⋯». - Главные кнопки и «⋯» стоят первыми и не сдвигаются; уезжать за край может
только раскрытое вторичное. Когда полоса не помещается, у правого края
появляется затухание — видно, что её можно прокрутить. - Сокращённый режим включается на тач-устройствах и в окне уже 768px, причём по
живому медиазапросу: поворот экрана или сужение окна перестраивают строку без
перезагрузки.
Исправлено — вложения выталкивали поле ввода за экран
При 10+ картинках полоса вложений росла на всю высоту вьюпорта, и поле ввода с
кнопкой «Отправить» уезжали под системную панель навигации. Отправить сообщение
было физически невозможно.
- Полоса вложений ограничена 110px с прокруткой; на телефоне это одна
строка-карусель высотой до 75px. - Все плашки над полем ввода (режим ассистента, режиссёр, ошибка, повтор, ответ,
вложения, загрузки) собраны в контейнер.composer-barsс потолком 38% высоты
экрана (30% на телефоне, 22% на низком экране) и собственной прокруткой. - Композер прижат к низу (
position: sticky), поле ввода растёт не выше
min(200px, 30dvh), высота страницы считается вdvh. На Android клавиатура
теперь сжимает страницу, а не перекрывает «Отправить»
(interactive-widget=resizes-contentвviewport).
Исправлено — закреплённые чаты уходили вниз
Клиентская сортировка переставляла реактивный массив на месте, а «Недавние
диалоги» и палитра брали свои срезы в собственном порядке, где закреп не
учитывался.
- Одно правило на сервере и в клиенте (
_sort_by_activityиsortChats):
закреплённые всегда выше; внутри обеих групп — последняя активность, свежие
выше. Новая реплика поднимает чат лишь внутри его группы и никогда не
перепрыгивает закреп. - Изменено поведение: закреплённые между собой теперь тоже идут по
активности, а не по времени закрепа (как было с 1.19.0): закреплённый чат, в
котором только что писали, больше не стоит под закреплённым позже, но молчащим. - Активность (
activity_atв ответах/api/sessions,/api/groups,
/api/sessions/shared) — последняя реплика либо создание чата, что позже.
Прежде ключом был только id последней реплики, и новый пустой чат (у него
реплик нет) падал в самый низ — под брошенные полгода назад. Форк и импорт
тоже встают наверх. - Правило действует в сайдбаре, в палитре (при равной релевантности поиска) и в
«Недавних диалогах» — теперь это голова того же единого списка вместе с
группами и расшаренными чатами. Расшаренные идут по своей активности; закреп
их владельца на мой список не влияет. - Тесты закрепляют, что реплика в незакреплённом чате не поднимает его выше
закреплённого (и для групп тоже), что новый пустой чат встаёт над старой
перепиской и что при ничьей по секундам чат с репликой выше пустого.
Исправлено — список чатов обновлялся только по F5
Созданный, удалённый, переименованный или закреплённый чат появлялся в сайдбаре
только после ручной перезагрузки страницы.
- Любая мутация шлёт событие
taleengine:chatlist-updated
(notifyChatListChanged, дебаунс 120 мс), сайдбар перечитывает список без
перезагрузки страницы. Действия, результат которых нужен сразу (создание,
форк, удаление, переименование, закреп, группы, импорт), обновляют список
немедленно (syncChatList). - Чат поднимается в момент отправки: сигнал идёт по событию
job, которое сервер
шлёт сразу после сохранения реплики, а не только по окончании ответа. - Соседние вкладки синхронизируются через
BroadcastChannel("taleengine"). Чат,
удалённый в другой вкладке, закрывается здесь только после того, как сервер
подтвердил его отсутствие (403/404): сетевая ошибка чат не закрывает. - Реплики из Telegram сервер в веб не присылает, поэтому список догоняет их при
возврате во вкладку (не чаще раза в 10 с) и опросом раз в 60 с, пока вкладка
видна. - Без моргания и без потери фокуса: строки держат стабильные ключи, список не
очищается на время загрузки, неудачная загрузка оставляет старые строки
(прежде она стирала весь список), фокус в строке сайдбара восстанавливается. - Заметка, персона, фон и часовой пояс чата больше не откатываются при повторном
открытии: сохранённое значение сразу попадает в строку списка.
Добавлено — трёхслойная память Horae для длинных чатов
В длинном чате модель получала всю переписку, насколько хватало бюджета, и тонула
в ней (Context Dilution): факты и хронология из начала размывались сотнями
реплик, а не влезшее в бюджет отрезалось — вместе с обещанием, данным на сотом
сообщении. Теперь контекст собирается конвейером из трёх слоёв (новый модуль
backend/horae_recall.py, подробно — docs/HORAE.md).
- Активное окно. Последние 20 сообщений идут в модель дословно. Всё старше
выбрасывается, но только если уже учтено сводкой: пока сводка догоняет длинный
или импортированный чат, модель видит историю целиком. Граница окна двигается
шагом в 4 сообщения, чтобы не ломать кэш промпта провайдера на каждом ходу, —
дословно идут 20–23 сообщения. Размер окна — во вкладке «Память» (0 = вся
история). - Хроника. Фоновая сводка (раз в
summary_everyсообщений, по умолчанию 10)
ведёт инкрементальный конспект по разделам: состояние сюжета и цель, решения и
события, персонажи со статусами и инвентарём, локации, незакрытые линии. В
контекст он идёт блоком[ХРОНИКА И СОСТОЯНИЕ ЧАТА]у конца. Сводка берёт
только сообщения старше окна — свайпы, «Продолжить» и правки свежих ответов
попадают в память в окончательном виде — и каждое сообщение целиком (прежде
реплика обрезалась до 1500 символов). - Факты. В тот же проход модель выписывает атомарные факты — до 20 на
фрагмент, до 300 символов каждый, с именами вместо местоимений — в новую
таблицуhorae_facts. На каждом ходу по реплике пользователя (и хвосту
разговора) ищутся до 5 похожих: по смыслу (косинус ≥ 0,75), если задана модель
эмбеддингов, иначе по словам (основы слов, IDF по фактам чата, вес имён;
порог 0,2). Ранжирование учитывает свежесть: полураспад 300 сообщений с полом
0,5 — старые факты ослабляются, но не обнуляются. Найденное идёт блоком
[HORAE RECALLED MEMORY: …]; нет похожих — нет и блока, чтобы модель не
«дополняла» пустую память выдумкой. - Короткие реплики вроде «ок» или «иду дальше» факты не вызывают: слово, которое
есть в четверти фактов чата (и не меньше чем в трёх), само по себе совпадением
не считается. - Факты вспоминаются и в групповых чатах. Активного окна у групп пока нет —
история там по-прежнему режется только бюджетом. - Сбой памяти не роняет ход: без модели эмбеддингов и при её ошибке поиск идёт по
словам, а если память не собралась целиком — модель получает историю, как
раньше. - Сводки, собранные до 2.4.0, пересобираются. Прежний сводчик резал каждую
реплику до 1500 символов, а бэклог импортированного чата — до первых 24 000
символов, и всё равно считал учтённым всё до последнего сообщения. Такому
указателю окно не верит: у старой сводки оно не выбрасывает ничего, и сразу
после обновления модель видит историю как раньше. Новый сводчик в фоне
пересобирает память с нуля (и заодно выписывает факты из всей старой истории),
а старая сводка остаётся в контексте, пока новая её не догонит. Ручные правки
старой авто-сводки в новую не переносятся — если вы её правили, перенесите
свои заметки в отдельную запись памяти до обновления. - Сводка, начатая заново (удалили запись «Память чата (авто)»), не извлекает
факты повторно из уже разобранных сообщений. А если сообщения куска переписали
или удалили, пока модель его пересказывала, такой проход не записывается и
повторяется: иначе память считала бы учтённым текст, которого не видела. - Сообщение длиннее куска сводки (вставленный документ, гигантский ответ) идёт в
сводку началом и концом. Целиком оно превышало бы окно быстрой модели: запрос
падал, и каждый следующий ход повторял ту же платную попытку. - Новые настройки: в подключении —
summary_model(быстрая фоновая модель для
сводки и фактов; пусто = модель чата) иembedding_model(пусто = поиск по
словам); во вкладке «Память» — активное окно (memory_window) и выключатель
фактов (horae_facts). Расход эмбеддингов виден в статистике строкой
embedding. - Инспектор промпта показывает раздел «Память Horae: окно, хроника, факты»:
размер окна, сколько сообщений сжато в хронику и до какого она доведена, режим
поиска фактов и каждый отобранный факт с его сходством.
Изменено — лимиты файлов и окна памяти задаются своим числом
Сколько мегабайт прежних вложений уходит модели за ход, из скольких последних
сообщений и какое активное окно памяти — выбиралось только из трёх-четырёх
готовых вариантов. Кому 8 МБ мало, а 20 уже дорого, промежуточного значения
поставить не мог.
- Во вкладке «Генерация» «📎 Файлы в памяти диалога» (МБ на ход) и «…только из
последних сообщений», во вкладке «Память» — активное окно: теперь это поля
для любого целого числа от 0 до 1000 (0 — без ограничения / вся история).
Прежние варианты остались кнопками в один клик. - Значение принимается по окончании ввода и приводится к целому в допустимых
границах: пустое поле или «-5» не уходят на сервер. Сервер проверяет эти
лимиты строго, и одно такое значение ломало бы ошибкой каждый следующий ход.
Исправлено — удаление сообщений пересобирало память с нуля
Если указатель сводки «учтено до» оказывался новее последнего сообщения (свежие
реплики удалили), он сбрасывался в 0, и весь чат заново пересказывался поверх
старой сводки — платно и с повторами. Теперь указатель прижимается к последнему
оставшемуся сообщению, а факты из удалённых сообщений удаляются вместе с ними.
Обновление
- Изменился серверный код, поэтому после
git pullсервис нужно перезапустить.
В Docker код бэкенда и бота запечён в образ — нужна пересборка:
docker compose up -d --build(простойrestartоставит старый бэкенд со
свежим интерфейсом). Без Docker — перезапускstart.shили systemd-сервиса. - Новых зависимостей, переменных
.envи ручных миграций нет: таблица
horae_factsсоздаётся сама при первом запуске. - Первые ходы в длинных чатах запускают фоновую догонку памяти: пересборку
старой сводки и извлечение фактов — до 12 служебных запросов за ход, пока
бэклог не кончится. Перед обновлением задайте дешёвую быструю модель
(«Подключение» →summary_model), иначе догонка пойдёт на модели чата. - Когда сводка догонит чат, модель будет видеть дословно последние 20–23
сообщения плюс хронику и факты: вход длинного чата сокращается в разы. Если
хочется прежнего поведения — «Память» → «Активное окно» → 0.
Также в этом выпуске: 2.3.0
Разбор интерфейса на двух независимых оценках (дизайн и детектор с браузерными
доказательствами) нашёл 2 блокирующие проблемы, 3 крупные и десять расхождений
между кодом и записанной дизайн-системой. Исправлено всё.
Исправлено — первый клик нового пользователя не давал ничего
startNewChat() при нуле персонажей звал createCharacter(), который создавал
запись и возвращался: ничего не выбрано, чат не создан, экран байт в байт
прежний. Открытая регистрация объявлена аудиторией продукта — с этим встречался
каждый незнакомец.
- Клик доводится до места, ради которого делался: персонаж выбран, чат создан и
открыт, карточка показана с фокусом в «Описании». - У раздела «Персонажи» появилось настоящее пустое состояние. Прежде оно
рисовалось только при активном фильтре, то есть при нуле персонажей человек
видел две кнопки и пустоту.
Исправлено — список чатов разрушался на телефоне
Вынос действий строки из потока жил внутри @media (hover: hover), а на
тач-устройствах это правило не срабатывает никогда. Пять кнопок по 44px
занимали 234 из 315 пикселей сайдбара: на заголовок и превью оставалось около
58 — отличить один чат от другого было нельзя. Та же коллизия давала ≈92px
панели значков под каждой репликой.
- Действия сворачиваются за одну кнопку «⋯», раскрытые уходят на собственную
строку и не сжимают заголовок. - Под репликой на тач-устройствах остаются копирование, ответ и перегенерация,
остальное — за «⋯».
Добавлено — режимы просмотра стали видимыми и честными
- Сегментированный переключатель в шапке вместо трёх пунктов в безымянном «⋯»
из двенадцати строк; три команды в палитре (Ctrl+K). - «Работа с документом» перестала быть ложью: прежде режим включался, только
если документ уже существовал, — у кого его не было, подсвеченный пункт не
делал ничего. Теперь режим либо открывает документ (создавая пустой
черновик), либо называет причину. - «Тёмный зал» выполняет обещанное: шапка и сайдбар уходят на цвет холста,
название чата отступает на ступень эмфазы ниже, приборы убираются. Кроме
предупреждения «обрезано N» — это не прибор, а единственное сообщение о
потере памяти, и второго места у него нет.
Исправлено — ворота входа и регистрации
Поля назывались только через placeholder, который исчезает при наборе
(нарушение 3.3.2), не было <form>, не было autocomplete (нарушение 1.3.5),
а значит менеджер паролей не предлагал сохранить пароль — в продукте, где
забытый пароль уничтожает все истории без самостоятельного восстановления.
<form>, видимые подписи,autocompleteна всех полях, подтверждение
пароля, правило длины, переключатель показа.- Постоянная строка о том, что восстановления пароля нет.
- Вкладки получили
role="tab"иaria-selected; коробка ворот больше не
вырастает за пределы недостижимого центрирования — слой прокручивается.
Исправлено — доступность
role="tab"/aria-selectedна вкладках шторки и канваса,role="listbox"
иaria-activedescendantв палитре (прежде перебор стрелками был для
скринридера полностью беззвучным).altу всех изображений,<h1>и ориентиры разметки, подписи у полей,
которые не имели их вовсе.- Четыре действия на
div/spanбез роли и обработчика клавиш стали
достижимыми с клавиатуры. - Убраны два
outline: noneбез замены — фокус в палитре и редакторе канваса
был невидим.
Изменено — дизайн-система
- Автопереключение темы по
prefers-color-scheme; явныйdata-theme
перебивает системную настройку в обе стороны. До этого светлая тема была
объявлена целиком и недостижима: ни медиазапроса, ни переключателя. - Цвет тревоги возвращён тревоге:
--danger-fillкрасил четыре обычных
переключателя, теперь остаётся только записи с микрофона. - Тень появилась у модалок и палитры, исчезла у точки-индикатора, вторая
акцентная полоса у закреплённых строк заменена непозиционным признаком. - Зоны нажатия добраны псевдоэлементами там, где растить сам элемент нельзя.
- Реакция на «меньше движения» перестала быть тотальным отключением.
Изменено — документы описывают реальность
PRODUCT.md утверждал, что WCAG 2.1 AA «уже реализована»; аудит нашёл отказы
1.3.5, 3.3.2, 4.1.2 и 2.4.7. Стандарт записан как цель с честным перечнем
недостигнутого. В DESIGN.md уточнены обещание зала, словарь теней и правило
длительностей.
Также в этом выпуске: 2.2.0
Дизайн-система записана и сведена: значения, которые раньше стояли числами
по месту, переехали в семантический слой.
Добавлено — PRODUCT.md, DESIGN.md и приложение к нему
PRODUCT.md— что за продукт, для кого и что менять нельзя. Отдельно
зафиксированы открытые вопросы открытой регистрации:/api/auth/register
ничем не ограничен, квот по пользователям нет (usage_days— общий агрегат),
ограничения частоты нет, восстановления пароля нет.DESIGN.md— визуальная система с посчитанными значениями контраста и
тринадцатью именованными правилами..impeccable/design.json— тональные шкалы, тени, движение, слои и
готовые фрагменты компонентов.
Исправлено — теги и полоса весов не переключались темой
Цвета тегов области видимости и шести сегментов полосы контекста были заданы
литералами прямо в правилах и потому оставались тёмными в светлой теме:
#8fb3ff на белом фоне давал 2.10:1.
- Обе группы вынесены в токены со светлыми парами. В светлой теме теги дают
7.74 / 6.53 / 6.29, сегменты полосы — от 5.19 до 8.70 при пороге 3:1. - У теней шторки, сайдбара, тоста и лайтбокса не было светлых значений: в
светлой теме они рисовали бы чёрное пятно 45%. Теперь у каждой роли две пары.
Изменено — движение и слои стали шкалой
- Четырнадцать длительностей сведены к четырём ролям:
--dur-press(80ms),
--dur-1(120ms),--dur-2(180ms),--dur-3(260ms). Прежние 0.12 и 0.14
стояли на одной и той же работе. - Кривая перехода —
--easeвместоeaseбраузера, появление —--ease-out.
Оба токена были объявлены ещё в первой волне и ни разу не использовались. - Пятнадцать
z-indexвразнобой заменены шкалой--z-nav…--z-gate.
Числа сохранены как были — изменились только имена. - Три подложки наложения (0.4 / 0.5 / 0.55) сведены к одной
--scrim;
лайтбокс остался на--scrim-strong.
Удалено
--fs-600, --sp-6, --edge-highlight — ноль обращений. Первые два
расходились с записанной шкалой, третий остался от подхода к глубине,
от которого отказались в пользу границ.