feat(ui): show GitLab merge request status in walkthrough, git view and work status
This commit is contained in:
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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<string, { at: number; mr: GitLabMergeRequestSummary | null }>();
|
||||
|
||||
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<GitLabMergeRequestSummary | null> => {
|
||||
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<GitLabMergeRequestSummary | null>(() =>
|
||||
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 };
|
||||
};
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -771,6 +771,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
"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<I18nKey, string> = {
|
||||
"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<I18nKey, string> = {
|
||||
'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<I18nKey, string> = {
|
||||
'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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -768,6 +768,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'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<I18nKey, string> = {
|
||||
'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<I18nKey, string> = {
|
||||
'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<I18nKey, string> = {
|
||||
'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': '目標を管理',
|
||||
|
||||
@@ -771,6 +771,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'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<I18nKey, string> = {
|
||||
'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<I18nKey, string> = {
|
||||
'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<I18nKey, string> = {
|
||||
'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': '목표 관리',
|
||||
|
||||
@@ -1498,6 +1498,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'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<I18nKey, string> = {
|
||||
'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<I18nKey, string> = {
|
||||
'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<I18nKey, string> = {
|
||||
'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',
|
||||
|
||||
@@ -771,6 +771,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
"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<I18nKey, string> = {
|
||||
"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<I18nKey, string> = {
|
||||
'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<I18nKey, string> = {
|
||||
'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',
|
||||
|
||||
@@ -771,6 +771,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
"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<I18nKey, string> = {
|
||||
"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<I18nKey, string> = {
|
||||
'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<I18nKey, string> = {
|
||||
'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': 'Керувати ціллю',
|
||||
|
||||
@@ -771,6 +771,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'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<I18nKey, string> = {
|
||||
'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<I18nKey, string> = {
|
||||
'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<I18nKey, string> = {
|
||||
'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': '管理目标',
|
||||
|
||||
@@ -784,6 +784,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'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<I18nKey, string> = {
|
||||
'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<I18nKey, string> = {
|
||||
'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<I18nKey, string> = {
|
||||
'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': '管理目標',
|
||||
|
||||
Reference in New Issue
Block a user