Files
utun2/doc/message_links.md
T

132 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Ссылки в сообщениях
Android и Qt распознают `http://`, `https://` и `utun://` в обычном тексте
личных и групповых сообщений. HTTP/HTTPS нечувствительны к регистру схемы;
`utun://` распознаётся только в нижнем регистре. Утилиты разбора не открывают ссылки.
Ссылки выделяются цветом и подчёркиванием; длинные веб-адреса переносятся по ширине.
Сокращённая строка структурированного PM-приглашения сохраняет полный `utun://` адрес.
Пробелы, управляющие символы, кавычки и угловые скобки отделяют ссылку от текста.
Конечные `. , ; : ! ?` и лишние закрывающие скобки удаляются из распознанного адреса.
Сбалансированные скобки в адресе, параметры, fragment и Unicode сохраняются.
Адреса без схемы не распознаются. Некорректные веб-адреса остаются обычным текстом.
Исходное сообщение в БД и протоколе не меняется.
## Форматирование сообщений в desktop
Qt-клиент поддерживает `**жирный**`, `__курсив__`, `~~зачёркнутый~~` и блоки кода.
Парные маркеры работают внутри одной строки, без вложенности; между маркером и текстом
не должно быть пробела. Маркеры внутри слова не открывают форматирование.
Незакрытые маркеры остаются буквально. Обратная косая черта экранирует `*`, `_`, `~`,
обратную кавычку и саму себя. Внутри URL escapes и открывающие маркеры не обрабатываются.
Открывающая строка кода содержит ровно три обратные кавычки и необязательное имя языка,
например `cpp`, `c`, `python`, `py`, `js`, `json`, `bash`, `sql`.
До/после кавычек и вокруг имени языка допустимы пробелы и табуляции: ` ``` cpp ` тоже работает.
Закрывающая строка содержит три обратные кавычки с необязательными пробелами вокруг. Начало/конец сообщения также
считаются границами строк; принимаются LF и CRLF. Несколько блоков в сообщении допустимы.
Пробелы внутри самого кода сохраняются. Кнопка `?` справа от Join открывает краткую подсказку по тегам и блокам кода.
````text
**Пример:**
```cpp
int value = 42;
```
````
Код рисуется моноширинным шрифтом на отдельном фоне; пробелы, табуляции и пустые строки
сохраняются. Длинные строки визуально переносятся без изменения копируемого текста.
Над каждым блоком находится кнопка Copy: копирует весь код без ограждений и метки языка,
включая пробелы, табуляции и пустые строки. Перетаскивание не активирует кнопку.
После копирования кнопка на 1,5 секунды подсвечивается и показывает «Скопировано»;
повторное нажатие продлевает подтверждение. Размер кнопки при этом не меняется.
В шапке слева показано имя указанного/распознанного языка (например, C++ или JavaScript), справа — Copy.
Если язык определить не удалось, показано `text`; неизвестная явная метка показывается как написана в ограждении.
Внутри кода нет форматирования маркерами, кликабельных ссылок и анимированных emoji.
При пустом языке небольшой модуль `tools/chatgui/src/code_language.cpp` определяет C, C++, Java,
Kotlin, Rust, PHP, JavaScript, CSS или HTML по содержимому. Явный язык имеет приоритет;
неизвестное имя и `text`/`plain` отключают автоопределение и оставляют код без раскраски.
При слабых или противоречивых признаках также остаётся обычный моноширинный текст.
Подсветки синтаксических ошибок нет.
Определитель анализирует только первые 16384 символа UTF-16, исключает комментарии и строковые
литералы и складывает веса характерных конструкций. Каждый признак учитывается один раз;
для выбора нужны оценка не ниже 6 и отрыв от второго кандидата не меньше 3.
Заголовки PHP/HTML и структурные HTML-теги имеют приоритет над вложенным кодом;
конструкции Rust отличают его `std::` от C++. Новых зависимостей нет.
Результат подсветки попадает в общий кеш сообщения. Лог `general=debug` показывает выбранный язык,
оценку, конкурирующую оценку и размер выборки, без самого текста.
Проверка на скачанных исходниках описана в [code_language_detection.md](code_language_detection.md).
Исходник остаётся в `MsgTextRole`, БД и протоколе; редактирование получает маркеры как были.
Выделение и Ctrl+C используют видимый текст без маркеров, в том числе при выборе нескольких
сообщений. Структурированные PM-приглашения сохраняют прежнее сокращение ссылки с копированием
полного адреса; разметка их автоматически сформированного заголовка не применяется.
Медиа и текст опросов не интерпретируются как размеченные сообщения. Android пока показывает исходные маркеры.
`src/message_text.cpp` в Qt-каталоге готовит видимый текст, стили и ссылки вне кода;
KSyntaxHighlighting возвращает цвета языковых токенов для светлой и тёмной темы.
Цвета не меняют метрики шрифта. Результат кешируется в GUI-потоке с бюджетом 8 МиБ;
изменение текста автоматически выбирает новый ключ. В форматированных сообщениях размеры,
рисование, координаты ссылок, emoji и выделение используют одинаковый `QTextLayout`.
Обычные сообщения сохраняют прежнюю отрисовку. Диагностика `general=debug` содержит длины,
число стилей/блоков/токенов и границы выделения, без содержимого сообщений.
Зависимость сборки: `KF6SyntaxHighlighting` для Qt 6, `KF5SyntaxHighlighting` для Qt 5.
Ubuntu/Debian: `sudo apt-get install libkf6syntaxhighlighting-dev` (для Qt 5 — пакет `libkf5syntaxhighlighting-dev`).
Ubuntu dev-пакет также требует инструменты Qt QML; само приложение связывается только с библиотекой подсветки,
которая использует уже подключённые Qt Core/Gui/Network.
В MSYS2 UCRT64 `build.sh` вызывает `install_deps_win.sh`: отсутствующие пакеты компилятора,
CMake/Ninja, Qt 6, OpenSSL, zlib и `mingw-w64-ucrt-x86_64-syntax-highlighting` устанавливаются
одним вызовом `pacman -S --needed`. Готовое окружение не требует сети.
DLL подсветки и её лицензии попадают в установщик через существующий обход PE-зависимостей.
Проверки: `test_message_text` — разбор, реальные определения C++, многострочное состояние,
неизвестный язык, кеш и неизменные метрики при смене темы; `test_invite_links` — рисование,
drag/copy в обе стороны, ссылки вне кода и сохранность исходника; `test_windows_installer` —
установка только отсутствующих пакетов, отказ при ошибке установки и комплект DLL без QML.
## Распознавание и открытие ссылок
У `utun://` распознаётся кандидат из обычного base64-алфавита с до двух конечных `=`.
На этом этапе не проверяются версия, поля и содержимое приглашения: `utun://abc+/==`
может получить подсветку, но быть отклонённым при открытии. Предпросмотр Qt и Android
использует общий `src/chat/invite_link.c` через Qt-обёртку/JNI; принимается версия 0x03.
Сам декодер не проверяет регистрацию/срок join_key и не даёт членства.
Разборщики возвращают непересекающиеся диапазоны исходного текста в порядке появления
и полные адреса в исходном написании: Qt — `start/length`, Android — `start/end`,
где end исключён. Единицы обоих API — UTF-16:
`tools/chatgui/src/message_links.cpp`, Android `data/MessageLinks.kt`.
Удаление внешней пунктуации занимает линейное время: баланс трёх видов скобок
считается один раз, хвост сокращается по индексу без повторного копирования адреса.
Порт веб-адреса пустой либо десятичный от 0 до 65535; знак перед числом недопустим.
Подсветка и нажатие используют эти диапазоны. Android подставляет emoji placeholders
только в обычный текст между ссылками: emoji внутри адреса остаётся частью полного URL.
Qt использует один QTextLayout для размеров, отрисовки, выделения и координат нажатия.
Перетаскивание выделяет текст без открытия ссылки.
HTTP/HTTPS открываются из GUI системным обработчиком: Android `ACTION_VIEW`,
Qt `QDesktopServices::openUrl`. На нажатии веб-адрес снова проверяется.
В Qt `utun://` сначала декодируется: уже существующая группа, где узел является
участником, открывается без JOIN; иначе показывается JoinDialog с автоматической попыткой входа.
На Android адрес передаётся в существующий invite/JOIN flow.
Ошибки открытия показываются через перевод интерфейса и логируются.
Диагностика открытия содержит схему и host, без полного пути/query.
Общие случаи: `tools/chatgui/tests/message_links_cases.tsv`.
Qt `test_invite_links` проверяет разбор, перенесённые веб-ссылки, смесь схем,
нажатие, drag/copy и сокращённые приглашения; `test_emoji_playback` — жесты приглашений.
Android `MessageLinksTest` проверяет тот же TSV через Gradle, некорректные authority
и длинный хвост пунктуации. Аналогичный тест длинного хвоста есть в Qt.
Оба разбора не открывают ссылки; некорректный пользовательский текст не создаёт логов.
```bash
cd tools/chatgui-android
./gradlew :app:testDebugUnitTest --tests com.utun.chat.data.MessageLinksTest
```
`tests/test_message_links.kt` остаётся отдельным CLI-тестом того же TSV;
ему передаётся путь к файлу, а в classpath нужны собранные классы `MessageLinks`.