diff --git a/webview-ui/playwright/gallery/stories.tsx b/webview-ui/playwright/gallery/stories.tsx index 1aa4f034df..08b4824f26 100644 --- a/webview-ui/playwright/gallery/stories.tsx +++ b/webview-ui/playwright/gallery/stories.tsx @@ -83,6 +83,23 @@ export const stories: Record = { await import("@/components/settings/providers/__tests__/OpenAICompatible.visual.fixture") return }, + "previous-user-message-button": async () => { + const [{ PreviousUserMessageButton }, { TooltipProvider }] = await Promise.all([ + import("@/components/chat/PreviousUserMessageButton"), + import("@/components/ui/tooltip"), + ]) + return ( + +
+ undefined} + /> +
+
+ ) + }, "rendered-content-contrast": async () => { const [{ AppProviders }, { RenderedContentContrastFixture }] = await Promise.all([ import("../AppProviders"), diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index 988c98ba30..d79a8e8f8d 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -52,6 +52,7 @@ import { CheckpointWarning } from "./CheckpointWarning" import { QueuedMessages } from "./QueuedMessages" import { WorktreeSelector } from "./WorktreeSelector" import FileChangesPanel from "./FileChangesPanel" +import { PreviousUserMessageButton } from "./PreviousUserMessageButton" import { useScrollLifecycle } from "@src/hooks/useScrollLifecycle" export interface ChatViewProps { @@ -1334,14 +1335,30 @@ const ChatViewComponent: React.ForwardRefRenderFunction { + const indices: number[] = [] + for (let i = 0; i < groupedMessages.length; i++) { + const message = groupedMessages[i] + if (message?.type === "say" && message.say === "user_feedback") { + indices.push(i) + } + } + return indices + }, [groupedMessages]) const hasLatestCheckpoint = checkpointIndices.length > 0 + const hasUserMessages = userMessageIndices.length > 0 const checkpointJumpCursorRef = useRef(null) + const userMessageJumpCursorRef = useRef(null) useEffect(() => { checkpointJumpCursorRef.current = null }, [task?.ts, checkpointIndices.length]) + useEffect(() => { + userMessageJumpCursorRef.current = null + }, [task?.ts, userMessageIndices.length]) + // Scroll lifecycle is managed by a dedicated hook to keep ChatView focused // on message handling and UI orchestration. const { @@ -1480,11 +1497,29 @@ const ChatViewComponent: React.ForwardRefRenderFunction { + const handleScrollToBottomAndResetNavigationCursors = useCallback(() => { checkpointJumpCursorRef.current = null + userMessageJumpCursorRef.current = null handleScrollToBottomClick() }, [handleScrollToBottomClick]) + const handleScrollToPreviousUserMessage = useCallback(() => { + if (userMessageIndices.length === 0 || userMessageJumpCursorRef.current === 0) { + return + } + + const previousCursor = userMessageJumpCursorRef.current + const nextCursor = previousCursor === null ? userMessageIndices.length - 1 : previousCursor - 1 + userMessageJumpCursorRef.current = nextCursor + + enterUserBrowsingHistory("keyboard-nav-up") + virtuosoRef.current?.scrollToIndex({ + index: userMessageIndices[nextCursor], + align: "center", + behavior: "smooth", + }) + }, [enterUserBrowsingHistory, userMessageIndices]) + const handleScrollToLatestCheckpoint = useCallback(() => { if (checkpointIndices.length === 0) { return @@ -1642,7 +1677,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction - {showScrollToBottom && !hasApprovalButtons ? ( - <> - - - + {!hasApprovalButtons ? ( +
+ {hasUserMessages && ( + + )} + {showScrollToBottom && ( + + + + )} {hasLatestCheckpoint && ( )} - +
) : ( <> + {hasUserMessages && ( + + )} {showScrollToBottom && ( diff --git a/webview-ui/src/components/chat/PreviousUserMessageButton.tsx b/webview-ui/src/components/chat/PreviousUserMessageButton.tsx new file mode 100644 index 0000000000..1ffa2e5072 --- /dev/null +++ b/webview-ui/src/components/chat/PreviousUserMessageButton.tsx @@ -0,0 +1,18 @@ +import { Button, StandardTooltip } from "@src/components/ui" + +interface PreviousUserMessageButtonProps { + title: string + className?: string + onClick: () => void +} + +export const PreviousUserMessageButton = ({ title, className, onClick }: PreviousUserMessageButtonProps) => ( + + + +) diff --git a/webview-ui/src/components/chat/__tests__/ChatView.scroll-debug-repro.spec.tsx b/webview-ui/src/components/chat/__tests__/ChatView.scroll-debug-repro.spec.tsx index 56b008b862..9073277175 100644 --- a/webview-ui/src/components/chat/__tests__/ChatView.scroll-debug-repro.spec.tsx +++ b/webview-ui/src/components/chat/__tests__/ChatView.scroll-debug-repro.spec.tsx @@ -232,6 +232,15 @@ const buildMessagesWithMultipleCheckpoints = (baseTs: number): ClineMessage[] => { type: "say", say: "text", ts: baseTs + 6, text: "row-6" }, ] +const buildMessagesWithUserFeedback = (baseTs: number): ClineMessage[] => [ + { type: "say", say: "text", ts: baseTs, text: "task" }, + { type: "say", say: "text", ts: baseTs + 1, text: "row-1" }, + { type: "say", say: "user_feedback", ts: baseTs + 2, text: "feedback-1" }, + { type: "say", say: "text", ts: baseTs + 3, text: "row-3" }, + { type: "say", say: "user_feedback", ts: baseTs + 4, text: "feedback-2" }, + { type: "say", say: "text", ts: baseTs + 5, text: "row-5" }, +] + const resolveFollowOutput = (isAtBottom: boolean): "auto" | false => { const followOutput = harness.followOutput if (typeof followOutput === "function") { @@ -361,6 +370,15 @@ const getScrollToCheckpointButton = (): HTMLButtonElement => { return button } +const getPreviousUserMessageButton = (): HTMLButtonElement => { + const button = document.querySelector("button[aria-label='chat:jumpToPreviousUserMessage']") + if (!(button instanceof HTMLButtonElement)) { + throw new Error("Expected jump-to-previous-user-message button") + } + + return button +} + const getButtonByText = (text: string): HTMLButtonElement => { const button = Array.from(document.querySelectorAll("button")).find((candidate) => candidate.textContent === text) if (!(button instanceof HTMLButtonElement)) { @@ -631,6 +649,68 @@ describe("ChatView scroll behavior regression coverage", () => { await expectChevronHidden() }) + it("only shows previous-user-message navigation when follow-up messages exist", async () => { + await hydrate(2) + await waitForCalls(2) + await waitForCallsSettled() + + expect(document.querySelector("button[aria-label='chat:jumpToPreviousUserMessage']")).toBeNull() + + await act(async () => { + postState(buildMessagesWithUserFeedback(Date.now() - 3_000)) + }) + await flushEffects() + + expect(getPreviousUserMessageButton()).toBeVisible() + }) + + it("steps backward through user messages and stops at the oldest", async () => { + await hydrate(2, buildMessagesWithUserFeedback(Date.now() - 3_000)) + await waitForCalls(2) + await waitForCallsSettled() + + const button = getPreviousUserMessageButton() + const callsBeforeClick = harness.scrollCalls + + await act(async () => { + button.click() + }) + expect(harness.scrollToIndexArgs.at(-1)).toMatchObject({ index: 3, align: "center", behavior: "smooth" }) + + await act(async () => { + button.click() + }) + expect(harness.scrollToIndexArgs.at(-1)).toMatchObject({ index: 1, align: "center", behavior: "smooth" }) + + await act(async () => { + button.click() + }) + expect(harness.scrollCalls).toBe(callsBeforeClick + 2) + }) + + it("resets previous-user-message navigation when a new user message arrives", async () => { + const baseTs = Date.now() - 3_000 + const initialMessages = buildMessagesWithUserFeedback(baseTs) + await hydrate(2, initialMessages) + await waitForCalls(2) + await waitForCallsSettled() + + await act(async () => { + getPreviousUserMessageButton().click() + getPreviousUserMessageButton().click() + }) + + await act(async () => { + postState([...initialMessages, { type: "say", say: "user_feedback", ts: baseTs + 6, text: "feedback-3" }]) + }) + await flushEffects() + + await act(async () => { + getPreviousUserMessageButton().click() + }) + expect(harness.scrollToIndexArgs.at(-1)).toMatchObject({ index: 5, align: "center", behavior: "smooth" }) + }) + it("shows jump-to-checkpoint button and scrolls to latest checkpoint", async () => { await hydrate(2, buildMessagesWithCheckpoint(Date.now() - 3_000)) await waitForCalls(2) diff --git a/webview-ui/src/components/chat/__tests__/PreviousUserMessageButton.visual.tsx b/webview-ui/src/components/chat/__tests__/PreviousUserMessageButton.visual.tsx new file mode 100644 index 0000000000..5e3f9b424d --- /dev/null +++ b/webview-ui/src/components/chat/__tests__/PreviousUserMessageButton.visual.tsx @@ -0,0 +1,25 @@ +import { expect, test } from "../../../../playwright/coverage-fixture" +import { mountedStory } from "../../../../playwright/mounted-story" + +const themes = [ + { name: "dark", bodyClass: "vscode-dark", themeId: "Default Dark Modern" }, + { name: "light", bodyClass: "vscode-light", themeId: "Default Light Modern" }, +] as const + +for (const theme of themes) { + test(`renders previous-user-message navigation in the VS Code ${theme.name} theme`, async ({ mount, page }) => { + const component = mountedStory(await mount("previous-user-message-button")) + await page.evaluate(({ bodyClass, themeId }) => { + document.documentElement.className = bodyClass + document.body.className = bodyClass + document.body.dataset.vscodeThemeId = themeId + }, theme) + + await component.evaluate(async () => { + await document.fonts.ready + await new Promise((resolve) => requestAnimationFrame(() => resolve())) + }) + + await expect(component).toHaveScreenshot(`previous-user-message-button-${theme.name}.png`) + }) +} diff --git a/webview-ui/src/components/chat/__tests__/__screenshots__/previous-user-message-button-dark.png b/webview-ui/src/components/chat/__tests__/__screenshots__/previous-user-message-button-dark.png new file mode 100644 index 0000000000..50ede537b9 Binary files /dev/null and b/webview-ui/src/components/chat/__tests__/__screenshots__/previous-user-message-button-dark.png differ diff --git a/webview-ui/src/components/chat/__tests__/__screenshots__/previous-user-message-button-light.png b/webview-ui/src/components/chat/__tests__/__screenshots__/previous-user-message-button-light.png new file mode 100644 index 0000000000..4d23bf03da Binary files /dev/null and b/webview-ui/src/components/chat/__tests__/__screenshots__/previous-user-message-button-light.png differ diff --git a/webview-ui/src/i18n/locales/ca/chat.json b/webview-ui/src/i18n/locales/ca/chat.json index 87cec9f61f..2d9f898ed8 100644 --- a/webview-ui/src/i18n/locales/ca/chat.json +++ b/webview-ui/src/i18n/locales/ca/chat.json @@ -98,6 +98,7 @@ "enqueueMessage": "Afegeix el missatge a la cua (s'enviarà quan acabi la tasca actual)", "scrollToBottom": "Desplaça't al final del xat", "scrollToLatestCheckpoint": "Desplaça't al checkpoint anterior", + "jumpToPreviousUserMessage": "Salta al missatge anterior que has enviat", "about": "Zoo Code és tot un equip de desenvolupament d'IA al teu editor.", "docs": "Consulta els nostres documents per a més informació.", "onboarding": "La teva llista de tasques en aquest espai de treball està buida.", diff --git a/webview-ui/src/i18n/locales/de/chat.json b/webview-ui/src/i18n/locales/de/chat.json index b9591fe3aa..36ee5ab9de 100644 --- a/webview-ui/src/i18n/locales/de/chat.json +++ b/webview-ui/src/i18n/locales/de/chat.json @@ -98,6 +98,7 @@ "enqueueMessage": "Nachricht zur Warteschlange hinzufügen (wird nach Abschluss der aktuellen Aufgabe gesendet)", "scrollToBottom": "Zum Chat-Ende scrollen", "scrollToLatestCheckpoint": "Zum vorherigen Checkpoint scrollen", + "jumpToPreviousUserMessage": "Zur vorherigen von dir gesendeten Nachricht springen", "about": "Zoo Code ist ein ganzes KI-Entwicklerteam in deinem Editor.", "docs": "Schau in unsere Dokumentation, um mehr zu erfahren.", "onboarding": "Deine Aufgabenliste in diesem Arbeitsbereich ist leer.", diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index fe241f6145..cf433fbfe4 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -125,6 +125,7 @@ }, "scrollToBottom": "Scroll to bottom of chat", "scrollToLatestCheckpoint": "Scroll to previous checkpoint", + "jumpToPreviousUserMessage": "Jump to previous message you sent", "about": "Zoo is a whole AI dev team in your editor", "docs": "Check our docs to get started", "onboarding": "What would you like to do?", diff --git a/webview-ui/src/i18n/locales/es/chat.json b/webview-ui/src/i18n/locales/es/chat.json index 1cbeab08e4..7d9bec0b89 100644 --- a/webview-ui/src/i18n/locales/es/chat.json +++ b/webview-ui/src/i18n/locales/es/chat.json @@ -98,6 +98,7 @@ "enqueueMessage": "Agregar mensaje a la cola (se enviará después de que termine la tarea actual)", "scrollToBottom": "Desplazarse al final del chat", "scrollToLatestCheckpoint": "Desplazarse al checkpoint anterior", + "jumpToPreviousUserMessage": "Saltar al mensaje anterior que enviaste", "about": "Zoo Code es todo un equipo de desarrollo de IA en tu editor.", "docs": "Consulta nuestra documentación para saber más.", "onboarding": "Tu lista de tareas en este espacio de trabajo está vacía.", diff --git a/webview-ui/src/i18n/locales/fr/chat.json b/webview-ui/src/i18n/locales/fr/chat.json index 7ab8213cd5..6f698da532 100644 --- a/webview-ui/src/i18n/locales/fr/chat.json +++ b/webview-ui/src/i18n/locales/fr/chat.json @@ -98,6 +98,7 @@ "enqueueMessage": "Ajouter le message à la file d'attente (sera envoyé après la fin de la tâche en cours)", "scrollToBottom": "Défiler jusqu'au bas du chat", "scrollToLatestCheckpoint": "Défiler jusqu'au checkpoint précédent", + "jumpToPreviousUserMessage": "Accéder au message précédent que vous avez envoyé", "about": "Zoo Code est une équipe complète de développeurs IA dans votre éditeur.", "docs": "Consultez notre documentation pour en savoir plus.", "onboarding": "Votre liste de tâches dans cet espace de travail est vide.", diff --git a/webview-ui/src/i18n/locales/hi/chat.json b/webview-ui/src/i18n/locales/hi/chat.json index 8b57cefc89..5fa9c8e237 100644 --- a/webview-ui/src/i18n/locales/hi/chat.json +++ b/webview-ui/src/i18n/locales/hi/chat.json @@ -98,6 +98,7 @@ "enqueueMessage": "संदेश को कतार में जोड़ें (वर्तमान कार्य पूरा होने के बाद भेजा जाएगा)", "scrollToBottom": "चैट के निचले हिस्से तक स्क्रॉल करें", "scrollToLatestCheckpoint": "पिछले चेकपॉइंट तक स्क्रॉल करें", + "jumpToPreviousUserMessage": "आपके भेजे गए पिछले संदेश पर जाएँ", "about": "Zoo Code आपके संपादक में एक पूरी AI देव टीम है।", "docs": "और जानने के लिए हमारे दस्तावेज़ देखें।", "onboarding": "इस कार्यक्षेत्र में आपकी कार्य सूची खाली है।", diff --git a/webview-ui/src/i18n/locales/id/chat.json b/webview-ui/src/i18n/locales/id/chat.json index fe3d6808d8..99e38d82b6 100644 --- a/webview-ui/src/i18n/locales/id/chat.json +++ b/webview-ui/src/i18n/locales/id/chat.json @@ -128,6 +128,7 @@ "enqueueMessage": "Tambahkan pesan ke antrean (akan dikirim setelah tugas saat ini selesai)", "scrollToBottom": "Gulir ke bawah chat", "scrollToLatestCheckpoint": "Gulir ke checkpoint sebelumnya", + "jumpToPreviousUserMessage": "Lompat ke pesan sebelumnya yang Anda kirim", "about": "Zoo Code adalah seluruh tim pengembang AI di editor Anda.", "docs": "Lihat dokumentasi kami untuk mempelajari lebih lanjut.", "onboarding": "Daftar tugas Anda di ruang kerja ini kosong.", diff --git a/webview-ui/src/i18n/locales/it/chat.json b/webview-ui/src/i18n/locales/it/chat.json index 9dcfd68997..ae9727bc8c 100644 --- a/webview-ui/src/i18n/locales/it/chat.json +++ b/webview-ui/src/i18n/locales/it/chat.json @@ -98,6 +98,7 @@ "enqueueMessage": "Aggiungi il messaggio alla coda (sarà inviato dopo che l'attività corrente sarà terminata)", "scrollToBottom": "Scorri fino alla fine della chat", "scrollToLatestCheckpoint": "Scorri al checkpoint precedente", + "jumpToPreviousUserMessage": "Vai al messaggio precedente che hai inviato", "about": "Zoo Code è un intero team di sviluppo AI nel tuo editor.", "docs": "Consulta la nostra documentazione per saperne di più.", "onboarding": "La tua lista di attività in questo spazio di lavoro è vuota.", diff --git a/webview-ui/src/i18n/locales/ja/chat.json b/webview-ui/src/i18n/locales/ja/chat.json index f098c68879..eafb0e8eb3 100644 --- a/webview-ui/src/i18n/locales/ja/chat.json +++ b/webview-ui/src/i18n/locales/ja/chat.json @@ -98,6 +98,7 @@ "enqueueMessage": "メッセージをキューに追加(現在のタスク完了後に送信されます)", "scrollToBottom": "チャットの最下部にスクロール", "scrollToLatestCheckpoint": "前のチェックポイントまでスクロール", + "jumpToPreviousUserMessage": "以前に送信したメッセージに移動", "about": "Zoo Codeは、エディタに常駐するAI開発チームです。", "docs": "詳細については、ドキュメントをご確認ください。", "onboarding": "このワークスペースのタスクリストは空です。", diff --git a/webview-ui/src/i18n/locales/ko/chat.json b/webview-ui/src/i18n/locales/ko/chat.json index 7c1b51f934..6a7500439f 100644 --- a/webview-ui/src/i18n/locales/ko/chat.json +++ b/webview-ui/src/i18n/locales/ko/chat.json @@ -98,6 +98,7 @@ "enqueueMessage": "메시지를 대기열에 추가 (현재 작업 완료 후 전송)", "scrollToBottom": "채팅 하단으로 스크롤", "scrollToLatestCheckpoint": "이전 체크포인트로 스크롤", + "jumpToPreviousUserMessage": "이전에 보낸 메시지로 이동", "about": "Zoo Code는 편집기 안에 있는 전체 AI 개발팀입니다.", "docs": "더 알아보려면 문서를 확인하세요.", "onboarding": "이 작업 공간의 작업 목록이 비어 있습니다.", diff --git a/webview-ui/src/i18n/locales/nl/chat.json b/webview-ui/src/i18n/locales/nl/chat.json index d75e451b81..ab68dba20e 100644 --- a/webview-ui/src/i18n/locales/nl/chat.json +++ b/webview-ui/src/i18n/locales/nl/chat.json @@ -98,6 +98,7 @@ "enqueueMessage": "Bericht aan de wachtrij toevoegen (wordt verzonden nadat de huidige taak is voltooid)", "scrollToBottom": "Scroll naar onderaan de chat", "scrollToLatestCheckpoint": "Scroll naar het vorige checkpoint", + "jumpToPreviousUserMessage": "Ga naar je vorige bericht", "about": "Zoo Code is een heel AI-ontwikkelteam in je editor.", "docs": "Bekijk onze documentatie voor meer informatie.", "onboarding": "Je takenlijst in deze werkruimte is leeg.", diff --git a/webview-ui/src/i18n/locales/pl/chat.json b/webview-ui/src/i18n/locales/pl/chat.json index 9769c48201..990865ea07 100644 --- a/webview-ui/src/i18n/locales/pl/chat.json +++ b/webview-ui/src/i18n/locales/pl/chat.json @@ -98,6 +98,7 @@ "enqueueMessage": "Dodaj wiadomość do kolejki (zostanie wysłana po zakończeniu bieżącego zadania)", "scrollToBottom": "Przewiń do dołu czatu", "scrollToLatestCheckpoint": "Przewiń do poprzedniego punktu kontrolnego", + "jumpToPreviousUserMessage": "Przejdź do poprzedniej wysłanej wiadomości", "about": "Zoo Code to cały zespół deweloperów AI w Twoim edytorze.", "docs": "Sprawdź naszą dokumentację, aby dowiedzieć się więcej.", "onboarding": "Twoja lista zadań w tym obszarze roboczym jest pusta.", diff --git a/webview-ui/src/i18n/locales/pt-BR/chat.json b/webview-ui/src/i18n/locales/pt-BR/chat.json index 1ce9610adc..18a90c3bde 100644 --- a/webview-ui/src/i18n/locales/pt-BR/chat.json +++ b/webview-ui/src/i18n/locales/pt-BR/chat.json @@ -98,6 +98,7 @@ "enqueueMessage": "Adicionar mensagem à fila (será enviada após a conclusão da tarefa atual)", "scrollToBottom": "Rolar para o final do chat", "scrollToLatestCheckpoint": "Rolar para o checkpoint anterior", + "jumpToPreviousUserMessage": "Ir para a mensagem anterior que você enviou", "about": "Zoo Code é uma equipe inteira de desenvolvimento de IA em seu editor.", "docs": "Confira nossa documentação para saber mais.", "onboarding": "Sua lista de tarefas neste espaço de trabalho está vazia.", diff --git a/webview-ui/src/i18n/locales/ru/chat.json b/webview-ui/src/i18n/locales/ru/chat.json index 4195d4d705..64f5853558 100644 --- a/webview-ui/src/i18n/locales/ru/chat.json +++ b/webview-ui/src/i18n/locales/ru/chat.json @@ -98,6 +98,7 @@ "enqueueMessage": "Добавить сообщение в очередь (будет отправлено после завершения текущей задачи)", "scrollToBottom": "Прокрутить чат вниз", "scrollToLatestCheckpoint": "Прокрутить к предыдущему чекпоинту", + "jumpToPreviousUserMessage": "Перейти к предыдущему отправленному вами сообщению", "about": "Zoo Code — это целая команда разработчиков ИИ в вашем редакторе.", "docs": "Ознакомьтесь с нашей документацией, чтобы узнать больше.", "onboarding": "Ваш список задач в этом рабочем пространстве пуст.", diff --git a/webview-ui/src/i18n/locales/tr/chat.json b/webview-ui/src/i18n/locales/tr/chat.json index 9fdffcd14b..c8ee7e4948 100644 --- a/webview-ui/src/i18n/locales/tr/chat.json +++ b/webview-ui/src/i18n/locales/tr/chat.json @@ -98,6 +98,7 @@ "enqueueMessage": "Mesajı kuyruğa ekle (mevcut görev tamamlandıktan sonra gönderilecek)", "scrollToBottom": "Sohbetin altına kaydır", "scrollToLatestCheckpoint": "Önceki checkpoint'e kaydır", + "jumpToPreviousUserMessage": "Gönderdiğiniz önceki mesaja atla", "about": "Zoo Code, düzenleyicinizdeki bütün bir yapay zeka geliştirme ekibidir.", "docs": "Daha fazla bilgi için belgelerimize göz atın.", "onboarding": "Bu çalışma alanındaki görev listeniz boş.", diff --git a/webview-ui/src/i18n/locales/vi/chat.json b/webview-ui/src/i18n/locales/vi/chat.json index 2812fbdccf..c1998d431e 100644 --- a/webview-ui/src/i18n/locales/vi/chat.json +++ b/webview-ui/src/i18n/locales/vi/chat.json @@ -98,6 +98,7 @@ "enqueueMessage": "Thêm tin nhắn vào hàng đợi (sẽ gửi sau khi nhiệm vụ hiện tại hoàn tất)", "scrollToBottom": "Cuộn xuống cuối cuộc trò chuyện", "scrollToLatestCheckpoint": "Cuộn đến checkpoint trước đó", + "jumpToPreviousUserMessage": "Chuyển đến tin nhắn trước đó bạn đã gửi", "about": "Zoo Code là một đội ngũ phát triển AI đầy đủ trong trình chỉnh sửa của bạn.", "docs": "Kiểm tra tài liệu của chúng tôi để tìm hiểu thêm.", "onboarding": "Danh sách nhiệm vụ của bạn trong không gian làm việc này đang trống.", diff --git a/webview-ui/src/i18n/locales/zh-CN/chat.json b/webview-ui/src/i18n/locales/zh-CN/chat.json index 5575dad5df..ec8055f3d5 100644 --- a/webview-ui/src/i18n/locales/zh-CN/chat.json +++ b/webview-ui/src/i18n/locales/zh-CN/chat.json @@ -98,6 +98,7 @@ "enqueueMessage": "将消息加入队列(当前任务完成后发送)", "scrollToBottom": "滚动到聊天底部", "scrollToLatestCheckpoint": "滚动到上一个检查点", + "jumpToPreviousUserMessage": "跳转到你之前发送的消息", "about": "Zoo Code 是您编辑器中的整个 AI 开发团队。", "docs": "查看我们的 文档 了解更多信息。", "onboarding": "此工作区中的任务列表为空。", diff --git a/webview-ui/src/i18n/locales/zh-TW/chat.json b/webview-ui/src/i18n/locales/zh-TW/chat.json index 184728d6a5..9c043489cc 100644 --- a/webview-ui/src/i18n/locales/zh-TW/chat.json +++ b/webview-ui/src/i18n/locales/zh-TW/chat.json @@ -125,6 +125,7 @@ }, "scrollToBottom": "捲動至對話底部", "scrollToLatestCheckpoint": "捲動至上一個檢查點", + "jumpToPreviousUserMessage": "跳至你先前傳送的訊息", "about": "Zoo 是編輯器中的完整 AI 開發團隊。", "docs": "請參閱 說明文件 開始使用。", "onboarding": "想要做什麼呢?",