Add i18n foundation and translations (#1027)
* feat: add i18n foundation * feat: localize sessions sidebar * Localize multirun/scheduled tasks and fix dialog dropdown interactions * localize git sidebar surface and add zh-CN keys * feat(ui): localize context panel, diff/plan views, and context sidebar content * fix(config): resolve user config home via fs/home before embedded home * localize header/chat UI and complete model/worktree panel strings * localize worktree + github issue/pr dialog flows * localize settings sections and split settings i18n dictionaries * localize additional settings sections and sidebars * localize more settings pages and dialogs * fix settings select trigger localization * localize tunnel settings ui surface * localize additional settings sections * localize keyboard shortcuts labels in settings * localize terminal and utility dialogs surfaces * feat(i18n): localize remaining UI strings * Add Ukrainian locale * Add Spanish locale * Add Brazilian Portuguese locale * Polish locale translations
This commit is contained in:
committed by
GitHub
parent
87db2ea210
commit
7d7285655d
@@ -24,6 +24,7 @@ import {
|
||||
RiLock2Line,
|
||||
} from '@remixicon/react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
const PROFILE_COLORS = [
|
||||
{ key: 'keyword', label: 'Green', cssVar: 'var(--syntax-keyword)' },
|
||||
@@ -56,6 +57,7 @@ export const GitIdentityEditorDialog: React.FC<GitIdentityEditorDialogProps> = (
|
||||
profileId,
|
||||
importData,
|
||||
}) => {
|
||||
const { t } = useI18n();
|
||||
const {
|
||||
getProfileById,
|
||||
createProfile,
|
||||
@@ -130,11 +132,11 @@ export const GitIdentityEditorDialog: React.FC<GitIdentityEditorDialogProps> = (
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!userName.trim() || !userEmail.trim()) {
|
||||
toast.error('User name and email are required');
|
||||
toast.error(t('settings.gitIdentities.editor.toast.userNameEmailRequired'));
|
||||
return;
|
||||
}
|
||||
if (authType === 'token' && !host.trim()) {
|
||||
toast.error('Host is required for token-based authentication');
|
||||
toast.error(t('settings.gitIdentities.editor.toast.hostRequiredForToken'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -161,14 +163,14 @@ export const GitIdentityEditorDialog: React.FC<GitIdentityEditorDialogProps> = (
|
||||
}
|
||||
|
||||
if (success) {
|
||||
toast.success(isNewProfile ? 'Profile created' : 'Profile updated');
|
||||
toast.success(isNewProfile ? t('settings.gitIdentities.editor.toast.profileCreated') : t('settings.gitIdentities.editor.toast.profileUpdated'));
|
||||
onOpenChange(false);
|
||||
} else {
|
||||
toast.error(isNewProfile ? 'Failed to create profile' : 'Failed to update profile');
|
||||
toast.error(isNewProfile ? t('settings.gitIdentities.editor.toast.createProfileFailed') : t('settings.gitIdentities.editor.toast.updateProfileFailed'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error saving profile:', error);
|
||||
toast.error('An error occurred while saving');
|
||||
toast.error(t('settings.gitIdentities.editor.toast.saveUnexpectedError'));
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
@@ -180,15 +182,15 @@ export const GitIdentityEditorDialog: React.FC<GitIdentityEditorDialogProps> = (
|
||||
try {
|
||||
const success = await deleteProfile(profileId);
|
||||
if (success) {
|
||||
toast.success('Profile deleted');
|
||||
toast.success(t('settings.gitIdentities.editor.toast.profileDeleted'));
|
||||
setIsDeleteDialogOpen(false);
|
||||
onOpenChange(false);
|
||||
} else {
|
||||
toast.error('Failed to delete profile');
|
||||
toast.error(t('settings.gitIdentities.editor.toast.deleteProfileFailed'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error deleting profile:', error);
|
||||
toast.error('An error occurred while deleting');
|
||||
toast.error(t('settings.gitIdentities.editor.toast.deleteUnexpectedError'));
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
}
|
||||
@@ -200,12 +202,12 @@ export const GitIdentityEditorDialog: React.FC<GitIdentityEditorDialogProps> = (
|
||||
}, [color]);
|
||||
|
||||
const title = importData
|
||||
? 'Import Credential'
|
||||
? t('settings.gitIdentities.editor.title.importCredential')
|
||||
: isNewProfile
|
||||
? 'New Identity'
|
||||
? t('settings.gitIdentities.editor.title.newIdentity')
|
||||
: isGlobalProfile
|
||||
? 'Global Identity'
|
||||
: (selectedProfile?.name || 'Edit Identity');
|
||||
? t('settings.gitIdentities.editor.title.globalIdentity')
|
||||
: (selectedProfile?.name || t('settings.gitIdentities.editor.title.editIdentity'));
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -215,10 +217,10 @@ export const GitIdentityEditorDialog: React.FC<GitIdentityEditorDialogProps> = (
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
<DialogDescription>
|
||||
{isGlobalProfile
|
||||
? 'System-wide Git identity (read-only)'
|
||||
? t('settings.gitIdentities.editor.description.globalReadOnly')
|
||||
: isNewProfile
|
||||
? 'Create a new Git identity profile'
|
||||
: 'Edit identity profile settings'}
|
||||
? t('settings.gitIdentities.editor.description.newProfile')
|
||||
: t('settings.gitIdentities.editor.description.editProfile')}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -227,17 +229,17 @@ export const GitIdentityEditorDialog: React.FC<GitIdentityEditorDialogProps> = (
|
||||
{!isGlobalProfile && (
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<label className="typography-ui-label text-foreground block mb-1.5">Profile Name</label>
|
||||
<label className="typography-ui-label text-foreground block mb-1.5">{t('settings.gitIdentities.editor.field.profileName')}</label>
|
||||
<Input
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="Work Profile, Personal, etc."
|
||||
placeholder={t('settings.gitIdentities.editor.field.profileNamePlaceholder')}
|
||||
className="h-8"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<span className="typography-ui-label text-foreground">Color</span>
|
||||
<span className="typography-ui-label text-foreground">{t('settings.gitIdentities.editor.field.color')}</span>
|
||||
<div className="flex gap-1.5">
|
||||
{PROFILE_COLORS.map((c) => (
|
||||
<button
|
||||
@@ -258,7 +260,7 @@ export const GitIdentityEditorDialog: React.FC<GitIdentityEditorDialogProps> = (
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<span className="typography-ui-label text-foreground">Icon</span>
|
||||
<span className="typography-ui-label text-foreground">{t('settings.gitIdentities.editor.field.icon')}</span>
|
||||
<div className="flex gap-1.5">
|
||||
{PROFILE_ICONS.map((i) => {
|
||||
const IconComponent = i.Icon;
|
||||
@@ -294,21 +296,21 @@ export const GitIdentityEditorDialog: React.FC<GitIdentityEditorDialogProps> = (
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<div className="flex items-center gap-1.5 mb-1.5">
|
||||
<label className="typography-ui-label text-foreground">User Name</label>
|
||||
<label className="typography-ui-label text-foreground">{t('settings.gitIdentities.editor.field.userName')}</label>
|
||||
{!isGlobalProfile && <span className="text-[var(--status-error)] text-xs">*</span>}
|
||||
<Tooltip delayDuration={1000}>
|
||||
<TooltipTrigger asChild>
|
||||
<RiInformationLine className="h-3.5 w-3.5 text-muted-foreground/60 cursor-help" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent sideOffset={8} className="max-w-xs">
|
||||
The name that will appear in Git commit messages.
|
||||
{t('settings.gitIdentities.editor.field.userNameTooltip')}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Input
|
||||
value={userName}
|
||||
onChange={(e) => setUserName(e.target.value)}
|
||||
placeholder="John Doe"
|
||||
placeholder={t('settings.gitIdentities.editor.field.userNamePlaceholder')}
|
||||
required={!isGlobalProfile}
|
||||
readOnly={isGlobalProfile}
|
||||
disabled={isGlobalProfile}
|
||||
@@ -318,14 +320,14 @@ export const GitIdentityEditorDialog: React.FC<GitIdentityEditorDialogProps> = (
|
||||
|
||||
<div>
|
||||
<div className="flex items-center gap-1.5 mb-1.5">
|
||||
<label className="typography-ui-label text-foreground">Email Address</label>
|
||||
<label className="typography-ui-label text-foreground">{t('settings.gitIdentities.editor.field.emailAddress')}</label>
|
||||
{!isGlobalProfile && <span className="text-[var(--status-error)] text-xs">*</span>}
|
||||
<Tooltip delayDuration={1000}>
|
||||
<TooltipTrigger asChild>
|
||||
<RiInformationLine className="h-3.5 w-3.5 text-muted-foreground/60 cursor-help" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent sideOffset={8} className="max-w-xs">
|
||||
Should match your email in GitHub/GitLab for proper attribution.
|
||||
{t('settings.gitIdentities.editor.field.emailAddressTooltip')}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@@ -333,7 +335,7 @@ export const GitIdentityEditorDialog: React.FC<GitIdentityEditorDialogProps> = (
|
||||
type="email"
|
||||
value={userEmail}
|
||||
onChange={(e) => setUserEmail(e.target.value)}
|
||||
placeholder="john@example.com"
|
||||
placeholder={t('settings.gitIdentities.editor.field.emailAddressPlaceholder')}
|
||||
required={!isGlobalProfile}
|
||||
readOnly={isGlobalProfile}
|
||||
disabled={isGlobalProfile}
|
||||
@@ -348,7 +350,7 @@ export const GitIdentityEditorDialog: React.FC<GitIdentityEditorDialogProps> = (
|
||||
<div className="border-t border-border/40" />
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<span className="typography-ui-label text-foreground">Auth Method</span>
|
||||
<span className="typography-ui-label text-foreground">{t('settings.gitIdentities.editor.field.authMethod')}</span>
|
||||
<div className="flex items-center gap-1">
|
||||
<Button size="sm"
|
||||
type="button"
|
||||
@@ -364,7 +366,7 @@ export const GitIdentityEditorDialog: React.FC<GitIdentityEditorDialogProps> = (
|
||||
aria-pressed={authType === 'token'}
|
||||
onClick={() => setAuthType('token')}
|
||||
>
|
||||
<RiKeyLine className="w-3.5 h-3.5 mr-1" /> Token
|
||||
<RiKeyLine className="w-3.5 h-3.5 mr-1" /> {t('settings.gitIdentities.editor.field.authToken')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -372,20 +374,20 @@ export const GitIdentityEditorDialog: React.FC<GitIdentityEditorDialogProps> = (
|
||||
{authType === 'ssh' && (
|
||||
<div>
|
||||
<div className="flex items-center gap-1.5 mb-1.5">
|
||||
<label className="typography-ui-label text-foreground">SSH Key Path</label>
|
||||
<label className="typography-ui-label text-foreground">{t('settings.gitIdentities.editor.field.sshKeyPath')}</label>
|
||||
<Tooltip delayDuration={1000}>
|
||||
<TooltipTrigger asChild>
|
||||
<RiInformationLine className="h-3.5 w-3.5 text-muted-foreground/60 cursor-help" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent sideOffset={8} className="max-w-xs">
|
||||
Optional path to private key. e.g. ~/.ssh/id_ed25519
|
||||
{t('settings.gitIdentities.editor.field.sshKeyPathTooltip')}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Input
|
||||
value={sshKey}
|
||||
onChange={(e) => setSshKey(e.target.value)}
|
||||
placeholder="~/.ssh/id_ed25519"
|
||||
placeholder={t('settings.gitIdentities.editor.field.sshKeyPathPlaceholder')}
|
||||
className="h-8 font-mono text-xs"
|
||||
/>
|
||||
</div>
|
||||
@@ -394,21 +396,21 @@ export const GitIdentityEditorDialog: React.FC<GitIdentityEditorDialogProps> = (
|
||||
{authType === 'token' && (
|
||||
<div>
|
||||
<div className="flex items-center gap-1.5 mb-1.5">
|
||||
<label className="typography-ui-label text-foreground">Host</label>
|
||||
<label className="typography-ui-label text-foreground">{t('settings.gitIdentities.editor.field.host')}</label>
|
||||
<span className="text-[var(--status-error)] text-xs">*</span>
|
||||
<Tooltip delayDuration={1000}>
|
||||
<TooltipTrigger asChild>
|
||||
<RiInformationLine className="h-3.5 w-3.5 text-muted-foreground/60 cursor-help" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent sideOffset={8} className="max-w-xs">
|
||||
Token will be read from ~/.git-credentials for this host.
|
||||
{t('settings.gitIdentities.editor.field.hostTooltip')}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Input
|
||||
value={host}
|
||||
onChange={(e) => setHost(e.target.value)}
|
||||
placeholder="github.com"
|
||||
placeholder={t('settings.gitIdentities.editor.field.hostPlaceholder')}
|
||||
required
|
||||
className="h-8 font-mono text-xs"
|
||||
/>
|
||||
@@ -427,15 +429,15 @@ export const GitIdentityEditorDialog: React.FC<GitIdentityEditorDialogProps> = (
|
||||
onClick={() => setIsDeleteDialogOpen(true)}
|
||||
className="text-[var(--status-error)] hover:text-[var(--status-error)] border-[var(--status-error)]/30 hover:bg-[var(--status-error)]/10 mr-auto"
|
||||
>
|
||||
<RiDeleteBinLine className="w-3.5 h-3.5 mr-1" /> Delete
|
||||
<RiDeleteBinLine className="w-3.5 h-3.5 mr-1" /> {t('settings.common.actions.delete')}
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="ghost" size="sm" onClick={() => onOpenChange(false)} className="text-foreground hover:bg-interactive-hover hover:text-foreground">
|
||||
{isGlobalProfile ? 'Close' : 'Cancel'}
|
||||
{isGlobalProfile ? t('settings.gitIdentities.editor.actions.close') : t('settings.common.actions.cancel')}
|
||||
</Button>
|
||||
{!isGlobalProfile && (
|
||||
<Button size="sm" onClick={handleSave} disabled={isSaving}>
|
||||
{isSaving ? 'Saving...' : isNewProfile ? 'Create' : 'Save'}
|
||||
{isSaving ? t('settings.common.actions.saving') : isNewProfile ? t('settings.gitIdentities.editor.actions.create') : t('settings.gitIdentities.editor.actions.save')}
|
||||
</Button>
|
||||
)}
|
||||
</DialogFooter>
|
||||
@@ -449,17 +451,17 @@ export const GitIdentityEditorDialog: React.FC<GitIdentityEditorDialogProps> = (
|
||||
>
|
||||
<DialogContent className="max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Delete Profile</DialogTitle>
|
||||
<DialogTitle>{t('settings.gitIdentities.page.deleteDialog.title')}</DialogTitle>
|
||||
<DialogDescription>
|
||||
Are you sure you want to delete "{selectedProfile?.name || name}"?
|
||||
{t('settings.gitIdentities.page.deleteDialog.description', { name: selectedProfile?.name || name })}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<Button variant="ghost" onClick={() => setIsDeleteDialogOpen(false)} disabled={isDeleting}>
|
||||
Cancel
|
||||
{t('settings.common.actions.cancel')}
|
||||
</Button>
|
||||
<Button size="sm" variant="destructive" onClick={() => void handleConfirmDelete()} disabled={isDeleting}>
|
||||
Delete
|
||||
{t('settings.common.actions.delete')}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
||||
@@ -34,6 +34,7 @@ import { GitHubSettings } from '@/components/sections/openchamber/GitHubSettings
|
||||
import { GitIdentityEditorDialog } from './GitIdentityEditorDialog';
|
||||
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
const ICON_MAP: Record<string, React.ComponentType<{ className?: string; style?: React.CSSProperties }>> = {
|
||||
branch: RiGitBranchLine,
|
||||
@@ -53,6 +54,7 @@ const COLOR_MAP: Record<string, string> = {
|
||||
};
|
||||
|
||||
export const GitPage: React.FC = () => {
|
||||
const { t } = useI18n();
|
||||
const {
|
||||
profiles,
|
||||
globalIdentity,
|
||||
@@ -91,10 +93,10 @@ export const GitPage: React.FC = () => {
|
||||
const next = defaultGitIdentityId === profileId ? null : profileId;
|
||||
const ok = await setDefaultGitIdentityId(next);
|
||||
if (!ok) {
|
||||
toast.error('Failed to update default identity');
|
||||
toast.error(t('settings.gitIdentities.page.toast.updateDefaultFailed'));
|
||||
return;
|
||||
}
|
||||
toast.success(next ? 'Default identity updated' : 'Default identity unset');
|
||||
toast.success(next ? t('settings.gitIdentities.page.toast.defaultUpdated') : t('settings.gitIdentities.page.toast.defaultUnset'));
|
||||
};
|
||||
|
||||
const handleConfirmDelete = async () => {
|
||||
@@ -102,10 +104,10 @@ export const GitPage: React.FC = () => {
|
||||
setIsDeletePending(true);
|
||||
const success = await deleteProfile(deleteDialogProfile.id);
|
||||
if (success) {
|
||||
toast.success(`Profile "${deleteDialogProfile.name}" deleted`);
|
||||
toast.success(t('settings.gitIdentities.page.toast.profileDeleted', { name: deleteDialogProfile.name }));
|
||||
setDeleteDialogProfile(null);
|
||||
} else {
|
||||
toast.error('Failed to delete profile');
|
||||
toast.error(t('settings.gitIdentities.page.toast.deleteProfileFailed'));
|
||||
}
|
||||
setIsDeletePending(false);
|
||||
};
|
||||
@@ -120,10 +122,10 @@ export const GitPage: React.FC = () => {
|
||||
<div className="border-t border-border/40 pt-6">
|
||||
<div className="mb-3 px-1 flex items-start justify-between gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="typography-ui-header font-semibold text-foreground">Identities</h3>
|
||||
<h3 className="typography-ui-header font-semibold text-foreground">{t('settings.gitIdentities.page.section.title')}</h3>
|
||||
</div>
|
||||
<Button size="sm" variant="outline" onClick={() => openEditor('new')}>
|
||||
<RiAddLine className="w-3.5 h-3.5 mr-1" /> New
|
||||
<RiAddLine className="w-3.5 h-3.5 mr-1" /> {t('settings.common.badge.new')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -157,8 +159,8 @@ export const GitPage: React.FC = () => {
|
||||
{!globalIdentity && profiles.length === 0 && unimportedCredentials.length === 0 && (
|
||||
<div className="py-8 px-4 text-center text-muted-foreground">
|
||||
<RiShieldKeyholeLine className="mx-auto mb-2 h-8 w-8 opacity-40" />
|
||||
<p className="typography-ui-label">No identities configured</p>
|
||||
<p className="typography-meta mt-1 opacity-75">Create one to manage Git author settings per project</p>
|
||||
<p className="typography-ui-label">{t('settings.gitIdentities.page.empty.title')}</p>
|
||||
<p className="typography-meta mt-1 opacity-75">{t('settings.gitIdentities.page.empty.description')}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -167,7 +169,7 @@ export const GitPage: React.FC = () => {
|
||||
<>
|
||||
<div className="px-4 py-2 border-t border-[var(--surface-subtle)]">
|
||||
<span className="typography-micro text-muted-foreground">
|
||||
Found in ~/.git-credentials
|
||||
{t('settings.gitIdentities.page.discoveredCredentials.title')}
|
||||
</span>
|
||||
</div>
|
||||
{unimportedCredentials.map((cred, i) => (
|
||||
@@ -202,17 +204,17 @@ export const GitPage: React.FC = () => {
|
||||
>
|
||||
<DialogContent className="max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Delete Profile</DialogTitle>
|
||||
<DialogTitle>{t('settings.gitIdentities.page.deleteDialog.title')}</DialogTitle>
|
||||
<DialogDescription>
|
||||
Are you sure you want to delete "{deleteDialogProfile?.name}"?
|
||||
{t('settings.gitIdentities.page.deleteDialog.description', { name: deleteDialogProfile?.name ?? '' })}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<Button variant="ghost" onClick={() => setDeleteDialogProfile(null)} disabled={isDeletePending}>
|
||||
Cancel
|
||||
{t('settings.common.actions.cancel')}
|
||||
</Button>
|
||||
<Button size="sm" variant="destructive" onClick={() => void handleConfirmDelete()} disabled={isDeletePending}>
|
||||
Delete
|
||||
{t('settings.common.actions.delete')}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
@@ -242,6 +244,7 @@ const IdentityRow: React.FC<IdentityRowProps> = ({
|
||||
isReadOnly,
|
||||
hasBorder,
|
||||
}) => {
|
||||
const { t } = useI18n();
|
||||
const IconComponent = ICON_MAP[profile.icon || 'branch'] || RiGitBranchLine;
|
||||
const iconColor = COLOR_MAP[profile.color || ''];
|
||||
const authType = profile.authType || 'ssh';
|
||||
@@ -267,12 +270,12 @@ const IdentityRow: React.FC<IdentityRowProps> = ({
|
||||
</span>
|
||||
{isDefault && (
|
||||
<span className="typography-micro text-primary bg-primary/12 px-1 rounded flex-shrink-0 leading-none pb-px border border-primary/25">
|
||||
default
|
||||
{t('settings.gitIdentities.page.badge.default')}
|
||||
</span>
|
||||
)}
|
||||
{isReadOnly && (
|
||||
<span className="typography-micro text-muted-foreground bg-muted px-1 rounded flex-shrink-0 leading-none pb-px border border-border/50">
|
||||
system
|
||||
{t('settings.agents.sidebar.badge.system')}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
@@ -295,7 +298,7 @@ const IdentityRow: React.FC<IdentityRowProps> = ({
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-fit min-w-28">
|
||||
<DropdownMenuItem onClick={(e) => { e.stopPropagation(); onToggleDefault(); }}>
|
||||
{isDefault ? 'Unset default' : 'Set as default'}
|
||||
{isDefault ? t('settings.gitIdentities.page.actions.unsetDefault') : t('settings.gitIdentities.page.actions.setAsDefault')}
|
||||
</DropdownMenuItem>
|
||||
{!isReadOnly && onDelete && (
|
||||
<DropdownMenuItem
|
||||
@@ -303,7 +306,7 @@ const IdentityRow: React.FC<IdentityRowProps> = ({
|
||||
className="text-destructive focus:text-destructive"
|
||||
>
|
||||
<RiDeleteBinLine className="h-4 w-4 mr-px" />
|
||||
Delete
|
||||
{t('settings.common.actions.delete')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
@@ -321,6 +324,7 @@ interface DiscoveredRowProps {
|
||||
}
|
||||
|
||||
const DiscoveredRow: React.FC<DiscoveredRowProps> = ({ credential, onImport, hasBorder }) => {
|
||||
const { t } = useI18n();
|
||||
const parts = credential.host.split('/');
|
||||
const displayName = parts.length >= 3 ? parts[parts.length - 1] : credential.host;
|
||||
const isRepoSpecific = credential.host.includes('/');
|
||||
@@ -340,7 +344,7 @@ const DiscoveredRow: React.FC<DiscoveredRowProps> = ({ credential, onImport, has
|
||||
</div>
|
||||
<Button size="sm" variant="ghost" onClick={onImport} className="gap-1 shrink-0">
|
||||
<RiDownloadLine className="h-3 w-3" />
|
||||
Import
|
||||
{t('settings.gitIdentities.page.actions.import')}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user