feat(web): виды сообщений, автокомплиты композера и ссылки на комнаты

- лента оформляет виды сообщений от сервера: action (/me) — имя акцентом и
  курсив, whisper — приглушённый блок с бейджем, scream — крупнее и жирнее,
  private (/ls) — рамка, замок и бейдж «лично», system — центрированная строка
  без аватара и панели действий; реакции, ответы, правка и удаление работают
  как раньше, группировка не сливает нестандартные виды с обычными;
- автокомплиты в поле ввода с навигацией ↑/↓, Enter/Tab, Escape и мышью:
  @ — участники сервера (в DM собеседник), # — видимые текстовые комнаты,
  : — словарь на 113 эмодзи, / — команды с описанием; подсказки не появляются
  внутри слова и в блоках кода, вставка восстанавливает каретку и не расходится
  со слоем подсветки;
- `<#id>` рендерится как #имя комнаты из снапшота (иначе #id);
- 287 тестов Vitest (было 262), check/lint/test/build зелёные.
This commit is contained in:
2026-09-20 12:06:22 +03:00
parent 65d0bafe0e
commit a126f50468
22 changed files with 1826 additions and 116 deletions
+21 -1
View File
@@ -30,13 +30,33 @@ export interface MessageReaction {
user_ids?: string[];
}
/**
* Виды сообщений из контракта (AGENT.md §7.6): обычное, действие `/me`, шёпот,
* крик, личное `/ls` и системное. Сервер разбирает команды сам, клиент только
* оформляет пришедший `type`.
*/
export const messageTypes = [
'default',
'action',
'whisper',
'scream',
'private',
'system',
] as const;
export type MessageType = (typeof messageTypes)[number];
/** Вид сообщения с запасным `default`: контракт допускает расширение. */
export function messageTypeOf(type: string | undefined): MessageType {
return messageTypes.find((known) => known === type) ?? 'default';
}
export interface Message {
id: string;
channel_id: string;
author_id?: string;
content: string;
reply_to_id?: string;
/** `default` | `system` | `whisper` — контракт допускает расширение. */
/** Вид сообщения: `default`, `action`, `whisper`, `scream`, `private`, `system`. */
type: string;
edited_at?: string;
pinned: boolean;