From 89db3251681fdd07762f08e716ca2752114ee61c Mon Sep 17 00:00:00 2001 From: bot-hermes Date: Thu, 13 Aug 2026 17:15:22 +0000 Subject: [PATCH] feat(ui): show GitLab merge request status in walkthrough, git view and work status --- .../work-status/WorkStatusPrimaryGroup.tsx | 38 +++++- packages/ui/src/components/views/GitView.tsx | 6 + .../ui/src/components/views/git/GitHeader.tsx | 40 ++++++ .../views/walkthrough/WalkthroughView.tsx | 22 +++- packages/ui/src/lib/gitlabMrStatus.test.ts | 119 ++++++++++++++++++ packages/ui/src/lib/gitlabMrStatus.ts | 110 ++++++++++++++++ packages/ui/src/lib/i18n/messages/de.ts | 4 + packages/ui/src/lib/i18n/messages/en.ts | 4 + packages/ui/src/lib/i18n/messages/es.ts | 4 + packages/ui/src/lib/i18n/messages/fr.ts | 4 + packages/ui/src/lib/i18n/messages/ja.ts | 4 + packages/ui/src/lib/i18n/messages/ko.ts | 4 + packages/ui/src/lib/i18n/messages/pl.ts | 4 + packages/ui/src/lib/i18n/messages/pt-BR.ts | 4 + packages/ui/src/lib/i18n/messages/uk.ts | 4 + packages/ui/src/lib/i18n/messages/zh-CN.ts | 4 + packages/ui/src/lib/i18n/messages/zh-TW.ts | 4 + 17 files changed, 373 insertions(+), 6 deletions(-) create mode 100644 packages/ui/src/lib/gitlabMrStatus.test.ts create mode 100644 packages/ui/src/lib/gitlabMrStatus.ts diff --git a/packages/ui/src/components/chat/work-status/WorkStatusPrimaryGroup.tsx b/packages/ui/src/components/chat/work-status/WorkStatusPrimaryGroup.tsx index a5f12e15..2099da54 100644 --- a/packages/ui/src/components/chat/work-status/WorkStatusPrimaryGroup.tsx +++ b/packages/ui/src/components/chat/work-status/WorkStatusPrimaryGroup.tsx @@ -4,6 +4,8 @@ import { useGitStore } from '@/stores/useGitStore'; import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs'; import { runBackgroundNetworkTask } from '@/lib/background-network'; import { getGitHubPrStatusKey, usePrVisualSummary } from '@/stores/useGitHubPrStatusStore'; +import { useGitLabMrForBranch } from '@/lib/gitlabMrStatus'; +import { useGitProvider } from '@/lib/gitProvider'; import { useSession, useSessionMessages } from '@/sync/sync-context'; import { useConfigStore } from '@/stores/useConfigStore'; import { useUIStore } from '@/stores/useUIStore'; @@ -113,6 +115,12 @@ export const WorkStatusPrimaryGroup: React.FC = ({ sessionId, directory, ); const prSummary = usePrVisualSummary(prKey); + // GitLab merge requests ride the same shared TTL cache as the git view and + // the walkthrough, so every surface that reports the branch's request stays + // consistent without extra requests. + const gitProvider = useGitProvider(directory); + const { mr: gitLabMr } = useGitLabMrForBranch(directory, branch); + // `getCurrentModel` is an imperative getter: its reference never changes, so // calling it in render subscribes to nothing. Subscribe to the selected model // ids and recompute the limits from those. @@ -201,7 +209,17 @@ export const WorkStatusPrimaryGroup: React.FC = ({ sessionId, directory, const cost = typeof session?.cost === 'number' && session.cost > 0 ? session.cost : null; const hasSession = showSession && (usagePercent !== null || cost !== null || Boolean(goalRow)); - const hasRepository = showRepository && Boolean(branch || changed || prSummary || attentionLabel); + const hasGitLabMr = gitProvider === 'gitlab' && gitLabMr !== null; + const gitLabMrVisualState = gitLabMr + ? gitLabMr.state === 'merged' + ? 'merged' + : gitLabMr.state === 'closed' + ? 'closed' + : gitLabMr.draft + ? 'draft' + : 'open' + : null; + const hasRepository = showRepository && Boolean(branch || changed || prSummary || attentionLabel || hasGitLabMr); useReportWorkStatusPresence('session-repository', hasSession || hasRepository); @@ -284,6 +302,24 @@ export const WorkStatusPrimaryGroup: React.FC = ({ sessionId, directory, /> ) : null} + {hasGitLabMr && gitLabMr ? ( + openSurface('pr') : undefined} + ariaLabel={t('chat.workStatus.action.openMr')} + iconColor={`var(--pr-${gitLabMrVisualState})`} + label={gitLabMr.title || t('chat.workStatus.mr.untitled')} + value={( + + {gitLabMr.draft ? t('chat.workStatus.pr.draft') : `!${gitLabMr.number}`} + + )} + /> + ) : null} + {prSummary ? ( <> = ({ isActive }) => { const openContextSurface = useUIStore((state) => state.openContextSurface); const prStatusBranch = status?.current ?? null; + const { mr: gitLabMr } = useGitLabMrForBranch(currentDirectory, prStatusBranch); const prChipStatus = useGitHubPrStatusStore((state) => { if (!currentDirectory || !prStatusBranch) { return null; @@ -2361,6 +2363,10 @@ export const GitView: React.FC = ({ isActive }) => { onOpenPullRequest={ currentDirectory ? () => openContextSurface(currentDirectory, 'pr') : undefined } + gitLabMr={gitLabMr} + onOpenGitLabMr={ + currentDirectory ? () => openContextSurface(currentDirectory, 'pr') : undefined + } /> {/* In-progress operation banner */} diff --git a/packages/ui/src/components/views/git/GitHeader.tsx b/packages/ui/src/components/views/git/GitHeader.tsx index c96a8a59..28f2ff07 100644 --- a/packages/ui/src/components/views/git/GitHeader.tsx +++ b/packages/ui/src/components/views/git/GitHeader.tsx @@ -19,6 +19,7 @@ import type { GitRemoteComparison, GitHubPullRequest, GitHubChecksSummary, + GitLabMergeRequestSummary, } from '@/lib/api/types'; import { useI18n } from '@/lib/i18n'; @@ -51,6 +52,8 @@ interface GitHeaderProps { pullRequest?: GitHubPullRequest | null; prChecks?: GitHubChecksSummary | null; onOpenPullRequest?: () => void; + gitLabMr?: GitLabMergeRequestSummary | null; + onOpenGitLabMr?: () => void; } const IDENTITY_ICON_MAP: Record = { @@ -258,6 +261,8 @@ export const GitHeader: React.FC = ({ pullRequest, prChecks, onOpenPullRequest, + gitLabMr, + onOpenGitLabMr, }) => { const { t } = useI18n(); if (!status) { @@ -371,6 +376,40 @@ export const GitHeader: React.FC = ({ ) : null; + // GitLab merge request chip, mirroring the GitHub PR chip above. GitLab + // states are surfaced with the same PR state palette so merged/closed/open + // read identically across providers. + const gitLabMrVisualState = gitLabMr + ? gitLabMr.state === 'merged' + ? 'merged' + : gitLabMr.state === 'closed' + ? 'closed' + : gitLabMr.draft + ? 'draft' + : 'open' + : null; + + const gitLabMrChip = gitLabMr && onOpenGitLabMr ? ( + + + + + {t('gitView.header.openMergeRequest')} + + ) : null; + const syncButtons = ( = ({
{prChip ?
{prChip}
: null} + {gitLabMrChip ?
{gitLabMrChip}
: null}
{upstreamStatusPill ? (
{upstreamStatusPill}
diff --git a/packages/ui/src/components/views/walkthrough/WalkthroughView.tsx b/packages/ui/src/components/views/walkthrough/WalkthroughView.tsx index 2882af7e..4d356b8b 100644 --- a/packages/ui/src/components/views/walkthrough/WalkthroughView.tsx +++ b/packages/ui/src/components/views/walkthrough/WalkthroughView.tsx @@ -14,6 +14,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip import { useI18n, type Locale } from '@/lib/i18n'; import { openExternalUrl } from '@/lib/url'; import { useGitProvider } from '@/lib/gitProvider'; +import { useGitLabMrForBranch } from '@/lib/gitlabMrStatus'; import { buildWalkthroughView } from '@/lib/walkthrough/model'; import type { WalkthroughSource, WalkthroughWorkingTreeScope } from '@/lib/walkthrough/types'; import { ModelSelector } from '@/components/sections/agents/ModelSelector'; @@ -210,6 +211,7 @@ export const WalkthroughView = ({ directory }: WalkthroughViewProps) => { const setPrStatusParams = useGitHubPrStatusStore((state) => state.setParams); const refreshPrStatusTargets = useGitHubPrStatusStore((state) => state.refreshTargets); const gitProvider = useGitProvider(directory); + const gitLabMr = useGitLabMrForBranch(directory, currentBranch); useEffect(() => { if (!directory || !currentBranch || !githubAuthChecked || !githubConnected || gitProvider !== 'github') return; @@ -250,12 +252,18 @@ export const WalkthroughView = ({ directory }: WalkthroughViewProps) => { [requestedSource, scope] ); - // Offer whichever pull request we know about: the one already selected, or - // the one this branch has. + // Offer whichever pull request or merge request we know about: the one + // already selected, or the one this branch has. GitLab repos get their MR + // number from the branch lookup; everything else falls back to the GitHub PR + // status store, which the polling effect above only fills for GitHub repos. const prSource = useMemo | null>(() => { if (source.kind === 'pr') return source; + if (gitProvider === 'gitlab') { + const number = gitLabMr.mr?.number; + return number ? { kind: 'pr', number } : null; + } return branchPrNumber ? { kind: 'pr', number: branchPrNumber } : null; - }, [branchPrNumber, source]); + }, [branchPrNumber, gitLabMr.mr, gitProvider, source]); const selectWorkingTree = useCallback( (value: WalkthroughWorkingTreeScope) => { @@ -341,7 +349,9 @@ export const WalkthroughView = ({ directory }: WalkthroughViewProps) => { const sourceLabel = source.kind === 'branch' ? t('walkthrough.scope.branch') : source.kind === 'pr' - ? t('walkthrough.scope.pullRequest', { number: source.number }) + ? gitProvider === 'gitlab' + ? t('walkthrough.scope.mergeRequest', { number: source.number }) + : t('walkthrough.scope.pullRequest', { number: source.number }) : scope === 'all' ? t('walkthrough.scope.all') : scope === 'staged' @@ -547,7 +557,9 @@ export const WalkthroughView = ({ directory }: WalkthroughViewProps) => { )} {prSource && ( - {t('walkthrough.scope.pullRequest', { number: prSource.number })} + {gitProvider === 'gitlab' + ? t('walkthrough.scope.mergeRequest', { number: prSource.number }) + : t('walkthrough.scope.pullRequest', { number: prSource.number })} )} diff --git a/packages/ui/src/lib/gitlabMrStatus.test.ts b/packages/ui/src/lib/gitlabMrStatus.test.ts new file mode 100644 index 00000000..5ab492ce --- /dev/null +++ b/packages/ui/src/lib/gitlabMrStatus.test.ts @@ -0,0 +1,119 @@ +import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test'; +import type { GitLabMergeRequestSummary } from '@/lib/api/types'; + +const mrsListCalls: Array<{ directory: string; options?: { sourceBranch?: string } }> = []; +let mrsListResult: GitLabMergeRequestSummary[] = []; +let mrsListFailure: Error | null = null; +let registryHasGitlab = true; + +mock.module('@/contexts/runtimeAPIRegistry', () => ({ + getRegisteredRuntimeAPIs: () => { + if (!registryHasGitlab) return null; + return { + gitlab: { + mrsList: (directory: string, options?: { sourceBranch?: string }) => { + mrsListCalls.push({ directory, options }); + if (mrsListFailure) { + return Promise.reject(mrsListFailure); + } + const all = mrsListResult; + const filtered = options?.sourceBranch + ? all.filter((item) => item.sourceBranch === options.sourceBranch) + : all; + return Promise.resolve({ mrs: filtered }); + }, + }, + }; + }, +})); + +const { resolveGitLabMrForBranch } = await import('./gitlabMrStatus'); + +const mr = (number: number, state: string, sourceBranch: string): GitLabMergeRequestSummary => ({ + number, + title: `MR ${number}`, + url: `https://gitlab.example/${number}`, + state, + draft: false, + author: { id: 1, username: 'user', name: 'User' }, + sourceBranch, + targetBranch: 'main', +}); + +describe('resolveGitLabMrForBranch', () => { + beforeEach(() => { + mrsListCalls.length = 0; + mrsListResult = []; + mrsListFailure = null; + registryHasGitlab = true; + }); + + afterEach(() => { + mrsListCalls.length = 0; + mrsListResult = []; + mrsListFailure = null; + registryHasGitlab = true; + }); + + test('prefers the opened MR over a merged one for the branch', async () => { + mrsListResult = [mr(3, 'merged', 'feat/a'), mr(7, 'opened', 'feat/a')]; + + const result = await resolveGitLabMrForBranch('/repo', 'feat/a'); + + expect(result?.number).toBe(7); + expect(mrsListCalls).toEqual([ + { directory: '/repo', options: { sourceBranch: 'feat/a' } }, + ]); + }); + + test('falls back to a merged MR when no opened one exists', async () => { + mrsListResult = [mr(5, 'merged', 'feat/b'), mr(9, 'closed', 'feat/b')]; + + const result = await resolveGitLabMrForBranch('/repo', 'feat/b'); + + expect(result?.number).toBe(5); + }); + + test('returns null when no MR matches the branch', async () => { + mrsListResult = [mr(5, 'merged', 'feat/a')]; + + const result = await resolveGitLabMrForBranch('/repo', 'feat/c'); + + expect(result).toBeNull(); + expect(mrsListCalls).toHaveLength(1); + }); + + test('returns null without calling the API when the runtime has no GitLab client', async () => { + registryHasGitlab = false; + + const result = await resolveGitLabMrForBranch('/repo', 'feat/a'); + + expect(result).toBeNull(); + expect(mrsListCalls).toHaveLength(0); + }); + + test('returns null and caches when the request fails', async () => { + mrsListFailure = new Error('boom'); + + const first = await resolveGitLabMrForBranch('/repo', 'fail-branch'); + expect(first).toBeNull(); + + mrsListFailure = null; + mrsListResult = [mr(1, 'opened', 'fail-branch')]; + // Same directory+branch within TTL must not re-request. + const second = await resolveGitLabMrForBranch('/repo', 'fail-branch'); + expect(second).toBeNull(); + expect(mrsListCalls).toHaveLength(1); + }); + + test('serves the second call from cache within the TTL window', async () => { + mrsListResult = [mr(7, 'opened', 'cache-branch')]; + + const first = await resolveGitLabMrForBranch('/repo', 'cache-branch'); + const second = await resolveGitLabMrForBranch('/repo', 'cache-branch'); + + expect(first?.number).toBe(7); + expect(second?.number).toBe(7); + expect(mrsListCalls).toHaveLength(1); + }); +}); diff --git a/packages/ui/src/lib/gitlabMrStatus.ts b/packages/ui/src/lib/gitlabMrStatus.ts new file mode 100644 index 00000000..8046019d --- /dev/null +++ b/packages/ui/src/lib/gitlabMrStatus.ts @@ -0,0 +1,110 @@ +import { useState, useEffect } from 'react'; +import { getRegisteredRuntimeAPIs } from '@/contexts/runtimeAPIRegistry'; +import type { GitLabMergeRequestSummary } from '@/lib/api/types'; + +// Branch MR lookups are cheap to re-request but visible to the user on every +// mount of the surfaces that display them (walkthrough header, git view, work +// status). A shared TTL cache keeps those surfaces consistent with each other +// and stops repeated GitLab calls while a branch is in view. +const CACHE_TTL_MS = 90_000; +const mrCache = new Map(); + +const cacheKeyFor = (directory: string, branch: string): string => `${directory}\n${branch}`; + +const readCachedMr = (directory: string, branch: string): GitLabMergeRequestSummary | null | undefined => { + const entry = mrCache.get(cacheKeyFor(directory, branch)); + return entry?.mr; +}; + +/** + * Resolve the merge request targeting `branch` in `directory`, preferring the + * opened request and falling back to a merged one so a just-merged branch still + * surfaces its request instead of nothing. + * + * Returns null when the runtime has no GitLab API, the request fails, or no MR + * matches — callers only use the result to show or hide an additive chip, so a + * null answer simply means "nothing to show". Results are cached per + * directory+branch for CACHE_TTL_MS, including the null case. + */ +export const resolveGitLabMrForBranch = async ( + directory: string, + branch: string, +): Promise => { + const gitlab = getRegisteredRuntimeAPIs()?.gitlab; + if (!gitlab?.mrsList || !directory || !branch) { + return null; + } + + const key = cacheKeyFor(directory, branch); + const cached = mrCache.get(key); + if (cached && Date.now() - cached.at < CACHE_TTL_MS) { + return cached.mr; + } + + let mr: GitLabMergeRequestSummary | null = null; + try { + const result = await gitlab.mrsList(directory, { sourceBranch: branch }); + const candidates = result.mrs ?? []; + mr = candidates.find((item) => item.state === 'opened') + ?? candidates.find((item) => item.state === 'merged') + ?? null; + } catch { + mr = null; + } + + mrCache.set(key, { at: Date.now(), mr }); + return mr; +}; + +/** + * Subscribe to the branch's merge request. Reads the TTL cache synchronously + * for the initial render so an already-resolved MR never flashes away while a + * refresh runs; a cache miss shows a loading state instead of a stale result + * from another branch. + */ +export const useGitLabMrForBranch = ( + directory: string | null | undefined, + branch: string | null | undefined, +): { mr: GitLabMergeRequestSummary | null; isLoading: boolean } => { + const [mr, setMr] = useState(() => + directory && branch ? (readCachedMr(directory, branch) ?? null) : null + ); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + if (!directory || !branch) { + setMr(null); + setIsLoading(false); + return; + } + + let mounted = true; + const cached = readCachedMr(directory, branch); + const cacheEntry = mrCache.get(cacheKeyFor(directory, branch)); + const fresh = cacheEntry !== undefined && Date.now() - cacheEntry.at < CACHE_TTL_MS; + + if (fresh) { + setMr(cached ?? null); + setIsLoading(false); + return; + } + + // A stale entry stays on screen while it refreshes; a missing one shows + // the loading state rather than a result from a previous branch. + setMr(cached ?? null); + setIsLoading(true); + + void resolveGitLabMrForBranch(directory, branch).then((resolved) => { + if (mounted) { + setMr(resolved); + setIsLoading(false); + } + }); + + return () => { + mounted = false; + }; + }, [directory, branch]); + + return { mr, isLoading }; +}; diff --git a/packages/ui/src/lib/i18n/messages/de.ts b/packages/ui/src/lib/i18n/messages/de.ts index 401031a3..14fd30d1 100644 --- a/packages/ui/src/lib/i18n/messages/de.ts +++ b/packages/ui/src/lib/i18n/messages/de.ts @@ -2968,6 +2968,7 @@ export const dict = { 'sessions.sidebar.session.status.movingToWorktree': 'Sitzung wird in einen neuen Worktree verschoben', 'gitView.header.updateBranch': 'Branch aktualisieren', 'gitView.header.openPullRequest': 'Pull Request öffnen', + 'gitView.header.openMergeRequest': 'Merge Request öffnen', 'gitView.history.refresh': 'Verlauf aktualisieren', 'gitView.operation.inProgressTitleManyConflicts': 'Operation {operation} läuft: {count} Konflikte', 'gitView.operation.inProgressTitleOneConflict': 'Operation {operation} läuft: {count} Konflikt', @@ -3022,6 +3023,7 @@ export const dict = { 'walkthrough.missing.languageAndModel': 'Noch kein Walkthrough auf dieser Sprache von diesem Modell vorhanden — zeige den zuletzt hier erstellten.', 'walkthrough.language.selectorAria': 'Sprache auswählen', 'walkthrough.scope.pullRequest': 'PR #{number}', + 'walkthrough.scope.mergeRequest': 'MR !{number}', 'walkthrough.action.generate': 'Generieren', 'walkthrough.action.regenerate': 'Erneut generieren', 'walkthrough.action.cancel': 'Abbrechen', @@ -3109,6 +3111,7 @@ export const dict = { 'chat.workStatus.git.changedFileSingle': '{count} Datei geändert', 'chat.workStatus.git.changedFilePlural': '{count} Dateien geändert', 'chat.workStatus.pr.untitled': 'Pull Request ohne Titel', + 'chat.workStatus.mr.untitled': 'Merge Request ohne Titel', 'chat.workStatus.pr.draft': 'Entwurf', 'chat.workStatus.pr.checks': 'Prüfungen', 'chat.workStatus.pr.checksFailed': '{count} fehlgeschlagen', @@ -3140,6 +3143,7 @@ export const dict = { 'chat.workStatus.action.openChanges': 'Änderungen öffnen', 'chat.workStatus.action.openGit': 'Git-Panel öffnen', 'chat.workStatus.action.openPr': 'Pull Request öffnen', + 'chat.workStatus.action.openMr': 'Merge Request öffnen', 'chat.workStatus.action.openSubagent': '{name} öffnen', 'chat.workStatus.section.usage': 'Nutzung', 'chat.workStatus.goal.open': 'Ziel verwalten', diff --git a/packages/ui/src/lib/i18n/messages/en.ts b/packages/ui/src/lib/i18n/messages/en.ts index a7c4d82a..85f8a435 100644 --- a/packages/ui/src/lib/i18n/messages/en.ts +++ b/packages/ui/src/lib/i18n/messages/en.ts @@ -770,6 +770,7 @@ export const dict = { 'gitView.header.repositoryViews': 'Repository views', 'gitView.header.updateBranch': 'Update branch', 'gitView.header.openPullRequest': 'Open pull request', + 'gitView.header.openMergeRequest': 'Open merge request', 'gitView.header.removeRemoteAria': 'Remove remote {name}', 'gitView.header.removeRemoteTitle': 'Remove remote {name}', 'gitView.header.upstreamSynced': 'synced', @@ -1160,6 +1161,7 @@ export const dict = { 'walkthrough.missing.languageAndModel': 'No walkthrough in this language from this model yet — showing the last one generated here.', 'walkthrough.language.selectorAria': 'Select the walkthrough language', 'walkthrough.scope.pullRequest': 'PR #{number}', + 'walkthrough.scope.mergeRequest': 'MR !{number}', 'walkthrough.action.generate': 'Generate walkthrough', 'walkthrough.action.regenerate': 'Regenerate', 'walkthrough.action.cancel': 'Cancel', @@ -3111,6 +3113,7 @@ export const dict = { 'chat.workStatus.git.changedFileSingle': '{count} file changed', 'chat.workStatus.git.changedFilePlural': '{count} files changed', 'chat.workStatus.pr.untitled': 'Untitled pull request', + 'chat.workStatus.mr.untitled': 'Untitled merge request', 'chat.workStatus.pr.draft': 'Draft', 'chat.workStatus.pr.checks': 'Checks', 'chat.workStatus.pr.checksFailed': '{count} failed', @@ -3142,6 +3145,7 @@ export const dict = { 'chat.workStatus.action.openChanges': 'Open changes', 'chat.workStatus.action.openGit': 'Open Git panel', 'chat.workStatus.action.openPr': 'Open pull request', + 'chat.workStatus.action.openMr': 'Open merge request', 'chat.workStatus.action.openSubagent': 'Open {name}', 'chat.workStatus.section.usage': 'Usage', 'chat.workStatus.goal.open': 'Manage goal', diff --git a/packages/ui/src/lib/i18n/messages/es.ts b/packages/ui/src/lib/i18n/messages/es.ts index 414469bf..28958594 100644 --- a/packages/ui/src/lib/i18n/messages/es.ts +++ b/packages/ui/src/lib/i18n/messages/es.ts @@ -771,6 +771,7 @@ export const dict: Record = { "gitView.header.repositoryViews": "Vistas del repositorio", "gitView.header.updateBranch": "Actualizar rama", "gitView.header.openPullRequest": "Abrir pull request", + "gitView.header.openMergeRequest": "Abrir solicitud de fusión", "gitView.header.removeRemoteAria": "Eliminar remoto", "gitView.header.removeRemoteTitle": "Eliminar remoto", "gitView.header.upstreamSynced": "sincronizado", @@ -1161,6 +1162,7 @@ export const dict: Record = { "walkthrough.missing.languageAndModel": "Aún no hay un recorrido en este idioma con este modelo: se muestra el último generado aquí.", "walkthrough.language.selectorAria": "Elegir el idioma del recorrido", "walkthrough.scope.pullRequest": "PR n.º {number}", + "walkthrough.scope.mergeRequest": "MR !{number}", "walkthrough.action.generate": "Generar recorrido", "walkthrough.action.regenerate": "Regenerar", "walkthrough.action.cancel": "Cancelar", @@ -3112,6 +3114,7 @@ export const dict: Record = { 'chat.workStatus.git.changedFileSingle': '{count} archivo modificado', 'chat.workStatus.git.changedFilePlural': '{count} archivos modificados', 'chat.workStatus.pr.untitled': 'Pull request sin título', + 'chat.workStatus.mr.untitled': 'Solicitud de fusión sin título', 'chat.workStatus.pr.draft': 'Borrador', 'chat.workStatus.pr.checks': 'Comprobaciones', 'chat.workStatus.pr.checksFailed': '{count} fallaron', @@ -3143,6 +3146,7 @@ export const dict: Record = { 'chat.workStatus.action.openChanges': 'Abrir cambios', 'chat.workStatus.action.openGit': 'Abrir panel de Git', 'chat.workStatus.action.openPr': 'Abrir pull request', + 'chat.workStatus.action.openMr': 'Abrir solicitud de fusión', 'chat.workStatus.action.openSubagent': 'Abrir {name}', 'chat.workStatus.section.usage': 'Uso', 'chat.workStatus.goal.open': 'Gestionar objetivo', diff --git a/packages/ui/src/lib/i18n/messages/fr.ts b/packages/ui/src/lib/i18n/messages/fr.ts index 10964f38..43241bff 100644 --- a/packages/ui/src/lib/i18n/messages/fr.ts +++ b/packages/ui/src/lib/i18n/messages/fr.ts @@ -594,6 +594,7 @@ export const dict = { 'gitView.header.repositoryViews': 'Vues du dépôt', 'gitView.header.updateBranch': 'Mettre à jour la branche', 'gitView.header.openPullRequest': 'Ouvrir la pull request', + 'gitView.header.openMergeRequest': 'Ouvrir la demande de fusion', 'gitView.header.removeRemoteAria': 'Supprimer le remote', 'gitView.header.removeRemoteTitle': 'Supprimer le remote', 'gitView.header.upstreamSynced': 'synchronisé', @@ -980,6 +981,7 @@ export const dict = { 'walkthrough.missing.languageAndModel': 'Pas encore de parcours dans cette langue avec ce modèle — voici le dernier généré ici.', 'walkthrough.language.selectorAria': 'Choisir la langue du parcours', 'walkthrough.scope.pullRequest': 'PR n° {number}', + 'walkthrough.scope.mergeRequest': 'MR !{number}', 'walkthrough.action.generate': 'Générer le parcours', 'walkthrough.action.regenerate': 'Régénérer', 'walkthrough.action.cancel': 'Annuler', @@ -3109,6 +3111,7 @@ export const dict = { 'chat.workStatus.git.changedFileSingle': '{count} fichier modifié', 'chat.workStatus.git.changedFilePlural': '{count} fichiers modifiés', 'chat.workStatus.pr.untitled': 'Pull request sans titre', + 'chat.workStatus.mr.untitled': 'Demande de fusion sans titre', 'chat.workStatus.pr.draft': 'Brouillon', 'chat.workStatus.pr.checks': 'Vérifications', 'chat.workStatus.pr.checksFailed': '{count} en échec', @@ -3140,6 +3143,7 @@ export const dict = { 'chat.workStatus.action.openChanges': 'Ouvrir les modifications', 'chat.workStatus.action.openGit': 'Ouvrir le panneau Git', 'chat.workStatus.action.openPr': 'Ouvrir la pull request', + 'chat.workStatus.action.openMr': 'Ouvrir la demande de fusion', 'chat.workStatus.action.openSubagent': 'Ouvrir {name}', 'chat.workStatus.section.usage': 'Utilisation', 'chat.workStatus.goal.open': 'Gérer l’objectif', diff --git a/packages/ui/src/lib/i18n/messages/ja.ts b/packages/ui/src/lib/i18n/messages/ja.ts index cf857617..7772970a 100644 --- a/packages/ui/src/lib/i18n/messages/ja.ts +++ b/packages/ui/src/lib/i18n/messages/ja.ts @@ -768,6 +768,7 @@ export const dict: Record = { 'gitView.header.repositoryViews': 'リポジトリビュー', 'gitView.header.updateBranch': 'ブランチを更新', 'gitView.header.openPullRequest': 'プルリクエストを開く', + 'gitView.header.openMergeRequest': 'マージリクエストを開く', 'gitView.header.removeRemoteAria': 'リモート{name}を削除', 'gitView.header.removeRemoteTitle': 'リモート{name}を削除', 'gitView.header.upstreamSynced': '同期済み', @@ -1157,6 +1158,7 @@ export const dict: Record = { 'walkthrough.missing.languageAndModel': 'この言語・このモデルのウォークスルーはまだありません。ここで最後に生成されたものを表示しています。', 'walkthrough.language.selectorAria': 'ウォークスルーの言語を選択', 'walkthrough.scope.pullRequest': 'PR #{number}', + 'walkthrough.scope.mergeRequest': 'MR !{number}', 'walkthrough.action.generate': 'ウォークスルーを生成', 'walkthrough.action.regenerate': '再生成', 'walkthrough.action.cancel': 'キャンセル', @@ -3111,6 +3113,7 @@ export const dict: Record = { 'chat.workStatus.git.changedFileSingle': '{count} 件のファイルを変更', 'chat.workStatus.git.changedFilePlural': '{count} 件のファイルを変更', 'chat.workStatus.pr.untitled': 'タイトルなしのプルリクエスト', + 'chat.workStatus.mr.untitled': 'タイトルなしのマージリクエスト', 'chat.workStatus.pr.draft': 'ドラフト', 'chat.workStatus.pr.checks': 'チェック', 'chat.workStatus.pr.checksFailed': '{count} 件失敗', @@ -3142,6 +3145,7 @@ export const dict: Record = { 'chat.workStatus.action.openChanges': '変更を開く', 'chat.workStatus.action.openGit': 'Git パネルを開く', 'chat.workStatus.action.openPr': 'プルリクエストを開く', + 'chat.workStatus.action.openMr': 'マージリクエストを開く', 'chat.workStatus.action.openSubagent': '{name} を開く', 'chat.workStatus.section.usage': '使用量', 'chat.workStatus.goal.open': '目標を管理', diff --git a/packages/ui/src/lib/i18n/messages/ko.ts b/packages/ui/src/lib/i18n/messages/ko.ts index ca1aef58..3ab1d5bb 100644 --- a/packages/ui/src/lib/i18n/messages/ko.ts +++ b/packages/ui/src/lib/i18n/messages/ko.ts @@ -771,6 +771,7 @@ export const dict: Record = { 'gitView.header.repositoryViews': '저장소 보기', 'gitView.header.updateBranch': '브랜치 업데이트', 'gitView.header.openPullRequest': '풀 리퀘스트 열기', + 'gitView.header.openMergeRequest': '머지 리퀘스트 열기', 'gitView.header.removeRemoteAria': '리모트 제거', 'gitView.header.removeRemoteTitle': '리모트 제거', 'gitView.header.upstreamSynced': '동기화됨', @@ -1161,6 +1162,7 @@ export const dict: Record = { 'walkthrough.missing.languageAndModel': '이 언어와 이 모델로 생성한 워크스루가 아직 없어 마지막으로 생성된 것을 표시합니다.', 'walkthrough.language.selectorAria': '워크스루 언어 선택', 'walkthrough.scope.pullRequest': 'PR #{number}', + 'walkthrough.scope.mergeRequest': 'MR !{number}', 'walkthrough.action.generate': '워크스루 생성', 'walkthrough.action.regenerate': '다시 생성', 'walkthrough.action.cancel': '취소', @@ -3111,6 +3113,7 @@ export const dict: Record = { 'chat.workStatus.git.changedFileSingle': '파일 {count}개 변경됨', 'chat.workStatus.git.changedFilePlural': '파일 {count}개 변경됨', 'chat.workStatus.pr.untitled': '제목 없는 풀 리퀘스트', + 'chat.workStatus.mr.untitled': '제목 없는 머지 리퀘스트', 'chat.workStatus.pr.draft': '초안', 'chat.workStatus.pr.checks': '검사', 'chat.workStatus.pr.checksFailed': '{count}개 실패', @@ -3142,6 +3145,7 @@ export const dict: Record = { 'chat.workStatus.action.openChanges': '변경 사항 열기', 'chat.workStatus.action.openGit': 'Git 패널 열기', 'chat.workStatus.action.openPr': '풀 리퀘스트 열기', + 'chat.workStatus.action.openMr': '머지 리퀘스트 열기', 'chat.workStatus.action.openSubagent': '{name} 열기', 'chat.workStatus.section.usage': '사용량', 'chat.workStatus.goal.open': '목표 관리', diff --git a/packages/ui/src/lib/i18n/messages/pl.ts b/packages/ui/src/lib/i18n/messages/pl.ts index 8d887778..f89d31ea 100644 --- a/packages/ui/src/lib/i18n/messages/pl.ts +++ b/packages/ui/src/lib/i18n/messages/pl.ts @@ -1498,6 +1498,7 @@ export const dict: Record = { 'walkthrough.missing.languageAndModel': 'Nie ma jeszcze przewodnika w tym języku od tego modelu — pokazujemy ostatni wygenerowany tutaj.', 'walkthrough.language.selectorAria': 'Wybierz język przewodnika', 'walkthrough.scope.pullRequest': 'PR #{number}', + 'walkthrough.scope.mergeRequest': 'MR !{number}', 'walkthrough.action.generate': 'Wygeneruj przewodnik', 'walkthrough.action.regenerate': 'Wygeneruj ponownie', 'walkthrough.action.cancel': 'Anuluj', @@ -2074,6 +2075,7 @@ export const dict: Record = { 'gitView.header.repositoryViews': 'Widoki repozytorium', 'gitView.header.updateBranch': 'Zaktualizuj gałąź', 'gitView.header.openPullRequest': 'Otwórz pull request', + 'gitView.header.openMergeRequest': 'Otwórz żądanie scalenia', 'gitView.header.removeRemoteAria': 'Usuń remote {name}', 'gitView.header.removeRemoteTitle': 'Usuń remote {name}', 'gitView.header.upstreamSynced': 'zsynchronizowano', @@ -3128,6 +3130,7 @@ export const dict: Record = { 'chat.workStatus.git.changedFileSingle': 'Zmieniono {count} plik', 'chat.workStatus.git.changedFilePlural': 'Zmieniono {count} plików', 'chat.workStatus.pr.untitled': 'Pull request bez tytułu', + 'chat.workStatus.mr.untitled': 'Żądanie scalenia bez tytułu', 'chat.workStatus.pr.draft': 'Szkic', 'chat.workStatus.pr.checks': 'Sprawdzenia', 'chat.workStatus.pr.checksFailed': '{count} nieudanych', @@ -3159,6 +3162,7 @@ export const dict: Record = { 'chat.workStatus.action.openChanges': 'Otwórz zmiany', 'chat.workStatus.action.openGit': 'Otwórz panel Git', 'chat.workStatus.action.openPr': 'Otwórz pull request', + 'chat.workStatus.action.openMr': 'Otwórz żądanie scalenia', 'chat.workStatus.action.openSubagent': 'Otwórz {name}', 'chat.workStatus.section.usage': 'Zużycie', 'chat.workStatus.goal.open': 'Zarządzaj celem', diff --git a/packages/ui/src/lib/i18n/messages/pt-BR.ts b/packages/ui/src/lib/i18n/messages/pt-BR.ts index 03569a30..40aca2d7 100644 --- a/packages/ui/src/lib/i18n/messages/pt-BR.ts +++ b/packages/ui/src/lib/i18n/messages/pt-BR.ts @@ -771,6 +771,7 @@ export const dict: Record = { "gitView.header.repositoryViews": "Visualizações do repositório", "gitView.header.updateBranch": "Atualizar branch", "gitView.header.openPullRequest": "Abrir pull request", + "gitView.header.openMergeRequest": "Abrir solicitação de merge", "gitView.header.removeRemoteAria": "Excluir remoto", "gitView.header.removeRemoteTitle": "Excluir remoto", "gitView.header.upstreamSynced": "sincronizado", @@ -1161,6 +1162,7 @@ export const dict: Record = { "walkthrough.missing.languageAndModel": "Ainda não há um percurso neste idioma com este modelo — exibindo o último gerado aqui.", "walkthrough.language.selectorAria": "Escolher o idioma do percurso", "walkthrough.scope.pullRequest": "PR nº {number}", + "walkthrough.scope.mergeRequest": "MR !{number}", "walkthrough.action.generate": "Gerar percurso", "walkthrough.action.regenerate": "Gerar novamente", "walkthrough.action.cancel": "Cancelar", @@ -3112,6 +3114,7 @@ export const dict: Record = { 'chat.workStatus.git.changedFileSingle': '{count} arquivo alterado', 'chat.workStatus.git.changedFilePlural': '{count} arquivos alterados', 'chat.workStatus.pr.untitled': 'Pull request sem título', + 'chat.workStatus.mr.untitled': 'Solicitação de merge sem título', 'chat.workStatus.pr.draft': 'Rascunho', 'chat.workStatus.pr.checks': 'Verificações', 'chat.workStatus.pr.checksFailed': '{count} falharam', @@ -3143,6 +3146,7 @@ export const dict: Record = { 'chat.workStatus.action.openChanges': 'Abrir alterações', 'chat.workStatus.action.openGit': 'Abrir painel do Git', 'chat.workStatus.action.openPr': 'Abrir pull request', + 'chat.workStatus.action.openMr': 'Abrir solicitação de merge', 'chat.workStatus.action.openSubagent': 'Abrir {name}', 'chat.workStatus.section.usage': 'Uso', 'chat.workStatus.goal.open': 'Gerenciar objetivo', diff --git a/packages/ui/src/lib/i18n/messages/uk.ts b/packages/ui/src/lib/i18n/messages/uk.ts index ae396daf..ff4c11b4 100644 --- a/packages/ui/src/lib/i18n/messages/uk.ts +++ b/packages/ui/src/lib/i18n/messages/uk.ts @@ -771,6 +771,7 @@ export const dict: Record = { "gitView.header.repositoryViews": "Перегляди репозиторію", "gitView.header.updateBranch": "Оновити гілку", "gitView.header.openPullRequest": "Відкрити pull request", + "gitView.header.openMergeRequest": "Відкрити запит на злиття", "gitView.header.removeRemoteAria": "Видалити remote", "gitView.header.removeRemoteTitle": "Видалити remote", "gitView.header.upstreamSynced": "синхронізовано", @@ -1161,6 +1162,7 @@ export const dict: Record = { "walkthrough.missing.languageAndModel": "Розбору цією мовою від цієї моделі ще немає — показано останній згенерований тут.", "walkthrough.language.selectorAria": "Обрати мову розбору", "walkthrough.scope.pullRequest": "PR #{number}", + "walkthrough.scope.mergeRequest": "MR !{number}", "walkthrough.action.generate": "Створити розбір", "walkthrough.action.regenerate": "Створити заново", "walkthrough.action.cancel": "Скасувати", @@ -3112,6 +3114,7 @@ export const dict: Record = { 'chat.workStatus.git.changedFileSingle': 'Змінено {count} файл', 'chat.workStatus.git.changedFilePlural': 'Змінено {count} файлів', 'chat.workStatus.pr.untitled': 'Pull request без назви', + 'chat.workStatus.mr.untitled': 'Запит на злиття без назви', 'chat.workStatus.pr.draft': 'Чернетка', 'chat.workStatus.pr.checks': 'Перевірки', 'chat.workStatus.pr.checksFailed': '{count} впало', @@ -3143,6 +3146,7 @@ export const dict: Record = { 'chat.workStatus.action.openChanges': 'Відкрити зміни', 'chat.workStatus.action.openGit': 'Відкрити панель Git', 'chat.workStatus.action.openPr': 'Відкрити pull request', + 'chat.workStatus.action.openMr': 'Відкрити запит на злиття', 'chat.workStatus.action.openSubagent': 'Відкрити {name}', 'chat.workStatus.section.usage': 'Використання', 'chat.workStatus.goal.open': 'Керувати ціллю', diff --git a/packages/ui/src/lib/i18n/messages/zh-CN.ts b/packages/ui/src/lib/i18n/messages/zh-CN.ts index c96e3db0..b226ccc6 100644 --- a/packages/ui/src/lib/i18n/messages/zh-CN.ts +++ b/packages/ui/src/lib/i18n/messages/zh-CN.ts @@ -771,6 +771,7 @@ export const dict: Record = { 'gitView.header.repositoryViews': '仓库视图', 'gitView.header.updateBranch': '更新分支', 'gitView.header.openPullRequest': '打开拉取请求', + 'gitView.header.openMergeRequest': '打开合并请求', 'gitView.header.removeRemoteAria': '移除远程 {name}', 'gitView.header.removeRemoteTitle': '移除 {name}', 'gitView.header.upstreamSynced': '已同步', @@ -1161,6 +1162,7 @@ export const dict: Record = { 'walkthrough.missing.languageAndModel': '尚无使用该语言和该模型生成的导读,当前显示最近一次生成的版本。', 'walkthrough.language.selectorAria': '选择导读语言', 'walkthrough.scope.pullRequest': 'PR #{number}', + 'walkthrough.scope.mergeRequest': 'MR !{number}', 'walkthrough.action.generate': '生成导读', 'walkthrough.action.regenerate': '重新生成', 'walkthrough.action.cancel': '取消', @@ -3112,6 +3114,7 @@ export const dict: Record = { 'chat.workStatus.git.changedFileSingle': '已更改 {count} 个文件', 'chat.workStatus.git.changedFilePlural': '已更改 {count} 个文件', 'chat.workStatus.pr.untitled': '未命名的拉取请求', + 'chat.workStatus.mr.untitled': '未命名的合并请求', 'chat.workStatus.pr.draft': '草稿', 'chat.workStatus.pr.checks': '检查', 'chat.workStatus.pr.checksFailed': '{count} 项失败', @@ -3143,6 +3146,7 @@ export const dict: Record = { 'chat.workStatus.action.openChanges': '打开更改', 'chat.workStatus.action.openGit': '打开 Git 面板', 'chat.workStatus.action.openPr': '打开拉取请求', + 'chat.workStatus.action.openMr': '打开合并请求', 'chat.workStatus.action.openSubagent': '打开 {name}', 'chat.workStatus.section.usage': '用量', 'chat.workStatus.goal.open': '管理目标', diff --git a/packages/ui/src/lib/i18n/messages/zh-TW.ts b/packages/ui/src/lib/i18n/messages/zh-TW.ts index 7860b7c1..5af61b4b 100644 --- a/packages/ui/src/lib/i18n/messages/zh-TW.ts +++ b/packages/ui/src/lib/i18n/messages/zh-TW.ts @@ -784,6 +784,7 @@ export const dict: Record = { 'gitView.header.repositoryViews': '儲存庫檢視', 'gitView.header.updateBranch': '更新分支', 'gitView.header.openPullRequest': '開啟提取請求', + 'gitView.header.openMergeRequest': '開啟合併請求', 'gitView.header.removeRemoteAria': '移除遠端 {name}', 'gitView.header.removeRemoteTitle': '移除 {name}', 'gitView.header.upstreamSynced': '已同步', @@ -1173,6 +1174,7 @@ export const dict: Record = { 'walkthrough.missing.languageAndModel': '尚無使用該語言與該模型產生的導讀,目前顯示最近一次產生的版本。', 'walkthrough.language.selectorAria': '選擇導讀語言', 'walkthrough.scope.pullRequest': 'PR #{number}', + 'walkthrough.scope.mergeRequest': 'MR !{number}', 'walkthrough.action.generate': '產生導讀', 'walkthrough.action.regenerate': '重新產生', 'walkthrough.action.cancel': '取消', @@ -3111,6 +3113,7 @@ export const dict: Record = { 'chat.workStatus.git.changedFileSingle': '已變更 {count} 個檔案', 'chat.workStatus.git.changedFilePlural': '已變更 {count} 個檔案', 'chat.workStatus.pr.untitled': '未命名的提取請求', + 'chat.workStatus.mr.untitled': '未命名的合併請求', 'chat.workStatus.pr.draft': '草稿', 'chat.workStatus.pr.checks': '檢查', 'chat.workStatus.pr.checksFailed': '{count} 項失敗', @@ -3142,6 +3145,7 @@ export const dict: Record = { 'chat.workStatus.action.openChanges': '開啟變更', 'chat.workStatus.action.openGit': '開啟 Git 面板', 'chat.workStatus.action.openPr': '開啟提取請求', + 'chat.workStatus.action.openMr': '開啟合併請求', 'chat.workStatus.action.openSubagent': '開啟 {name}', 'chat.workStatus.section.usage': '用量', 'chat.workStatus.goal.open': '管理目標',