From a453e2d231fd2275d6a0ac84bb17e3e888e29abb Mon Sep 17 00:00:00 2001 From: kirillk Date: Thu, 27 Aug 2026 13:58:15 -0400 Subject: [PATCH] feat(jetbrains): hint worktree usage on empty session Show a branch-aware tip under the logo on the empty session screen. On a plain checkout it nudges toward running the task in a worktree, with "run it in a worktree" as an inline link that opens the New Worktree flow. In a worktree it confirms the work is isolated instead. The tip falls back to the generic welcome whenever a claim would be wrong: before branch status resolves, when git is missing, and on a detached HEAD. Read-only surfaces show no tip and skip the fetch. Branch status now feeds the empty panel as well as the branch dock, so worktree editor tabs get it too even though they render no dock. Translate the new strings into all 18 locales. The tip keys carry placeholders and therefore go through MessageFormat, where a lone apostrophe silently swallows surrounding text, so KiloBundleLocaleTest formats every locale for real and checks apostrophe escaping. --- .../jetbrains-empty-session-worktree-tip.md | 5 + .../ai/kilocode/client/session/SessionHost.kt | 1 + .../kilocode/client/session/SessionManager.kt | 1 + .../ai/kilocode/client/session/SessionUi.kt | 18 ++- .../session/ui/empty/EmptySessionPanel.kt | 123 +++++++++++++-- .../resources/messages/KiloBundle.properties | 4 + .../messages/KiloBundle_ar.properties | 6 + .../messages/KiloBundle_bs.properties | 6 + .../messages/KiloBundle_da.properties | 6 + .../messages/KiloBundle_de.properties | 6 + .../messages/KiloBundle_es.properties | 6 + .../messages/KiloBundle_fr.properties | 6 + .../messages/KiloBundle_ja.properties | 6 + .../messages/KiloBundle_ko.properties | 6 + .../messages/KiloBundle_nl.properties | 6 + .../messages/KiloBundle_no.properties | 6 + .../messages/KiloBundle_pl.properties | 6 + .../messages/KiloBundle_pt_BR.properties | 6 + .../messages/KiloBundle_ru.properties | 6 + .../messages/KiloBundle_th.properties | 6 + .../messages/KiloBundle_tr.properties | 6 + .../messages/KiloBundle_uk.properties | 6 + .../messages/KiloBundle_zh_CN.properties | 6 + .../messages/KiloBundle_zh_TW.properties | 6 + .../client/plugin/KiloBundleLocaleTest.kt | 100 ++++++++++++ .../session/ui/EmptySessionPanelTest.kt | 145 +++++++++++++++++- 26 files changed, 492 insertions(+), 13 deletions(-) create mode 100644 .changeset/jetbrains-empty-session-worktree-tip.md create mode 100644 packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/plugin/KiloBundleLocaleTest.kt diff --git a/.changeset/jetbrains-empty-session-worktree-tip.md b/.changeset/jetbrains-empty-session-worktree-tip.md new file mode 100644 index 0000000000..3ceeb723f9 --- /dev/null +++ b/.changeset/jetbrains-empty-session-worktree-tip.md @@ -0,0 +1,5 @@ +--- +"@kilocode/kilo-jetbrains": minor +--- + +Show a branch-aware tip on the empty session screen: suggest running work in a worktree with a link when you're on a plain checkout, and confirm isolation when you're already in one. diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionHost.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionHost.kt index 46b71aed5b..aebbb18afa 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionHost.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionHost.kt @@ -101,6 +101,7 @@ abstract class SessionHost( activity = { activity() }, titles = { titles() }, timers = timers, + newWorktree = if (supportsNewWorktree) ({ newWorktree() }) else null, ) @RequiresEdt diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionManager.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionManager.kt index 619ee030d3..958b2601eb 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionManager.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionManager.kt @@ -52,6 +52,7 @@ interface SessionManager { history = { showHistory() }, activity = { activity() }, titles = { titles() }, + newWorktree = if (supportsNewWorktree) ({ newWorktree() }) else null, ) fun openSession(session: SessionDto) { diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt index b340f6c8be..37e999f9a7 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt @@ -72,6 +72,7 @@ import ai.kilocode.client.util.UiTimerSource import ai.kilocode.client.util.UiTimers import ai.kilocode.client.vfs.KiloVfsManager import ai.kilocode.log.ChatLogSummary +import ai.kilocode.rpc.dto.BranchStatusDto import ai.kilocode.rpc.dto.ModelLimitDto import ai.kilocode.rpc.dto.DiffFileDto import ai.kilocode.rpc.dto.PromptDto @@ -213,6 +214,12 @@ class SessionUi( private lateinit var load: LoadingPanel private lateinit var migrationWizard: MigrationWizardPanel private var empty: EmptySessionPanel? = null + + /** + * Last observed branch/worktree status. Retained so an empty panel created after the fetch shows + * its tip immediately instead of waiting for the next refresh. + */ + private var branch: BranchStatusDto? = null private var modalFocus: (() -> JComponent)? = null private var style = SessionEditorStyle.current() private val selection = SessionSelection() @@ -247,8 +254,8 @@ class SessionUi( dock?.let { syncDock() refreshBranchChanges() - refreshBranch() } + refreshBranch() loaded?.let(::finishOpen) } @@ -613,6 +620,7 @@ class SessionUi( val panel = manager?.emptyPanel(this, controller) ?: EmptySessionPanel(this, controller, controller.recents(), timers = timers) empty = panel + panel.setBranch(branch) scroll.show(panel.view) } @@ -1023,7 +1031,9 @@ class SessionUi( * split mode — so the PR always matches the branch checked out in this session's directory. */ private fun refreshBranch() { - val dock = dock ?: return + // Also feeds the empty panel's branch/worktree tip, so this runs even on surfaces without a + // dock (worktree editor tabs). Read-only surfaces show no tip and get no fetch. + if (readonly) return branchJob?.cancel() branchJob = cs.launch { val status = runCatching { service().branchStatus(workspace.directory) } @@ -1034,7 +1044,9 @@ class SessionUi( } withContext(Dispatchers.Main) { if (disposed || project.isDisposed) return@withContext - dock.setBranch(status) + branch = status + dock?.setBranch(status) + empty?.setBranch(status) } } } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/empty/EmptySessionPanel.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/empty/EmptySessionPanel.kt index c0e717938d..04fec65c3b 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/empty/EmptySessionPanel.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/empty/EmptySessionPanel.kt @@ -14,12 +14,16 @@ import ai.kilocode.client.ui.layout.VAlign import ai.kilocode.client.ui.layout.align import ai.kilocode.client.util.UiTimerSource import ai.kilocode.client.util.UiTimers +import ai.kilocode.rpc.dto.BranchStatusDto +import ai.kilocode.rpc.dto.GhAvailability import ai.kilocode.rpc.dto.SessionDto import com.intellij.icons.AllIcons import com.intellij.ide.BrowserUtil import com.intellij.openapi.Disposable import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.IconLoader +import com.intellij.openapi.util.text.HtmlChunk +import com.intellij.openapi.util.text.StringUtil import com.intellij.util.concurrency.annotations.RequiresEdt import com.intellij.ui.components.JBLabel import com.intellij.util.ui.Centerizer @@ -39,6 +43,8 @@ import java.awt.event.MouseEvent import javax.swing.JButton import javax.swing.JComponent import javax.swing.SwingUtilities +import javax.swing.event.HyperlinkEvent +import javax.swing.event.HyperlinkListener /** * Empty-session panel. @@ -56,6 +62,7 @@ class EmptySessionPanel( private val browse: (String) -> Unit = BrowserUtil::browse, private val timers: UiTimerSource = UiTimers, private val minimal: Boolean = false, + private val newWorktree: (() -> Unit)? = null, ) : BorderLayoutPanel(), Disposable, SessionEditorStyleTarget { private var style = SessionEditorStyle.current() val view: Align = align( @@ -71,6 +78,12 @@ class EmptySessionPanel( addActionListener { history() } } + /** + * Branch/worktree status behind the tip under the logo. Null until [setBranch] delivers it, so + * the first paint falls back to the generic welcome rather than flashing a wrong claim. + */ + private var branch: BranchStatusDto? = null + private val feedback = EmptySessionFeedback(browse) private val logo = JBLabel( @@ -79,10 +92,21 @@ class EmptySessionPanel( horizontalAlignment = JBLabel.CENTER } - private val welcomeLabel = JBLabel(welcomeHtml()).apply { + /** + * Text is set by [syncTip], which picks the generic welcome or a branch/worktree tip. Copyable + * mode swaps the label's internals for an HTML pane, which is what makes the inline worktree + * link clickable; auto-wrapping must be set first so that pane's CSS allows line breaks. + */ + private val welcomeLabel = object : JBLabel() { + override fun createHyperlinkListener() = HyperlinkListener { e -> + if (e.eventType != HyperlinkEvent.EventType.ACTIVATED) return@HyperlinkListener + if (e.description == WORKTREE_HREF) newWorktree?.invoke() + } + }.apply { foreground = SessionUiStyle.Text.Secondary.foreground() horizontalAlignment = JBLabel.CENTER setAllowAutoWrapping(true) + setCopyable(true) } private val description = object : BorderLayoutPanel() { @@ -101,6 +125,13 @@ class EmptySessionPanel( add(welcomeLabel, BorderLayout.CENTER) } + private val descriptionSlot = description.align(HAlign.CENTER, VAlign.CENTER) + + private val header = BorderLayoutPanel(0, UiStyle.Gap.pad()).apply { + isOpaque = false + add(logo, BorderLayout.NORTH) + } + init { Disposer.register(parent, this) Disposer.register(this, feedback) @@ -119,12 +150,6 @@ class EmptySessionPanel( val gap = UiStyle.Gap.pad() layout = BorderLayout(0, gap) - val header = BorderLayoutPanel(0, gap).apply { - isOpaque = false - add(logo, BorderLayout.NORTH) - if (!minimal) add(description.align(HAlign.CENTER, VAlign.CENTER), BorderLayout.CENTER) - } - val actions = Stack.vertical(gap = UiStyle.Gap.lg()) if (!minimal) actions.next(Centerizer(historyButton, Centerizer.TYPE.HORIZONTAL)) actions.next(Centerizer(feedback.button, Centerizer.TYPE.HORIZONTAL)) @@ -136,6 +161,68 @@ class EmptySessionPanel( add(header, BorderLayout.NORTH) if (!minimal && recent.hasSessions()) add(recent, BorderLayout.CENTER) add(south, BorderLayout.SOUTH) + syncTip() + } + + /** + * Applies the branch/worktree status behind the tip under the logo. Called again whenever the + * status is refreshed, so it must stay a no-op when nothing changed. + */ + @RequiresEdt + fun setBranch(status: BranchStatusDto?) { + if (branch == status) return + branch = status + syncTip() + } + + /** + * The tip under the logo, as an HTML fragment: an isolation reminder on a worktree, a nudge + * towards one on a plain checkout, where "run it in a worktree" is an inline link. Null when the + * status is unknown, git is missing, or no branch is checked out — the generic welcome covers + * those rather than asserting something wrong. + */ + private fun tip(): String? { + val status = branch ?: return null + if (status.availability == GhAvailability.GIT_MISSING) return null + val name = name()?.let { XmlStringUtil.escapeString(it) } + if (status.worktree) { + return name?.let { KiloBundle.message("session.empty.worktree", it) } + ?: KiloBundle.message("session.empty.worktree.unknown") + } + if (name == null) return null + return KiloBundle.message("session.empty.branch", name, worktreePhrase()) + } + + /** + * "run it in a worktree" as a link, or as plain text on surfaces that cannot open the flow, so + * the sentence reads the same either way. + */ + private fun worktreePhrase(): String { + val phrase = KiloBundle.message("session.empty.branch.link") + if (newWorktree == null) return XmlStringUtil.escapeString(phrase) + return HtmlChunk.link(WORKTREE_HREF, phrase).toString() + } + + /** Branch name trimmed to fit the fixed-width description, or null when there is no branch. */ + private fun name(): String? { + val value = branch?.branch?.trim().orEmpty() + if (value.isEmpty() || value == DETACHED) return null + return StringUtil.shortenTextWithEllipsis(value, BRANCH_MAX, 0, true) + } + + @RequiresEdt + private fun syncTip() { + val tip = tip() + welcomeLabel.text = centeredHtml( + tip ?: XmlStringUtil.escapeString(KiloBundle.message("session.empty.welcome")), + ) + // Minimal surfaces (worktree/subagent editor tabs) skip the generic blurb but still want a + // state-specific tip, so the slot is attached on demand instead of once at construction. + val described = tip != null || !minimal + if (described && descriptionSlot.parent == null) header.add(descriptionSlot, BorderLayout.CENTER) + if (!described && descriptionSlot.parent != null) header.remove(descriptionSlot) + revalidate() + repaint() } internal fun recentCount() = recent.count() @@ -184,6 +271,16 @@ class EmptySessionPanel( internal fun explanationText() = KiloBundle.message("session.empty.welcome") + /** The tip as the user reads it, with the inline link's markup stripped. */ + internal fun tipText() = tip()?.let { StringUtil.removeHtmlTags(it) } + + /** The plain text currently under the logo: the state-specific tip, or the generic welcome. */ + internal fun descriptionText() = tipText() ?: KiloBundle.message("session.empty.welcome") + + internal fun worktreeLinked() = tip()?.contains("href=\"$WORKTREE_HREF\"") == true + + internal fun worktreeHref() = WORKTREE_HREF + internal fun welcomeLabelAlignment() = welcomeLabel.horizontalAlignment internal fun descriptionPreferredSize() = description.preferredSize @@ -272,11 +369,19 @@ class EmptySessionPanel( repaint() } - private fun welcomeHtml() = XmlStringUtil.wrapInHtml( - "
${XmlStringUtil.escapeString(KiloBundle.message("session.empty.welcome"))}
" + /** [body] must already be escaped or generated HTML — this only wraps and centers it. */ + private fun centeredHtml(body: String) = XmlStringUtil.wrapInHtml( + "
$body
" ) private companion object { const val ACTIVITY_MS = 3_000 + + /** Keeps a long branch name from wrapping the fixed-width description into a wall of text. */ + const val BRANCH_MAX = 28 + const val DETACHED = "(detached)" + + /** Href of the inline worktree link; matched in the label's hyperlink listener. */ + const val WORKTREE_HREF = "worktree" } } diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties index 93c9e4f31f..fc17b67220 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties @@ -28,6 +28,10 @@ session.connection.warning.config=Configuration warnings notification.group.kilo=Kilo Code session.empty.welcome=Kilo Code is an AI coding assistant. Ask it to build features, fix bugs, or explain your codebase. +session.empty.branch=You''re working directly on {0}. Start a task, or {1} to keep changes isolated. +session.empty.branch.link=run it in a worktree +session.empty.worktree=You''re in an isolated worktree on {0}. Work freely — your main checkout stays untouched. +session.empty.worktree.unknown=You're in an isolated worktree. Work freely — your main checkout stays untouched. session.account.balance=Balance: {0} session.account.switcher=Switch account session.empty.loading=Loading... diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_ar.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_ar.properties index 55522110ac..89f1fe80ab 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_ar.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_ar.properties @@ -17,6 +17,10 @@ session.connection.unsupported.options=الخيار 1: افتح المشروع session.connection.warning.config=تحذيرات التكوين session.empty.welcome=Kilo Code هو مساعد برمجة بالذكاء الاصطناعي. اطلب منه بناء ميزات أو إصلاح أخطاء أو شرح قاعدة الكود. +session.empty.branch=تعمل مباشرةً على {0}. ابدأ مهمة، أو {1} لإبقاء التغييرات معزولة. +session.empty.branch.link=شغّلها في worktree +session.empty.worktree=أنت في worktree معزول على {0}. اعمل بحرية — تبقى نسختك الرئيسية دون تغيير. +session.empty.worktree.unknown=أنت في worktree معزول. اعمل بحرية — تبقى نسختك الرئيسية دون تغيير. session.empty.loading=جاري التحميل… session.empty.recent=الحديثة session.showHistory=عرض السجل @@ -163,6 +167,8 @@ action.Kilo.Settings.text=الإعدادات action.Kilo.Settings.description=إعدادات Kilo Code action.Kilo.NewSession.text=جلسة جديدة action.Kilo.NewSession.description=بدء جلسة Kilo جديدة +action.Kilo.NewSession.toolbar=جلسة +action.Kilo.NewWorktree.toolbar=Worktree action.Kilo.History.text=السجل action.Kilo.History.description=عرض سجل الجلسات action.Kilo.SendPrompt.text=إرسال الطلب diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_bs.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_bs.properties index 301f8c2fec..75aeba15c3 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_bs.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_bs.properties @@ -17,6 +17,10 @@ session.connection.unsupported.options=Opcija 1: Otvorite projekat u kontejneru session.connection.warning.config=Upozorenja konfiguracije session.empty.welcome=Kilo Code je AI asistent za kodiranje. Zatražite od njega da gradi funkcije, ispravlja greške ili objašnjava vašu bazu koda. +session.empty.branch=Radite direktno na {0}. Započnite zadatak ili {1} da izmjene ostanu izolovane. +session.empty.branch.link=pokrenite ga u worktree-u +session.empty.worktree=Nalazite se u izolovanom worktree-u na {0}. Radite slobodno — vaš glavni checkout ostaje nepromijenjen. +session.empty.worktree.unknown=Nalazite se u izolovanom worktree-u. Radite slobodno — vaš glavni checkout ostaje nepromijenjen. session.empty.loading=Učitavanje… session.empty.recent=NEDAVNO session.showHistory=Prikaži historiju @@ -163,6 +167,8 @@ action.Kilo.Settings.text=Postavke action.Kilo.Settings.description=Postavke Kilo Code action.Kilo.NewSession.text=Nova sesija action.Kilo.NewSession.description=Pokrenite novu Kilo sesiju +action.Kilo.NewSession.toolbar=Sesija +action.Kilo.NewWorktree.toolbar=Worktree action.Kilo.History.text=Historija action.Kilo.History.description=Prikaži historiju sesija action.Kilo.SendPrompt.text=Pošalji upit diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_da.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_da.properties index f10dc7e7b8..d447386eb1 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_da.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_da.properties @@ -17,6 +17,10 @@ session.connection.unsupported.options=Mulighed 1: Åbn projektet i containeren session.connection.warning.config=Konfigurationsadvarsler session.empty.welcome=Kilo Code er en AI-kodningsassistent. Bed den om at bygge funktioner, rette fejl eller forklare din kodebase. +session.empty.branch=Du arbejder direkte på {0}. Start en opgave, eller {1} for at holde ændringer isoleret. +session.empty.branch.link=kør den i et worktree +session.empty.worktree=Du er i et isoleret worktree på {0}. Arbejd frit — dit primære checkout forbliver urørt. +session.empty.worktree.unknown=Du er i et isoleret worktree. Arbejd frit — dit primære checkout forbliver urørt. session.empty.loading=Indlæser… session.empty.recent=SENESTE session.showHistory=Vis historik @@ -163,6 +167,8 @@ action.Kilo.Settings.text=Indstillinger action.Kilo.Settings.description=Kilo Code-indstillinger action.Kilo.NewSession.text=Ny session action.Kilo.NewSession.description=Start en ny Kilo-session +action.Kilo.NewSession.toolbar=Session +action.Kilo.NewWorktree.toolbar=Worktree action.Kilo.History.text=Historik action.Kilo.History.description=Vis sessionshistorik action.Kilo.SendPrompt.text=Send prompt diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_de.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_de.properties index 7df185f3ae..e7127d8cda 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_de.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_de.properties @@ -17,6 +17,10 @@ session.connection.unsupported.options=Option 1: Öffnen Sie das Projekt im Cont session.connection.warning.config=Konfigurationswarnungen session.empty.welcome=Kilo Code ist ein KI-Coding-Assistent. Bitten Sie ihn, Funktionen zu erstellen, Fehler zu beheben oder Ihre Codebasis zu erklären. +session.empty.branch=Sie arbeiten direkt auf {0}. Starten Sie eine Aufgabe oder {1}, um Änderungen isoliert zu halten. +session.empty.branch.link=führen Sie sie in einem Worktree aus +session.empty.worktree=Sie sind in einem isolierten Worktree auf {0}. Arbeiten Sie frei — Ihr Haupt-Checkout bleibt unberührt. +session.empty.worktree.unknown=Sie sind in einem isolierten Worktree. Arbeiten Sie frei — Ihr Haupt-Checkout bleibt unberührt. session.empty.loading=Wird geladen… session.empty.recent=ZULETZT session.showHistory=Verlauf anzeigen @@ -163,6 +167,8 @@ action.Kilo.Settings.text=Einstellungen action.Kilo.Settings.description=Kilo Code Einstellungen action.Kilo.NewSession.text=Neue Sitzung action.Kilo.NewSession.description=Neue Kilo-Sitzung starten +action.Kilo.NewSession.toolbar=Sitzung +action.Kilo.NewWorktree.toolbar=Worktree action.Kilo.History.text=Verlauf action.Kilo.History.description=Sitzungsverlauf anzeigen action.Kilo.SendPrompt.text=Anfrage senden diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_es.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_es.properties index 8b6d662355..8d408f7323 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_es.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_es.properties @@ -17,6 +17,10 @@ session.connection.unsupported.options=Opción 1: Abre el proyecto en el contene session.connection.warning.config=Advertencias de configuración session.empty.welcome=Kilo Code es un asistente de codificación con IA. Pida que construya funciones, corrija errores o explique su base de código. +session.empty.branch=Estás trabajando directamente en {0}. Inicia una tarea o {1} para mantener los cambios aislados. +session.empty.branch.link=ejecútala en un worktree +session.empty.worktree=Estás en un worktree aislado en {0}. Trabaja con libertad — tu checkout principal no se toca. +session.empty.worktree.unknown=Estás en un worktree aislado. Trabaja con libertad — tu checkout principal no se toca. session.empty.loading=Cargando… session.empty.recent=RECIENTE session.showHistory=Mostrar historial @@ -163,6 +167,8 @@ action.Kilo.Settings.text=Configuración action.Kilo.Settings.description=Configuración de Kilo Code action.Kilo.NewSession.text=Nueva sesión action.Kilo.NewSession.description=Iniciar una nueva sesión de Kilo +action.Kilo.NewSession.toolbar=Sesión +action.Kilo.NewWorktree.toolbar=Worktree action.Kilo.History.text=Historial action.Kilo.History.description=Mostrar el historial de sesiones action.Kilo.SendPrompt.text=Enviar mensaje diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_fr.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_fr.properties index 40c1aed80c..3bea71d199 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_fr.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_fr.properties @@ -17,6 +17,10 @@ session.connection.unsupported.options=Option 1 : ouvrez le projet dans le conte session.connection.warning.config=Avertissements de configuration session.empty.welcome=Kilo Code est un assistant de codage IA. Demandez-lui de créer des fonctionnalités, corriger des bugs ou expliquer votre base de code. +session.empty.branch=Vous travaillez directement sur {0}. Lancez une tâche ou {1} pour isoler vos modifications. +session.empty.branch.link=exécutez-la dans un worktree +session.empty.worktree=Vous êtes dans un worktree isolé sur {0}. Travaillez librement — votre checkout principal reste intact. +session.empty.worktree.unknown=Vous êtes dans un worktree isolé. Travaillez librement — votre checkout principal reste intact. session.empty.loading=Chargement… session.empty.recent=RÉCENT session.showHistory=Afficher l'historique @@ -163,6 +167,8 @@ action.Kilo.Settings.text=Paramètres action.Kilo.Settings.description=Paramètres de Kilo Code action.Kilo.NewSession.text=Nouvelle session action.Kilo.NewSession.description=Démarrer une nouvelle session Kilo +action.Kilo.NewSession.toolbar=Session +action.Kilo.NewWorktree.toolbar=Worktree action.Kilo.History.text=Historique action.Kilo.History.description=Afficher l'historique des sessions action.Kilo.SendPrompt.text=Envoyer l'invite diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_ja.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_ja.properties index d1e39040ee..c6e0aaf577 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_ja.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_ja.properties @@ -17,6 +17,10 @@ session.connection.unsupported.options=オプション1: JetBrains Gateway を session.connection.warning.config=設定の警告 session.empty.welcome=Kilo CodeはAIコーディングアシスタントです。機能の作成、バグの修正、またはコードベースの説明を依頼できます。 +session.empty.branch={0} で直接作業しています。タスクを開始するか、{1}すると変更を隔離できます。 +session.empty.branch.link=worktree で実行 +session.empty.worktree={0} の独立した worktree にいます。メインのチェックアウトには影響しないので自由に作業できます。 +session.empty.worktree.unknown=独立した worktree にいます。メインのチェックアウトには影響しないので自由に作業できます。 session.empty.loading=読み込み中… session.empty.recent=最近 session.showHistory=履歴を表示 @@ -163,6 +167,8 @@ action.Kilo.Settings.text=設定 action.Kilo.Settings.description=Kilo Codeの設定 action.Kilo.NewSession.text=新しいセッション action.Kilo.NewSession.description=新しいKiloセッションを開始 +action.Kilo.NewSession.toolbar=セッション +action.Kilo.NewWorktree.toolbar=Worktree action.Kilo.History.text=履歴 action.Kilo.History.description=セッション履歴を表示 action.Kilo.SendPrompt.text=プロンプトを送信 diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_ko.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_ko.properties index 78c97e633f..419721fabe 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_ko.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_ko.properties @@ -17,6 +17,10 @@ session.connection.unsupported.options=옵션 1: JetBrains Gateway로 컨테이 session.connection.warning.config=구성 경고 session.empty.welcome=Kilo Code는 AI 코딩 어시스턴트입니다. 기능 구축, 버그 수정, 코드베이스 설명을 요청하세요. +session.empty.branch={0}에서 직접 작업하고 있습니다. 작업을 시작하거나 {1}하여 변경 사항을 분리하세요. +session.empty.branch.link=worktree에서 실행 +session.empty.worktree={0}의 격리된 worktree에 있습니다. 메인 체크아웃은 영향을 받지 않으니 자유롭게 작업하세요. +session.empty.worktree.unknown=격리된 worktree에 있습니다. 메인 체크아웃은 영향을 받지 않으니 자유롭게 작업하세요. session.empty.loading=로딩 중… session.empty.recent=최근 session.showHistory=기록 보기 @@ -163,6 +167,8 @@ action.Kilo.Settings.text=설정 action.Kilo.Settings.description=Kilo Code 설정 action.Kilo.NewSession.text=새 세션 action.Kilo.NewSession.description=새 Kilo 세션 시작 +action.Kilo.NewSession.toolbar=세션 +action.Kilo.NewWorktree.toolbar=Worktree action.Kilo.History.text=기록 action.Kilo.History.description=세션 기록 표시 action.Kilo.SendPrompt.text=프롬프트 전송 diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_nl.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_nl.properties index ea86c878b0..4af910037b 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_nl.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_nl.properties @@ -17,6 +17,10 @@ session.connection.unsupported.options=Optie 1: Open het project in de container session.connection.warning.config=Configuratiewaarschuwingen session.empty.welcome=Kilo Code is een AI-codeerassistent. Vraag het om functies te bouwen, bugs te repareren of uw codebase uit te leggen. +session.empty.branch=Je werkt direct op {0}. Start een taak of {1} om wijzigingen geïsoleerd te houden. +session.empty.branch.link=voer hem uit in een worktree +session.empty.worktree=Je zit in een geïsoleerde worktree op {0}. Werk vrij — je hoofdcheckout blijft ongemoeid. +session.empty.worktree.unknown=Je zit in een geïsoleerde worktree. Werk vrij — je hoofdcheckout blijft ongemoeid. session.empty.loading=Laden… session.empty.recent=RECENT session.showHistory=Geschiedenis weergeven @@ -163,6 +167,8 @@ action.Kilo.Settings.text=Instellingen action.Kilo.Settings.description=Kilo Code-instellingen action.Kilo.NewSession.text=Nieuwe sessie action.Kilo.NewSession.description=Een nieuwe Kilo-sessie starten +action.Kilo.NewSession.toolbar=Sessie +action.Kilo.NewWorktree.toolbar=Worktree action.Kilo.History.text=Geschiedenis action.Kilo.History.description=Sessiegeschiedenis weergeven action.Kilo.SendPrompt.text=Prompt verzenden diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_no.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_no.properties index d7d4d01344..73d2b6ee80 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_no.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_no.properties @@ -17,6 +17,10 @@ session.connection.unsupported.options=Alternativ 1: Åpne prosjektet i containe session.connection.warning.config=Konfigurasjonsadvarsler session.empty.welcome=Kilo Code er en AI-kodingsassistent. Be den om å bygge funksjoner, fikse feil eller forklare kodebasen din. +session.empty.branch=Du jobber direkte på {0}. Start en oppgave, eller {1} for å holde endringene isolert. +session.empty.branch.link=kjør den i et worktree +session.empty.worktree=Du er i et isolert worktree på {0}. Jobb fritt — hovedutsjekkingen din er urørt. +session.empty.worktree.unknown=Du er i et isolert worktree. Jobb fritt — hovedutsjekkingen din er urørt. session.empty.loading=Laster… session.empty.recent=NYLIGE session.showHistory=Vis historikk @@ -168,6 +172,8 @@ action.Kilo.Settings.text=Innstillinger action.Kilo.Settings.description=Kilo Code-innstillinger action.Kilo.NewSession.text=Ny økt action.Kilo.NewSession.description=Start en ny Kilo-økt +action.Kilo.NewSession.toolbar=Økt +action.Kilo.NewWorktree.toolbar=Worktree action.Kilo.History.text=Historikk action.Kilo.History.description=Vis økthistorikk action.Kilo.SendPrompt.text=Send forespørsel diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_pl.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_pl.properties index 140f89f54b..d75874e8d8 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_pl.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_pl.properties @@ -17,6 +17,10 @@ session.connection.unsupported.options=Opcja 1: Otwórz projekt w kontenerze lub session.connection.warning.config=Ostrzeżenia konfiguracji session.empty.welcome=Kilo Code to asystent kodowania AI. Poproś go o tworzenie funkcji, naprawianie błędów lub wyjaśnianie bazy kodu. +session.empty.branch=Pracujesz bezpośrednio na {0}. Rozpocznij zadanie lub {1}, aby odizolować zmiany. +session.empty.branch.link=uruchom je w worktree +session.empty.worktree=Jesteś w odizolowanym worktree na {0}. Pracuj swobodnie — twój główny checkout pozostaje nietknięty. +session.empty.worktree.unknown=Jesteś w odizolowanym worktree. Pracuj swobodnie — twój główny checkout pozostaje nietknięty. session.empty.loading=Ładowanie… session.empty.recent=OSTATNIE session.showHistory=Pokaż historię @@ -168,6 +172,8 @@ action.Kilo.Settings.text=Ustawienia action.Kilo.Settings.description=Ustawienia Kilo Code action.Kilo.NewSession.text=Nowa sesja action.Kilo.NewSession.description=Rozpocznij nową sesję Kilo +action.Kilo.NewSession.toolbar=Sesja +action.Kilo.NewWorktree.toolbar=Worktree action.Kilo.History.text=Historia action.Kilo.History.description=Pokaż historię sesji action.Kilo.SendPrompt.text=Wyślij monit diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_pt_BR.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_pt_BR.properties index ed51cb5a9b..d39ebc987d 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_pt_BR.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_pt_BR.properties @@ -17,6 +17,10 @@ session.connection.unsupported.options=Opção 1: Abra o projeto no contêiner o session.connection.warning.config=Avisos de configuração session.empty.welcome=Kilo Code é um assistente de codificação com IA. Peça que construa funcionalidades, corrija bugs ou explique sua base de código. +session.empty.branch=Você está trabalhando diretamente em {0}. Inicie uma tarefa ou {1} para manter as alterações isoladas. +session.empty.branch.link=execute-a em um worktree +session.empty.worktree=Você está em um worktree isolado em {0}. Trabalhe livremente — seu checkout principal permanece intacto. +session.empty.worktree.unknown=Você está em um worktree isolado. Trabalhe livremente — seu checkout principal permanece intacto. session.empty.loading=Carregando… session.empty.recent=RECENTE session.showHistory=Mostrar histórico @@ -168,6 +172,8 @@ action.Kilo.Settings.text=Configurações action.Kilo.Settings.description=Configurações do Kilo Code action.Kilo.NewSession.text=Nova sessão action.Kilo.NewSession.description=Iniciar uma nova sessão do Kilo +action.Kilo.NewSession.toolbar=Sessão +action.Kilo.NewWorktree.toolbar=Worktree action.Kilo.History.text=Histórico action.Kilo.History.description=Exibir histórico de sessões action.Kilo.SendPrompt.text=Enviar prompt diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_ru.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_ru.properties index 94c8e65c0d..94735d04b8 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_ru.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_ru.properties @@ -17,6 +17,10 @@ session.connection.unsupported.options=Вариант 1: откройте про session.connection.warning.config=Предупреждения конфигурации session.empty.welcome=Kilo Code — это AI-ассистент по программированию. Попросите его разработать функции, исправить ошибки или объяснить кодовую базу. +session.empty.branch=Вы работаете напрямую в {0}. Начните задачу или {1}, чтобы изолировать изменения. +session.empty.branch.link=запустите её в worktree +session.empty.worktree=Вы в изолированном worktree на {0}. Работайте свободно — основная рабочая копия не затрагивается. +session.empty.worktree.unknown=Вы в изолированном worktree. Работайте свободно — основная рабочая копия не затрагивается. session.empty.loading=Загрузка… session.empty.recent=НЕДАВНИЕ session.showHistory=Показать историю @@ -168,6 +172,8 @@ action.Kilo.Settings.text=Настройки action.Kilo.Settings.description=Настройки Kilo Code action.Kilo.NewSession.text=Новая сессия action.Kilo.NewSession.description=Запустить новую сессию Kilo +action.Kilo.NewSession.toolbar=Сессия +action.Kilo.NewWorktree.toolbar=Worktree action.Kilo.History.text=История action.Kilo.History.description=Показать историю сессий action.Kilo.SendPrompt.text=Отправить запрос diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_th.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_th.properties index 031d159ed1..666f666268 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_th.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_th.properties @@ -17,6 +17,10 @@ session.connection.unsupported.options=ตัวเลือกที่ 1: เ session.connection.warning.config=คำเตือนการกำหนดค่า session.empty.welcome=Kilo Code คือผู้ช่วยเขียนโค้ด AI ขอให้สร้างฟีเจอร์แก้สันข้อผิดพลาด หรืออธิบายโค้ดเบสของคุณ +session.empty.branch=คุณกำลังทำงานบน {0} โดยตรง เริ่มงานใหม่ หรือ {1} เพื่อแยกการเปลี่ยนแปลงออกจากกัน +session.empty.branch.link=รันใน worktree +session.empty.worktree=คุณอยู่ใน worktree ที่แยกอิสระบน {0} ทำงานได้อย่างอิสระ — เช็คเอาต์หลักของคุณไม่ถูกแตะต้อง +session.empty.worktree.unknown=คุณอยู่ใน worktree ที่แยกอิสระ ทำงานได้อย่างอิสระ — เช็คเอาต์หลักของคุณไม่ถูกแตะต้อง session.empty.loading=กำลังโหลด… session.empty.recent=ล่าสุด session.showHistory=แสดงประวัติ @@ -168,6 +172,8 @@ action.Kilo.Settings.text=การตั้งค่า action.Kilo.Settings.description=การตั้งค่า Kilo Code action.Kilo.NewSession.text=เซสชันใหม่ action.Kilo.NewSession.description=เริ่มเซสชัน Kilo ใหม่ +action.Kilo.NewSession.toolbar=เซสชัน +action.Kilo.NewWorktree.toolbar=Worktree action.Kilo.History.text=ประวัติ action.Kilo.History.description=แสดงประวัติเซสชัน action.Kilo.SendPrompt.text=ส่งคำขอ diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_tr.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_tr.properties index edfdcafd34..d5ae24c989 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_tr.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_tr.properties @@ -17,6 +17,10 @@ session.connection.unsupported.options=Seçenek 1: Projeyi JetBrains Gateway ile session.connection.warning.config=Yapılandırma uyarıları session.empty.welcome=Kilo Code, bir yapay zeka kodlama asistanıdır. Özellik oluşturmasını, hata düzeltirlmesi veya kod tabanınızı açıklamasını isteyin. +session.empty.branch=Doğrudan {0} üzerinde çalışıyorsunuz. Bir görev başlatın ya da değişiklikleri izole tutmak için {1}. +session.empty.branch.link=bir worktree içinde çalıştırın +session.empty.worktree={0} üzerinde izole bir worktree içindesiniz. Rahatça çalışın — ana checkout''unuz olduğu gibi kalır. +session.empty.worktree.unknown=İzole bir worktree içindesiniz. Rahatça çalışın — ana checkout'unuz olduğu gibi kalır. session.empty.loading=Yükleniyor… session.empty.recent=SON session.showHistory=Geçmişi göster @@ -168,6 +172,8 @@ action.Kilo.Settings.text=Ayarlar action.Kilo.Settings.description=Kilo Code ayarları action.Kilo.NewSession.text=Yeni oturum action.Kilo.NewSession.description=Yeni bir Kilo oturumu başlat +action.Kilo.NewSession.toolbar=Oturum +action.Kilo.NewWorktree.toolbar=Worktree action.Kilo.History.text=Geçmiş action.Kilo.History.description=Oturum geçmişini göster action.Kilo.SendPrompt.text=İstem gönder diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_uk.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_uk.properties index 51b50da55d..10a0ff9316 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_uk.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_uk.properties @@ -17,6 +17,10 @@ session.connection.unsupported.options=Варіант 1: відкрийте пр session.connection.warning.config=Попередження конфігурації session.empty.welcome=Kilo Code — це AI-асистент для програмування. Попросіть його створити функції, виправити помилки або пояснити ваш код. +session.empty.branch=Ви працюєте безпосередньо в {0}. Почніть завдання або {1}, щоб ізолювати зміни. +session.empty.branch.link=запустіть його в worktree +session.empty.worktree=Ви в ізольованому worktree на {0}. Працюйте вільно — основна робоча копія залишається незмінною. +session.empty.worktree.unknown=Ви в ізольованому worktree. Працюйте вільно — основна робоча копія залишається незмінною. session.empty.loading=Завантаження… session.empty.recent=НЕДАВНІ session.showHistory=Показати історію @@ -163,6 +167,8 @@ action.Kilo.Settings.text=Налаштування action.Kilo.Settings.description=Налаштування Kilo Code action.Kilo.NewSession.text=Нова сесія action.Kilo.NewSession.description=Розпочати нову сесію Kilo +action.Kilo.NewSession.toolbar=Сесія +action.Kilo.NewWorktree.toolbar=Worktree action.Kilo.History.text=Історія action.Kilo.History.description=Показати історію сесій action.Kilo.SendPrompt.text=Надіслати запит diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_zh_CN.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_zh_CN.properties index 7c79292775..8de4b4f4e8 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_zh_CN.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_zh_CN.properties @@ -17,6 +17,10 @@ session.connection.unsupported.options=选项 1:使用 JetBrains Gateway 在 session.connection.warning.config=配置警告 session.empty.welcome=Kilo Code 是一个 AI 编程助手。可请它构建功能、修复错误或解释您的代码库。 +session.empty.branch=你正在 {0} 上直接工作。开始一个任务,或{1}以隔离改动。 +session.empty.branch.link=在 worktree 中运行 +session.empty.worktree=你在 {0} 的独立 worktree 中。可以放心工作 — 主工作副本不会受到影响。 +session.empty.worktree.unknown=你在独立的 worktree 中。可以放心工作 — 主工作副本不会受到影响。 session.empty.loading=加载中… session.empty.recent=最近 session.showHistory=显示历史 @@ -163,6 +167,8 @@ action.Kilo.Settings.text=设置 action.Kilo.Settings.description=Kilo Code 设置 action.Kilo.NewSession.text=新建会话 action.Kilo.NewSession.description=开始新的 Kilo 会话 +action.Kilo.NewSession.toolbar=会话 +action.Kilo.NewWorktree.toolbar=Worktree action.Kilo.History.text=历史 action.Kilo.History.description=显示会话历史 action.Kilo.SendPrompt.text=发送提示 diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_zh_TW.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_zh_TW.properties index c135ded064..85cda84a91 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_zh_TW.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_zh_TW.properties @@ -17,6 +17,10 @@ session.connection.unsupported.options=選項 1:使用 JetBrains Gateway 在 session.connection.warning.config=設定警告 session.empty.welcome=Kilo Code 是 AI 程式輔助。可請它建置功能、修復錯誤或解釋您的程式程式庫。 +session.empty.branch=你正在 {0} 上直接工作。開始一個任務,或{1}以隔離變更。 +session.empty.branch.link=在 worktree 中執行 +session.empty.worktree=你在 {0} 的獨立 worktree 中。可以放心工作 — 主工作副本不會受到影響。 +session.empty.worktree.unknown=你在獨立的 worktree 中。可以放心工作 — 主工作副本不會受到影響。 session.empty.loading=載入中… session.empty.recent=最近 session.showHistory=顯示歷史 @@ -163,6 +167,8 @@ action.Kilo.Settings.text=設定 action.Kilo.Settings.description=Kilo Code 設定 action.Kilo.NewSession.text=新建工作階段 action.Kilo.NewSession.description=開始新的 Kilo 工作階段 +action.Kilo.NewSession.toolbar=會話 +action.Kilo.NewWorktree.toolbar=Worktree action.Kilo.History.text=歷史 action.Kilo.History.description=顯示工作階段歷史 action.Kilo.SendPrompt.text=發送提示 diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/plugin/KiloBundleLocaleTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/plugin/KiloBundleLocaleTest.kt new file mode 100644 index 0000000000..fa882be3f2 --- /dev/null +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/plugin/KiloBundleLocaleTest.kt @@ -0,0 +1,100 @@ +package ai.kilocode.client.plugin + +import com.intellij.testFramework.fixtures.BasePlatformTestCase +import java.io.InputStreamReader +import java.nio.charset.StandardCharsets +import java.text.MessageFormat +import java.util.Locale +import java.util.Properties + +/** + * Guards the localized empty-session tip and toolbar labels. + * + * The tip keys carry `{0}`/`{1}`, so they go through [MessageFormat], where a lone apostrophe + * silently swallows the surrounding text and `''` collapses to one. Translations are easy to get + * wrong here, so every locale is formatted for real rather than compared as a raw string. + */ +class KiloBundleLocaleTest : BasePlatformTestCase() { + fun `test parameterized tips format cleanly in every locale`() { + for (locale in LOCALES) { + val props = load(locale) + + val branch = props.getProperty("session.empty.branch") + assertNotNull("$locale: missing session.empty.branch", branch) + assertEscaped(locale, "session.empty.branch", branch!!) + val rendered = format(branch, "main", "LINK_PHRASE") + assertTrue("$locale: branch tip dropped the branch name -> $rendered", rendered.contains("main")) + assertTrue("$locale: branch tip dropped the link -> $rendered", rendered.contains("LINK_PHRASE")) + assertClean(locale, "session.empty.branch", rendered) + + val worktree = props.getProperty("session.empty.worktree") + assertNotNull("$locale: missing session.empty.worktree", worktree) + assertEscaped(locale, "session.empty.worktree", worktree!!) + val tree = format(worktree, "feature/x") + assertTrue("$locale: worktree tip dropped the branch name -> $tree", tree.contains("feature/x")) + assertClean(locale, "session.empty.worktree", tree) + } + } + + fun `test plain keys are present and carry no placeholders`() { + for (locale in LOCALES) { + val props = load(locale) + for (key in PLAIN) { + val value = props.getProperty(key) + assertNotNull("$locale: missing $key", value) + assertTrue("$locale: $key is blank", value!!.isNotBlank()) + assertFalse("$locale: $key should not contain a placeholder -> $value", value.contains("{0}")) + assertFalse( + "$locale: $key has no placeholders so apostrophes must not be doubled -> $value", + value.contains("''"), + ) + } + } + } + + private fun format(pattern: String, vararg args: String) = + MessageFormat(pattern, Locale.ROOT).format(args) + + /** + * Every apostrophe in a MessageFormat pattern must be doubled. A lone one opens a quoted run + * that silently eats itself (and any placeholder it spans), which formatting alone will not + * always reveal — so the raw pattern is checked directly. + */ + private fun assertEscaped(locale: String, key: String, pattern: String) { + for (run in Regex("'+").findAll(pattern)) { + assertTrue( + "$locale: $key has an unescaped apostrophe, double it -> $pattern", + run.value.length % 2 == 0, + ) + } + } + + /** After formatting, MessageFormat has consumed its quoting — leftovers mean a bad pattern. */ + private fun assertClean(locale: String, key: String, rendered: String) { + assertFalse("$locale: $key still has a doubled apostrophe -> $rendered", rendered.contains("''")) + assertFalse("$locale: $key left an unformatted placeholder -> $rendered", rendered.contains("{")) + } + + private fun load(locale: String): Properties { + val name = if (locale == "en") "/messages/KiloBundle.properties" else "/messages/KiloBundle_$locale.properties" + val stream = javaClass.getResourceAsStream(name) + assertNotNull("$locale: $name not on the classpath", stream) + return Properties().apply { + InputStreamReader(stream!!, StandardCharsets.UTF_8).use { load(it) } + } + } + + private companion object { + val LOCALES = listOf( + "en", "ar", "bs", "da", "de", "es", "fr", "ja", "ko", "nl", + "no", "pl", "pt_BR", "ru", "th", "tr", "uk", "zh_CN", "zh_TW", + ) + + val PLAIN = listOf( + "session.empty.branch.link", + "session.empty.worktree.unknown", + "action.Kilo.NewSession.toolbar", + "action.Kilo.NewWorktree.toolbar", + ) + } +} diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/EmptySessionPanelTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/EmptySessionPanelTest.kt index 4d8f105a8b..5840b1fe84 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/EmptySessionPanelTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/EmptySessionPanelTest.kt @@ -15,6 +15,8 @@ import ai.kilocode.client.ui.FilledBadgeIcon import ai.kilocode.client.testing.FakeAppRpcApi import ai.kilocode.client.testing.FakeSessionRpcApi import ai.kilocode.client.testing.FakeWorkspaceRpcApi +import ai.kilocode.rpc.dto.BranchStatusDto +import ai.kilocode.rpc.dto.GhAvailability import ai.kilocode.rpc.dto.KiloAppStateDto import ai.kilocode.rpc.dto.KiloAppStatusDto import ai.kilocode.rpc.dto.KiloWorkspaceStateDto @@ -34,6 +36,8 @@ import kotlinx.coroutines.runBlocking import java.awt.BorderLayout import java.awt.Cursor import javax.swing.JButton +import javax.swing.JEditorPane +import javax.swing.event.HyperlinkEvent @Suppress("UnstableApiUsage") class EmptySessionPanelTest : BasePlatformTestCase() { @@ -159,6 +163,135 @@ class EmptySessionPanelTest : BasePlatformTestCase() { ) } + // ---- branch/worktree tip under the logo ---- + + fun `test no tip until branch status arrives`() { + val panel = panel(newWorktree = {}) + + assertNull(panel.tipText()) + assertFalse(panel.worktreeLinked()) + assertEquals(panel.explanationText(), panel.descriptionText()) + } + + fun `test branch promotes running the task in a worktree`() { + val panel = panel(newWorktree = {}) + + panel.setBranch(BranchStatusDto(branch = "main", worktree = false)) + + assertEquals( + "You're working directly on main. Start a task, or run it in a worktree to keep changes isolated.", + panel.tipText(), + ) + assertTrue(panel.worktreeLinked()) + } + + fun `test worktree hints isolation and links nothing`() { + val panel = panel(newWorktree = {}) + + panel.setBranch(BranchStatusDto(branch = "feature/x", worktree = true)) + + assertEquals( + "You're in an isolated worktree on feature/x. Work freely — your main checkout stays untouched.", + panel.tipText(), + ) + assertFalse(panel.worktreeLinked()) + } + + fun `test worktree without a branch name falls back to the generic worktree tip`() { + val panel = panel() + + panel.setBranch(BranchStatusDto(branch = "(detached)", worktree = true)) + + assertEquals( + "You're in an isolated worktree. Work freely — your main checkout stays untouched.", + panel.tipText(), + ) + } + + fun `test detached plain checkout keeps the generic welcome`() { + val panel = panel(newWorktree = {}) + + panel.setBranch(BranchStatusDto(branch = "(detached)", worktree = false)) + + assertNull(panel.tipText()) + assertFalse(panel.worktreeLinked()) + assertEquals(panel.explanationText(), panel.descriptionText()) + } + + fun `test missing git keeps the generic welcome`() { + val panel = panel(newWorktree = {}) + + panel.setBranch( + BranchStatusDto(branch = "main", worktree = false, availability = GhAvailability.GIT_MISSING), + ) + + assertNull(panel.tipText()) + assertFalse(panel.worktreeLinked()) + } + + fun `test long branch name is shortened`() { + val panel = panel() + + panel.setBranch(BranchStatusDto(branch = "feature/a-very-long-branch-name-that-keeps-going")) + + val tip = panel.tipText().orEmpty() + assertTrue(tip, tip.contains("…")) + assertFalse(tip, tip.contains("keeps-going")) + } + + fun `test minimal surface shows a worktree tip but no generic welcome`() { + val panel = panel(minimal = true) + + assertFalse(panel.descriptionVisible()) + + panel.setBranch(BranchStatusDto(branch = "feature/x", worktree = true)) + + assertTrue(panel.descriptionVisible()) + } + + fun `test phrase stays plain text without a callback`() { + val panel = panel() + + panel.setBranch(BranchStatusDto(branch = "main", worktree = false)) + + assertEquals( + "You're working directly on main. Start a task, or run it in a worktree to keep changes isolated.", + panel.tipText(), + ) + assertFalse(panel.worktreeLinked()) + } + + fun `test activating the inline link invokes the callback`() { + var fired = 0 + val panel = panel(newWorktree = { fired++ }) + panel.setBranch(BranchStatusDto(branch = "main", worktree = false)) + + activateLink(panel) + + assertEquals(1, fired) + } + + fun `test activating the inline link ignores other hrefs`() { + var fired = 0 + val panel = panel(newWorktree = { fired++ }) + panel.setBranch(BranchStatusDto(branch = "main", worktree = false)) + + activateLink(panel, href = "https://example.test") + + assertEquals(0, fired) + } + + /** + * Fires the activation through the editor pane that `setCopyable(true)` installs, so the real + * listener wiring is exercised rather than a stand-in. + */ + private fun activateLink(panel: EmptySessionPanel, href: String = panel.worktreeHref()) { + val pane = UIUtil.uiTraverser(panel).filter(JEditorPane::class.java).first() + assertNotNull(pane) + val event = HyperlinkEvent(pane, HyperlinkEvent.EventType.ACTIVATED, null, href) + pane!!.hyperlinkListeners.forEach { it.hyperlinkUpdate(event) } + } + fun `test selecting recent session does not open it`() { val panel = panel(listOf(session("ses_1"), session("ses_2"))) @@ -373,7 +506,17 @@ class EmptySessionPanelTest : BasePlatformTestCase() { activity: () -> Map = { sessions.activitySnapshot() }, titles: () -> Map = { emptyMap() }, minimal: Boolean = false, - ) = EmptySessionPanel(testRootDisposable, controller, recents, history, activity, titles, minimal = minimal) + newWorktree: (() -> Unit)? = null, + ) = EmptySessionPanel( + testRootDisposable, + controller, + recents, + history, + activity, + titles, + minimal = minimal, + newWorktree = newWorktree, + ) private fun flush() = runBlocking { delay(100)