feat: Redesign git changes to split stage/unstaged files. (#1359)

* feat: Redesign git changes to split stage/unstaged files.

Signed-off-by: Paolo Insogna <paolo@cowtech.it>

* fixup

Signed-off-by: Paolo Insogna <paolo@cowtech.it>

* fixup

Signed-off-by: Paolo Insogna <paolo@cowtech.it>

* refactor: streamline git changes panel

* fix: label staged and working diff tabs

* fix: isolate staged and working diff files

* fix: scope staged and working diff updates

* fix: scope git row revert to working changes

---------

Signed-off-by: Paolo Insogna <paolo@cowtech.it>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
Paolo Insogna
2026-05-24 00:49:38 +03:00
committed by GitHub
co-authored by Bohdan Triapitsyn
parent 9af0de0056
commit e16097b05d
42 changed files with 2987 additions and 923 deletions
+6 -1
View File
@@ -388,6 +388,7 @@ export interface GitRemoveRemotePayload {
export interface CreateGitCommitOptions {
addAll?: boolean;
files?: string[];
stageFiles?: string[];
}
export interface GitLogOptions {
@@ -421,7 +422,11 @@ export interface GitAPI {
getGitStatus(directory: string, options?: { mode?: 'light' }): Promise<GitStatus>;
getGitDiff(directory: string, options: GetGitDiffOptions): Promise<GitDiffResponse>;
getGitFileDiff(directory: string, options: GetGitFileDiffOptions): Promise<GitFileDiffResponse>;
revertGitFile(directory: string, filePath: string): Promise<void>;
revertGitFile(directory: string, filePath: string, options?: { scope?: 'all' | 'working' }): Promise<void>;
stageGitFile(directory: string, filePath: string): Promise<void>;
stageGitFiles?(directory: string, filePaths: string[]): Promise<void>;
unstageGitFile(directory: string, filePath: string): Promise<void>;
unstageGitFiles?(directory: string, filePaths: string[]): Promise<void>;
isLinkedWorktree(directory: string): Promise<boolean>;
getGitBranches(directory: string): Promise<GitBranch>;
deleteGitBranch(directory: string, payload: GitDeleteBranchPayload): Promise<{ success: boolean }>;
+63 -1
View File
@@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test"
import type { GitAPI, GitStatus } from "./api/types"
import { getGitStatus } from "./gitApi"
import { getGitStatus, stageGitFile, stageGitFiles, unstageGitFile, unstageGitFiles } from "./gitApi"
const status: GitStatus = {
current: "main",
@@ -48,3 +48,65 @@ describe("getGitStatus", () => {
expect(received).toEqual({ directory: "/repo", options: { mode: "light" } })
})
})
describe("git index mutations", () => {
test("forwards bulk stage requests to runtime git APIs", async () => {
let received: { directory: string; paths: string[] } | null = null
const runtimeGit = {
stageGitFiles: async (directory: string, paths: string[]) => {
received = { directory, paths }
},
} as Partial<GitAPI> as GitAPI
await withRuntimeGit(runtimeGit, async () => {
await stageGitFiles("/repo", ["a.ts", "b.ts"])
})
expect(received).toEqual({ directory: "/repo", paths: ["a.ts", "b.ts"] })
})
test("forwards bulk unstage requests to runtime git APIs", async () => {
let received: { directory: string; paths: string[] } | null = null
const runtimeGit = {
unstageGitFiles: async (directory: string, paths: string[]) => {
received = { directory, paths }
},
} as Partial<GitAPI> as GitAPI
await withRuntimeGit(runtimeGit, async () => {
await unstageGitFiles("/repo", ["a.ts", "b.ts"])
})
expect(received).toEqual({ directory: "/repo", paths: ["a.ts", "b.ts"] })
})
test("keeps single-file stage wrapper routed to runtime single-file API", async () => {
let received: { directory: string; path: string } | null = null
const runtimeGit = {
stageGitFile: async (directory: string, path: string) => {
received = { directory, path }
},
} as Partial<GitAPI> as GitAPI
await withRuntimeGit(runtimeGit, async () => {
await stageGitFile("/repo", "a.ts")
})
expect(received).toEqual({ directory: "/repo", path: "a.ts" })
})
test("keeps single-file unstage wrapper routed to runtime single-file API", async () => {
let received: { directory: string; path: string } | null = null
const runtimeGit = {
unstageGitFile: async (directory: string, path: string) => {
received = { directory, path }
},
} as Partial<GitAPI> as GitAPI
await withRuntimeGit(runtimeGit, async () => {
await unstageGitFile("/repo", "a.ts")
})
expect(received).toEqual({ directory: "/repo", path: "a.ts" })
})
})
+31 -3
View File
@@ -126,10 +126,38 @@ export async function getGitFileDiff(
return gitHttp.getGitFileDiff(directory, options);
}
export async function revertGitFile(directory: string, filePath: string): Promise<void> {
export async function revertGitFile(
directory: string,
filePath: string,
options?: { scope?: 'all' | 'working' }
): Promise<void> {
const runtime = getRuntimeGit();
if (runtime) return runtime.revertGitFile(directory, filePath);
return gitHttp.revertGitFile(directory, filePath);
if (runtime) return runtime.revertGitFile(directory, filePath, options);
return gitHttp.revertGitFile(directory, filePath, options);
}
export async function stageGitFile(directory: string, filePath: string): Promise<void> {
const runtime = getRuntimeGit();
if (runtime?.stageGitFile) return runtime.stageGitFile(directory, filePath);
return gitHttp.stageGitFile(directory, filePath);
}
export async function stageGitFiles(directory: string, filePaths: string[]): Promise<void> {
const runtime = getRuntimeGit();
if (runtime?.stageGitFiles) return runtime.stageGitFiles(directory, filePaths);
return gitHttp.stageGitFiles(directory, filePaths);
}
export async function unstageGitFile(directory: string, filePath: string): Promise<void> {
const runtime = getRuntimeGit();
if (runtime?.unstageGitFile) return runtime.unstageGitFile(directory, filePath);
return gitHttp.unstageGitFile(directory, filePath);
}
export async function unstageGitFiles(directory: string, filePaths: string[]): Promise<void> {
const runtime = getRuntimeGit();
if (runtime?.unstageGitFiles) return runtime.unstageGitFiles(directory, filePaths);
return gitHttp.unstageGitFiles(directory, filePaths);
}
export async function isLinkedWorktree(directory: string): Promise<boolean> {
+112
View File
@@ -0,0 +1,112 @@
import { describe, expect, test } from 'bun:test';
import { stageGitFile, stageGitFiles, unstageGitFile, unstageGitFiles } from './gitApiHttp';
type FetchCall = {
input: RequestInfo | URL;
init?: RequestInit;
};
const previousFetch = globalThis.fetch;
const previousWindowDescriptor = Object.getOwnPropertyDescriptor(globalThis, 'window');
const installFetchMock = () => {
const calls: FetchCall[] = [];
globalThis.fetch = (async (input, init) => {
calls.push({ input, init });
return new Response(JSON.stringify({ success: true }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}) as typeof fetch;
return calls;
};
const installWindowMock = () => {
Object.defineProperty(globalThis, 'window', {
configurable: true,
value: {
location: { origin: 'http://localhost:3000' },
},
});
};
const restoreMocks = () => {
globalThis.fetch = previousFetch;
if (previousWindowDescriptor) {
Object.defineProperty(globalThis, 'window', previousWindowDescriptor);
} else {
delete (globalThis as { window?: Window }).window;
}
};
const captureError = async (callback: () => Promise<void>): Promise<unknown> => {
try {
await callback();
return null;
} catch (error) {
return error;
}
};
describe('gitApiHttp index mutations', () => {
test('sends bulk stage payloads as paths', async () => {
installWindowMock();
const calls = installFetchMock();
try {
await stageGitFiles('/repo', ['a.ts', 'b.ts']);
expect(calls).toHaveLength(1);
expect(String(calls[0].input)).toBe('http://localhost:3000/api/git/stage?directory=%2Frepo');
expect(calls[0].init?.method).toBe('POST');
expect(JSON.parse(String(calls[0].init?.body))).toEqual({ paths: ['a.ts', 'b.ts'] });
} finally {
restoreMocks();
}
});
test('sends bulk unstage payloads as paths', async () => {
installWindowMock();
const calls = installFetchMock();
try {
await unstageGitFiles('/repo', ['a.ts', 'b.ts']);
expect(calls).toHaveLength(1);
expect(String(calls[0].input)).toBe('http://localhost:3000/api/git/unstage?directory=%2Frepo');
expect(calls[0].init?.method).toBe('POST');
expect(JSON.parse(String(calls[0].init?.body))).toEqual({ paths: ['a.ts', 'b.ts'] });
} finally {
restoreMocks();
}
});
test('single-file helpers use the bulk paths payload shape', async () => {
installWindowMock();
const calls = installFetchMock();
try {
await stageGitFile('/repo', 'a.ts');
await unstageGitFile('/repo', 'b.ts');
expect(JSON.parse(String(calls[0].init?.body))).toEqual({ paths: ['a.ts'] });
expect(JSON.parse(String(calls[1].init?.body))).toEqual({ paths: ['b.ts'] });
} finally {
restoreMocks();
}
});
test('rejects empty bulk path lists before fetching', async () => {
installWindowMock();
const calls = installFetchMock();
try {
const stageError = await captureError(() => stageGitFiles('/repo', [' ', '']));
const unstageError = await captureError(() => unstageGitFiles('/repo', []));
expect(stageError).toBeInstanceOf(Error);
expect((stageError as Error).message).toBe('path is required to stage git changes');
expect(unstageError).toBeInstanceOf(Error);
expect((unstageError as Error).message).toBe('path is required to unstage git changes');
expect(calls).toHaveLength(0);
} finally {
restoreMocks();
}
});
});
+53 -2
View File
@@ -200,7 +200,11 @@ export async function getGitFileDiff(directory: string, options: GetGitFileDiffO
return response.json();
}
export async function revertGitFile(directory: string, filePath: string): Promise<void> {
export async function revertGitFile(
directory: string,
filePath: string,
options?: { scope?: 'all' | 'working' }
): Promise<void> {
if (!filePath) {
throw new Error('path is required to revert git changes');
}
@@ -208,7 +212,7 @@ export async function revertGitFile(directory: string, filePath: string): Promis
const response = await fetch(buildUrl(`${API_BASE}/revert`, directory), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ path: filePath }),
body: JSON.stringify({ path: filePath, scope: options?.scope }),
});
if (!response.ok) {
@@ -219,6 +223,52 @@ export async function revertGitFile(directory: string, filePath: string): Promis
}
}
export async function stageGitFile(directory: string, filePath: string): Promise<void> {
await stageGitFiles(directory, [filePath]);
}
export async function stageGitFiles(directory: string, filePaths: string[]): Promise<void> {
const paths = filePaths.map((path) => path.trim()).filter(Boolean);
if (paths.length === 0) {
throw new Error('path is required to stage git changes');
}
const response = await fetch(buildUrl(`${API_BASE}/stage`, directory), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ paths }),
});
if (!response.ok) {
const message = await response.json().catch(() => ({ error: response.statusText }));
throw new Error(message.error || 'Failed to stage git changes');
}
}
export async function unstageGitFile(directory: string, filePath: string): Promise<void> {
await unstageGitFiles(directory, [filePath]);
}
export async function unstageGitFiles(directory: string, filePaths: string[]): Promise<void> {
const paths = filePaths.map((path) => path.trim()).filter(Boolean);
if (paths.length === 0) {
throw new Error('path is required to unstage git changes');
}
const response = await fetch(buildUrl(`${API_BASE}/unstage`, directory), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ paths }),
});
if (!response.ok) {
const message = await response.json().catch(() => ({ error: response.statusText }));
throw new Error(message.error || 'Failed to unstage git changes');
}
}
export async function isLinkedWorktree(directory: string): Promise<boolean> {
if (!directory) {
return false;
@@ -494,6 +544,7 @@ export async function createGitCommit(
message,
addAll: options.addAll ?? false,
files: options.files,
stageFiles: options.stageFiles,
}),
});
if (!response.ok) {
+15
View File
@@ -451,8 +451,16 @@ export const dict = {
'gitView.changes.reverting': 'Reverting...',
'gitView.changes.selectAllAria': 'Select all files',
'gitView.changes.selectFileAria': 'Select File aria label',
'gitView.changes.stagedTitle': 'Staged',
'gitView.changes.resizeSplitAria': 'Resize staged and unstaged changes',
'gitView.changes.stageAllAria': 'Stage all changes',
'gitView.changes.stageDirectoryAria': 'Stage all changes in {path}',
'gitView.changes.stageFileAria': 'Stage {path}',
'gitView.changes.title': 'Changes',
'gitView.changes.toggleDirectorySelectionAria': 'Toggle Directory Selection aria label',
'gitView.changes.unstageAllAria': 'Unstage all changes',
'gitView.changes.unstageDirectoryAria': 'Unstage all changes in {path}',
'gitView.changes.unstageFileAria': 'Unstage {path}',
'gitView.commit.addGitmoji': 'Add gitmoji',
'gitView.commit.aiHighlights.insertAria': 'Insert aria label',
'gitView.commit.aiHighlights.insertTooltip': 'Insert tooltip',
@@ -467,6 +475,7 @@ export const dict = {
'gitView.commit.pushAria': 'Commit and sync',
'gitView.commit.pushing': 'Syncing...',
'gitView.commit.selectFilesHint': 'Select files in Changes to enable commit.',
'gitView.commit.stageFilesHint': 'Stage files to enable commit.',
'gitView.commit.title': 'Commit',
'gitView.common.cancel': 'Cancel',
'gitView.common.close': 'Close',
@@ -775,15 +784,21 @@ export const dict = {
'gitView.toast.revertedFilesSingle': 'Reverted {count} file',
'gitView.toast.revertedSomePlural': 'Reverted {success} files, {failed} failed',
'gitView.toast.revertedSomeSingle': 'Reverted {success} file, {failed} failed',
'gitView.toast.stageFileFailed': 'Failed to stage changes',
'gitView.toast.stageFileToCommit': 'Stage at least one file to commit',
'gitView.toast.stageFileToDescribe': 'Stage at least one file to describe',
'gitView.toast.selectFileToCommit': 'Select at least one file to commit',
'gitView.toast.selectFileToDescribe': 'Select at least one file to describe',
'gitView.toast.stashedRestored': 'Stashed changes restored',
'gitView.toast.syncActionFailed': '{action} failed',
'gitView.toast.unstageFileFailed': 'Failed to unstage changes',
'gitView.toast.upstreamSet': 'Set upstream for {branch} to {remote}',
'gitView.worktree.availableInWorktreeMode': 'Available only in worktree mode',
'contextPanel.mode.chat': 'Chat',
'contextPanel.mode.files': 'Files',
'contextPanel.mode.diff': 'Diff',
'contextPanel.mode.stagedDiff': 'Staged Diff',
'contextPanel.mode.workingDiff': 'Working Diff',
'contextPanel.mode.plan': 'Plan',
'contextPanel.mode.context': 'Context',
'contextPanel.mode.preview': 'Preview',
+15
View File
@@ -452,8 +452,16 @@ export const dict: Record<I18nKey, string> = {
"gitView.changes.reverting": "Revertiendo...",
"gitView.changes.selectAllAria": "Seleccionar todos los archivos",
"gitView.changes.selectFileAria": "Seleccionar archivo",
"gitView.changes.stagedTitle": "Preparados",
"gitView.changes.resizeSplitAria": "Ajustar tamaño de cambios preparados y sin preparar",
"gitView.changes.stageAllAria": "Preparar todos los cambios",
"gitView.changes.stageDirectoryAria": "Preparar todos los cambios en {path}",
"gitView.changes.stageFileAria": "Preparar {path}",
"gitView.changes.title": "Cambios",
"gitView.changes.toggleDirectorySelectionAria": "Alternar selección de directorio",
"gitView.changes.unstageAllAria": "Quitar todos los cambios del área preparada",
"gitView.changes.unstageDirectoryAria": "Quitar del área preparada todos los cambios en {path}",
"gitView.changes.unstageFileAria": "Quitar {path} del área preparada",
"gitView.commit.addGitmoji": "Añadir gitmoji",
"gitView.commit.aiHighlights.insertAria": "Insertar",
"gitView.commit.aiHighlights.insertTooltip": "Insertar",
@@ -468,6 +476,7 @@ export const dict: Record<I18nKey, string> = {
"gitView.commit.pushAria": "Commit and sync",
"gitView.commit.pushing": "Sincronizando...",
"gitView.commit.selectFilesHint": "Selecciona archivos en Cambios para habilitar el commit.",
"gitView.commit.stageFilesHint": "Prepara archivos para habilitar el commit.",
"gitView.commit.title": "Commit",
"gitView.common.cancel": "Cancelar",
"gitView.common.close": "Cerrar",
@@ -776,15 +785,21 @@ export const dict: Record<I18nKey, string> = {
"gitView.toast.revertedFilesSingle": "{count} archivo revertido",
"gitView.toast.revertedSomePlural": "{success} archivos revertidos, {failed} fallidos",
"gitView.toast.revertedSomeSingle": "{success} archivo revertido, {failed} fallido",
"gitView.toast.stageFileFailed": "No se pudieron preparar los cambios",
"gitView.toast.stageFileToCommit": "Prepara al menos un archivo para el commit",
"gitView.toast.stageFileToDescribe": "Prepara al menos un archivo para describir",
"gitView.toast.selectFileToCommit": "Selecciona al menos un archivo para el commit",
"gitView.toast.selectFileToDescribe": "Selecciona al menos un archivo para describir",
"gitView.toast.stashedRestored": "Cambios del stash restaurados",
"gitView.toast.syncActionFailed": "{action} falló",
"gitView.toast.unstageFileFailed": "No se pudieron quitar los cambios del área preparada",
"gitView.toast.upstreamSet": "Upstream de {branch} configurado como {remote}",
"gitView.worktree.availableInWorktreeMode": "Disponible solo en modo worktree",
"contextPanel.mode.chat": "Chat",
"contextPanel.mode.files": "Archivos",
"contextPanel.mode.diff": "Diff",
"contextPanel.mode.stagedDiff": "Staged Diff",
"contextPanel.mode.workingDiff": "Working Diff",
"contextPanel.mode.plan": "Plan",
"contextPanel.mode.context": "Contexto",
"contextPanel.mode.preview": "Vista previa",
+15
View File
@@ -452,8 +452,16 @@ export const dict: Record<I18nKey, string> = {
'gitView.changes.reverting': '되돌리는 중…',
'gitView.changes.selectAllAria': '모든 파일 선택',
'gitView.changes.selectFileAria': '파일 선택',
'gitView.changes.stagedTitle': '스테이징됨',
'gitView.changes.resizeSplitAria': '스테이징 및 미스테이징 변경사항 크기 조정',
'gitView.changes.stageAllAria': '모든 변경사항 스테이징',
'gitView.changes.stageDirectoryAria': '{path}의 모든 변경사항 스테이징',
'gitView.changes.stageFileAria': '{path} 스테이징',
'gitView.changes.title': '변경사항',
'gitView.changes.toggleDirectorySelectionAria': '디렉터리 선택 전환',
'gitView.changes.unstageAllAria': '모든 변경사항 스테이징 해제',
'gitView.changes.unstageDirectoryAria': '{path}의 모든 변경사항 스테이징 해제',
'gitView.changes.unstageFileAria': '{path} 스테이징 해제',
'gitView.commit.addGitmoji': 'gitmoji 추가',
'gitView.commit.aiHighlights.insertAria': '커밋 메시지에 삽입',
'gitView.commit.aiHighlights.insertTooltip': '커밋 메시지에 삽입',
@@ -468,6 +476,7 @@ export const dict: Record<I18nKey, string> = {
'gitView.commit.pushAria': 'Commit and sync',
'gitView.commit.pushing': 'sync 중…',
'gitView.commit.selectFilesHint': '커밋하려면 변경 사항에서 파일을 선택하세요.',
'gitView.commit.stageFilesHint': '커밋하려면 파일을 스테이징하세요.',
'gitView.commit.title': '커밋',
'gitView.common.cancel': '취소',
'gitView.common.close': '닫기',
@@ -776,15 +785,21 @@ export const dict: Record<I18nKey, string> = {
'gitView.toast.revertedFilesSingle': '파일 {count}개 되돌림',
'gitView.toast.revertedSomePlural': '파일 {success}개 되돌림, {failed}개 실패',
'gitView.toast.revertedSomeSingle': '파일 {success}개 되돌림, {failed}개 실패',
'gitView.toast.stageFileFailed': '변경사항 스테이징 실패',
'gitView.toast.stageFileToCommit': '커밋하려면 파일을 하나 이상 스테이징하세요',
'gitView.toast.stageFileToDescribe': '설명하려면 파일을 하나 이상 스테이징하세요',
'gitView.toast.selectFileToCommit': '커밋할 파일을 하나 이상 선택하세요',
'gitView.toast.selectFileToDescribe': '설명할 파일을 하나 이상 선택하세요',
'gitView.toast.stashedRestored': 'stash한 변경 사항이 복원되었습니다',
'gitView.toast.syncActionFailed': '{action} 실패',
'gitView.toast.unstageFileFailed': '변경사항 스테이징 해제 실패',
'gitView.toast.upstreamSet': '{branch}의 업스트림을 {remote}(으)로 설정했습니다',
'gitView.worktree.availableInWorktreeMode': '워크트리 모드에서만 사용할 수 있습니다',
'contextPanel.mode.chat': '채팅',
'contextPanel.mode.files': '파일',
'contextPanel.mode.diff': '변경사항',
'contextPanel.mode.stagedDiff': 'Staged Diff',
'contextPanel.mode.workingDiff': 'Working Diff',
'contextPanel.mode.plan': '계획',
'contextPanel.mode.context': '컨텍스트',
'contextPanel.mode.preview': '미리보기',
+15
View File
@@ -1073,6 +1073,8 @@ export const dict: Record<I18nKey, string> = {
'contextPanel.mode.chat': 'Chat',
'contextPanel.mode.context': 'Context',
'contextPanel.mode.diff': 'Różnice',
'contextPanel.mode.stagedDiff': 'Staged Diff',
'contextPanel.mode.workingDiff': 'Working Diff',
'contextPanel.mode.files': 'Pliki',
'contextPanel.mode.plan': 'Plan',
'contextPanel.mode.preview': 'Podgląd',
@@ -1421,8 +1423,16 @@ export const dict: Record<I18nKey, string> = {
'gitView.changes.reverting': 'Cofanie...',
'gitView.changes.selectAllAria': 'Zaznacz wszystkie pliki',
'gitView.changes.selectFileAria': 'Zaznacz plik',
'gitView.changes.stagedTitle': 'W indeksie',
'gitView.changes.resizeSplitAria': 'Zmień rozmiar zmian w indeksie i poza indeksem',
'gitView.changes.stageAllAria': 'Dodaj wszystkie zmiany do indeksu',
'gitView.changes.stageDirectoryAria': 'Dodaj do indeksu wszystkie zmiany w {path}',
'gitView.changes.stageFileAria': 'Dodaj {path} do indeksu',
'gitView.changes.title': 'Zmiany',
'gitView.changes.toggleDirectorySelectionAria': 'Przełącz zaznaczenie katalogu',
'gitView.changes.unstageAllAria': 'Usuń wszystkie zmiany z indeksu',
'gitView.changes.unstageDirectoryAria': 'Usuń z indeksu wszystkie zmiany w {path}',
'gitView.changes.unstageFileAria': 'Usuń {path} z indeksu',
'gitView.commit.addGitmoji': 'Dodaj gitmoji',
'gitView.commit.aiHighlights.insertAria': 'Wstaw',
'gitView.commit.aiHighlights.insertTooltip': 'Wstaw',
@@ -1437,6 +1447,7 @@ export const dict: Record<I18nKey, string> = {
'gitView.commit.pushAria': 'Wypchnij',
'gitView.commit.pushing': 'Wypychanie...',
'gitView.commit.selectFilesHint': 'Zaznacz pliki w sekcji Zmiany, aby włączyć commit.',
'gitView.commit.stageFilesHint': 'Dodaj pliki do indeksu, aby włączyć commit.',
'gitView.commit.title': 'Commit',
'gitView.common.cancel': 'Anuluj',
'gitView.common.close': 'Zamknij',
@@ -1700,10 +1711,14 @@ export const dict: Record<I18nKey, string> = {
'gitView.toast.revertedFilesSingle': 'Cofnięto {count} plik',
'gitView.toast.revertedSomePlural': 'Cofnięto {success} plików, {failed} nieudanych',
'gitView.toast.revertedSomeSingle': 'Cofnięto {success} plik, {failed} nieudanych',
'gitView.toast.stageFileFailed': 'Nie udało się dodać zmian do indeksu',
'gitView.toast.stageFileToCommit': 'Dodaj do indeksu co najmniej jeden plik do commita',
'gitView.toast.stageFileToDescribe': 'Dodaj do indeksu co najmniej jeden plik do opisu',
'gitView.toast.selectFileToCommit': 'Zaznacz co najmniej jeden plik do commita',
'gitView.toast.selectFileToDescribe': 'Zaznacz co najmniej jeden plik do opisu',
'gitView.toast.stashedRestored': 'Przywrócono odłożone zmiany',
'gitView.toast.syncActionFailed': 'Operacja {action} nie powiodła się',
'gitView.toast.unstageFileFailed': 'Nie udało się usunąć zmian z indeksu',
'gitView.toast.upstreamSet': 'Ustawiono upstream dla {branch} na {remote}',
'gitView.worktree.availableInWorktreeMode': 'Dostępne tylko w trybie drzewa pracy',
'header.actions.backAria': 'Wstecz',
@@ -452,8 +452,16 @@ export const dict: Record<I18nKey, string> = {
"gitView.changes.reverting": "Revertiendo...",
"gitView.changes.selectAllAria": "Selecionar todos os arquivos",
"gitView.changes.selectFileAria": "Selecionar arquivo",
"gitView.changes.stagedTitle": "Staged",
"gitView.changes.resizeSplitAria": "Ajustar tamanho das alterações staged e unstaged",
"gitView.changes.stageAllAria": "Adicionar todas as alterações ao stage",
"gitView.changes.stageDirectoryAria": "Adicionar todas as alterações em {path} ao stage",
"gitView.changes.stageFileAria": "Adicionar {path} ao stage",
"gitView.changes.title": "Alterações",
"gitView.changes.toggleDirectorySelectionAria": "Alternar selección de diretório",
"gitView.changes.unstageAllAria": "Remover todas as alterações do stage",
"gitView.changes.unstageDirectoryAria": "Remover todas as alterações em {path} do stage",
"gitView.changes.unstageFileAria": "Remover {path} do stage",
"gitView.commit.addGitmoji": "Adicionar gitmoji",
"gitView.commit.aiHighlights.insertAria": "Insertar",
"gitView.commit.aiHighlights.insertTooltip": "Insertar",
@@ -468,6 +476,7 @@ export const dict: Record<I18nKey, string> = {
"gitView.commit.pushAria": "Commit and sync",
"gitView.commit.pushing": "Sincronizando...",
"gitView.commit.selectFilesHint": "Selecione arquivos em Alterações para habilitar o commit.",
"gitView.commit.stageFilesHint": "Adicione arquivos ao stage para habilitar o commit.",
"gitView.commit.title": "Commit",
"gitView.common.cancel": "Cancelar",
"gitView.common.close": "Fechar",
@@ -776,15 +785,21 @@ export const dict: Record<I18nKey, string> = {
"gitView.toast.revertedFilesSingle": "{count} arquivo revertido",
"gitView.toast.revertedSomePlural": "{success} arquivos revertidos, {failed} com falha",
"gitView.toast.revertedSomeSingle": "{success} arquivo revertido, {failed} falhou",
"gitView.toast.stageFileFailed": "Não foi possível adicionar as alterações ao stage",
"gitView.toast.stageFileToCommit": "Adicione ao menos um arquivo ao stage para o commit",
"gitView.toast.stageFileToDescribe": "Adicione ao menos um arquivo ao stage para descrever",
"gitView.toast.selectFileToCommit": "Selecione ao menos um arquivo para o commit",
"gitView.toast.selectFileToDescribe": "Selecione ao menos um arquivo para descrever",
"gitView.toast.stashedRestored": "Alterações do stash restaurados",
"gitView.toast.syncActionFailed": "{action} falhou",
"gitView.toast.unstageFileFailed": "Não foi possível remover as alterações do stage",
"gitView.toast.upstreamSet": "Upstream de {branch} configurado como {remote}",
"gitView.worktree.availableInWorktreeMode": "Disponível apenas em modo worktree",
"contextPanel.mode.chat": "Chat",
"contextPanel.mode.files": "Arquivos",
"contextPanel.mode.diff": "Diff",
"contextPanel.mode.stagedDiff": "Staged Diff",
"contextPanel.mode.workingDiff": "Working Diff",
"contextPanel.mode.plan": "Plano",
"contextPanel.mode.context": "Contexto",
"contextPanel.mode.preview": "Prévia",
+15
View File
@@ -452,8 +452,16 @@ export const dict: Record<I18nKey, string> = {
"gitView.changes.reverting": "Скасування...",
"gitView.changes.selectAllAria": "Вибрати всі файли",
"gitView.changes.selectFileAria": "Вибрати файл",
"gitView.changes.stagedTitle": "Індексовані",
"gitView.changes.resizeSplitAria": "Змінити розмір індексованих і неіндексованих змін",
"gitView.changes.stageAllAria": "Додати всі зміни до індексу",
"gitView.changes.stageDirectoryAria": "Додати до індексу всі зміни в {path}",
"gitView.changes.stageFileAria": "Додати {path} до індексу",
"gitView.changes.title": "Зміни",
"gitView.changes.toggleDirectorySelectionAria": "Перемкнути вибір каталогу",
"gitView.changes.unstageAllAria": "Прибрати всі зміни з індексу",
"gitView.changes.unstageDirectoryAria": "Прибрати з індексу всі зміни в {path}",
"gitView.changes.unstageFileAria": "Прибрати {path} з індексу",
"gitView.commit.addGitmoji": "Додати gitmoji",
"gitView.commit.aiHighlights.insertAria": "Вставити підказку",
"gitView.commit.aiHighlights.insertTooltip": "Вставити підказку",
@@ -468,6 +476,7 @@ export const dict: Record<I18nKey, string> = {
"gitView.commit.pushAria": "Commit and sync",
"gitView.commit.pushing": "Sync...",
"gitView.commit.selectFilesHint": "Виберіть файли в розділі «Зміни», щоб увімкнути коміт.",
"gitView.commit.stageFilesHint": "Додайте файли до індексу, щоб увімкнути коміт.",
"gitView.commit.title": "Коміт",
"gitView.common.cancel": "Скасувати",
"gitView.common.close": "Закрити",
@@ -776,15 +785,21 @@ export const dict: Record<I18nKey, string> = {
"gitView.toast.revertedFilesSingle": "Скасовано зміни у файлі: {count}",
"gitView.toast.revertedSomePlural": "Скасовано змін у файлах: {success}, не вдалося: {failed}",
"gitView.toast.revertedSomeSingle": "Скасовано змін у файлах: {success}, не вдалося: {failed}",
"gitView.toast.stageFileFailed": "Не вдалося додати зміни до індексу",
"gitView.toast.stageFileToCommit": "Додайте до індексу принаймні один файл для коміту",
"gitView.toast.stageFileToDescribe": "Додайте до індексу принаймні один файл для опису",
"gitView.toast.selectFileToCommit": "Виберіть принаймні один файл для коміту",
"gitView.toast.selectFileToDescribe": "Виберіть хоча б один файл для опису",
"gitView.toast.stashedRestored": "Зміни зі stash відновлено",
"gitView.toast.syncActionFailed": "{action} не вдалося",
"gitView.toast.unstageFileFailed": "Не вдалося прибрати зміни з індексу",
"gitView.toast.upstreamSet": "Upstream для {branch} встановлено на {remote}",
"gitView.worktree.availableInWorktreeMode": "Доступно лише в режимі worktree",
"contextPanel.mode.chat": "Чат",
"contextPanel.mode.files": "Файли",
"contextPanel.mode.diff": "Diff",
"contextPanel.mode.stagedDiff": "Staged Diff",
"contextPanel.mode.workingDiff": "Working Diff",
"contextPanel.mode.plan": "План",
"contextPanel.mode.context": "Контекст",
"contextPanel.mode.preview": "Перегляд",
@@ -452,8 +452,16 @@ export const dict: Record<I18nKey, string> = {
'gitView.changes.reverting': '正在还原...',
'gitView.changes.selectAllAria': '全选文件',
'gitView.changes.selectFileAria': '选择 {path}',
'gitView.changes.stagedTitle': '已暂存',
'gitView.changes.resizeSplitAria': '调整已暂存和未暂存更改区域大小',
'gitView.changes.stageAllAria': '暂存所有更改',
'gitView.changes.stageDirectoryAria': '暂存 {path} 中的所有更改',
'gitView.changes.stageFileAria': '暂存 {path}',
'gitView.changes.title': '更改',
'gitView.changes.toggleDirectorySelectionAria': '切换目录 {path} 的选择',
'gitView.changes.unstageAllAria': '取消暂存所有更改',
'gitView.changes.unstageDirectoryAria': '取消暂存 {path} 中的所有更改',
'gitView.changes.unstageFileAria': '取消暂存 {path}',
'gitView.commit.addGitmoji': '添加 gitmoji',
'gitView.commit.aiHighlights.insertAria': '将高亮插入提交信息',
'gitView.commit.aiHighlights.insertTooltip': '将高亮追加到提交信息',
@@ -468,6 +476,7 @@ export const dict: Record<I18nKey, string> = {
'gitView.commit.pushAria': '提交并同步',
'gitView.commit.pushing': '同步中...',
'gitView.commit.selectFilesHint': '在“更改”中选择文件以启用提交。',
'gitView.commit.stageFilesHint': '暂存文件以启用提交。',
'gitView.commit.title': '提交',
'gitView.common.cancel': '取消',
'gitView.common.close': '关闭',
@@ -776,15 +785,21 @@ export const dict: Record<I18nKey, string> = {
'gitView.toast.revertedFilesSingle': '已回退 {count} 个文件',
'gitView.toast.revertedSomePlural': '已回退 {success} 个文件,{failed} 个失败',
'gitView.toast.revertedSomeSingle': '已回退 {success} 个文件,{failed} 个失败',
'gitView.toast.stageFileFailed': '暂存更改失败',
'gitView.toast.stageFileToCommit': '请至少暂存一个文件再提交',
'gitView.toast.stageFileToDescribe': '请至少暂存一个文件再生成描述',
'gitView.toast.selectFileToCommit': '请至少选择一个文件再提交',
'gitView.toast.selectFileToDescribe': '请至少选择一个文件再生成描述',
'gitView.toast.stashedRestored': '已恢复储藏的更改',
'gitView.toast.syncActionFailed': '{action} 失败',
'gitView.toast.unstageFileFailed': '取消暂存更改失败',
'gitView.toast.upstreamSet': '已将 {branch} 的上游设置为 {remote}',
'gitView.worktree.availableInWorktreeMode': '仅在工作树模式下可用',
'contextPanel.mode.chat': '聊天',
'contextPanel.mode.files': '文件',
'contextPanel.mode.diff': '差异',
'contextPanel.mode.stagedDiff': 'Staged Diff',
'contextPanel.mode.workingDiff': 'Working Diff',
'contextPanel.mode.plan': '计划',
'contextPanel.mode.context': '上下文',
'contextPanel.mode.preview': '预览',