feat: add repository cloning to project setup

Add project can clone repositories into a chosen folder
Command palette now opens the add project flow
Clone flow supports selecting Git identities
This commit is contained in:
Bohdan Triapitsyn
2026-05-07 01:10:17 +03:00
parent fd3557e5c3
commit d764ad6872
13 changed files with 419 additions and 126 deletions
@@ -11,9 +11,11 @@ import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { useDirectoryStore } from '@/stores/useDirectoryStore'; import { useDirectoryStore } from '@/stores/useDirectoryStore';
import { useProjectsStore } from '@/stores/useProjectsStore'; import { useProjectsStore } from '@/stores/useProjectsStore';
import { useGitIdentitiesStore } from '@/stores/useGitIdentitiesStore';
import { useFileSystemAccess } from '@/hooks/useFileSystemAccess'; import { useFileSystemAccess } from '@/hooks/useFileSystemAccess';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { toast } from '@/components/ui'; import { toast } from '@/components/ui';
import { IdentityDropdown } from '@/components/views/git/GitHeader';
import { import {
RiArrowDownSLine, RiArrowDownSLine,
RiArrowLeftSLine, RiArrowLeftSLine,
@@ -152,6 +154,12 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
const homeDirectory = useDirectoryStore((s) => s.homeDirectory); const homeDirectory = useDirectoryStore((s) => s.homeDirectory);
const projects = useProjectsStore((s) => s.projects); const projects = useProjectsStore((s) => s.projects);
const addProject = useProjectsStore((s) => s.addProject); const addProject = useProjectsStore((s) => s.addProject);
const gitIdentityProfiles = useGitIdentitiesStore((s) => s.profiles);
const globalGitIdentity = useGitIdentitiesStore((s) => s.globalIdentity);
const defaultGitIdentityId = useGitIdentitiesStore((s) => s.defaultGitIdentityId);
const loadGitIdentityProfiles = useGitIdentitiesStore((s) => s.loadProfiles);
const loadGlobalGitIdentity = useGitIdentitiesStore((s) => s.loadGlobalIdentity);
const loadDefaultGitIdentityId = useGitIdentitiesStore((s) => s.loadDefaultGitIdentityId);
const showHidden = useDirectoryShowHidden(); const showHidden = useDirectoryShowHidden();
const { isDesktop, requestAccess, startAccessing } = useFileSystemAccess(); const { isDesktop, requestAccess, startAccessing } = useFileSystemAccess();
const { isMobile } = useDeviceInfo(); const { isMobile } = useDeviceInfo();
@@ -167,6 +175,9 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
const [isConfirming, setIsConfirming] = React.useState(false); const [isConfirming, setIsConfirming] = React.useState(false);
const [isOpeningFinder, setIsOpeningFinder] = React.useState(false); const [isOpeningFinder, setIsOpeningFinder] = React.useState(false);
const [addButtonWidth, setAddButtonWidth] = React.useState(0); const [addButtonWidth, setAddButtonWidth] = React.useState(0);
const [isCloneMode, setIsCloneMode] = React.useState(false);
const [cloneRemoteUrl, setCloneRemoteUrl] = React.useState('');
const [selectedGitIdentityId, setSelectedGitIdentityId] = React.useState<string | null>(null);
const explorerRootDirectory = dialogHomeDirectory || homeDirectory; const explorerRootDirectory = dialogHomeDirectory || homeDirectory;
@@ -183,6 +194,9 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
setHighlightedIndex(0); setHighlightedIndex(0);
setIsConfirming(false); setIsConfirming(false);
setIsOpeningFinder(false); setIsOpeningFinder(false);
setIsCloneMode(false);
setCloneRemoteUrl('');
setSelectedGitIdentityId(null);
requestAnimationFrame(() => focusPathInput(inputRef.current)); requestAnimationFrame(() => focusPathInput(inputRef.current));
let cancelled = false; let cancelled = false;
@@ -198,6 +212,42 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
}; };
}, [homeDirectory, open]); }, [homeDirectory, open]);
React.useEffect(() => {
if (!open) return;
void loadGitIdentityProfiles();
void loadGlobalGitIdentity();
void loadDefaultGitIdentityId();
}, [loadDefaultGitIdentityId, loadGitIdentityProfiles, loadGlobalGitIdentity, open]);
const availableGitIdentities = React.useMemo(() => {
const unique = new Map<string, NonNullable<typeof globalGitIdentity>>();
if (globalGitIdentity) {
unique.set(globalGitIdentity.id, globalGitIdentity);
}
for (const profile of gitIdentityProfiles) {
unique.set(profile.id, profile);
}
return Array.from(unique.values());
}, [gitIdentityProfiles, globalGitIdentity]);
React.useEffect(() => {
if (!open || !isCloneMode || selectedGitIdentityId !== null) return;
const defaultId = typeof defaultGitIdentityId === 'string' ? defaultGitIdentityId.trim() : '';
if (defaultId && availableGitIdentities.some((identity) => identity.id === defaultId)) {
setSelectedGitIdentityId(defaultId);
return;
}
const firstSshIdentity = availableGitIdentities.find((identity) => identity.authType === 'ssh' || identity.sshKey);
if (firstSshIdentity) {
setSelectedGitIdentityId(firstSshIdentity.id);
}
}, [availableGitIdentities, defaultGitIdentityId, isCloneMode, open, selectedGitIdentityId]);
const selectedGitIdentity = React.useMemo(
() => availableGitIdentities.find((identity) => identity.id === selectedGitIdentityId) ?? null,
[availableGitIdentities, selectedGitIdentityId]
);
const browseDirectoryDisplayPath = React.useMemo(() => getBrowseDirectoryPath(query), [query]); const browseDirectoryDisplayPath = React.useMemo(() => getBrowseDirectoryPath(query), [query]);
const browseFilterQuery = React.useMemo( const browseFilterQuery = React.useMemo(
() => (hasTrailingPathSeparator(query) ? '' : getBrowseLeafPathSegment(query)), () => (hasTrailingPathSeparator(query) ? '' : getBrowseLeafPathSegment(query)),
@@ -294,6 +344,7 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
) )
); );
const canAddProject = !isConfirming && !isOpeningFinder && !isAlreadyAdded && Boolean(targetPath); const canAddProject = !isConfirming && !isOpeningFinder && !isAlreadyAdded && Boolean(targetPath);
const canSubmitClone = canAddProject && cloneRemoteUrl.trim().length > 0;
const highlightedRow = rows[highlightedIndex] ?? null; const highlightedRow = rows[highlightedIndex] ?? null;
const hasHighlightedBrowseItem = Boolean( const hasHighlightedBrowseItem = Boolean(
highlightedRow && (highlightedRow.type === 'up' || (highlightedRow.type === 'directory' && !highlightedRow.disabled)) highlightedRow && (highlightedRow.type === 'up' || (highlightedRow.type === 'directory' && !highlightedRow.disabled))
@@ -303,6 +354,12 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
: 'Ctrl'; : 'Ctrl';
const submitActionLabel = isAlreadyAdded const submitActionLabel = isAlreadyAdded
? t('directoryExplorerDialog.actions.alreadyAdded') ? t('directoryExplorerDialog.actions.alreadyAdded')
: isCloneMode
? isConfirming
? t('directoryExplorerDialog.actions.cloning')
: t('directoryExplorerDialog.actions.cloneAndAdd')
: isConfirming
? t('directoryExplorerDialog.actions.adding')
: shouldCreateTarget : shouldCreateTarget
? t('directoryExplorerDialog.actions.createAndAdd') ? t('directoryExplorerDialog.actions.createAndAdd')
: t('directoryExplorerDialog.actions.addProject'); : t('directoryExplorerDialog.actions.addProject');
@@ -345,14 +402,27 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
if (!target || isConfirming) return; if (!target || isConfirming) return;
const normalized = normalizeDirectoryPath(target); const normalized = normalizeDirectoryPath(target);
if (normalized && addedProjectPaths.has(normalized)) return; if (normalized && addedProjectPaths.has(normalized)) return;
let selectedTarget = target;
setIsConfirming(true); setIsConfirming(true);
try { try {
const shouldCreateSelection = shouldCreateTarget && normalizeDirectoryPath(target) === normalizeDirectoryPath(targetPath); const shouldCreateSelection = !isCloneMode && shouldCreateTarget && normalizeDirectoryPath(target) === normalizeDirectoryPath(targetPath);
if (shouldCreateSelection) { if (isCloneMode) {
const remoteUrl = cloneRemoteUrl.trim();
if (!remoteUrl) {
toast.error(t('directoryExplorerDialog.toast.cloneUrlRequired'));
return;
}
const result = await opencodeClient.cloneRepository({
remoteUrl,
destinationPath: target,
gitIdentityId: selectedGitIdentity?.id ?? null,
});
selectedTarget = result.path;
} else if (shouldCreateSelection) {
await opencodeClient.createDirectory(target, { allowOutsideWorkspace: true }); await opencodeClient.createDirectory(target, { allowOutsideWorkspace: true });
} }
const added = addProject(target); const added = addProject(selectedTarget);
if (!added) { if (!added) {
toast.error(t('directoryExplorerDialog.toast.failedToAddProject'), { toast.error(t('directoryExplorerDialog.toast.failedToAddProject'), {
description: t('directoryExplorerDialog.toast.selectValidDirectoryPath'), description: t('directoryExplorerDialog.toast.selectValidDirectoryPath'),
@@ -367,7 +437,7 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
} finally { } finally {
setIsConfirming(false); setIsConfirming(false);
} }
}, [addProject, addedProjectPaths, handleClose, isConfirming, shouldCreateTarget, targetPath, t]); }, [addProject, addedProjectPaths, cloneRemoteUrl, handleClose, isCloneMode, isConfirming, selectedGitIdentity?.id, shouldCreateTarget, targetPath, t]);
const browseToDisplayPath = React.useCallback((displayPath: string) => { const browseToDisplayPath = React.useCallback((displayPath: string) => {
setQuery(ensureBrowseDirectoryPath(displayPath)); setQuery(ensureBrowseDirectoryPath(displayPath));
@@ -459,36 +529,59 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
); );
const inputSection = ( const inputSection = (
<div className="relative px-2.5 py-1.5"> <div className="px-2.5 py-1.5">
<RiFolderAddLine className="pointer-events-none absolute left-5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground/80" /> {isCloneMode ? (
<Input <div className="mb-1.5 flex items-center gap-1.5">
ref={inputRef} <Input
value={query} value={cloneRemoteUrl}
onChange={(event) => setQuery(normalizeSeparators(event.target.value))} onChange={(event) => setCloneRemoteUrl(event.target.value)}
onKeyDown={handleKeyDown} placeholder={t('directoryExplorerDialog.clone.remoteUrlPlaceholder')}
placeholder={t('directoryExplorerDialog.pathInput.placeholder')} className="min-w-0 flex-1 border-border/60 bg-[var(--surface-elevated)] font-mono typography-ui-label shadow-none"
className="border-transparent bg-transparent pl-9 font-mono typography-ui-label shadow-none focus-visible:ring-0" spellCheck={false}
style={!isMobile && addButtonWidth > 0 ? { paddingRight: `${addButtonWidth + 24}px` } : undefined} autoComplete="off"
spellCheck={false} autoCorrect="off"
autoComplete="off" autoCapitalize="off"
autoCorrect="off" />
autoCapitalize="off" <IdentityDropdown
/> activeProfile={selectedGitIdentity}
{!isMobile ? ( identities={availableGitIdentities}
<Button onSelect={(profile) => setSelectedGitIdentityId(profile.id)}
ref={addButtonRef} isApplying={isConfirming}
variant="outline" iconOnly
size="xs" />
tabIndex={-1} </div>
className="absolute right-4 top-1/2 h-7 -translate-y-1/2 gap-1 px-2 typography-meta"
disabled={!canAddProject}
onMouseDown={(event) => event.preventDefault()}
onClick={() => void finalizeSelection(targetPath)}
title={submitActionLabel}
>
{submitActionLabel}
</Button>
) : null} ) : null}
<div className="relative">
<RiFolderAddLine className="pointer-events-none absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground/80" />
<Input
ref={inputRef}
value={query}
onChange={(event) => setQuery(normalizeSeparators(event.target.value))}
onKeyDown={handleKeyDown}
placeholder={t('directoryExplorerDialog.pathInput.placeholder')}
className="border-transparent bg-transparent pl-9 font-mono typography-ui-label shadow-none focus-visible:ring-0"
style={!isMobile && addButtonWidth > 0 ? { paddingRight: `${addButtonWidth + 24}px` } : undefined}
spellCheck={false}
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
/>
{!isMobile ? (
<Button
ref={addButtonRef}
variant="outline"
size="xs"
tabIndex={-1}
className="absolute right-1.5 top-1/2 h-7 -translate-y-1/2 gap-1 px-2 typography-meta"
disabled={isCloneMode ? !canSubmitClone : !canAddProject}
onMouseDown={(event) => event.preventDefault()}
onClick={() => void finalizeSelection(targetPath)}
title={submitActionLabel}
>
{submitActionLabel}
</Button>
) : null}
</div>
</div> </div>
); );
@@ -577,10 +670,6 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
<RiCornerDownLeftLine className="h-3.5 w-3.5" /> <RiCornerDownLeftLine className="h-3.5 w-3.5" />
{t('directoryExplorerDialog.footer.add')} {t('directoryExplorerDialog.footer.add')}
</span> </span>
<span className="inline-flex items-center gap-1">
<span>Esc</span>
{t('directoryExplorerDialog.footer.close')}
</span>
</div> </div>
); );
@@ -589,15 +678,15 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
{!isMobile ? footerHints : null} {!isMobile ? footerHints : null}
<div className={cn('flex w-full flex-row justify-end gap-2 sm:w-auto', isMobile && 'justify-stretch')}> <div className={cn('flex w-full flex-row justify-end gap-2 sm:w-auto', isMobile && 'justify-stretch')}>
{isDesktop ? ( {isDesktop ? (
<Button variant="ghost" size="xs" onClick={handleOpenInFinder} disabled={isConfirming || isOpeningFinder}> <Button variant="ghost" size="xs" onClick={handleOpenInFinder} disabled={isConfirming || isOpeningFinder || isCloneMode}>
{isOpeningFinder ? t('directoryExplorerDialog.actions.openingFinder') : t('directoryExplorerDialog.actions.openInFinder')} {isOpeningFinder ? t('directoryExplorerDialog.actions.openingFinder') : t('directoryExplorerDialog.actions.openInFinder')}
</Button> </Button>
) : null} ) : null}
<Button variant="ghost" size="xs" onClick={handleClose} disabled={isConfirming || isOpeningFinder} className={cn(isMobile && 'flex-1')}> <Button variant="ghost" size="xs" onClick={() => setIsCloneMode((value) => !value)} disabled={isConfirming || isOpeningFinder} className={cn(isMobile && 'flex-1')}>
{t('directoryExplorerDialog.actions.cancel')} {isCloneMode ? t('directoryExplorerDialog.actions.addLocalProject') : t('directoryExplorerDialog.actions.cloneRepository')}
</Button> </Button>
{isMobile ? ( {isMobile ? (
<Button size="xs" onClick={() => void finalizeSelection(targetPath)} disabled={!canAddProject} className="flex-1"> <Button size="xs" onClick={() => void finalizeSelection(targetPath)} disabled={isCloneMode ? !canSubmitClone : !canAddProject} className="flex-1">
{submitActionLabel} {submitActionLabel}
</Button> </Button>
) : null} ) : null}
@@ -125,10 +125,6 @@ export const AboutDialog: React.FC<AboutDialogProps> = ({
)} )}
</div> </div>
<p className="typography-meta text-muted-foreground">
{t('aboutDialog.tagline')}
</p>
<div className="flex flex-col items-center gap-2 pt-2"> <div className="flex flex-col items-center gap-2 pt-2">
<button <button
onClick={handleCopyDiagnostics} onClick={handleCopyDiagnostics}
@@ -31,6 +31,7 @@ import { FileTypeIcon } from '@/components/icons/FileTypeIcon';
import { import {
RiAddLine, RiAddLine,
RiChatAi3Line, RiChatAi3Line,
RiFolderAddLine,
RiGitBranchLine, RiGitBranchLine,
RiLayoutLeftLine, RiLayoutLeftLine,
RiLayoutRightLine, RiLayoutRightLine,
@@ -47,6 +48,7 @@ import { getSettingsNavIcon } from '@/components/views/SettingsView';
import { scoreByFuzzyQuery } from '@/lib/search/fuzzySearch'; import { scoreByFuzzyQuery } from '@/lib/search/fuzzySearch';
import { truncatePathMiddle } from '@/lib/utils'; import { truncatePathMiddle } from '@/lib/utils';
import { useI18n } from '@/lib/i18n'; import { useI18n } from '@/lib/i18n';
import { sessionEvents } from '@/lib/sessionEvents';
type CommandEntry = { type CommandEntry = {
id: string; id: string;
@@ -168,6 +170,15 @@ export const CommandPalette: React.FC = () => {
void createWorktreeSession(); void createWorktreeSession();
}), }),
}, },
{
id: 'add-project',
title: t('commandPalette.item.addProject'),
icon: <RiFolderAddLine className="mr-2 h-4 w-4" />,
searchText: t('commandPalette.item.addProject'),
onSelect: run(() => {
sessionEvents.requestDirectoryDialog();
}),
},
{ {
id: 'toggle-sidebar', id: 'toggle-sidebar',
title: isMobile title: isMobile
@@ -109,7 +109,7 @@ interface IdentityDropdownProps {
iconOnly?: boolean; iconOnly?: boolean;
} }
const IdentityDropdown: React.FC<IdentityDropdownProps> = ({ export const IdentityDropdown: React.FC<IdentityDropdownProps> = ({
activeProfile, activeProfile,
identities, identities,
onSelect, onSelect,
+8 -4
View File
@@ -422,7 +422,7 @@ export const dict = {
'gitView.commit.addGitmoji': 'Add gitmoji', 'gitView.commit.addGitmoji': 'Add gitmoji',
'gitView.commit.aiHighlights.insertAria': 'Insert aria label', 'gitView.commit.aiHighlights.insertAria': 'Insert aria label',
'gitView.commit.aiHighlights.insertTooltip': 'Insert tooltip', 'gitView.commit.aiHighlights.insertTooltip': 'Insert tooltip',
'gitView.commit.aiHighlights.title': 'Title', 'gitView.commit.aiHighlights.title': 'Highlights',
'gitView.commit.commit': 'Commit', 'gitView.commit.commit': 'Commit',
'gitView.commit.commitAria': 'Commit aria label', 'gitView.commit.commitAria': 'Commit aria label',
'gitView.commit.committing': 'Committing...', 'gitView.commit.committing': 'Committing...',
@@ -1103,13 +1103,17 @@ export const dict = {
'directoryExplorerDialog.description': 'Choose a folder to add as a project.', 'directoryExplorerDialog.description': 'Choose a folder to add as a project.',
'directoryExplorerDialog.toggle.showHidden': 'Show hidden', 'directoryExplorerDialog.toggle.showHidden': 'Show hidden',
'directoryExplorerDialog.pathInput.placeholder': 'Enter path or select from tree...', 'directoryExplorerDialog.pathInput.placeholder': 'Enter path or select from tree...',
'directoryExplorerDialog.actions.cancel': 'Cancel',
'directoryExplorerDialog.actions.openingFinder': 'Opening...', 'directoryExplorerDialog.actions.openingFinder': 'Opening...',
'directoryExplorerDialog.actions.openInFinder': 'Open in Finder', 'directoryExplorerDialog.actions.openInFinder': 'Open in Finder',
'directoryExplorerDialog.actions.adding': 'Adding...', 'directoryExplorerDialog.actions.adding': 'Adding...',
'directoryExplorerDialog.actions.addProject': 'Add project', 'directoryExplorerDialog.actions.addProject': 'Add project',
'directoryExplorerDialog.actions.addLocalProject': 'Add local project',
'directoryExplorerDialog.actions.cloneRepository': 'Clone repository',
'directoryExplorerDialog.actions.cloneAndAdd': 'Clone & add',
'directoryExplorerDialog.actions.cloning': 'Cloning...',
'directoryExplorerDialog.actions.createAndAdd': 'Create & add', 'directoryExplorerDialog.actions.createAndAdd': 'Create & add',
'directoryExplorerDialog.actions.alreadyAdded': 'Already added', 'directoryExplorerDialog.actions.alreadyAdded': 'Already added',
'directoryExplorerDialog.clone.remoteUrlPlaceholder': 'Repository URL (HTTPS or SSH)',
'directoryExplorerDialog.browse.directories': 'Directories', 'directoryExplorerDialog.browse.directories': 'Directories',
'directoryExplorerDialog.browse.loading': 'Loading directories...', 'directoryExplorerDialog.browse.loading': 'Loading directories...',
'directoryExplorerDialog.browse.empty': 'No matching directories.', 'directoryExplorerDialog.browse.empty': 'No matching directories.',
@@ -1118,13 +1122,13 @@ export const dict = {
'directoryExplorerDialog.footer.navigate': 'Navigate', 'directoryExplorerDialog.footer.navigate': 'Navigate',
'directoryExplorerDialog.footer.select': 'Select', 'directoryExplorerDialog.footer.select': 'Select',
'directoryExplorerDialog.footer.add': 'Add', 'directoryExplorerDialog.footer.add': 'Add',
'directoryExplorerDialog.footer.close': 'Close',
'directoryExplorerDialog.shortcut.enter': 'Enter', 'directoryExplorerDialog.shortcut.enter': 'Enter',
'directoryExplorerDialog.toast.unableToAccessDirectory': 'Unable to access directory', 'directoryExplorerDialog.toast.unableToAccessDirectory': 'Unable to access directory',
'directoryExplorerDialog.toast.desktopDeniedAccess': 'Desktop denied directory access.', 'directoryExplorerDialog.toast.desktopDeniedAccess': 'Desktop denied directory access.',
'directoryExplorerDialog.toast.failedToOpenDirectory': 'Failed to open directory', 'directoryExplorerDialog.toast.failedToOpenDirectory': 'Failed to open directory',
'directoryExplorerDialog.toast.desktopCouldNotGrantAccess': 'Desktop could not grant file access.', 'directoryExplorerDialog.toast.desktopCouldNotGrantAccess': 'Desktop could not grant file access.',
'directoryExplorerDialog.toast.failedToAddProject': 'Failed to add project', 'directoryExplorerDialog.toast.failedToAddProject': 'Failed to add project',
'directoryExplorerDialog.toast.cloneUrlRequired': 'Enter a repository URL before cloning.',
'directoryExplorerDialog.toast.selectValidDirectoryPath': 'Please select a valid directory path.', 'directoryExplorerDialog.toast.selectValidDirectoryPath': 'Please select a valid directory path.',
'directoryExplorerDialog.toast.failedToSelectDirectory': 'Failed to select directory', 'directoryExplorerDialog.toast.failedToSelectDirectory': 'Failed to select directory',
'directoryExplorerDialog.toast.unknownError': 'Unknown error occurred.', 'directoryExplorerDialog.toast.unknownError': 'Unknown error occurred.',
@@ -1140,7 +1144,6 @@ export const dict = {
'directoryTree.section.pinned': 'Pinned', 'directoryTree.section.pinned': 'Pinned',
'directoryTree.section.browse': 'Browse', 'directoryTree.section.browse': 'Browse',
'aboutDialog.versionLabel': 'Version {version}', 'aboutDialog.versionLabel': 'Version {version}',
'aboutDialog.tagline': 'Coding with agents, in a workspace built with care.',
'aboutDialog.actions.copyDiagnostics': 'Copy diagnostics', 'aboutDialog.actions.copyDiagnostics': 'Copy diagnostics',
'aboutDialog.actions.preparingDiagnostics': 'Preparing diagnostics...', 'aboutDialog.actions.preparingDiagnostics': 'Preparing diagnostics...',
'aboutDialog.actions.diagnosticsCopied': 'Diagnostics copied', 'aboutDialog.actions.diagnosticsCopied': 'Diagnostics copied',
@@ -1649,6 +1652,7 @@ export const dict = {
'commandPalette.empty.searchingFiles': 'Searching files...', 'commandPalette.empty.searchingFiles': 'Searching files...',
'commandPalette.item.newSession': 'New Session', 'commandPalette.item.newSession': 'New Session',
'commandPalette.item.newWorktreeDraft': 'New Worktree Draft', 'commandPalette.item.newWorktreeDraft': 'New Worktree Draft',
'commandPalette.item.addProject': 'Add Project',
'commandPalette.item.showSessionSwitcher': 'Show Session Switcher', 'commandPalette.item.showSessionSwitcher': 'Show Session Switcher',
'commandPalette.item.toggleSidebar': 'Toggle Sidebar', 'commandPalette.item.toggleSidebar': 'Toggle Sidebar',
'commandPalette.item.toggleRightSidebar': 'Toggle Right Sidebar', 'commandPalette.item.toggleRightSidebar': 'Toggle Right Sidebar',
+8 -4
View File
@@ -423,7 +423,7 @@ export const dict: Record<I18nKey, string> = {
"gitView.commit.addGitmoji": "Añadir gitmoji", "gitView.commit.addGitmoji": "Añadir gitmoji",
"gitView.commit.aiHighlights.insertAria": "Insertar", "gitView.commit.aiHighlights.insertAria": "Insertar",
"gitView.commit.aiHighlights.insertTooltip": "Insertar", "gitView.commit.aiHighlights.insertTooltip": "Insertar",
"gitView.commit.aiHighlights.title": "Título", "gitView.commit.aiHighlights.title": "Puntos destacados",
"gitView.commit.commit": "Commit", "gitView.commit.commit": "Commit",
"gitView.commit.commitAria": "Commit", "gitView.commit.commitAria": "Commit",
"gitView.commit.committing": "Creando commit...", "gitView.commit.committing": "Creando commit...",
@@ -1069,13 +1069,17 @@ export const dict: Record<I18nKey, string> = {
"directoryExplorerDialog.description": "Elige una carpeta para añadir como proyecto.", "directoryExplorerDialog.description": "Elige una carpeta para añadir como proyecto.",
"directoryExplorerDialog.toggle.showHidden": "Mostrar ocultos", "directoryExplorerDialog.toggle.showHidden": "Mostrar ocultos",
"directoryExplorerDialog.pathInput.placeholder": "Escribe la ruta o selecciona desde el árbol...", "directoryExplorerDialog.pathInput.placeholder": "Escribe la ruta o selecciona desde el árbol...",
"directoryExplorerDialog.actions.cancel": "Cancelar",
"directoryExplorerDialog.actions.openingFinder": "Abriendo...", "directoryExplorerDialog.actions.openingFinder": "Abriendo...",
"directoryExplorerDialog.actions.openInFinder": "Abrir en Finder", "directoryExplorerDialog.actions.openInFinder": "Abrir en Finder",
"directoryExplorerDialog.actions.adding": "Añadiendo...", "directoryExplorerDialog.actions.adding": "Añadiendo...",
"directoryExplorerDialog.actions.addProject": "Añadir proyecto", "directoryExplorerDialog.actions.addProject": "Añadir proyecto",
"directoryExplorerDialog.actions.addLocalProject": "Añadir proyecto local",
"directoryExplorerDialog.actions.cloneRepository": "Clonar repositorio",
"directoryExplorerDialog.actions.cloneAndAdd": "Clonar y añadir",
"directoryExplorerDialog.actions.cloning": "Clonando...",
"directoryExplorerDialog.actions.createAndAdd": "Crear y añadir", "directoryExplorerDialog.actions.createAndAdd": "Crear y añadir",
"directoryExplorerDialog.actions.alreadyAdded": "Ya añadido", "directoryExplorerDialog.actions.alreadyAdded": "Ya añadido",
"directoryExplorerDialog.clone.remoteUrlPlaceholder": "URL del repositorio (HTTPS o SSH)",
"directoryExplorerDialog.browse.directories": "Directorios", "directoryExplorerDialog.browse.directories": "Directorios",
"directoryExplorerDialog.browse.loading": "Cargando directorios...", "directoryExplorerDialog.browse.loading": "Cargando directorios...",
"directoryExplorerDialog.browse.empty": "No hay directorios coincidentes.", "directoryExplorerDialog.browse.empty": "No hay directorios coincidentes.",
@@ -1084,13 +1088,13 @@ export const dict: Record<I18nKey, string> = {
"directoryExplorerDialog.footer.navigate": "Navegar", "directoryExplorerDialog.footer.navigate": "Navegar",
"directoryExplorerDialog.footer.select": "Seleccionar", "directoryExplorerDialog.footer.select": "Seleccionar",
"directoryExplorerDialog.footer.add": "Añadir", "directoryExplorerDialog.footer.add": "Añadir",
"directoryExplorerDialog.footer.close": "Cerrar",
"directoryExplorerDialog.shortcut.enter": "Enter", "directoryExplorerDialog.shortcut.enter": "Enter",
"directoryExplorerDialog.toast.unableToAccessDirectory": "No se puede acceder al directorio", "directoryExplorerDialog.toast.unableToAccessDirectory": "No se puede acceder al directorio",
"directoryExplorerDialog.toast.desktopDeniedAccess": "El escritorio denegó el acceso al directorio.", "directoryExplorerDialog.toast.desktopDeniedAccess": "El escritorio denegó el acceso al directorio.",
"directoryExplorerDialog.toast.failedToOpenDirectory": "No se pudo abrir el directorio", "directoryExplorerDialog.toast.failedToOpenDirectory": "No se pudo abrir el directorio",
"directoryExplorerDialog.toast.desktopCouldNotGrantAccess": "El escritorio no pudo otorgar acceso al archivo.", "directoryExplorerDialog.toast.desktopCouldNotGrantAccess": "El escritorio no pudo otorgar acceso al archivo.",
"directoryExplorerDialog.toast.failedToAddProject": "No se pudo añadir el proyecto", "directoryExplorerDialog.toast.failedToAddProject": "No se pudo añadir el proyecto",
"directoryExplorerDialog.toast.cloneUrlRequired": "Introduce una URL de repositorio antes de clonar.",
"directoryExplorerDialog.toast.selectValidDirectoryPath": "Por favor selecciona una ruta de directorio válida.", "directoryExplorerDialog.toast.selectValidDirectoryPath": "Por favor selecciona una ruta de directorio válida.",
"directoryExplorerDialog.toast.failedToSelectDirectory": "No se pudo seleccionar el directorio", "directoryExplorerDialog.toast.failedToSelectDirectory": "No se pudo seleccionar el directorio",
"directoryExplorerDialog.toast.unknownError": "Ocurrió un error desconocido.", "directoryExplorerDialog.toast.unknownError": "Ocurrió un error desconocido.",
@@ -1106,7 +1110,6 @@ export const dict: Record<I18nKey, string> = {
"directoryTree.section.pinned": "Fijados", "directoryTree.section.pinned": "Fijados",
"directoryTree.section.browse": "Explorar", "directoryTree.section.browse": "Explorar",
"aboutDialog.versionLabel": "Versión {version}", "aboutDialog.versionLabel": "Versión {version}",
"aboutDialog.tagline": "Programar con agentes en un espacio de trabajo cuidado.",
"aboutDialog.actions.copyDiagnostics": "Copiar diagnósticos", "aboutDialog.actions.copyDiagnostics": "Copiar diagnósticos",
"aboutDialog.actions.preparingDiagnostics": "Preparando diagnósticos...", "aboutDialog.actions.preparingDiagnostics": "Preparando diagnósticos...",
"aboutDialog.actions.diagnosticsCopied": "Diagnósticos copiados", "aboutDialog.actions.diagnosticsCopied": "Diagnósticos copiados",
@@ -1615,6 +1618,7 @@ export const dict: Record<I18nKey, string> = {
"commandPalette.empty.searchingFiles": "Buscando archivos...", "commandPalette.empty.searchingFiles": "Buscando archivos...",
"commandPalette.item.newSession": "Nueva sesión", "commandPalette.item.newSession": "Nueva sesión",
"commandPalette.item.newWorktreeDraft": "Nuevo borrador de worktree", "commandPalette.item.newWorktreeDraft": "Nuevo borrador de worktree",
"commandPalette.item.addProject": "Añadir proyecto",
"commandPalette.item.showSessionSwitcher": "Mostrar cambiador de sesiones", "commandPalette.item.showSessionSwitcher": "Mostrar cambiador de sesiones",
"commandPalette.item.toggleSidebar": "Mostrar u ocultar barra lateral", "commandPalette.item.toggleSidebar": "Mostrar u ocultar barra lateral",
"commandPalette.item.toggleRightSidebar": "Mostrar u ocultar barra lateral derecha", "commandPalette.item.toggleRightSidebar": "Mostrar u ocultar barra lateral derecha",
+8 -4
View File
@@ -423,7 +423,7 @@ export const dict: Record<I18nKey, string> = {
'gitView.commit.addGitmoji': 'gitmoji 추가', 'gitView.commit.addGitmoji': 'gitmoji 추가',
'gitView.commit.aiHighlights.insertAria': '커밋 메시지에 삽입', 'gitView.commit.aiHighlights.insertAria': '커밋 메시지에 삽입',
'gitView.commit.aiHighlights.insertTooltip': '커밋 메시지에 삽입', 'gitView.commit.aiHighlights.insertTooltip': '커밋 메시지에 삽입',
'gitView.commit.aiHighlights.title': '제목', 'gitView.commit.aiHighlights.title': '주요 내용',
'gitView.commit.commit': '커밋', 'gitView.commit.commit': '커밋',
'gitView.commit.commitAria': '커밋 생성', 'gitView.commit.commitAria': '커밋 생성',
'gitView.commit.committing': '커밋 중…', 'gitView.commit.committing': '커밋 중…',
@@ -1105,13 +1105,17 @@ export const dict: Record<I18nKey, string> = {
'directoryExplorerDialog.description': '프로젝트로 추가할 폴더를 선택하세요.', 'directoryExplorerDialog.description': '프로젝트로 추가할 폴더를 선택하세요.',
'directoryExplorerDialog.toggle.showHidden': '숨김 항목 표시', 'directoryExplorerDialog.toggle.showHidden': '숨김 항목 표시',
'directoryExplorerDialog.pathInput.placeholder': '경로를 입력하거나 트리에서 선택…', 'directoryExplorerDialog.pathInput.placeholder': '경로를 입력하거나 트리에서 선택…',
'directoryExplorerDialog.actions.cancel': '취소',
'directoryExplorerDialog.actions.openingFinder': '여는 중...', 'directoryExplorerDialog.actions.openingFinder': '여는 중...',
'directoryExplorerDialog.actions.openInFinder': 'Finder에서 열기', 'directoryExplorerDialog.actions.openInFinder': 'Finder에서 열기',
'directoryExplorerDialog.actions.adding': '추가 중…', 'directoryExplorerDialog.actions.adding': '추가 중…',
'directoryExplorerDialog.actions.addProject': '프로젝트 추가', 'directoryExplorerDialog.actions.addProject': '프로젝트 추가',
'directoryExplorerDialog.actions.addLocalProject': '로컬 프로젝트 추가',
'directoryExplorerDialog.actions.cloneRepository': '저장소 복제',
'directoryExplorerDialog.actions.cloneAndAdd': '복제하고 추가',
'directoryExplorerDialog.actions.cloning': '복제 중...',
'directoryExplorerDialog.actions.createAndAdd': '생성하고 추가', 'directoryExplorerDialog.actions.createAndAdd': '생성하고 추가',
'directoryExplorerDialog.actions.alreadyAdded': '이미 추가됨', 'directoryExplorerDialog.actions.alreadyAdded': '이미 추가됨',
'directoryExplorerDialog.clone.remoteUrlPlaceholder': '저장소 URL (HTTPS 또는 SSH)',
'directoryExplorerDialog.browse.directories': '디렉터리', 'directoryExplorerDialog.browse.directories': '디렉터리',
'directoryExplorerDialog.browse.loading': '디렉터리 로드 중...', 'directoryExplorerDialog.browse.loading': '디렉터리 로드 중...',
'directoryExplorerDialog.browse.empty': '일치하는 디렉터리가 없습니다.', 'directoryExplorerDialog.browse.empty': '일치하는 디렉터리가 없습니다.',
@@ -1120,13 +1124,13 @@ export const dict: Record<I18nKey, string> = {
'directoryExplorerDialog.footer.navigate': '탐색', 'directoryExplorerDialog.footer.navigate': '탐색',
'directoryExplorerDialog.footer.select': '선택', 'directoryExplorerDialog.footer.select': '선택',
'directoryExplorerDialog.footer.add': '추가', 'directoryExplorerDialog.footer.add': '추가',
'directoryExplorerDialog.footer.close': '닫기',
'directoryExplorerDialog.shortcut.enter': 'Enter', 'directoryExplorerDialog.shortcut.enter': 'Enter',
'directoryExplorerDialog.toast.unableToAccessDirectory': '디렉터리에 접근할 수 없음', 'directoryExplorerDialog.toast.unableToAccessDirectory': '디렉터리에 접근할 수 없음',
'directoryExplorerDialog.toast.desktopDeniedAccess': '데스크톱에서 디렉터리 접근이 거부되었습니다.', 'directoryExplorerDialog.toast.desktopDeniedAccess': '데스크톱에서 디렉터리 접근이 거부되었습니다.',
'directoryExplorerDialog.toast.failedToOpenDirectory': '디렉터리를 열지 못했습니다', 'directoryExplorerDialog.toast.failedToOpenDirectory': '디렉터리를 열지 못했습니다',
'directoryExplorerDialog.toast.desktopCouldNotGrantAccess': '데스크톱에서 파일 접근 권한을 부여하지 못했습니다.', 'directoryExplorerDialog.toast.desktopCouldNotGrantAccess': '데스크톱에서 파일 접근 권한을 부여하지 못했습니다.',
'directoryExplorerDialog.toast.failedToAddProject': '프로젝트 추가 실패', 'directoryExplorerDialog.toast.failedToAddProject': '프로젝트 추가 실패',
'directoryExplorerDialog.toast.cloneUrlRequired': '복제하기 전에 저장소 URL을 입력하세요.',
'directoryExplorerDialog.toast.selectValidDirectoryPath': '유효한 디렉터리 경로를 선택하세요.', 'directoryExplorerDialog.toast.selectValidDirectoryPath': '유효한 디렉터리 경로를 선택하세요.',
'directoryExplorerDialog.toast.failedToSelectDirectory': '디렉터리 선택 실패', 'directoryExplorerDialog.toast.failedToSelectDirectory': '디렉터리 선택 실패',
'directoryExplorerDialog.toast.unknownError': '알 수 없는 오류가 발생했습니다.', 'directoryExplorerDialog.toast.unknownError': '알 수 없는 오류가 발생했습니다.',
@@ -1142,7 +1146,6 @@ export const dict: Record<I18nKey, string> = {
'directoryTree.section.pinned': '고정됨', 'directoryTree.section.pinned': '고정됨',
'directoryTree.section.browse': '찾아보기', 'directoryTree.section.browse': '찾아보기',
'aboutDialog.versionLabel': '버전 {version}', 'aboutDialog.versionLabel': '버전 {version}',
'aboutDialog.tagline': '워크스페이스에서 에이전트와 함께 코딩하세요.',
'aboutDialog.actions.copyDiagnostics': '진단 정보 복사', 'aboutDialog.actions.copyDiagnostics': '진단 정보 복사',
'aboutDialog.actions.preparingDiagnostics': '진단 정보 준비 중…', 'aboutDialog.actions.preparingDiagnostics': '진단 정보 준비 중…',
'aboutDialog.actions.diagnosticsCopied': '진단 정보 복사됨', 'aboutDialog.actions.diagnosticsCopied': '진단 정보 복사됨',
@@ -1649,6 +1652,7 @@ export const dict: Record<I18nKey, string> = {
'commandPalette.empty.searchingFiles': '파일 검색 중…', 'commandPalette.empty.searchingFiles': '파일 검색 중…',
'commandPalette.item.newSession': '새 세션', 'commandPalette.item.newSession': '새 세션',
'commandPalette.item.newWorktreeDraft': '새 워크트리 드래프트', 'commandPalette.item.newWorktreeDraft': '새 워크트리 드래프트',
'commandPalette.item.addProject': '프로젝트 추가',
'commandPalette.item.showSessionSwitcher': '세션 전환기 표시', 'commandPalette.item.showSessionSwitcher': '세션 전환기 표시',
'commandPalette.item.toggleSidebar': '토글 사이드바', 'commandPalette.item.toggleSidebar': '토글 사이드바',
'commandPalette.item.toggleRightSidebar': '오른쪽 사이드바 전환', 'commandPalette.item.toggleRightSidebar': '오른쪽 사이드바 전환',
+60 -56
View File
@@ -640,7 +640,6 @@ export const dict: Record<I18nKey, string> = {
'aboutDialog.actions.preparingDiagnostics': 'Przygotowywanie diagnostyki...', 'aboutDialog.actions.preparingDiagnostics': 'Przygotowywanie diagnostyki...',
'aboutDialog.diagnosticsDescription': 'Zawiera stan OpenChamber, kondycję OpenCode, katalogi i projekty.', 'aboutDialog.diagnosticsDescription': 'Zawiera stan OpenChamber, kondycję OpenCode, katalogi i projekty.',
'aboutDialog.footerNote': 'Stworzone z sercem dla społeczności', 'aboutDialog.footerNote': 'Stworzone z sercem dla społeczności',
'aboutDialog.tagline': 'Kodowanie z agentami w starannie zaprojektowanej przestrzeni roboczej.',
'aboutDialog.toast.copyFailed': 'Nie udało się skopiować', 'aboutDialog.toast.copyFailed': 'Nie udało się skopiować',
'aboutDialog.toast.diagnosticsCopied': 'Diagnostyka skopiowana', 'aboutDialog.toast.diagnosticsCopied': 'Diagnostyka skopiowana',
'aboutDialog.toast.diagnosticsNotReady': 'Diagnostyka nie jest jeszcze gotowa. Poczekaj chwilę i spróbuj ponownie.', 'aboutDialog.toast.diagnosticsNotReady': 'Diagnostyka nie jest jeszcze gotowa. Poczekaj chwilę i spróbuj ponownie.',
@@ -963,6 +962,7 @@ export const dict: Record<I18nKey, string> = {
'commandPalette.input.placeholder': 'Szukaj plików, sesji i poleceń...', 'commandPalette.input.placeholder': 'Szukaj plików, sesji i poleceń...',
'commandPalette.item.newSession': 'Nowa sesja', 'commandPalette.item.newSession': 'Nowa sesja',
'commandPalette.item.newWorktreeDraft': 'Nowy szkic drzewa pracy', 'commandPalette.item.newWorktreeDraft': 'Nowy szkic drzewa pracy',
'commandPalette.item.addProject': 'Dodaj projekt',
'commandPalette.item.openSettings': 'Otwórz ustawienia...', 'commandPalette.item.openSettings': 'Otwórz ustawienia...',
'commandPalette.item.showContextUsage': 'Pokaż użycie kontekstu', 'commandPalette.item.showContextUsage': 'Pokaż użycie kontekstu',
'commandPalette.item.showSessionSwitcher': 'Pokaż przełącznik sesji', 'commandPalette.item.showSessionSwitcher': 'Pokaż przełącznik sesji',
@@ -1142,67 +1142,71 @@ export const dict: Record<I18nKey, string> = {
'diffView.change.renamed': 'Plik o zmienionej nazwie', 'diffView.change.renamed': 'Plik o zmienionej nazwie',
'diffView.change.untracked': 'Nieśledzony plik', 'diffView.change.untracked': 'Nieśledzony plik',
'diffView.image.modified': 'Zmodyfikowany', 'diffView.image.modified': 'Zmodyfikowany',
'diffView.image.modifiedAlt': 'Modified: {path}', 'diffView.image.modifiedAlt': 'Zmodyfikowano: {path}',
'diffView.image.new': 'Nowy', 'diffView.image.new': 'Nowy',
'diffView.image.original': 'Original', 'diffView.image.original': 'Oryginał',
'diffView.image.originalAlt': 'Original: {path}', 'diffView.image.originalAlt': 'Oryginał: {path}',
'diffView.mode.single.description': 'Show one file at a time', 'diffView.mode.single.description': 'Pokazuj jeden plik naraz',
'diffView.mode.single.label': 'Single file', 'diffView.mode.single.label': 'Pojedynczy plik',
'diffView.mode.stacked.description': 'Stack all modified files together', 'diffView.mode.stacked.description': 'Pokaż wszystkie zmodyfikowane pliki razem',
'diffView.mode.stacked.label': 'All files', 'diffView.mode.stacked.label': 'Wszystkie pliki',
'diffView.section.files': 'Pliki', 'diffView.section.files': 'Pliki',
'diffView.selector.selectFile': 'Select file', 'diffView.selector.selectFile': 'Wybierz plik',
'diffView.selector.viewMode': 'View mode', 'diffView.selector.viewMode': 'Tryb widoku',
'diffView.state.cleanWorkingTree': 'Working tree clean, no changes to display', 'diffView.state.cleanWorkingTree': 'Drzewo robocze jest czyste, brak zmian do wyświetlenia',
'diffView.state.failedToLoadDiff': 'Failed to load diff', 'diffView.state.failedToLoadDiff': 'Nie udało się wczytać diffu',
'diffView.state.loadingChanges': 'Loading changes...', 'diffView.state.loadingChanges': 'Ładowanie zmian...',
'diffView.state.loadingDiff': 'Loading diff...', 'diffView.state.loadingDiff': 'Ładowanie diffu...',
'diffView.state.loadingRepositoryStatus': 'Loading repository status...', 'diffView.state.loadingRepositoryStatus': 'Ładowanie stanu repozytorium...',
'diffView.state.notGitRepository': 'Not a git repository. Use the Git tab to initialize or change directories.', 'diffView.state.notGitRepository': 'To nie jest repozytorium Git. Użyj karty Git, aby zainicjować lub zmienić katalog.',
'diffView.state.selectSessionDirectory': 'Select a session directory to view diffs', 'diffView.state.selectSessionDirectory': 'Wybierz katalog sesji, aby zobaczyć diffy',
'diffView.summary.changedFilesPlural': '{count} files changed', 'diffView.summary.changedFilesPlural': 'Zmieniono {count} plików',
'diffView.summary.changedFilesSingle': '{count} file changed', 'diffView.summary.changedFilesSingle': 'Zmieniono {count} plik',
'directoryExplorerDialog.actions.addProject': 'Add project', 'directoryExplorerDialog.actions.addProject': 'Dodaj projekt',
'directoryExplorerDialog.actions.adding': 'Adding...', 'directoryExplorerDialog.actions.addLocalProject': 'Dodaj projekt lokalny',
'directoryExplorerDialog.actions.alreadyAdded': 'Already added', 'directoryExplorerDialog.actions.adding': 'Dodawanie...',
'directoryExplorerDialog.actions.cancel': 'Anuluj', 'directoryExplorerDialog.actions.alreadyAdded': 'Już dodano',
'directoryExplorerDialog.actions.createAndAdd': 'Create & add', 'directoryExplorerDialog.actions.cloneAndAdd': 'Sklonuj i dodaj',
'directoryExplorerDialog.actions.openInFinder': 'Open in Finder', 'directoryExplorerDialog.actions.cloneRepository': 'Sklonuj repozytorium',
'directoryExplorerDialog.actions.openingFinder': 'Opening...', 'directoryExplorerDialog.actions.cloning': 'Klonowanie...',
'directoryExplorerDialog.browse.addedBadge': 'Added', 'directoryExplorerDialog.actions.createAndAdd': 'Utwórz i dodaj',
'directoryExplorerDialog.browse.directories': 'Directories', 'directoryExplorerDialog.actions.openInFinder': 'Otwórz w Finderze',
'directoryExplorerDialog.browse.empty': 'No matching directories.', 'directoryExplorerDialog.actions.openingFinder': 'Otwieranie...',
'directoryExplorerDialog.browse.loading': 'Loading directories...', 'directoryExplorerDialog.browse.addedBadge': 'Dodano',
'directoryExplorerDialog.browse.parentDirectory': 'Parent directory', 'directoryExplorerDialog.browse.directories': 'Katalogi',
'directoryExplorerDialog.description': 'Choose a folder to add as a project.', 'directoryExplorerDialog.browse.empty': 'Brak pasujących katalogów.',
'directoryExplorerDialog.browse.loading': 'Ładowanie katalogów...',
'directoryExplorerDialog.browse.parentDirectory': 'Katalog nadrzędny',
'directoryExplorerDialog.description': 'Wybierz folder, który chcesz dodać jako projekt.',
'directoryExplorerDialog.clone.remoteUrlPlaceholder': 'URL repozytorium (HTTPS lub SSH)',
'directoryExplorerDialog.footer.add': 'Dodaj', 'directoryExplorerDialog.footer.add': 'Dodaj',
'directoryExplorerDialog.footer.close': 'Zamknij', 'directoryExplorerDialog.footer.navigate': 'Nawiguj',
'directoryExplorerDialog.footer.navigate': 'Navigate',
'directoryExplorerDialog.footer.select': 'Wybierz', 'directoryExplorerDialog.footer.select': 'Wybierz',
'directoryExplorerDialog.pathInput.placeholder': 'Enter path or select from tree...', 'directoryExplorerDialog.pathInput.placeholder': 'Wpisz ścieżkę lub wybierz z drzewa...',
'directoryExplorerDialog.shortcut.enter': 'Enter', 'directoryExplorerDialog.shortcut.enter': 'Enter',
'directoryExplorerDialog.title': 'Add project directory', 'directoryExplorerDialog.title': 'Dodaj katalog projektu',
'directoryExplorerDialog.toast.desktopCouldNotGrantAccess': 'Desktop could not grant file access.', 'directoryExplorerDialog.toast.desktopCouldNotGrantAccess': 'Aplikacja desktopowa nie mogła przyznać dostępu do pliku.',
'directoryExplorerDialog.toast.desktopDeniedAccess': 'Desktop denied directory access.', 'directoryExplorerDialog.toast.desktopDeniedAccess': 'Aplikacja desktopowa odmówiła dostępu do katalogu.',
'directoryExplorerDialog.toast.failedToAddProject': 'Failed to add project', 'directoryExplorerDialog.toast.failedToAddProject': 'Nie udało się dodać projektu',
'directoryExplorerDialog.toast.failedToOpenDirectory': 'Failed to open directory', 'directoryExplorerDialog.toast.cloneUrlRequired': 'Wpisz URL repozytorium przed klonowaniem.',
'directoryExplorerDialog.toast.failedToSelectDirectory': 'Failed to select directory', 'directoryExplorerDialog.toast.failedToOpenDirectory': 'Nie udało się otworzyć katalogu',
'directoryExplorerDialog.toast.selectValidDirectoryPath': 'Please select a valid directory path.', 'directoryExplorerDialog.toast.failedToSelectDirectory': 'Nie udało się wybrać katalogu',
'directoryExplorerDialog.toast.unableToAccessDirectory': 'Unable to access directory', 'directoryExplorerDialog.toast.selectValidDirectoryPath': 'Wybierz prawidłową ścieżkę katalogu.',
'directoryExplorerDialog.toast.unknownError': 'Unknown error occurred.', 'directoryExplorerDialog.toast.unableToAccessDirectory': 'Nie można uzyskać dostępu do katalogu',
'directoryExplorerDialog.toggle.showHidden': 'Show hidden', 'directoryExplorerDialog.toast.unknownError': 'Wystąpił nieznany błąd.',
'directoryExplorerDialog.toggle.showHidden': 'Pokaż ukryte',
'directoryTree.actions.cancel': 'Anuluj', 'directoryTree.actions.cancel': 'Anuluj',
'directoryTree.actions.createDirectory': 'Create directory', 'directoryTree.actions.createDirectory': 'Utwórz katalog',
'directoryTree.actions.createNewDirectory': 'Create new directory', 'directoryTree.actions.createNewDirectory': 'Utwórz nowy katalog',
'directoryTree.actions.pinDirectory': 'Pin directory', 'directoryTree.actions.pinDirectory': 'Przypnij katalog',
'directoryTree.actions.selectWorkingDirectoryAria': 'Select working directory', 'directoryTree.actions.selectWorkingDirectoryAria': 'Wybierz katalog roboczy',
'directoryTree.actions.unpinDirectory': 'Unpin directory', 'directoryTree.actions.unpinDirectory': 'Odepnij katalog',
'directoryTree.field.newDirectoryPlaceholder': 'new_directory', 'directoryTree.field.newDirectoryPlaceholder': 'new_directory',
'directoryTree.section.browse': 'Browse', 'directoryTree.section.browse': 'Przeglądaj',
'directoryTree.section.pinned': 'Pinned', 'directoryTree.section.pinned': 'Przypięte',
'directoryTree.state.loading': 'Loading...', 'directoryTree.state.loading': 'Ładowanie...',
'directoryTree.state.locatingHomeDirectory': 'Locating home directory...', 'directoryTree.state.locatingHomeDirectory': 'Wyszukiwanie katalogu domowego...',
'directoryTree.state.noDirectoriesFound': 'No directories found', 'directoryTree.state.noDirectoriesFound': 'Nie znaleziono katalogów',
'filesView.dialog.cancel': 'Anuluj', 'filesView.dialog.cancel': 'Anuluj',
'filesView.dialog.confirm': 'Potwierdź', 'filesView.dialog.confirm': 'Potwierdź',
'filesView.dialog.createFile.description': 'Utwórz nowy plik w {path}', 'filesView.dialog.createFile.description': 'Utwórz nowy plik w {path}',
@@ -1319,7 +1323,7 @@ export const dict: Record<I18nKey, string> = {
'gitView.commit.addGitmoji': 'Dodaj gitmoji', 'gitView.commit.addGitmoji': 'Dodaj gitmoji',
'gitView.commit.aiHighlights.insertAria': 'Wstaw', 'gitView.commit.aiHighlights.insertAria': 'Wstaw',
'gitView.commit.aiHighlights.insertTooltip': 'Wstaw', 'gitView.commit.aiHighlights.insertTooltip': 'Wstaw',
'gitView.commit.aiHighlights.title': 'Tytuł', 'gitView.commit.aiHighlights.title': 'Najważniejsze',
'gitView.commit.commit': 'Zatwierdź', 'gitView.commit.commit': 'Zatwierdź',
'gitView.commit.commitAria': 'Zatwierdź zmiany', 'gitView.commit.commitAria': 'Zatwierdź zmiany',
'gitView.commit.committing': 'Zatwierdzanie...', 'gitView.commit.committing': 'Zatwierdzanie...',
+8 -4
View File
@@ -423,7 +423,7 @@ export const dict: Record<I18nKey, string> = {
"gitView.commit.addGitmoji": "Adicionar gitmoji", "gitView.commit.addGitmoji": "Adicionar gitmoji",
"gitView.commit.aiHighlights.insertAria": "Insertar", "gitView.commit.aiHighlights.insertAria": "Insertar",
"gitView.commit.aiHighlights.insertTooltip": "Insertar", "gitView.commit.aiHighlights.insertTooltip": "Insertar",
"gitView.commit.aiHighlights.title": "Título", "gitView.commit.aiHighlights.title": "Destaques",
"gitView.commit.commit": "Commit", "gitView.commit.commit": "Commit",
"gitView.commit.commitAria": "Commit", "gitView.commit.commitAria": "Commit",
"gitView.commit.committing": "Criando commit...", "gitView.commit.committing": "Criando commit...",
@@ -1069,13 +1069,17 @@ export const dict: Record<I18nKey, string> = {
"directoryExplorerDialog.description": "Escolha uma pasta para adicionar como projeto.", "directoryExplorerDialog.description": "Escolha uma pasta para adicionar como projeto.",
"directoryExplorerDialog.toggle.showHidden": "Mostrar ocultos", "directoryExplorerDialog.toggle.showHidden": "Mostrar ocultos",
"directoryExplorerDialog.pathInput.placeholder": "Digite o caminho ou selecione na árvore...", "directoryExplorerDialog.pathInput.placeholder": "Digite o caminho ou selecione na árvore...",
"directoryExplorerDialog.actions.cancel": "Cancelar",
"directoryExplorerDialog.actions.openingFinder": "Abrindo...", "directoryExplorerDialog.actions.openingFinder": "Abrindo...",
"directoryExplorerDialog.actions.openInFinder": "Abrir no Finder", "directoryExplorerDialog.actions.openInFinder": "Abrir no Finder",
"directoryExplorerDialog.actions.adding": "Adicionando...", "directoryExplorerDialog.actions.adding": "Adicionando...",
"directoryExplorerDialog.actions.addProject": "Adicionar projeto", "directoryExplorerDialog.actions.addProject": "Adicionar projeto",
"directoryExplorerDialog.actions.addLocalProject": "Adicionar projeto local",
"directoryExplorerDialog.actions.cloneRepository": "Clonar repositório",
"directoryExplorerDialog.actions.cloneAndAdd": "Clonar e adicionar",
"directoryExplorerDialog.actions.cloning": "Clonando...",
"directoryExplorerDialog.actions.createAndAdd": "Criar e adicionar", "directoryExplorerDialog.actions.createAndAdd": "Criar e adicionar",
"directoryExplorerDialog.actions.alreadyAdded": "Já adicionado", "directoryExplorerDialog.actions.alreadyAdded": "Já adicionado",
"directoryExplorerDialog.clone.remoteUrlPlaceholder": "URL do repositório (HTTPS ou SSH)",
"directoryExplorerDialog.browse.directories": "Diretórios", "directoryExplorerDialog.browse.directories": "Diretórios",
"directoryExplorerDialog.browse.loading": "Carregando diretórios...", "directoryExplorerDialog.browse.loading": "Carregando diretórios...",
"directoryExplorerDialog.browse.empty": "Nenhum diretório correspondente.", "directoryExplorerDialog.browse.empty": "Nenhum diretório correspondente.",
@@ -1084,13 +1088,13 @@ export const dict: Record<I18nKey, string> = {
"directoryExplorerDialog.footer.navigate": "Navegar", "directoryExplorerDialog.footer.navigate": "Navegar",
"directoryExplorerDialog.footer.select": "Selecionar", "directoryExplorerDialog.footer.select": "Selecionar",
"directoryExplorerDialog.footer.add": "Adicionar", "directoryExplorerDialog.footer.add": "Adicionar",
"directoryExplorerDialog.footer.close": "Fechar",
"directoryExplorerDialog.shortcut.enter": "Enter", "directoryExplorerDialog.shortcut.enter": "Enter",
"directoryExplorerDialog.toast.unableToAccessDirectory": "Não é possível acessar o diretório", "directoryExplorerDialog.toast.unableToAccessDirectory": "Não é possível acessar o diretório",
"directoryExplorerDialog.toast.desktopDeniedAccess": "O desktop negou o acesso ao diretório.", "directoryExplorerDialog.toast.desktopDeniedAccess": "O desktop negou o acesso ao diretório.",
"directoryExplorerDialog.toast.failedToOpenDirectory": "Não foi possível abrir o diretório", "directoryExplorerDialog.toast.failedToOpenDirectory": "Não foi possível abrir o diretório",
"directoryExplorerDialog.toast.desktopCouldNotGrantAccess": "O desktop não pôde conceder acesso ao arquivo.", "directoryExplorerDialog.toast.desktopCouldNotGrantAccess": "O desktop não pôde conceder acesso ao arquivo.",
"directoryExplorerDialog.toast.failedToAddProject": "Não foi possível adicionar o projeto", "directoryExplorerDialog.toast.failedToAddProject": "Não foi possível adicionar o projeto",
"directoryExplorerDialog.toast.cloneUrlRequired": "Insira uma URL de repositório antes de clonar.",
"directoryExplorerDialog.toast.selectValidDirectoryPath": "Selecione um caminho de diretório válido.", "directoryExplorerDialog.toast.selectValidDirectoryPath": "Selecione um caminho de diretório válido.",
"directoryExplorerDialog.toast.failedToSelectDirectory": "Não foi possível selecionar o diretório", "directoryExplorerDialog.toast.failedToSelectDirectory": "Não foi possível selecionar o diretório",
"directoryExplorerDialog.toast.unknownError": "Ocorreu um erro desconhecido.", "directoryExplorerDialog.toast.unknownError": "Ocorreu um erro desconhecido.",
@@ -1106,7 +1110,6 @@ export const dict: Record<I18nKey, string> = {
"directoryTree.section.pinned": "Fixados", "directoryTree.section.pinned": "Fixados",
"directoryTree.section.browse": "Explorar", "directoryTree.section.browse": "Explorar",
"aboutDialog.versionLabel": "Versão {version}", "aboutDialog.versionLabel": "Versão {version}",
"aboutDialog.tagline": "Programação com agentes em um espaço de trabalho feito com cuidado.",
"aboutDialog.actions.copyDiagnostics": "Copiar diagnósticos", "aboutDialog.actions.copyDiagnostics": "Copiar diagnósticos",
"aboutDialog.actions.preparingDiagnostics": "Preparando diagnósticos...", "aboutDialog.actions.preparingDiagnostics": "Preparando diagnósticos...",
"aboutDialog.actions.diagnosticsCopied": "Diagnósticos copiados", "aboutDialog.actions.diagnosticsCopied": "Diagnósticos copiados",
@@ -1615,6 +1618,7 @@ export const dict: Record<I18nKey, string> = {
"commandPalette.empty.searchingFiles": "Buscando arquivos...", "commandPalette.empty.searchingFiles": "Buscando arquivos...",
"commandPalette.item.newSession": "Nova sessão", "commandPalette.item.newSession": "Nova sessão",
"commandPalette.item.newWorktreeDraft": "Novo rascunho de worktree", "commandPalette.item.newWorktreeDraft": "Novo rascunho de worktree",
"commandPalette.item.addProject": "Adicionar projeto",
"commandPalette.item.showSessionSwitcher": "Mostrar seletor de sessões", "commandPalette.item.showSessionSwitcher": "Mostrar seletor de sessões",
"commandPalette.item.toggleSidebar": "Mostrar ou ocultar barra lateral", "commandPalette.item.toggleSidebar": "Mostrar ou ocultar barra lateral",
"commandPalette.item.toggleRightSidebar": "Mostrar ou ocultar barra lateral direita", "commandPalette.item.toggleRightSidebar": "Mostrar ou ocultar barra lateral direita",
+8 -4
View File
@@ -423,7 +423,7 @@ export const dict: Record<I18nKey, string> = {
"gitView.commit.addGitmoji": "Додати gitmoji", "gitView.commit.addGitmoji": "Додати gitmoji",
"gitView.commit.aiHighlights.insertAria": "Вставити підказку", "gitView.commit.aiHighlights.insertAria": "Вставити підказку",
"gitView.commit.aiHighlights.insertTooltip": "Вставити підказку", "gitView.commit.aiHighlights.insertTooltip": "Вставити підказку",
"gitView.commit.aiHighlights.title": "Назва", "gitView.commit.aiHighlights.title": "Основне",
"gitView.commit.commit": "Коміт", "gitView.commit.commit": "Коміт",
"gitView.commit.commitAria": "Створити коміт", "gitView.commit.commitAria": "Створити коміт",
"gitView.commit.committing": "Створення коміту...", "gitView.commit.committing": "Створення коміту...",
@@ -1069,13 +1069,17 @@ export const dict: Record<I18nKey, string> = {
"directoryExplorerDialog.description": "Виберіть папку, щоб додати її як проєкт.", "directoryExplorerDialog.description": "Виберіть папку, щоб додати її як проєкт.",
"directoryExplorerDialog.toggle.showHidden": "Показати приховані", "directoryExplorerDialog.toggle.showHidden": "Показати приховані",
"directoryExplorerDialog.pathInput.placeholder": "Введіть шлях або виберіть із дерева...", "directoryExplorerDialog.pathInput.placeholder": "Введіть шлях або виберіть із дерева...",
"directoryExplorerDialog.actions.cancel": "Скасувати",
"directoryExplorerDialog.actions.openingFinder": "Відкриття...", "directoryExplorerDialog.actions.openingFinder": "Відкриття...",
"directoryExplorerDialog.actions.openInFinder": "Відкрити у Finder", "directoryExplorerDialog.actions.openInFinder": "Відкрити у Finder",
"directoryExplorerDialog.actions.adding": "Додавання...", "directoryExplorerDialog.actions.adding": "Додавання...",
"directoryExplorerDialog.actions.addProject": "Додати проєкт", "directoryExplorerDialog.actions.addProject": "Додати проєкт",
"directoryExplorerDialog.actions.addLocalProject": "Додати локальний проєкт",
"directoryExplorerDialog.actions.cloneRepository": "Клонувати репозиторій",
"directoryExplorerDialog.actions.cloneAndAdd": "Клонувати й додати",
"directoryExplorerDialog.actions.cloning": "Клонування...",
"directoryExplorerDialog.actions.createAndAdd": "Створити й додати", "directoryExplorerDialog.actions.createAndAdd": "Створити й додати",
"directoryExplorerDialog.actions.alreadyAdded": "Уже додано", "directoryExplorerDialog.actions.alreadyAdded": "Уже додано",
"directoryExplorerDialog.clone.remoteUrlPlaceholder": "URL репозиторію (HTTPS або SSH)",
"directoryExplorerDialog.browse.directories": "Каталоги", "directoryExplorerDialog.browse.directories": "Каталоги",
"directoryExplorerDialog.browse.loading": "Завантаження каталогів...", "directoryExplorerDialog.browse.loading": "Завантаження каталогів...",
"directoryExplorerDialog.browse.empty": "Немає відповідних каталогів.", "directoryExplorerDialog.browse.empty": "Немає відповідних каталогів.",
@@ -1084,13 +1088,13 @@ export const dict: Record<I18nKey, string> = {
"directoryExplorerDialog.footer.navigate": "Навігація", "directoryExplorerDialog.footer.navigate": "Навігація",
"directoryExplorerDialog.footer.select": "Вибрати", "directoryExplorerDialog.footer.select": "Вибрати",
"directoryExplorerDialog.footer.add": "Додати", "directoryExplorerDialog.footer.add": "Додати",
"directoryExplorerDialog.footer.close": "Закрити",
"directoryExplorerDialog.shortcut.enter": "Enter", "directoryExplorerDialog.shortcut.enter": "Enter",
"directoryExplorerDialog.toast.unableToAccessDirectory": "Неможливо отримати доступ до каталогу", "directoryExplorerDialog.toast.unableToAccessDirectory": "Неможливо отримати доступ до каталогу",
"directoryExplorerDialog.toast.desktopDeniedAccess": "Десктопному застосунку заборонено доступ до каталогу.", "directoryExplorerDialog.toast.desktopDeniedAccess": "Десктопному застосунку заборонено доступ до каталогу.",
"directoryExplorerDialog.toast.failedToOpenDirectory": "Не вдалося відкрити каталог", "directoryExplorerDialog.toast.failedToOpenDirectory": "Не вдалося відкрити каталог",
"directoryExplorerDialog.toast.desktopCouldNotGrantAccess": "Десктопний застосунок не зміг надати доступ до файлу.", "directoryExplorerDialog.toast.desktopCouldNotGrantAccess": "Десктопний застосунок не зміг надати доступ до файлу.",
"directoryExplorerDialog.toast.failedToAddProject": "Не вдалося додати проєкт", "directoryExplorerDialog.toast.failedToAddProject": "Не вдалося додати проєкт",
"directoryExplorerDialog.toast.cloneUrlRequired": "Введіть URL репозиторію перед клонуванням.",
"directoryExplorerDialog.toast.selectValidDirectoryPath": "Виберіть правильний шлях до каталогу.", "directoryExplorerDialog.toast.selectValidDirectoryPath": "Виберіть правильний шлях до каталогу.",
"directoryExplorerDialog.toast.failedToSelectDirectory": "Не вдалося вибрати каталог", "directoryExplorerDialog.toast.failedToSelectDirectory": "Не вдалося вибрати каталог",
"directoryExplorerDialog.toast.unknownError": "Сталася невідома помилка.", "directoryExplorerDialog.toast.unknownError": "Сталася невідома помилка.",
@@ -1106,7 +1110,6 @@ export const dict: Record<I18nKey, string> = {
"directoryTree.section.pinned": "Закріплено", "directoryTree.section.pinned": "Закріплено",
"directoryTree.section.browse": "Огляд", "directoryTree.section.browse": "Огляд",
"aboutDialog.versionLabel": "Версія {version}", "aboutDialog.versionLabel": "Версія {version}",
"aboutDialog.tagline": "Кодування з агентами в робочому просторі, створеному з турботою.",
"aboutDialog.actions.copyDiagnostics": "Скопіювати діагностику", "aboutDialog.actions.copyDiagnostics": "Скопіювати діагностику",
"aboutDialog.actions.preparingDiagnostics": "Підготовка діагностики...", "aboutDialog.actions.preparingDiagnostics": "Підготовка діагностики...",
"aboutDialog.actions.diagnosticsCopied": "Діагностику скопійовано", "aboutDialog.actions.diagnosticsCopied": "Діагностику скопійовано",
@@ -1615,6 +1618,7 @@ export const dict: Record<I18nKey, string> = {
"commandPalette.empty.searchingFiles": "Пошук файлів...", "commandPalette.empty.searchingFiles": "Пошук файлів...",
"commandPalette.item.newSession": "Нова сесія", "commandPalette.item.newSession": "Нова сесія",
"commandPalette.item.newWorktreeDraft": "Чернетка нового worktree", "commandPalette.item.newWorktreeDraft": "Чернетка нового worktree",
"commandPalette.item.addProject": "Додати проєкт",
"commandPalette.item.showSessionSwitcher": "Показати перемикач сесій", "commandPalette.item.showSessionSwitcher": "Показати перемикач сесій",
"commandPalette.item.toggleSidebar": "Перемкнути бічну панель", "commandPalette.item.toggleSidebar": "Перемкнути бічну панель",
"commandPalette.item.toggleRightSidebar": "Перемкнути праву бічну панель", "commandPalette.item.toggleRightSidebar": "Перемкнути праву бічну панель",
+8 -4
View File
@@ -423,7 +423,7 @@ export const dict: Record<I18nKey, string> = {
'gitView.commit.addGitmoji': '添加 gitmoji', 'gitView.commit.addGitmoji': '添加 gitmoji',
'gitView.commit.aiHighlights.insertAria': '将高亮插入提交信息', 'gitView.commit.aiHighlights.insertAria': '将高亮插入提交信息',
'gitView.commit.aiHighlights.insertTooltip': '将高亮追加到提交信息', 'gitView.commit.aiHighlights.insertTooltip': '将高亮追加到提交信息',
'gitView.commit.aiHighlights.title': 'AI 高亮', 'gitView.commit.aiHighlights.title': '重点',
'gitView.commit.commit': '提交', 'gitView.commit.commit': '提交',
'gitView.commit.commitAria': '提交', 'gitView.commit.commitAria': '提交',
'gitView.commit.committing': '提交中...', 'gitView.commit.committing': '提交中...',
@@ -1069,13 +1069,17 @@ export const dict: Record<I18nKey, string> = {
'directoryExplorerDialog.description': '选择一个文件夹添加为项目。', 'directoryExplorerDialog.description': '选择一个文件夹添加为项目。',
'directoryExplorerDialog.toggle.showHidden': '显示隐藏项', 'directoryExplorerDialog.toggle.showHidden': '显示隐藏项',
'directoryExplorerDialog.pathInput.placeholder': '输入路径或从树中选择...', 'directoryExplorerDialog.pathInput.placeholder': '输入路径或从树中选择...',
'directoryExplorerDialog.actions.cancel': '取消',
'directoryExplorerDialog.actions.openingFinder': '正在打开...', 'directoryExplorerDialog.actions.openingFinder': '正在打开...',
'directoryExplorerDialog.actions.openInFinder': '在 Finder 中打开', 'directoryExplorerDialog.actions.openInFinder': '在 Finder 中打开',
'directoryExplorerDialog.actions.adding': '添加中...', 'directoryExplorerDialog.actions.adding': '添加中...',
'directoryExplorerDialog.actions.addProject': '添加项目', 'directoryExplorerDialog.actions.addProject': '添加项目',
'directoryExplorerDialog.actions.addLocalProject': '添加本地项目',
'directoryExplorerDialog.actions.cloneRepository': '克隆仓库',
'directoryExplorerDialog.actions.cloneAndAdd': '克隆并添加',
'directoryExplorerDialog.actions.cloning': '正在克隆...',
'directoryExplorerDialog.actions.createAndAdd': '创建并添加', 'directoryExplorerDialog.actions.createAndAdd': '创建并添加',
'directoryExplorerDialog.actions.alreadyAdded': '已添加', 'directoryExplorerDialog.actions.alreadyAdded': '已添加',
'directoryExplorerDialog.clone.remoteUrlPlaceholder': '仓库 URL(HTTPS 或 SSH)',
'directoryExplorerDialog.browse.directories': '目录', 'directoryExplorerDialog.browse.directories': '目录',
'directoryExplorerDialog.browse.loading': '正在加载目录...', 'directoryExplorerDialog.browse.loading': '正在加载目录...',
'directoryExplorerDialog.browse.empty': '没有匹配的目录。', 'directoryExplorerDialog.browse.empty': '没有匹配的目录。',
@@ -1084,13 +1088,13 @@ export const dict: Record<I18nKey, string> = {
'directoryExplorerDialog.footer.navigate': '导航', 'directoryExplorerDialog.footer.navigate': '导航',
'directoryExplorerDialog.footer.select': '选择', 'directoryExplorerDialog.footer.select': '选择',
'directoryExplorerDialog.footer.add': '添加', 'directoryExplorerDialog.footer.add': '添加',
'directoryExplorerDialog.footer.close': '关闭',
'directoryExplorerDialog.shortcut.enter': 'Enter', 'directoryExplorerDialog.shortcut.enter': 'Enter',
'directoryExplorerDialog.toast.unableToAccessDirectory': '无法访问目录', 'directoryExplorerDialog.toast.unableToAccessDirectory': '无法访问目录',
'directoryExplorerDialog.toast.desktopDeniedAccess': '桌面端拒绝了目录访问。', 'directoryExplorerDialog.toast.desktopDeniedAccess': '桌面端拒绝了目录访问。',
'directoryExplorerDialog.toast.failedToOpenDirectory': '打开目录失败', 'directoryExplorerDialog.toast.failedToOpenDirectory': '打开目录失败',
'directoryExplorerDialog.toast.desktopCouldNotGrantAccess': '桌面端无法授予文件访问权限。', 'directoryExplorerDialog.toast.desktopCouldNotGrantAccess': '桌面端无法授予文件访问权限。',
'directoryExplorerDialog.toast.failedToAddProject': '添加项目失败', 'directoryExplorerDialog.toast.failedToAddProject': '添加项目失败',
'directoryExplorerDialog.toast.cloneUrlRequired': '克隆前请输入仓库 URL。',
'directoryExplorerDialog.toast.selectValidDirectoryPath': '请选择有效的目录路径。', 'directoryExplorerDialog.toast.selectValidDirectoryPath': '请选择有效的目录路径。',
'directoryExplorerDialog.toast.failedToSelectDirectory': '选择目录失败', 'directoryExplorerDialog.toast.failedToSelectDirectory': '选择目录失败',
'directoryExplorerDialog.toast.unknownError': '发生未知错误。', 'directoryExplorerDialog.toast.unknownError': '发生未知错误。',
@@ -1106,7 +1110,6 @@ export const dict: Record<I18nKey, string> = {
'directoryTree.section.pinned': '已固定', 'directoryTree.section.pinned': '已固定',
'directoryTree.section.browse': '浏览', 'directoryTree.section.browse': '浏览',
'aboutDialog.versionLabel': '版本 {version}', 'aboutDialog.versionLabel': '版本 {version}',
'aboutDialog.tagline': '与智能体协作编码,在精心打造的工作区中完成。',
'aboutDialog.actions.copyDiagnostics': '复制诊断信息', 'aboutDialog.actions.copyDiagnostics': '复制诊断信息',
'aboutDialog.actions.preparingDiagnostics': '正在准备诊断信息...', 'aboutDialog.actions.preparingDiagnostics': '正在准备诊断信息...',
'aboutDialog.actions.diagnosticsCopied': '诊断信息已复制', 'aboutDialog.actions.diagnosticsCopied': '诊断信息已复制',
@@ -1615,6 +1618,7 @@ export const dict: Record<I18nKey, string> = {
'commandPalette.empty.searchingFiles': '正在搜索文件...', 'commandPalette.empty.searchingFiles': '正在搜索文件...',
'commandPalette.item.newSession': '新建会话', 'commandPalette.item.newSession': '新建会话',
'commandPalette.item.newWorktreeDraft': '新建工作树草稿', 'commandPalette.item.newWorktreeDraft': '新建工作树草稿',
'commandPalette.item.addProject': '添加项目',
'commandPalette.item.showSessionSwitcher': '显示会话切换器', 'commandPalette.item.showSessionSwitcher': '显示会话切换器',
'commandPalette.item.toggleSidebar': '切换侧边栏', 'commandPalette.item.toggleSidebar': '切换侧边栏',
'commandPalette.item.toggleRightSidebar': '切换右侧边栏', 'commandPalette.item.toggleRightSidebar': '切换右侧边栏',
+18
View File
@@ -1415,6 +1415,24 @@ class OpencodeService {
return result; return result;
} }
async cloneRepository(input: { remoteUrl: string; destinationPath: string; gitIdentityId?: string | null }): Promise<{ success: boolean; path: string; output?: string }> {
const response = await fetch(`${this.baseUrl}/fs/clone`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify(input),
});
if (!response.ok) {
const error = await response.json().catch(() => ({ error: 'Failed to clone repository' }));
throw new Error(error.error || 'Failed to clone repository');
}
return await response.json();
}
async listLocalDirectory(directoryPath: string | null | undefined, options?: { respectGitignore?: boolean }): Promise<FilesystemEntry[]> { async listLocalDirectory(directoryPath: string | null | undefined, options?: { respectGitignore?: boolean }): Promise<FilesystemEntry[]> {
const normalizedDirectoryPath = typeof directoryPath === 'string' ? normalizeFsPath(directoryPath.trim()) : ''; const normalizedDirectoryPath = typeof directoryPath === 'string' ? normalizeFsPath(directoryPath.trim()) : '';
const cacheKey = `${normalizedDirectoryPath}|${options?.respectGitignore ? '1' : '0'}`; const cacheKey = `${normalizedDirectoryPath}|${options?.respectGitignore ? '1' : '0'}`;
+151
View File
@@ -95,6 +95,48 @@ const resolveWorkspacePathFromContext = async ({ req, targetPath, resolveProject
}); });
}; };
const deriveCloneDirectoryName = (remoteUrl) => {
const remote = typeof remoteUrl === 'string' ? remoteUrl.trim() : '';
if (!remote) return '';
const withoutQuery = remote.split(/[?#]/, 1)[0] || remote;
const match = withoutQuery.match(/([^/:]+?)(?:\.git)?\/?$/);
return match?.[1]?.trim() || '';
};
const resolveCloneGitIdentity = async (gitIdentityId) => {
const id = typeof gitIdentityId === 'string' ? gitIdentityId.trim() : '';
if (!id) return null;
const { getProfile, getGlobalIdentity } = await import('../git/index.js');
if (id === 'global') {
const globalIdentity = await getGlobalIdentity();
if (!globalIdentity?.userName || !globalIdentity?.userEmail) return null;
return {
id: 'global',
name: 'Global Identity',
userName: globalIdentity.userName,
userEmail: globalIdentity.userEmail,
sshKey: globalIdentity.sshCommand ? globalIdentity.sshCommand.replace('ssh -i ', '') : null,
};
}
return getProfile(id) || null;
};
const escapeCloneSshKeyPath = (sshKeyPath) => {
const raw = String(sshKeyPath || '').trim();
if (!raw) return '';
const normalized = process.platform === 'win32' ? raw.replace(/\\/g, '/') : raw;
const dangerousChars = /[`$!"';&|<>(){}[\]*?#~]/;
if (dangerousChars.test(normalized)) {
throw new Error(`SSH key path contains invalid characters: ${raw}`);
}
if (process.platform === 'win32') {
const driveMatch = normalized.match(/^([A-Za-z]):\//);
const unixPath = driveMatch ? `/${driveMatch[1].toLowerCase()}${normalized.slice(2)}` : normalized;
return `'${unixPath}'`;
}
return `'${normalized.replace(/'/g, "'\\''")}'`;
};
const resolveReadPathFromContext = async ({ req, targetPath, resolveProjectDirectory, path, os, normalizeDirectoryPath, openchamberUserConfigRoot }) => { const resolveReadPathFromContext = async ({ req, targetPath, resolveProjectDirectory, path, os, normalizeDirectoryPath, openchamberUserConfigRoot }) => {
if (req.query?.allowOutsideWorkspace === 'true') { if (req.query?.allowOutsideWorkspace === 'true') {
const normalized = normalizeDirectoryPath(targetPath); const normalized = normalizeDirectoryPath(targetPath);
@@ -304,6 +346,115 @@ export const registerFsRoutes = (app, dependencies) => {
} }
}); });
app.post('/api/fs/clone', async (req, res) => {
try {
const { remoteUrl, destinationPath, gitIdentityId } = req.body ?? {};
const remote = typeof remoteUrl === 'string' ? remoteUrl.trim() : '';
const destination = typeof destinationPath === 'string' ? destinationPath.trim() : '';
if (!remote) {
return res.status(400).json({ error: 'Repository URL is required' });
}
if (!destination) {
return res.status(400).json({ error: 'Destination path is required' });
}
let resolvedDestination = path.resolve(normalizeDirectoryPath(destination));
let parentPath = path.dirname(resolvedDestination);
let directoryName = path.basename(resolvedDestination);
const cloneIntoDestinationDirectory = destination.endsWith('/') || destination.endsWith('\\');
if (cloneIntoDestinationDirectory) {
const inferredName = deriveCloneDirectoryName(remote);
if (!inferredName) {
return res.status(400).json({ error: 'Could not infer repository directory name from URL' });
}
parentPath = resolvedDestination;
directoryName = inferredName;
resolvedDestination = path.join(parentPath, directoryName);
} else {
try {
const stat = await fsPromises.stat(resolvedDestination);
if (stat.isDirectory()) {
const inferredName = deriveCloneDirectoryName(remote);
if (!inferredName) {
return res.status(400).json({ error: 'Could not infer repository directory name from URL' });
}
parentPath = resolvedDestination;
directoryName = inferredName;
resolvedDestination = path.join(parentPath, directoryName);
}
} catch (error) {
if (!error || error.code !== 'ENOENT') {
throw error;
}
}
}
if (!directoryName || directoryName === '.' || directoryName === '..') {
return res.status(400).json({ error: 'Destination path must include a directory name' });
}
const identity = await resolveCloneGitIdentity(gitIdentityId);
const gitArgs = ['clone', '--', remote, directoryName];
const sshKeyPath = typeof identity?.sshKey === 'string' ? identity.sshKey.trim() : '';
if (sshKeyPath) {
gitArgs.unshift(`core.sshCommand=ssh -i ${escapeCloneSshKeyPath(sshKeyPath)} -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=accept-new`);
gitArgs.unshift('-c');
}
await fsPromises.mkdir(parentPath, { recursive: true });
try {
await fsPromises.access(resolvedDestination);
return res.status(409).json({ error: 'Destination path already exists' });
} catch (error) {
if (!error || error.code !== 'ENOENT') {
throw error;
}
}
const output = await new Promise((resolve, reject) => {
const child = spawn(resolveGitBinaryForSpawn(), gitArgs, {
cwd: parentPath,
windowsHide: true,
stdio: ['ignore', 'pipe', 'pipe'],
env: {
...process.env,
PATH: buildAugmentedPath ? buildAugmentedPath(process.env.PATH || '') : process.env.PATH,
GIT_TERMINAL_PROMPT: '0',
},
});
let stdout = '';
let stderr = '';
child.stdout.on('data', (data) => { stdout += data.toString(); });
child.stderr.on('data', (data) => { stderr += data.toString(); });
child.on('error', reject);
child.on('close', (code) => {
const combined = `${stdout}\n${stderr}`.trim();
if (code === 0) {
resolve(combined);
return;
}
const message = combined || `git clone failed with exit code ${code}`;
reject(new Error(message));
});
});
if (identity?.userName && identity?.userEmail) {
try {
const { setLocalIdentity } = await import('../git/index.js');
await setLocalIdentity(resolvedDestination, identity);
} catch (error) {
console.warn('Failed to apply git identity after clone:', error);
}
}
return res.json({ success: true, path: resolvedDestination, output });
} catch (error) {
console.error('Failed to clone repository:', error);
return res.status(500).json({ error: error.message || 'Failed to clone repository' });
}
});
app.get('/api/fs/stat', async (req, res) => { app.get('/api/fs/stat', async (req, res) => {
const filePath = typeof req.query.path === 'string' ? req.query.path.trim() : ''; const filePath = typeof req.query.path === 'string' ? req.query.path.trim() : '';
if (!filePath) { if (!filePath) {