.
This commit is contained in:
@@ -73,6 +73,8 @@ from the **Accounts** panel (each gets a one-time access key to hand over).
|
|||||||
| POST | `/api/auth/totp` | challenge | second login step `{totpToken, code}` → session |
|
| POST | `/api/auth/totp` | challenge | second login step `{totpToken, code}` → session |
|
||||||
| GET/PATCH/DELETE | `/api/conversations/:id` | owner | incl. messages on GET |
|
| GET/PATCH/DELETE | `/api/conversations/:id` | owner | incl. messages on GET |
|
||||||
| POST/GET/DELETE | `/api/conversations/:id/share` | owner | public link `/share/:publicId` |
|
| POST/GET/DELETE | `/api/conversations/:id/share` | owner | public link `/share/:publicId` |
|
||||||
|
| GET | `/api/conversations/archived` | Bearer | archived chats (hidden from sidebar + search) |
|
||||||
|
| POST | `/api/conversations/:id/archive`, `/api/conversations/:id/unarchive` | owner | archive (read-only) / restore to the list |
|
||||||
| GET | `/api/share/:publicId` | **public** | read-only shared chat JSON |
|
| GET | `/api/share/:publicId` | **public** | read-only shared chat JSON |
|
||||||
| GET | `/api/models` | Bearer | `[{id: "driver:model", …}]` across enabled drivers |
|
| GET | `/api/models` | Bearer | `[{id: "driver:model", …}]` across enabled drivers |
|
||||||
| POST | `/api/chat` | Bearer | `{model, messages, conversationId?, stream?}` → JSON or SSE |
|
| POST | `/api/chat` | Bearer | `{model, messages, conversationId?, stream?}` → JSON or SSE |
|
||||||
@@ -84,7 +86,7 @@ reply into that conversation (ownership enforced) and auto-titles new chats.
|
|||||||
|
|
||||||
`$DATA_DIR/kisassistant.db` (WAL mode). Tables: `users` (id, username,
|
`$DATA_DIR/kisassistant.db` (WAL mode). Tables: `users` (id, username,
|
||||||
display_name, avatar_url, theme, email, key_hash), `sessions` (hashed Bearer tokens),
|
display_name, avatar_url, theme, email, key_hash), `sessions` (hashed Bearer tokens),
|
||||||
`conversations` (id, user, title, topic, model), `messages`, `shares`,
|
`conversations` (id, user, title, topic, model, archived_at), `messages`, `shares`,
|
||||||
`resets` (recovery tokens), `settings` (feature flags)
|
`resets` (recovery tokens), `settings` (feature flags)
|
||||||
(conv ↔ public id). No migration system — schema is created idempotently at boot.
|
(conv ↔ public id). No migration system — schema is created idempotently at boot.
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ browser ──► Bun.serve (src/index.ts)
|
|||||||
→ SSE events `token` / `done` / `error`. The full reply is persisted after a
|
→ SSE events `token` / `done` / `error`. The full reply is persisted after a
|
||||||
successful stream; on error the partial text is kept as-is (no internal
|
successful stream; on error the partial text is kept as-is (no internal
|
||||||
markers). Server logs each stream open/done/abort per driver when
|
markers). Server logs each stream open/done/abort per driver when
|
||||||
`DRIVER_DEBUG=true`.
|
`DRIVER_DEBUG=true`. `POST /api/chat` with an archived `conversationId`
|
||||||
|
is rejected (403) — archived chats are read-only.
|
||||||
|
|
||||||
Streaming guarantees (ChatGPT-style, no buffering):
|
Streaming guarantees (ChatGPT-style, no buffering):
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ Created idempotently at boot in `getDb()` (no migration framework; keep it so).
|
|||||||
| `sessions` | `token_hash PK, user_id, created_at, expires_at`. Bearer tokens, 256-bit, hashed with sha256. Expired rows are purged lazily on access. |
|
| `sessions` | `token_hash PK, user_id, created_at, expires_at`. Bearer tokens, 256-bit, hashed with sha256. Expired rows are purged lazily on access. |
|
||||||
| `resets` | `token_hash PK, user_id, created_at, expires_at`. One-time key-recovery tokens (single use, consumed on POST). |
|
| `resets` | `token_hash PK, user_id, created_at, expires_at`. One-time key-recovery tokens (single use, consumed on POST). |
|
||||||
| `settings` | `key PK, value, updated_at`. Admin-controlled feature flags (`registration_enabled` default off, `tools_ocr_enabled` default on). |
|
| `settings` | `key PK, value, updated_at`. Admin-controlled feature flags (`registration_enabled` default off, `tools_ocr_enabled` default on). |
|
||||||
| `conversations` | `id, user_id, title, topic, model, created_at, updated_at`. |
|
| `conversations` | `id, user_id, title, topic, model, archived_at, created_at, updated_at`. `archived_at = 0` means live; otherwise a unix-ms archive timestamp (added idempotently via `ALTER TABLE`, like `users.email`). |
|
||||||
| `messages` | `id AUTOINCREMENT, conv_id → conversations ON DELETE CASCADE, role, content, created_at`. |
|
| `messages` | `id AUTOINCREMENT, conv_id → conversations ON DELETE CASCADE, role, content, created_at`. |
|
||||||
| `shares` | `conv_id PK → conversations ON DELETE CASCADE, public_id UNIQUE, created_at`. |
|
| `shares` | `conv_id PK → conversations ON DELETE CASCADE, public_id UNIQUE, created_at`. |
|
||||||
|
|
||||||
|
|||||||
+7
-4
@@ -35,15 +35,18 @@ read-only `SharePage` (no auth), everything else renders the authenticated app.
|
|||||||
**Never use a native `<select>`**; use `Picker` for model and theme choices.
|
**Never use a native `<select>`**; use `Picker` for model and theme choices.
|
||||||
- `Login.tsx` — username + access-key card.
|
- `Login.tsx` — username + access-key card.
|
||||||
- `Sidebar.tsx` — collapsible conversation list with per-item context menu
|
- `Sidebar.tsx` — collapsible conversation list with per-item context menu
|
||||||
(right-click or `···`: Rename / Set topic / Share publicly / Delete), user
|
(right-click or `···`: Rename / Set topic / Share publicly / Archive /
|
||||||
chip (opens settings), Accounts button (admin), logout.
|
Delete), user chip (opens settings), Accounts button (admin), logout.
|
||||||
|
Archived chats leave the list (and search); they live in settings.
|
||||||
- `Chat.tsx` — header (title, topic badge, driver-grouped model `Picker`,
|
- `Chat.tsx` — header (title, topic badge, driver-grouped model `Picker`,
|
||||||
share button), message list (`Markdown` for assistant, emerald bubble for
|
share button), message list (`Markdown` for assistant, emerald bubble for
|
||||||
user), rounded composer with paperclip attach menu, attachment chips
|
user), rounded composer with paperclip attach menu, attachment chips
|
||||||
(Enter to send, Shift+Enter for newline).
|
(Enter to send, Shift+Enter for newline). With `readOnly` (archived chat)
|
||||||
|
the composer and share button are replaced by an unarchive banner.
|
||||||
- `dialogs.tsx` — `ConvEditDialog` (title + topic), `ShareModal` (public link,
|
- `dialogs.tsx` — `ConvEditDialog` (title + topic), `ShareModal` (public link,
|
||||||
copy, unshare), `SettingsModal` (display name, avatar upload box with
|
copy, unshare), `SettingsModal` (display name, avatar upload box with
|
||||||
drag & drop, avatar URL, theme).
|
drag & drop, avatar URL, theme, plus an "Archived chats" category to
|
||||||
|
view / unarchive / delete archived conversations).
|
||||||
- `AdminPanel.tsx` — No-KYC account management: create (key shown once),
|
- `AdminPanel.tsx` — No-KYC account management: create (key shown once),
|
||||||
regenerate (revokes sessions), edit, delete with confirm, plus server-side
|
regenerate (revokes sessions), edit, delete with confirm, plus server-side
|
||||||
search / sort (newest, oldest, A–Z, Z–A) / email filter / pagination.
|
search / sort (newest, oldest, A–Z, Z–A) / email filter / pagination.
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ const en = {
|
|||||||
"common.light": "Light",
|
"common.light": "Light",
|
||||||
"common.dark": "Dark",
|
"common.dark": "Dark",
|
||||||
"common.saveFailed": "Save failed",
|
"common.saveFailed": "Save failed",
|
||||||
|
"common.loadFailed": "Load failed",
|
||||||
|
"common.deleteFailed": "Delete failed",
|
||||||
|
"common.loadFailed": "Load failed",
|
||||||
|
"common.deleteFailed": "Delete failed",
|
||||||
"common.backToLogin": "Back to login",
|
"common.backToLogin": "Back to login",
|
||||||
|
|
||||||
"loading.tagline": "Preparing your chats",
|
"loading.tagline": "Preparing your chats",
|
||||||
@@ -83,6 +87,7 @@ const en = {
|
|||||||
"sidebar.adminCenter": "Admin Center",
|
"sidebar.adminCenter": "Admin Center",
|
||||||
"sidebar.adminCenterTip": "Open the Admin Center",
|
"sidebar.adminCenterTip": "Open the Admin Center",
|
||||||
"sidebar.settingsTip": "Profile & settings",
|
"sidebar.settingsTip": "Profile & settings",
|
||||||
|
"sidebar.logout": "Log out",
|
||||||
"sidebar.archive": "Archive",
|
"sidebar.archive": "Archive",
|
||||||
|
|
||||||
"chat.expand": "Expand sidebar",
|
"chat.expand": "Expand sidebar",
|
||||||
@@ -460,6 +465,16 @@ const en = {
|
|||||||
|
|
||||||
"archived.readonly": "This chat is archived and read-only.",
|
"archived.readonly": "This chat is archived and read-only.",
|
||||||
"archived.unarchive": "Unarchive",
|
"archived.unarchive": "Unarchive",
|
||||||
|
"archived.title": "Archived chats",
|
||||||
|
"archived.empty": "No archived chats.",
|
||||||
|
"archived.view": "View",
|
||||||
|
"archived.deleteTitle": "Delete this archived chat?",
|
||||||
|
"archived.deleteMsg": "It will be permanently removed, with all its messages.",
|
||||||
|
"archived.title": "Archived chats",
|
||||||
|
"archived.empty": "No archived conversations yet.",
|
||||||
|
"archived.view": "View",
|
||||||
|
"archived.deleteTitle": "Delete this archived chat?",
|
||||||
|
"archived.deleteMsg": "This permanently removes the conversation and all its messages.",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type StringKey = keyof typeof en;
|
export type StringKey = keyof typeof en;
|
||||||
@@ -485,6 +500,10 @@ const es: Dict = {
|
|||||||
"common.light": "Claro",
|
"common.light": "Claro",
|
||||||
"common.dark": "Oscuro",
|
"common.dark": "Oscuro",
|
||||||
"common.saveFailed": "No se pudo guardar",
|
"common.saveFailed": "No se pudo guardar",
|
||||||
|
"common.loadFailed": "No se pudo cargar",
|
||||||
|
"common.deleteFailed": "No se pudo eliminar",
|
||||||
|
"common.loadFailed": "No se pudo cargar",
|
||||||
|
"common.deleteFailed": "No se pudo eliminar",
|
||||||
"common.backToLogin": "Volver al inicio de sesión",
|
"common.backToLogin": "Volver al inicio de sesión",
|
||||||
|
|
||||||
"loading.tagline": "Preparando tus chats",
|
"loading.tagline": "Preparando tus chats",
|
||||||
@@ -530,6 +549,7 @@ const es: Dict = {
|
|||||||
"sidebar.adminCenter": "Centro de admin",
|
"sidebar.adminCenter": "Centro de admin",
|
||||||
"sidebar.adminCenterTip": "Abrir el Centro de admin",
|
"sidebar.adminCenterTip": "Abrir el Centro de admin",
|
||||||
"sidebar.settingsTip": "Perfil y ajustes",
|
"sidebar.settingsTip": "Perfil y ajustes",
|
||||||
|
"sidebar.logout": "Cerrar sesión",
|
||||||
"sidebar.archive": "Archivar",
|
"sidebar.archive": "Archivar",
|
||||||
|
|
||||||
"chat.expand": "Mostrar barra lateral",
|
"chat.expand": "Mostrar barra lateral",
|
||||||
@@ -902,6 +922,16 @@ const es: Dict = {
|
|||||||
|
|
||||||
"archived.readonly": "Este chat está archivado y es de solo lectura.",
|
"archived.readonly": "Este chat está archivado y es de solo lectura.",
|
||||||
"archived.unarchive": "Desarchivar",
|
"archived.unarchive": "Desarchivar",
|
||||||
|
"archived.title": "Archivados",
|
||||||
|
"archived.empty": "Sin chats archivados.",
|
||||||
|
"archived.view": "Ver",
|
||||||
|
"archived.deleteTitle": "¿Eliminar este chat archivado?",
|
||||||
|
"archived.deleteMsg": "Se eliminará para siempre, con todos sus mensajes.",
|
||||||
|
"archived.title": "Chats archivados",
|
||||||
|
"archived.empty": "No hay conversaciones archivadas.",
|
||||||
|
"archived.view": "Ver",
|
||||||
|
"archived.deleteTitle": "¿Eliminar este chat archivado?",
|
||||||
|
"archived.deleteMsg": "Esto elimina permanentemente la conversación y todos sus mensajes.",
|
||||||
};
|
};
|
||||||
|
|
||||||
const fr: Dict = {
|
const fr: Dict = {
|
||||||
@@ -924,6 +954,10 @@ const fr: Dict = {
|
|||||||
"common.light": "Clair",
|
"common.light": "Clair",
|
||||||
"common.dark": "Sombre",
|
"common.dark": "Sombre",
|
||||||
"common.saveFailed": "Échec de l'enregistrement",
|
"common.saveFailed": "Échec de l'enregistrement",
|
||||||
|
"common.loadFailed": "Chargement impossible",
|
||||||
|
"common.deleteFailed": "Suppression impossible",
|
||||||
|
"common.loadFailed": "Échec du chargement",
|
||||||
|
"common.deleteFailed": "Échec de la suppression",
|
||||||
"common.backToLogin": "Retour à la connexion",
|
"common.backToLogin": "Retour à la connexion",
|
||||||
|
|
||||||
"loading.tagline": "Préparation de vos chats",
|
"loading.tagline": "Préparation de vos chats",
|
||||||
@@ -969,6 +1003,7 @@ const fr: Dict = {
|
|||||||
"sidebar.adminCenter": "Centre admin",
|
"sidebar.adminCenter": "Centre admin",
|
||||||
"sidebar.adminCenterTip": "Ouvrir le Centre admin",
|
"sidebar.adminCenterTip": "Ouvrir le Centre admin",
|
||||||
"sidebar.settingsTip": "Profil et réglages",
|
"sidebar.settingsTip": "Profil et réglages",
|
||||||
|
"sidebar.logout": "Déconnexion",
|
||||||
"sidebar.archive": "Archiver",
|
"sidebar.archive": "Archiver",
|
||||||
|
|
||||||
"chat.expand": "Afficher la barre latérale",
|
"chat.expand": "Afficher la barre latérale",
|
||||||
@@ -1341,6 +1376,16 @@ const fr: Dict = {
|
|||||||
|
|
||||||
"archived.readonly": "Ce chat est archivé en lecture seule.",
|
"archived.readonly": "Ce chat est archivé en lecture seule.",
|
||||||
"archived.unarchive": "Désarchiver",
|
"archived.unarchive": "Désarchiver",
|
||||||
|
"archived.title": "Archives",
|
||||||
|
"archived.empty": "Aucun chat archivé.",
|
||||||
|
"archived.view": "Voir",
|
||||||
|
"archived.deleteTitle": "Supprimer ce chat archivé ?",
|
||||||
|
"archived.deleteMsg": "Il sera définitivement supprimé, avec tous ses messages.",
|
||||||
|
"archived.title": "Conversations archivées",
|
||||||
|
"archived.empty": "Aucune conversation archivée.",
|
||||||
|
"archived.view": "Voir",
|
||||||
|
"archived.deleteTitle": "Supprimer cette conversation archivée ?",
|
||||||
|
"archived.deleteMsg": "Cela supprime définitivement la conversation et tous ses messages.",
|
||||||
};
|
};
|
||||||
|
|
||||||
const ru: Dict = {
|
const ru: Dict = {
|
||||||
@@ -1363,6 +1408,10 @@ const ru: Dict = {
|
|||||||
"common.light": "Светлая",
|
"common.light": "Светлая",
|
||||||
"common.dark": "Тёмная",
|
"common.dark": "Тёмная",
|
||||||
"common.saveFailed": "Не удалось сохранить",
|
"common.saveFailed": "Не удалось сохранить",
|
||||||
|
"common.loadFailed": "Не загрузилось",
|
||||||
|
"common.deleteFailed": "Не удалилось",
|
||||||
|
"common.loadFailed": "Не удалось загрузить",
|
||||||
|
"common.deleteFailed": "Не удалось удалить",
|
||||||
"common.backToLogin": "Назад ко входу",
|
"common.backToLogin": "Назад ко входу",
|
||||||
|
|
||||||
"loading.tagline": "Готовим ваши чаты",
|
"loading.tagline": "Готовим ваши чаты",
|
||||||
@@ -1408,6 +1457,7 @@ const ru: Dict = {
|
|||||||
"sidebar.adminCenter": "Админ-центр",
|
"sidebar.adminCenter": "Админ-центр",
|
||||||
"sidebar.adminCenterTip": "Открыть Админ-центр",
|
"sidebar.adminCenterTip": "Открыть Админ-центр",
|
||||||
"sidebar.settingsTip": "Профиль и настройки",
|
"sidebar.settingsTip": "Профиль и настройки",
|
||||||
|
"sidebar.logout": "Выйти",
|
||||||
"sidebar.archive": "В архив",
|
"sidebar.archive": "В архив",
|
||||||
|
|
||||||
"chat.expand": "Показать панель",
|
"chat.expand": "Показать панель",
|
||||||
@@ -1767,6 +1817,16 @@ const ru: Dict = {
|
|||||||
|
|
||||||
"archived.readonly": "Чат в архиве, только чтение.",
|
"archived.readonly": "Чат в архиве, только чтение.",
|
||||||
"archived.unarchive": "Вернуть",
|
"archived.unarchive": "Вернуть",
|
||||||
|
"archived.title": "Архив",
|
||||||
|
"archived.empty": "Архив пуст.",
|
||||||
|
"archived.view": "Открыть",
|
||||||
|
"archived.deleteTitle": "Удалить архивный чат?",
|
||||||
|
"archived.deleteMsg": "Будет удалён навсегда вместе с сообщениями.",
|
||||||
|
"archived.title": "Архив чатов",
|
||||||
|
"archived.empty": "Нет архивных чатов.",
|
||||||
|
"archived.view": "Открыть",
|
||||||
|
"archived.deleteTitle": "Удалить этот архивный чат?",
|
||||||
|
"archived.deleteMsg": "Это навсегда удалит разговор и все сообщения.",
|
||||||
};
|
};
|
||||||
|
|
||||||
const it: Dict = {
|
const it: Dict = {
|
||||||
@@ -1789,6 +1849,10 @@ const it: Dict = {
|
|||||||
"common.light": "Chiaro",
|
"common.light": "Chiaro",
|
||||||
"common.dark": "Scuro",
|
"common.dark": "Scuro",
|
||||||
"common.saveFailed": "Salvataggio non riuscito",
|
"common.saveFailed": "Salvataggio non riuscito",
|
||||||
|
"common.loadFailed": "Caricamento fallito",
|
||||||
|
"common.deleteFailed": "Eliminazione fallita",
|
||||||
|
"common.loadFailed": "Caricamento non riuscito",
|
||||||
|
"common.deleteFailed": "Eliminazione non riuscita",
|
||||||
"common.backToLogin": "Torna al login",
|
"common.backToLogin": "Torna al login",
|
||||||
|
|
||||||
"loading.tagline": "Preparo le tue chat",
|
"loading.tagline": "Preparo le tue chat",
|
||||||
@@ -1834,6 +1898,7 @@ const it: Dict = {
|
|||||||
"sidebar.adminCenter": "Centro admin",
|
"sidebar.adminCenter": "Centro admin",
|
||||||
"sidebar.adminCenterTip": "Apri il Centro admin",
|
"sidebar.adminCenterTip": "Apri il Centro admin",
|
||||||
"sidebar.settingsTip": "Profilo e impostazioni",
|
"sidebar.settingsTip": "Profilo e impostazioni",
|
||||||
|
"sidebar.logout": "Esci",
|
||||||
"sidebar.archive": "Archivia",
|
"sidebar.archive": "Archivia",
|
||||||
|
|
||||||
"chat.expand": "Mostra barra laterale",
|
"chat.expand": "Mostra barra laterale",
|
||||||
@@ -2199,6 +2264,16 @@ const it: Dict = {
|
|||||||
|
|
||||||
"archived.readonly": "Questa chat è archiviata e in sola lettura.",
|
"archived.readonly": "Questa chat è archiviata e in sola lettura.",
|
||||||
"archived.unarchive": "Ripristina",
|
"archived.unarchive": "Ripristina",
|
||||||
|
"archived.title": "Archiviate",
|
||||||
|
"archived.empty": "Nessuna chat archiviata.",
|
||||||
|
"archived.view": "Apri",
|
||||||
|
"archived.deleteTitle": "Eliminare questa chat archiviata?",
|
||||||
|
"archived.deleteMsg": "Sarà eliminata per sempre, con tutti i suoi messaggi.",
|
||||||
|
"archived.title": "Chat archiviate",
|
||||||
|
"archived.empty": "Nessuna conversazione archiviata.",
|
||||||
|
"archived.view": "Vedi",
|
||||||
|
"archived.deleteTitle": "Eliminare questa chat archiviata?",
|
||||||
|
"archived.deleteMsg": "Questo rimuove definitivamente la conversazione e tutti i suoi messaggi.",
|
||||||
};
|
};
|
||||||
|
|
||||||
const pt: Dict = {
|
const pt: Dict = {
|
||||||
@@ -2221,6 +2296,10 @@ const pt: Dict = {
|
|||||||
"common.light": "Claro",
|
"common.light": "Claro",
|
||||||
"common.dark": "Escuro",
|
"common.dark": "Escuro",
|
||||||
"common.saveFailed": "Falha ao guardar",
|
"common.saveFailed": "Falha ao guardar",
|
||||||
|
"common.loadFailed": "Falha ao carregar",
|
||||||
|
"common.deleteFailed": "Falha ao eliminar",
|
||||||
|
"common.loadFailed": "Falha ao carregar",
|
||||||
|
"common.deleteFailed": "Falha ao eliminar",
|
||||||
"common.backToLogin": "Voltar ao login",
|
"common.backToLogin": "Voltar ao login",
|
||||||
|
|
||||||
"loading.tagline": "A preparar os teus chats",
|
"loading.tagline": "A preparar os teus chats",
|
||||||
@@ -2266,6 +2345,7 @@ const pt: Dict = {
|
|||||||
"sidebar.adminCenter": "Centro admin",
|
"sidebar.adminCenter": "Centro admin",
|
||||||
"sidebar.adminCenterTip": "Abrir o Centro admin",
|
"sidebar.adminCenterTip": "Abrir o Centro admin",
|
||||||
"sidebar.settingsTip": "Perfil e definições",
|
"sidebar.settingsTip": "Perfil e definições",
|
||||||
|
"sidebar.logout": "Sair",
|
||||||
"sidebar.archive": "Arquivar",
|
"sidebar.archive": "Arquivar",
|
||||||
|
|
||||||
"chat.expand": "Mostrar barra lateral",
|
"chat.expand": "Mostrar barra lateral",
|
||||||
@@ -2622,6 +2702,16 @@ const pt: Dict = {
|
|||||||
|
|
||||||
"archived.readonly": "Este chat está arquivado e é só de leitura.",
|
"archived.readonly": "Este chat está arquivado e é só de leitura.",
|
||||||
"archived.unarchive": "Desarquivar",
|
"archived.unarchive": "Desarquivar",
|
||||||
|
"archived.title": "Arquivados",
|
||||||
|
"archived.empty": "Sem chats arquivados.",
|
||||||
|
"archived.view": "Abrir",
|
||||||
|
"archived.deleteTitle": "Eliminar este chat arquivado?",
|
||||||
|
"archived.deleteMsg": "Será eliminado para sempre, com todas as mensagens.",
|
||||||
|
"archived.title": "Conversas arquivadas",
|
||||||
|
"archived.empty": "Nenhuma conversa arquivada.",
|
||||||
|
"archived.view": "Ver",
|
||||||
|
"archived.deleteTitle": "Eliminar esta conversa arquivada?",
|
||||||
|
"archived.deleteMsg": "Isto remove permanentemente a conversa e todas as mensagens.",
|
||||||
};
|
};
|
||||||
|
|
||||||
const dicts: Record<LangId, Dict> = { en, es, fr, ru, it, pt };
|
const dicts: Record<LangId, Dict> = { en, es, fr, ru, it, pt };
|
||||||
|
|||||||
Reference in New Issue
Block a user