diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 4f495d27d135..a74ba60261c3 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -224,6 +224,7 @@ export const PromptInput: Component = (props) => { mode: store.mode, commentCount: commentCount(), example: language.t(EXAMPLES[store.placeholder]), + agent: local.agent.current(), t: (key, params) => language.t(key as Parameters[0], params as never), }), ) diff --git a/packages/app/src/components/prompt-input/placeholder.test.ts b/packages/app/src/components/prompt-input/placeholder.test.ts index b633df829561..26c535f2709e 100644 --- a/packages/app/src/components/prompt-input/placeholder.test.ts +++ b/packages/app/src/components/prompt-input/placeholder.test.ts @@ -2,7 +2,13 @@ import { describe, expect, test } from "bun:test" import { promptPlaceholder } from "./placeholder" describe("promptPlaceholder", () => { - const t = (key: string, params?: Record) => `${key}${params?.example ? `:${params.example}` : ""}` + const t = (key: string, params?: Record) => { + const parts = [key] + if (params?.example) parts.push(params.example) + if (params?.name) parts.push(params.name) + if (params?.description) parts.push(params.description) + return parts.join(":") + } test("returns shell placeholder in shell mode", () => { const value = promptPlaceholder({ @@ -23,6 +29,28 @@ describe("promptPlaceholder", () => { ) }) + test("returns agent description placeholder when agent has description", () => { + const value = promptPlaceholder({ + mode: "normal", + commentCount: 0, + example: "example", + agent: { name: "build", description: "a coding agent" }, + t, + }) + expect(value).toBe("prompt.placeholder.agent:build:a coding agent") + }) + + test("returns default placeholder when agent has no description", () => { + const value = promptPlaceholder({ + mode: "normal", + commentCount: 0, + example: "translated-example", + agent: { name: "build" }, + t, + }) + expect(value).toBe("prompt.placeholder.normal:translated-example") + }) + test("returns default placeholder with example", () => { const value = promptPlaceholder({ mode: "normal", diff --git a/packages/app/src/components/prompt-input/placeholder.ts b/packages/app/src/components/prompt-input/placeholder.ts index 07f6a43b510f..be116b099ea2 100644 --- a/packages/app/src/components/prompt-input/placeholder.ts +++ b/packages/app/src/components/prompt-input/placeholder.ts @@ -2,6 +2,7 @@ type PromptPlaceholderInput = { mode: "normal" | "shell" commentCount: number example: string + agent?: { name: string; description?: string } t: (key: string, params?: Record) => string } @@ -9,5 +10,7 @@ export function promptPlaceholder(input: PromptPlaceholderInput) { if (input.mode === "shell") return input.t("prompt.placeholder.shell") if (input.commentCount > 1) return input.t("prompt.placeholder.summarizeComments") if (input.commentCount === 1) return input.t("prompt.placeholder.summarizeComment") + if (input.agent?.description) + return input.t("prompt.placeholder.agent", { name: input.agent.name, description: input.agent.description }) return input.t("prompt.placeholder.normal", { example: input.example }) } diff --git a/packages/app/src/i18n/ar.ts b/packages/app/src/i18n/ar.ts index e3792a3c3cc3..5df9c53548fd 100644 --- a/packages/app/src/i18n/ar.ts +++ b/packages/app/src/i18n/ar.ts @@ -208,6 +208,7 @@ export const dict = { "prompt.placeholder.normal": 'اسأل أي شيء... "{{example}}"', "prompt.placeholder.summarizeComments": "لخّص التعليقات…", "prompt.placeholder.summarizeComment": "لخّص التعليق…", + "prompt.placeholder.agent": "أنا {{name}}، {{description}}", "prompt.mode.shell": "Shell", "prompt.mode.shell.exit": "esc للخروج", "prompt.example.1": "إصلاح TODO في قاعدة التعليمات البرمجية", diff --git a/packages/app/src/i18n/br.ts b/packages/app/src/i18n/br.ts index 07d6ce467aeb..82b286ae9620 100644 --- a/packages/app/src/i18n/br.ts +++ b/packages/app/src/i18n/br.ts @@ -208,6 +208,7 @@ export const dict = { "prompt.placeholder.normal": 'Pergunte qualquer coisa... "{{example}}"', "prompt.placeholder.summarizeComments": "Resumir comentários…", "prompt.placeholder.summarizeComment": "Resumir comentário…", + "prompt.placeholder.agent": "Eu sou {{name}}, {{description}}", "prompt.mode.shell": "Shell", "prompt.mode.shell.exit": "esc para sair", "prompt.example.1": "Corrigir um TODO no código", diff --git a/packages/app/src/i18n/bs.ts b/packages/app/src/i18n/bs.ts index 7d10da6ed875..bbacbb6f0a3d 100644 --- a/packages/app/src/i18n/bs.ts +++ b/packages/app/src/i18n/bs.ts @@ -226,6 +226,7 @@ export const dict = { "prompt.placeholder.normal": 'Pitaj bilo šta... "{{example}}"', "prompt.placeholder.summarizeComments": "Sažmi komentare…", "prompt.placeholder.summarizeComment": "Sažmi komentar…", + "prompt.placeholder.agent": "Ja sam {{name}}, {{description}}", "prompt.mode.shell": "Shell", "prompt.mode.shell.exit": "esc za izlaz", diff --git a/packages/app/src/i18n/da.ts b/packages/app/src/i18n/da.ts index ac5c4d494b12..89212f558bca 100644 --- a/packages/app/src/i18n/da.ts +++ b/packages/app/src/i18n/da.ts @@ -224,6 +224,7 @@ export const dict = { "prompt.placeholder.normal": 'Spørg om hvad som helst... "{{example}}"', "prompt.placeholder.summarizeComments": "Opsummér kommentarer…", "prompt.placeholder.summarizeComment": "Opsummér kommentar…", + "prompt.placeholder.agent": "Jeg er {{name}}, {{description}}", "prompt.mode.shell": "Shell", "prompt.mode.shell.exit": "esc for at afslutte", diff --git a/packages/app/src/i18n/de.ts b/packages/app/src/i18n/de.ts index 99a950631074..eddabcdd9774 100644 --- a/packages/app/src/i18n/de.ts +++ b/packages/app/src/i18n/de.ts @@ -213,6 +213,7 @@ export const dict = { "prompt.placeholder.normal": 'Fragen Sie alles... "{{example}}"', "prompt.placeholder.summarizeComments": "Kommentare zusammenfassen…", "prompt.placeholder.summarizeComment": "Kommentar zusammenfassen…", + "prompt.placeholder.agent": "Ich bin {{name}}, {{description}}", "prompt.mode.shell": "Shell", "prompt.mode.shell.exit": "esc zum Verlassen", "prompt.example.1": "Ein TODO in der Codebasis beheben", diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index c138c7b61456..9376620771ff 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -225,6 +225,7 @@ export const dict = { "prompt.placeholder.normal": 'Ask anything... "{{example}}"', "prompt.placeholder.summarizeComments": "Summarize comments…", "prompt.placeholder.summarizeComment": "Summarize comment…", + "prompt.placeholder.agent": "I am {{name}}, {{description}}", "prompt.mode.shell": "Shell", "prompt.mode.shell.exit": "esc to exit", diff --git a/packages/app/src/i18n/es.ts b/packages/app/src/i18n/es.ts index 7a6c4974e091..0cc8044d12bf 100644 --- a/packages/app/src/i18n/es.ts +++ b/packages/app/src/i18n/es.ts @@ -225,6 +225,7 @@ export const dict = { "prompt.placeholder.normal": 'Pregunta cualquier cosa... "{{example}}"', "prompt.placeholder.summarizeComments": "Resumir comentarios…", "prompt.placeholder.summarizeComment": "Resumir comentario…", + "prompt.placeholder.agent": "Soy {{name}}, {{description}}", "prompt.mode.shell": "Shell", "prompt.mode.shell.exit": "esc para salir", diff --git a/packages/app/src/i18n/fr.ts b/packages/app/src/i18n/fr.ts index fc3bf2667943..f696c876f810 100644 --- a/packages/app/src/i18n/fr.ts +++ b/packages/app/src/i18n/fr.ts @@ -208,6 +208,7 @@ export const dict = { "prompt.placeholder.normal": 'Demandez n\'importe quoi... "{{example}}"', "prompt.placeholder.summarizeComments": "Résumer les commentaires…", "prompt.placeholder.summarizeComment": "Résumer le commentaire…", + "prompt.placeholder.agent": "Je suis {{name}}, {{description}}", "prompt.mode.shell": "Shell", "prompt.mode.shell.exit": "esc pour quitter", "prompt.example.1": "Corriger un TODO dans la base de code", diff --git a/packages/app/src/i18n/ja.ts b/packages/app/src/i18n/ja.ts index b597db02a586..ea65a7cce368 100644 --- a/packages/app/src/i18n/ja.ts +++ b/packages/app/src/i18n/ja.ts @@ -207,6 +207,7 @@ export const dict = { "prompt.placeholder.normal": '何でも聞いてください... "{{example}}"', "prompt.placeholder.summarizeComments": "コメントを要約…", "prompt.placeholder.summarizeComment": "コメントを要約…", + "prompt.placeholder.agent": "私は {{name}} です。{{description}}", "prompt.mode.shell": "シェル", "prompt.mode.shell.exit": "escで終了", "prompt.example.1": "コードベースのTODOを修正", diff --git a/packages/app/src/i18n/ko.ts b/packages/app/src/i18n/ko.ts index 525bd035651f..423e0e4cf268 100644 --- a/packages/app/src/i18n/ko.ts +++ b/packages/app/src/i18n/ko.ts @@ -211,6 +211,7 @@ export const dict = { "prompt.placeholder.normal": '무엇이든 물어보세요... "{{example}}"', "prompt.placeholder.summarizeComments": "댓글 요약…", "prompt.placeholder.summarizeComment": "댓글 요약…", + "prompt.placeholder.agent": "저는 {{name}}입니다. {{description}}", "prompt.mode.shell": "셸", "prompt.mode.shell.exit": "종료하려면 esc", "prompt.example.1": "코드베이스의 TODO 수정", diff --git a/packages/app/src/i18n/no.ts b/packages/app/src/i18n/no.ts index 98e79e1896af..5c0fc4986be1 100644 --- a/packages/app/src/i18n/no.ts +++ b/packages/app/src/i18n/no.ts @@ -228,6 +228,7 @@ export const dict = { "prompt.placeholder.normal": 'Spør om hva som helst... "{{example}}"', "prompt.placeholder.summarizeComments": "Oppsummer kommentarer…", "prompt.placeholder.summarizeComment": "Oppsummer kommentar…", + "prompt.placeholder.agent": "Jeg er {{name}}, {{description}}", "prompt.mode.shell": "Shell", "prompt.mode.shell.exit": "ESC for å avslutte", diff --git a/packages/app/src/i18n/pl.ts b/packages/app/src/i18n/pl.ts index 983c9c14ac1b..f866ff339e3a 100644 --- a/packages/app/src/i18n/pl.ts +++ b/packages/app/src/i18n/pl.ts @@ -209,6 +209,7 @@ export const dict = { "prompt.placeholder.normal": 'Zapytaj o cokolwiek... "{{example}}"', "prompt.placeholder.summarizeComments": "Podsumuj komentarze…", "prompt.placeholder.summarizeComment": "Podsumuj komentarz…", + "prompt.placeholder.agent": "Jestem {{name}}, {{description}}", "prompt.mode.shell": "Terminal", "prompt.mode.shell.exit": "esc aby wyjść", "prompt.example.1": "Napraw TODO w bazie kodu", diff --git a/packages/app/src/i18n/ru.ts b/packages/app/src/i18n/ru.ts index f2c87fe0f1ed..66204c60b827 100644 --- a/packages/app/src/i18n/ru.ts +++ b/packages/app/src/i18n/ru.ts @@ -225,6 +225,7 @@ export const dict = { "prompt.placeholder.normal": 'Спросите что угодно... "{{example}}"', "prompt.placeholder.summarizeComments": "Суммировать комментарии…", "prompt.placeholder.summarizeComment": "Суммировать комментарий…", + "prompt.placeholder.agent": "Я {{name}}, {{description}}", "prompt.mode.shell": "Оболочка", "prompt.mode.shell.exit": "esc для выхода", diff --git a/packages/app/src/i18n/th.ts b/packages/app/src/i18n/th.ts index 689e82118968..47ddef446a8e 100644 --- a/packages/app/src/i18n/th.ts +++ b/packages/app/src/i18n/th.ts @@ -225,6 +225,7 @@ export const dict = { "prompt.placeholder.normal": 'ถามอะไรก็ได้... "{{example}}"', "prompt.placeholder.summarizeComments": "สรุปความคิดเห็น…", "prompt.placeholder.summarizeComment": "สรุปความคิดเห็น…", + "prompt.placeholder.agent": "ฉันคือ {{name}}, {{description}}", "prompt.mode.shell": "เชลล์", "prompt.mode.shell.exit": "กด esc เพื่อออก", diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index 1b40013b60de..cc475e807478 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -246,6 +246,7 @@ export const dict = { "prompt.placeholder.normal": '随便问点什么... "{{example}}"', "prompt.placeholder.summarizeComments": "总结评论…", "prompt.placeholder.summarizeComment": "总结该评论…", + "prompt.placeholder.agent": "我是 {{name}}, {{description}}", "prompt.mode.shell": "Shell", "prompt.mode.shell.exit": "按 esc 退出", "prompt.example.1": "修复代码库中的一个 TODO", diff --git a/packages/app/src/i18n/zht.ts b/packages/app/src/i18n/zht.ts index 34aec01b9cb1..2a61dfc15cd8 100644 --- a/packages/app/src/i18n/zht.ts +++ b/packages/app/src/i18n/zht.ts @@ -225,6 +225,7 @@ export const dict = { "prompt.placeholder.normal": '隨便問點什麼... "{{example}}"', "prompt.placeholder.summarizeComments": "摘要評論…", "prompt.placeholder.summarizeComment": "摘要這則評論…", + "prompt.placeholder.agent": "我是 {{name}}, {{description}}", "prompt.mode.shell": "Shell", "prompt.mode.shell.exit": "按 esc 退出", diff --git a/packages/app/src/pages/directory-layout.tsx b/packages/app/src/pages/directory-layout.tsx index f36bb7ab4e85..95cba95ec303 100644 --- a/packages/app/src/pages/directory-layout.tsx +++ b/packages/app/src/pages/directory-layout.tsx @@ -11,6 +11,8 @@ import { decode64 } from "@/utils/base64" import { showToast } from "@opencode-ai/ui/toast" import { useLanguage } from "@/context/language" +import { base64Encode } from "@opencode-ai/util/encode" + export default function Layout(props: ParentProps) { const params = useParams() const navigate = useNavigate() @@ -61,6 +63,26 @@ export default function Layout(props: ParentProps) { const syncSession = (sessionID: string) => sync.session.sync(sessionID) + const undoMessage = async (sessionID: string, messageID: string) => { + const status = sync.data.session_status[sessionID] + if (status?.type !== "idle") { + await sdk.client.session.abort({ sessionID }).catch(() => {}) + } + await sdk.client.session.revert({ sessionID, messageID }) + } + + const forkMessage = async (sessionID: string, messageID: string) => { + const msgs = sync.data.message[sessionID] + let cutoffID: string | undefined + if (msgs) { + const idx = msgs.findIndex((m) => m.id === messageID) + if (idx !== -1 && idx + 1 < msgs.length) cutoffID = msgs[idx + 1]!.id + } + const result = await sdk.client.session.fork({ sessionID, messageID: cutoffID }) + if (!result.data) return + navigate(`/${base64Encode(sdk.directory)}/session/${result.data.id}`) + } + return ( {props.children} diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index 9453dd703c7b..c71cfb124662 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -25,6 +25,7 @@ import { useLayout } from "@/context/layout" import { checksum, base64Encode } from "@opencode-ai/util/encode" import { findLast } from "@opencode-ai/util/array" import { useDialog } from "@opencode-ai/ui/context/dialog" +import { useData } from "@opencode-ai/ui/context" import { DialogSelectFile } from "@/components/dialog-select-file" import FileTree from "@/components/file-tree" import { useCommand } from "@/context/command" @@ -40,6 +41,7 @@ import { showToast } from "@opencode-ai/ui/toast" import { SessionHeader, SessionContextTab, SortableTab, FileVisual, NewSessionView } from "@/components/session" import { navMark, navParams } from "@/utils/perf" import { same } from "@/utils/same" +import { extractPromptFromParts } from "@/utils/prompt" import { createOpenReviewFile, focusTerminalById, getTabReorderIndex } from "@/pages/session/helpers" import { createScrollSpy } from "@/pages/session/scroll-spy" import { createFileTabListSync } from "@/pages/session/file-tab-scroll" @@ -100,6 +102,7 @@ export default function Page() { const navigate = useNavigate() const sdk = useSDK() const prompt = usePrompt() + const data = useData() const comments = useComments() const permission = usePermission() @@ -117,6 +120,18 @@ export default function Page() { const blocked = createMemo(() => !!permRequest() || !!questionRequest()) + createEffect( + on(data.pendingRestore, (parts) => { + if (!parts) return + const restored = extractPromptFromParts(parts, { + directory: sdk.directory, + attachmentName: language.t("common.attachment"), + }) + data.consumeRestore() + requestAnimationFrame(() => prompt.set(restored)) + }), + ) + const [ui, setUi] = createStore({ responding: false, pendingMessage: undefined as string | undefined, diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index 544c6abdd214..f3f25b02e83c 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -55,6 +55,7 @@ const icons = { "layout-bottom-full": ``, "dot-grid": ``, "circle-check": ``, + undo: ``, copy: ``, check: ``, photo: ``, diff --git a/packages/ui/src/components/message-part.css b/packages/ui/src/components/message-part.css index 9a18810dc943..8e25d435a58c 100644 --- a/packages/ui/src/components/message-part.css +++ b/packages/ui/src/components/message-part.css @@ -89,15 +89,17 @@ color: var(--syntax-type); } - [data-slot="user-message-copy-wrapper"] { + [data-slot="user-message-actions"] { position: absolute; top: 7px; right: 7px; + display: flex; + gap: 5px; opacity: 0; transition: opacity 0.15s ease; } - &:hover [data-slot="user-message-copy-wrapper"] { + &:hover [data-slot="user-message-actions"] { opacity: 1; } } diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index 3f61b3186d30..3c67a8eaf5ce 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -302,12 +302,22 @@ export function AssistantMessageDisplay(props: { message: AssistantMessage; part } export function UserMessageDisplay(props: { message: UserMessage; parts: PartType[] }) { + const data = useData() const dialog = useDialog() const i18n = useI18n() const [copied, setCopied] = createSignal(false) const [expanded, setExpanded] = createSignal(false) const [canExpand, setCanExpand] = createSignal(false) + const [confirmingUndo, setConfirmingUndo] = createSignal(false) + const [confirmingFork, setConfirmingFork] = createSignal(false) let textRef: HTMLDivElement | undefined + let undoTimer: ReturnType | undefined + let forkTimer: ReturnType | undefined + + onCleanup(() => { + if (undoTimer) clearTimeout(undoTimer) + if (forkTimer) clearTimeout(forkTimer) + }) const updateCanExpand = () => { const el = textRef @@ -417,7 +427,61 @@ export function UserMessageDisplay(props: { message: UserMessage; parts: PartTyp > -
+
+ + + e.preventDefault()} + onClick={(event) => { + event.stopPropagation() + if (confirmingFork()) { + if (forkTimer) clearTimeout(forkTimer) + setConfirmingFork(false) + data.forkMessage?.(props.message.sessionID, props.message.id) + return + } + setConfirmingFork(true) + forkTimer = setTimeout(() => setConfirmingFork(false), 3000) + }} + aria-label={confirmingFork() ? i18n.t("ui.message.fork.confirm") : i18n.t("ui.message.fork")} + /> + + + + + e.preventDefault()} + onClick={(event) => { + event.stopPropagation() + if (confirmingUndo()) { + if (undoTimer) clearTimeout(undoTimer) + setConfirmingUndo(false) + data.undoMessage?.(props.message.sessionID, props.message.id) + return + } + setConfirmingUndo(true) + undoTimer = setTimeout(() => setConfirmingUndo(false), 3000) + }} + aria-label={confirmingUndo() ? i18n.t("ui.message.undo.confirm") : i18n.t("ui.message.undo")} + /> + + string export type SyncSessionFn = (sessionID: string) => void | Promise +export type UndoMessageFn = (sessionID: string, messageID: string) => void | Promise + +export type ForkMessageFn = (sessionID: string, messageID: string) => void | Promise + export const { use: useData, provider: DataProvider } = createSimpleContext({ name: "Data", init: (props: { @@ -63,7 +68,19 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ onNavigateToSession?: NavigateToSessionFn onSessionHref?: SessionHrefFn onSyncSession?: SyncSessionFn + onUndoMessage?: UndoMessageFn + onForkMessage?: ForkMessageFn }) => { + const [pendingRestore, setPendingRestore] = createSignal() + + const undoMessage: UndoMessageFn | undefined = props.onUndoMessage + ? async (sessionID, messageID) => { + const parts = props.data.part[messageID] ?? [] + await props.onUndoMessage!(sessionID, messageID) + if (parts.length > 0) setPendingRestore([...parts]) + } + : undefined + return { get store() { return props.data @@ -77,6 +94,14 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ navigateToSession: props.onNavigateToSession, sessionHref: props.onSessionHref, syncSession: props.onSyncSession, + undoMessage, + forkMessage: props.onForkMessage, + pendingRestore, + consumeRestore() { + const parts = pendingRestore() + setPendingRestore(undefined) + return parts + }, } }, }) diff --git a/packages/ui/src/i18n/ar.ts b/packages/ui/src/i18n/ar.ts index 7ee17e2e0102..be9f09d51e44 100644 --- a/packages/ui/src/i18n/ar.ts +++ b/packages/ui/src/i18n/ar.ts @@ -90,6 +90,11 @@ export const dict = { "ui.message.collapse": "طي الرسالة", "ui.message.copy": "نسخ", "ui.message.copied": "تم النسخ!", + "ui.message.undo": "تراجع", + "ui.message.undo.confirm": "انقر مرة أخرى للتراجع", + "ui.message.fork": "تفرع", + "ui.message.fork.confirm": "انقر مرة أخرى للتفرع", + "ui.message.fork.description": "تفرع من هذه الرسالة", "ui.message.attachment.alt": "مرفق", "ui.patch.action.deleted": "محذوف", diff --git a/packages/ui/src/i18n/br.ts b/packages/ui/src/i18n/br.ts index 6d7449d8457d..c191122e8ed0 100644 --- a/packages/ui/src/i18n/br.ts +++ b/packages/ui/src/i18n/br.ts @@ -90,6 +90,11 @@ export const dict = { "ui.message.collapse": "Recolher mensagem", "ui.message.copy": "Copiar", "ui.message.copied": "Copiado!", + "ui.message.undo": "Desfazer", + "ui.message.undo.confirm": "Clique novamente para desfazer", + "ui.message.fork": "Bifurcar", + "ui.message.fork.confirm": "Clique novamente para bifurcar", + "ui.message.fork.description": "Bifurcar a partir desta mensagem", "ui.message.attachment.alt": "anexo", "ui.patch.action.deleted": "Excluído", diff --git a/packages/ui/src/i18n/bs.ts b/packages/ui/src/i18n/bs.ts index 24e4c12068ee..cf6717f75d87 100644 --- a/packages/ui/src/i18n/bs.ts +++ b/packages/ui/src/i18n/bs.ts @@ -94,6 +94,11 @@ export const dict = { "ui.message.collapse": "Sažmi poruku", "ui.message.copy": "Kopiraj", "ui.message.copied": "Kopirano!", + "ui.message.undo": "Poništi", + "ui.message.undo.confirm": "Kliknite ponovo za poništavanje", + "ui.message.fork": "Račvaj", + "ui.message.fork.confirm": "Kliknite ponovo za račvanje", + "ui.message.fork.description": "Račvaj od ove poruke", "ui.message.attachment.alt": "prilog", "ui.patch.action.deleted": "Obrisano", diff --git a/packages/ui/src/i18n/da.ts b/packages/ui/src/i18n/da.ts index 218f3b26a494..8d3fd5d07bb8 100644 --- a/packages/ui/src/i18n/da.ts +++ b/packages/ui/src/i18n/da.ts @@ -89,6 +89,11 @@ export const dict = { "ui.message.collapse": "Skjul besked", "ui.message.copy": "Kopier", "ui.message.copied": "Kopieret!", + "ui.message.undo": "Fortryd", + "ui.message.undo.confirm": "Klik igen for at fortryde", + "ui.message.fork": "Forgren", + "ui.message.fork.confirm": "Klik igen for at forgrene", + "ui.message.fork.description": "Forgren fra denne besked", "ui.message.attachment.alt": "vedhæftning", "ui.patch.action.deleted": "Slettet", diff --git a/packages/ui/src/i18n/de.ts b/packages/ui/src/i18n/de.ts index 921a12c99675..7d7d167257a3 100644 --- a/packages/ui/src/i18n/de.ts +++ b/packages/ui/src/i18n/de.ts @@ -93,6 +93,11 @@ export const dict = { "ui.message.collapse": "Nachricht reduzieren", "ui.message.copy": "Kopieren", "ui.message.copied": "Kopiert!", + "ui.message.undo": "Rückgängig", + "ui.message.undo.confirm": "Erneut klicken zum Rückgängigmachen", + "ui.message.fork": "Verzweigen", + "ui.message.fork.confirm": "Erneut klicken zum Verzweigen", + "ui.message.fork.description": "Ab dieser Nachricht verzweigen", "ui.message.attachment.alt": "Anhang", "ui.patch.action.deleted": "Gelöscht", diff --git a/packages/ui/src/i18n/en.ts b/packages/ui/src/i18n/en.ts index 631bc660a65d..c979470df273 100644 --- a/packages/ui/src/i18n/en.ts +++ b/packages/ui/src/i18n/en.ts @@ -90,6 +90,11 @@ export const dict = { "ui.message.collapse": "Collapse message", "ui.message.copy": "Copy", "ui.message.copied": "Copied!", + "ui.message.undo": "Undo", + "ui.message.undo.confirm": "Click again to undo", + "ui.message.fork": "Fork", + "ui.message.fork.confirm": "Click again to fork", + "ui.message.fork.description": "Fork from this message", "ui.message.attachment.alt": "attachment", "ui.patch.action.deleted": "Deleted", diff --git a/packages/ui/src/i18n/es.ts b/packages/ui/src/i18n/es.ts index 4fd921b606b1..18970dee8cc9 100644 --- a/packages/ui/src/i18n/es.ts +++ b/packages/ui/src/i18n/es.ts @@ -90,6 +90,11 @@ export const dict = { "ui.message.collapse": "Colapsar mensaje", "ui.message.copy": "Copiar", "ui.message.copied": "¡Copiado!", + "ui.message.undo": "Deshacer", + "ui.message.undo.confirm": "Haz clic de nuevo para deshacer", + "ui.message.fork": "Bifurcar", + "ui.message.fork.confirm": "Haz clic de nuevo para bifurcar", + "ui.message.fork.description": "Bifurcar desde este mensaje", "ui.message.attachment.alt": "adjunto", "ui.patch.action.deleted": "Eliminado", diff --git a/packages/ui/src/i18n/fr.ts b/packages/ui/src/i18n/fr.ts index 537d01bba941..64521b6fb924 100644 --- a/packages/ui/src/i18n/fr.ts +++ b/packages/ui/src/i18n/fr.ts @@ -90,6 +90,11 @@ export const dict = { "ui.message.collapse": "Réduire le message", "ui.message.copy": "Copier", "ui.message.copied": "Copié !", + "ui.message.undo": "Annuler", + "ui.message.undo.confirm": "Cliquez à nouveau pour annuler", + "ui.message.fork": "Bifurquer", + "ui.message.fork.confirm": "Cliquez à nouveau pour bifurquer", + "ui.message.fork.description": "Bifurquer à partir de ce message", "ui.message.attachment.alt": "pièce jointe", "ui.patch.action.deleted": "Supprimé", diff --git a/packages/ui/src/i18n/ja.ts b/packages/ui/src/i18n/ja.ts index 6086070bdb2c..c4b4adf1b4c3 100644 --- a/packages/ui/src/i18n/ja.ts +++ b/packages/ui/src/i18n/ja.ts @@ -89,6 +89,11 @@ export const dict = { "ui.message.collapse": "メッセージを折りたたむ", "ui.message.copy": "コピー", "ui.message.copied": "コピーしました!", + "ui.message.undo": "元に戻す", + "ui.message.undo.confirm": "もう一度クリックで取り消し", + "ui.message.fork": "フォーク", + "ui.message.fork.confirm": "もう一度クリックでフォーク", + "ui.message.fork.description": "このメッセージからフォーク", "ui.message.attachment.alt": "添付ファイル", "ui.patch.action.deleted": "削除済み", diff --git a/packages/ui/src/i18n/ko.ts b/packages/ui/src/i18n/ko.ts index fd394dbb7b52..9a5ab84279b0 100644 --- a/packages/ui/src/i18n/ko.ts +++ b/packages/ui/src/i18n/ko.ts @@ -90,6 +90,11 @@ export const dict = { "ui.message.collapse": "메시지 접기", "ui.message.copy": "복사", "ui.message.copied": "복사됨!", + "ui.message.undo": "실행 취소", + "ui.message.undo.confirm": "다시 클릭하여 실행 취소", + "ui.message.fork": "포크", + "ui.message.fork.confirm": "다시 클릭하여 포크", + "ui.message.fork.description": "이 메시지에서 포크", "ui.message.attachment.alt": "첨부 파일", "ui.patch.action.deleted": "삭제됨", diff --git a/packages/ui/src/i18n/no.ts b/packages/ui/src/i18n/no.ts index dcb353614d30..47fd3841566b 100644 --- a/packages/ui/src/i18n/no.ts +++ b/packages/ui/src/i18n/no.ts @@ -93,6 +93,11 @@ export const dict: Record = { "ui.message.collapse": "Skjul melding", "ui.message.copy": "Kopier", "ui.message.copied": "Kopiert!", + "ui.message.undo": "Angre", + "ui.message.undo.confirm": "Klikk igjen for å angre", + "ui.message.fork": "Forgren", + "ui.message.fork.confirm": "Klikk igjen for å forgrene", + "ui.message.fork.description": "Forgren fra denne meldingen", "ui.message.attachment.alt": "vedlegg", "ui.patch.action.deleted": "Slettet", diff --git a/packages/ui/src/i18n/pl.ts b/packages/ui/src/i18n/pl.ts index fb10debbb92d..c67c8a6fcad0 100644 --- a/packages/ui/src/i18n/pl.ts +++ b/packages/ui/src/i18n/pl.ts @@ -89,6 +89,11 @@ export const dict = { "ui.message.collapse": "Zwiń wiadomość", "ui.message.copy": "Kopiuj", "ui.message.copied": "Skopiowano!", + "ui.message.undo": "Cofnij", + "ui.message.undo.confirm": "Kliknij ponownie, aby cofnąć", + "ui.message.fork": "Rozgałęź", + "ui.message.fork.confirm": "Kliknij ponownie, aby rozgałęzić", + "ui.message.fork.description": "Rozgałęź od tej wiadomości", "ui.message.attachment.alt": "załącznik", "ui.patch.action.deleted": "Usunięto", diff --git a/packages/ui/src/i18n/ru.ts b/packages/ui/src/i18n/ru.ts index 417fe0ce8bfe..8983d7474087 100644 --- a/packages/ui/src/i18n/ru.ts +++ b/packages/ui/src/i18n/ru.ts @@ -89,6 +89,11 @@ export const dict = { "ui.message.collapse": "Свернуть сообщение", "ui.message.copy": "Копировать", "ui.message.copied": "Скопировано!", + "ui.message.undo": "Отменить", + "ui.message.undo.confirm": "Нажмите ещё раз для отмены", + "ui.message.fork": "Ответвить", + "ui.message.fork.confirm": "Нажмите ещё раз для ответвления", + "ui.message.fork.description": "Ответвить от этого сообщения", "ui.message.attachment.alt": "вложение", "ui.patch.action.deleted": "Удалено", diff --git a/packages/ui/src/i18n/th.ts b/packages/ui/src/i18n/th.ts index 68bb0d733d99..0dab2965e009 100644 --- a/packages/ui/src/i18n/th.ts +++ b/packages/ui/src/i18n/th.ts @@ -90,6 +90,11 @@ export const dict = { "ui.message.collapse": "ย่อข้อความ", "ui.message.copy": "คัดลอก", "ui.message.copied": "คัดลอกแล้ว!", + "ui.message.undo": "เลิกทำ", + "ui.message.undo.confirm": "คลิกอีกครั้งเพื่อเลิกทำ", + "ui.message.fork": "แยกสาขา", + "ui.message.fork.confirm": "คลิกอีกครั้งเพื่อแยกสาขา", + "ui.message.fork.description": "แยกสาขาจากข้อความนี้", "ui.message.attachment.alt": "ไฟล์แนบ", "ui.patch.action.deleted": "ลบ", diff --git a/packages/ui/src/i18n/zh.ts b/packages/ui/src/i18n/zh.ts index 53beeb1e4f0f..7a950b72ed00 100644 --- a/packages/ui/src/i18n/zh.ts +++ b/packages/ui/src/i18n/zh.ts @@ -94,6 +94,11 @@ export const dict = { "ui.message.collapse": "收起消息", "ui.message.copy": "复制", "ui.message.copied": "已复制!", + "ui.message.undo": "撤回", + "ui.message.undo.confirm": "再次点击以撤回", + "ui.message.fork": "分支", + "ui.message.fork.confirm": "再次点击以创建分支", + "ui.message.fork.description": "从此消息创建分支", "ui.message.attachment.alt": "附件", "ui.patch.action.deleted": "已删除", diff --git a/packages/ui/src/i18n/zht.ts b/packages/ui/src/i18n/zht.ts index 1449b0530ac1..c557cfca09a9 100644 --- a/packages/ui/src/i18n/zht.ts +++ b/packages/ui/src/i18n/zht.ts @@ -94,6 +94,11 @@ export const dict = { "ui.message.collapse": "收合訊息", "ui.message.copy": "複製", "ui.message.copied": "已複製!", + "ui.message.undo": "復原", + "ui.message.undo.confirm": "再次點擊以復原", + "ui.message.fork": "分支", + "ui.message.fork.confirm": "再次點擊以建立分支", + "ui.message.fork.description": "從此訊息建立分支", "ui.message.attachment.alt": "附件", "ui.patch.action.deleted": "已刪除",