perf(ui): fix O(n²) in liveSegmentChars and selectedTokenTotal, add i18n + SSR guard

Addresses bot review feedback on commit 224cab6.

## Performance: liveSegmentChars O(n²) → O(n)

The memo had three inner loops scanning all props.segments per unique
messageId. Added a single O(n) pre-pass building a
segmentsByMessageId Map, then replaced all three inner loops with
map lookups. Total complexity: O(n) instead of O(m×n).

File: packages/ui/src/components/message-timeline.tsx

## Performance: selectedTokenTotal O(n²) → O(n)

For each selected messageId, the memo scanned all segments to sum
chars. On "Select all" this was O(selected × segments). Now builds a
charsByMessageId map in one O(n) pass and does O(1) lookups per
selected message. Same pattern as aggregateTokensByMessageId.

File: packages/ui/src/components/message-section.tsx

## SSR guard: resize listener

window.addEventListener("resize", computeBadgeLayout) lacked a
typeof window !== "undefined" guard. Other window usage in the file
was guarded. Wrapped the addEventListener, requestAnimationFrame, and
onCleanup block in the guard.

File: packages/ui/src/components/message-timeline.tsx

## i18n: mirror selectionHint key in 5 locale files

messageSection.bulkDelete.selectionHint was only defined in
en/messaging.ts. Added the key (English string, since Ctrl/Shift/Esc
are universal keyboard labels) to es, fr, ja, ru, and zh-Hans.

Files: packages/ui/src/lib/i18n/messages/{es,fr,ja,ru,zh-Hans}/messaging.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
VooDisss
2026-03-02 17:46:51 +02:00
parent 224cab6a42
commit 2c27fc53ad
7 changed files with 40 additions and 30 deletions

View File

@@ -91,6 +91,7 @@ export const messagingMessages = {
"messageSection.bulkDelete.cancelTitle": "Cancelar selección",
"messageSection.bulkDelete.failedTitle": "Error al eliminar",
"messageSection.bulkDelete.failedMessage": "No se pudieron eliminar los mensajes seleccionados",
"messageSection.bulkDelete.selectionHint": "Ctrl+Click toggle \u00b7 Shift+Click range \u00b7 Esc clear",
"messageItem.status.queued": "EN COLA",
"messageItem.status.generating": "Generando...",
"messageItem.status.sending": "Enviando...",

View File

@@ -91,6 +91,7 @@ export const messagingMessages = {
"messageSection.bulkDelete.cancelTitle": "Annuler la sélection",
"messageSection.bulkDelete.failedTitle": "Échec de suppression",
"messageSection.bulkDelete.failedMessage": "Impossible de supprimer les messages sélectionnés",
"messageSection.bulkDelete.selectionHint": "Ctrl+Click toggle \u00b7 Shift+Click range \u00b7 Esc clear",
"messageItem.status.queued": "EN FILE",
"messageItem.status.generating": "Génération...",
"messageItem.status.sending": "Envoi...",

View File

@@ -91,6 +91,7 @@ export const messagingMessages = {
"messageSection.bulkDelete.cancelTitle": "選択をキャンセル",
"messageSection.bulkDelete.failedTitle": "削除に失敗しました",
"messageSection.bulkDelete.failedMessage": "選択したメッセージの削除に失敗しました",
"messageSection.bulkDelete.selectionHint": "Ctrl+Click toggle \u00b7 Shift+Click range \u00b7 Esc clear",
"messageItem.status.queued": "待機中",
"messageItem.status.generating": "生成中...",
"messageItem.status.sending": "送信中...",

View File

@@ -91,6 +91,7 @@ export const messagingMessages = {
"messageSection.bulkDelete.cancelTitle": "Отменить выбор",
"messageSection.bulkDelete.failedTitle": "Ошибка удаления",
"messageSection.bulkDelete.failedMessage": "Не удалось удалить выбранные сообщения",
"messageSection.bulkDelete.selectionHint": "Ctrl+Click toggle \u00b7 Shift+Click range \u00b7 Esc clear",
"messageItem.status.queued": "В ОЧЕРЕДИ",
"messageItem.status.generating": "Генерация…",
"messageItem.status.sending": "Отправка…",

View File

@@ -91,6 +91,7 @@ export const messagingMessages = {
"messageSection.bulkDelete.cancelTitle": "取消选择",
"messageSection.bulkDelete.failedTitle": "删除失败",
"messageSection.bulkDelete.failedMessage": "无法删除已选择的消息",
"messageSection.bulkDelete.selectionHint": "Ctrl+Click toggle \u00b7 Shift+Click range \u00b7 Esc clear",
"messageItem.status.queued": "排队中",
"messageItem.status.generating": "正在生成...",
"messageItem.status.sending": "正在发送...",