diff --git a/apps/desktop/src/app/shell/hooks/use-compact-conversation.test.tsx b/apps/desktop/src/app/shell/hooks/use-compact-conversation.test.tsx index afb0149d86..218a818d45 100644 --- a/apps/desktop/src/app/shell/hooks/use-compact-conversation.test.tsx +++ b/apps/desktop/src/app/shell/hooks/use-compact-conversation.test.tsx @@ -1,15 +1,18 @@ import { act, renderHook } from '@testing-library/react' import { afterEach, expect, it, vi } from 'vitest' +import { createClientSessionState } from '@/lib/chat-runtime' import { $confirmRequest, settleConfirm } from '@/store/confirm' -import { $activeSessionId } from '@/store/session' -import { type SessionTileDelegate, setSessionTileDelegate } from '@/store/session-states' +import { $activeSessionId, $sessions } from '@/store/session' +import { $sessionStates, type SessionTileDelegate, setSessionTileDelegate } from '@/store/session-states' import { useCompactConversation } from './use-compact-conversation' afterEach(() => { settleConfirm(false) $activeSessionId.set(null) + $sessions.set([]) + $sessionStates.set({}) setSessionTileDelegate(null as unknown as SessionTileDelegate) }) @@ -45,3 +48,15 @@ it('compacts nothing when the user switched chats while the confirmation was ope expect(executeSlash).not.toHaveBeenCalled() }) + +it('names the chat it will compact in the confirmation when the chat has a title', () => { + setSessionTileDelegate({ executeSlash: vi.fn(async () => {}) } as unknown as SessionTileDelegate) + $activeSessionId.set('rt-a') + $sessionStates.set({ 'rt-a': { ...createClientSessionState(), storedSessionId: 'stored-a' } as never }) + $sessions.set([{ id: 'stored-a', title: 'Release notes' } as never]) + + const { result } = renderHook(() => useCompactConversation({ busy: false, sessionId: 'rt-a' })) + + act(() => result.current.onRequest()) + expect($confirmRequest.get()?.title).toBe('Compress “Release notes” into a summary?') +}) diff --git a/apps/desktop/src/app/shell/hooks/use-compact-conversation.ts b/apps/desktop/src/app/shell/hooks/use-compact-conversation.ts index 38052a3d18..80e9841d2b 100644 --- a/apps/desktop/src/app/shell/hooks/use-compact-conversation.ts +++ b/apps/desktop/src/app/shell/hooks/use-compact-conversation.ts @@ -5,7 +5,7 @@ import { useI18n } from '@/i18n' import { $compactingSessions } from '@/store/compaction' import { confirm } from '@/store/confirm' import { notifyError } from '@/store/notifications' -import { $activeSessionId, $busy } from '@/store/session' +import { $activeSessionId, $busy, $sessions } from '@/store/session' import { $focusedRuntimeId, $sessionStates, sessionTileDelegate } from '@/store/session-states' export interface CompactConversationAction { @@ -18,6 +18,20 @@ export interface CompactConversationAction { onRequest: () => void } +/** The chat's own title, so the confirm names what compacts when another + * surface (a Bot group room) is in front of it. Untitled chats keep the + * generic copy rather than a preview snippet. */ +function chatTitle(runtimeId: string): string | undefined { + const storedId = $sessionStates.get()[runtimeId]?.storedSessionId + + return storedId + ? $sessions + .get() + .find(session => session.id === storedId) + ?.title?.trim() || undefined + : undefined +} + function sessionBusy(sessionId: string): boolean { return Boolean($sessionStates.get()[sessionId]?.busy) || ($activeSessionId.get() === sessionId && $busy.get()) } @@ -54,7 +68,13 @@ export function useCompactConversation({ } void (async () => { - const ok = await confirm({ confirmLabel: copy.confirm, description: copy.hint, title: copy.question }) + const title = chatTitle(target) + + const ok = await confirm({ + confirmLabel: copy.confirm, + description: copy.hint, + title: title ? copy.questionNamed(title) : copy.question + }) if (!ok || $focusedRuntimeId.get() !== target || sessionBusy(target) || $compactingSessions.get()[target]) { return @@ -62,10 +82,7 @@ export function useCompactConversation({ await sessionTileDelegate()?.executeSlash('/compress', target, { typed: false }) })().catch(error => notifyError(error, failed)) - }, [busy, copy.confirm, copy.hint, copy.question, failed, pending, sessionId]) + }, [busy, copy, failed, pending, sessionId]) - return useMemo( - () => ({ busy, onRequest, pending, unavailable: !sessionId }), - [busy, onRequest, pending, sessionId] - ) + return useMemo(() => ({ busy, onRequest, pending, unavailable: !sessionId }), [busy, onRequest, pending, sessionId]) } diff --git a/apps/desktop/src/i18n/ar_chrome.ts b/apps/desktop/src/i18n/ar_chrome.ts index bde78061d9..39f3049818 100644 --- a/apps/desktop/src/i18n/ar_chrome.ts +++ b/apps/desktop/src/i18n/ar_chrome.ts @@ -469,6 +469,7 @@ export const arChrome = { confirm: 'ضغط', hint: 'لخّص السياق القديم لإفساح المجال. تابع في هذه المحادثة نفسها.', question: 'ضغط هذه المحادثة في ملخص؟', + questionNamed: title => `ضغط «${title}» في ملخص؟`, working: 'جارٍ الضغط…' } }, diff --git a/apps/desktop/src/i18n/de.ts b/apps/desktop/src/i18n/de.ts index 8ca4751aa7..41785f625f 100644 --- a/apps/desktop/src/i18n/de.ts +++ b/apps/desktop/src/i18n/de.ts @@ -4648,6 +4648,7 @@ export const deOverrides = { confirm: 'Verdichten', hint: 'Älteren Kontext zusammenfassen, um Platz zu schaffen. Dieses Gespräch wird fortgesetzt.', question: 'Diese Unterhaltung zu einer Zusammenfassung verdichten?', + questionNamed: title => `„${title}“ zu einer Zusammenfassung verdichten?`, working: 'Verdichte …' }, empty: 'Noch keine Kontext-Daten', diff --git a/apps/desktop/src/i18n/en.ts b/apps/desktop/src/i18n/en.ts index d147cc9023..d52c7fa76f 100644 --- a/apps/desktop/src/i18n/en.ts +++ b/apps/desktop/src/i18n/en.ts @@ -4214,6 +4214,7 @@ export const en: Translations = { confirm: 'Compact', hint: 'Summarize older context to make room. Continue in this same conversation.', question: 'Compress this chat into a summary?', + questionNamed: title => `Compress “${title}” into a summary?`, working: 'Compacting…' }, empty: 'No context data yet', diff --git a/apps/desktop/src/i18n/es.ts b/apps/desktop/src/i18n/es.ts index b885598e51..e4bf193af7 100644 --- a/apps/desktop/src/i18n/es.ts +++ b/apps/desktop/src/i18n/es.ts @@ -4632,6 +4632,7 @@ export const esOverrides = { confirm: 'Compactar', hint: 'Resume el contexto anterior para hacer espacio. Continúa en esta misma conversación.', question: '¿Compactar esta conversación en un resumen?', + questionNamed: title => `¿Compactar «${title}» en un resumen?`, working: 'Compactando…' }, empty: 'Aún no hay datos de contexto', diff --git a/apps/desktop/src/i18n/fr.ts b/apps/desktop/src/i18n/fr.ts index 5640a486e5..4a0a9f5461 100644 --- a/apps/desktop/src/i18n/fr.ts +++ b/apps/desktop/src/i18n/fr.ts @@ -4657,6 +4657,7 @@ export const frOverrides = { confirm: 'Compacter', hint: 'Résumez le contexte ancien pour libérer de la place. Continuez dans cette même conversation.', question: 'Compacter cette conversation en un résumé ?', + questionNamed: title => `Compacter « ${title} » en un résumé ?`, working: 'Compaction…' }, empty: 'Aucune donnée de contexte pour le moment', diff --git a/apps/desktop/src/i18n/ja.ts b/apps/desktop/src/i18n/ja.ts index 9044efe8fc..731c783f0b 100644 --- a/apps/desktop/src/i18n/ja.ts +++ b/apps/desktop/src/i18n/ja.ts @@ -3053,6 +3053,7 @@ export const jaOverrides = { confirm: '圧縮', hint: '古いコンテキストを要約して空きを作ります。同じ会話を続けられます。', question: 'この会話を要約に圧縮しますか?', + questionNamed: title => `「${title}」を要約に圧縮しますか?`, working: '圧縮中…' }, empty: 'コンテキストデータはまだありません', diff --git a/apps/desktop/src/i18n/ru.ts b/apps/desktop/src/i18n/ru.ts index 14ab1955d2..3e9368dfc5 100644 --- a/apps/desktop/src/i18n/ru.ts +++ b/apps/desktop/src/i18n/ru.ts @@ -3306,6 +3306,7 @@ export const ruOverrides = { confirm: 'Сжать', hint: 'Сожмите старый контекст, чтобы освободить место. Разговор продолжится здесь же.', question: 'Сжать этот диалог в краткое изложение?', + questionNamed: title => `Сжать «${title}» в краткое изложение?`, working: 'Сжимаю…' }, empty: 'Данных контекста пока нет', diff --git a/apps/desktop/src/i18n/types.ts b/apps/desktop/src/i18n/types.ts index 0b4551556a..d3de8f4dcf 100644 --- a/apps/desktop/src/i18n/types.ts +++ b/apps/desktop/src/i18n/types.ts @@ -3679,6 +3679,7 @@ export interface Translations extends NoticeTranslations { confirm: string hint: string question: string + questionNamed: (title: string) => string working: string } empty: string diff --git a/apps/desktop/src/i18n/zh-hant_chrome.ts b/apps/desktop/src/i18n/zh-hant_chrome.ts index fa225d90dc..83d6c7b539 100644 --- a/apps/desktop/src/i18n/zh-hant_chrome.ts +++ b/apps/desktop/src/i18n/zh-hant_chrome.ts @@ -408,6 +408,7 @@ export const zhHantChrome = { confirm: '壓縮', hint: '摘要較早的上下文以騰出空間,繼續目前的對話。', question: '將此對話壓縮為摘要?', + questionNamed: title => `將「${title}」壓縮為摘要?`, working: '正在壓縮…' }, empty: '尚無上下文資料', diff --git a/apps/desktop/src/i18n/zh.ts b/apps/desktop/src/i18n/zh.ts index 5f8686c3d5..bfc2ff6ac3 100644 --- a/apps/desktop/src/i18n/zh.ts +++ b/apps/desktop/src/i18n/zh.ts @@ -4057,6 +4057,7 @@ export const zhOverrides = { confirm: '压缩', hint: '总结较早的上下文以腾出空间,继续当前对话。', question: '将此对话压缩为摘要?', + questionNamed: title => `将“${title}”压缩为摘要?`, working: '正在压缩…' }, empty: '暂无上下文数据', diff --git a/locales/_keys.desktop.json b/locales/_keys.desktop.json index 57416d65b4..c046ca2eaa 100644 --- a/locales/_keys.desktop.json +++ b/locales/_keys.desktop.json @@ -4593,6 +4593,7 @@ "shell.statusbar.contextUsagePanel.compact.confirm", "shell.statusbar.contextUsagePanel.compact.hint", "shell.statusbar.contextUsagePanel.compact.question", + "shell.statusbar.contextUsagePanel.compact.questionNamed", "shell.statusbar.contextUsagePanel.compact.working", "shell.statusbar.contextUsagePanel.empty", "shell.statusbar.contextUsagePanel.loading",