feat(desktop): compact confirmation names the chat it will compact

With a Bot group room in front, the panel targets the primary chat behind
it (consistent with the meter's numbers), but the dialog only said "this
chat". The confirm title now carries the target chat's title, falling back
to the generic question for untitled chats. New key questionNamed in en,
types and all 8 overlays; _keys.desktop.json regenerated.
This commit is contained in:
Teknium committed 2026-11-04 20:53:13 -08:00
1 parent d64292c1b3
commit e08cf6f308
13 files changed
+52 -9

No files matched your search

@@ -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?')
})
@@ -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])
}
+1
View File
@@ -469,6 +469,7 @@ export const arChrome = {
confirm: 'ضغط',
hint: 'لخّص السياق القديم لإفساح المجال. تابع في هذه المحادثة نفسها.',
question: 'ضغط هذه المحادثة في ملخص؟',
questionNamed: title => `ضغط «${title}» في ملخص؟`,
working: 'جارٍ الضغط…'
}
},
+1
View File
@@ -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',
+1
View File
@@ -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',
+1
View File
@@ -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',
+1
View File
@@ -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',
+1
View File
@@ -3053,6 +3053,7 @@ export const jaOverrides = {
confirm: '圧縮',
hint: '古いコンテキストを要約して空きを作ります。同じ会話を続けられます。',
question: 'この会話を要約に圧縮しますか?',
questionNamed: title => `「${title}」を要約に圧縮しますか?`,
working: '圧縮中…'
},
empty: 'コンテキストデータはまだありません',
+1
View File
@@ -3306,6 +3306,7 @@ export const ruOverrides = {
confirm: 'Сжать',
hint: 'Сожмите старый контекст, чтобы освободить место. Разговор продолжится здесь же.',
question: 'Сжать этот диалог в краткое изложение?',
questionNamed: title => `Сжать «${title}» в краткое изложение?`,
working: 'Сжимаю…'
},
empty: 'Данных контекста пока нет',
+1
View File
@@ -3679,6 +3679,7 @@ export interface Translations extends NoticeTranslations {
confirm: string
hint: string
question: string
questionNamed: (title: string) => string
working: string
}
empty: string
+1
View File
@@ -408,6 +408,7 @@ export const zhHantChrome = {
confirm: '壓縮',
hint: '摘要較早的上下文以騰出空間,繼續目前的對話。',
question: '將此對話壓縮為摘要?',
questionNamed: title => `將「${title}」壓縮為摘要?`,
working: '正在壓縮…'
},
empty: '尚無上下文資料',
+1
View File
@@ -4057,6 +4057,7 @@ export const zhOverrides = {
confirm: '压缩',
hint: '总结较早的上下文以腾出空间,继续当前对话。',
question: '将此对话压缩为摘要?',
questionNamed: title => `将“${title}”压缩为摘要?`,
working: '正在压缩…'
},
empty: '暂无上下文数据',
+1
View File
@@ -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",