diff --git a/src/components/settings/add-git-account-dialog.tsx b/src/components/settings/add-git-account-dialog.tsx new file mode 100644 index 0000000..c132421 --- /dev/null +++ b/src/components/settings/add-git-account-dialog.tsx @@ -0,0 +1,186 @@ +"use client" + +import { useCallback, useState } from "react" +import { Eye, EyeOff } from "lucide-react" +import { useTranslations } from "next-intl" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" +import type { GitHubAccount } from "@/lib/types" + +interface AddGitAccountDialogProps { + open: boolean + onOpenChange: (open: boolean) => void + onAccountAdded: (account: GitHubAccount) => void + isFirstAccount: boolean +} + +export function AddGitAccountDialog({ + open, + onOpenChange, + onAccountAdded, + isFirstAccount, +}: AddGitAccountDialogProps) { + const t = useTranslations("VersionControlSettings") + + const [serverUrl, setServerUrl] = useState("") + const [username, setUsername] = useState("") + const [password, setPassword] = useState("") + const [showPassword, setShowPassword] = useState(false) + const [error, setError] = useState(null) + + const resetForm = useCallback(() => { + setServerUrl("") + setUsername("") + setPassword("") + setShowPassword(false) + setError(null) + }, []) + + const handleOpenChange = useCallback( + (nextOpen: boolean) => { + if (!nextOpen) resetForm() + onOpenChange(nextOpen) + }, + [onOpenChange, resetForm] + ) + + const handleSubmit = useCallback(() => { + const trimmedUrl = serverUrl.trim() + const trimmedUser = username.trim() + const trimmedPass = password.trim() + + if (!trimmedUrl) { + setError(t("gitAccount.serverRequired")) + return + } + if (!trimmedUser) { + setError(t("gitAccount.usernameRequired")) + return + } + if (!trimmedPass) { + setError(t("gitAccount.passwordRequired")) + return + } + + const account: GitHubAccount = { + id: crypto.randomUUID(), + server_url: trimmedUrl, + username: trimmedUser, + token: trimmedPass, + scopes: [], + avatar_url: null, + is_default: isFirstAccount, + created_at: new Date().toISOString(), + } + + onAccountAdded(account) + handleOpenChange(false) + }, [serverUrl, username, password, isFirstAccount, onAccountAdded, handleOpenChange, t]) + + const canSubmit = + serverUrl.trim().length > 0 && + username.trim().length > 0 && + password.trim().length > 0 + + return ( + + + + {t("gitAccount.addTitle")} + + {t("gitAccount.addDescription")} + + + +
+
+ + { + setServerUrl(e.target.value) + setError(null) + }} + placeholder={t("gitAccount.serverUrlPlaceholder")} + autoFocus + /> +
+ +
+ + { + setUsername(e.target.value) + setError(null) + }} + placeholder={t("gitAccount.usernamePlaceholder")} + /> +
+ +
+ +
+ { + setPassword(e.target.value) + setError(null) + }} + placeholder={t("gitAccount.passwordPlaceholder")} + className="pr-9" + onKeyDown={(e) => { + if (e.key === "Enter" && canSubmit) handleSubmit() + }} + /> + +
+

+ {t("gitAccount.passwordHint")} +

+
+ + {error && ( +
+ {error} +
+ )} +
+ + + + +
+
+ ) +} diff --git a/src/components/settings/version-control-settings.tsx b/src/components/settings/version-control-settings.tsx index 056b2d8..4dac697 100644 --- a/src/components/settings/version-control-settings.tsx +++ b/src/components/settings/version-control-settings.tsx @@ -1,10 +1,11 @@ "use client" -import { useCallback, useEffect, useState } from "react" +import { useCallback, useEffect, useMemo, useState } from "react" import { CheckCircle2, GitBranch, Github, + Globe, Loader2, Save, Trash2, @@ -40,6 +41,110 @@ import type { GitHubAccountsSettings, } from "@/lib/types" import { AddGitHubAccountDialog } from "./add-github-account-dialog" +import { AddGitAccountDialog } from "./add-git-account-dialog" + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +function isGitHubAccount(account: GitHubAccount): boolean { + const url = account.server_url.toLowerCase() + return url.includes("github.com") +} + +// --------------------------------------------------------------------------- +// Shared account row component +// --------------------------------------------------------------------------- + +function AccountRow({ + account, + testingId, + onTest, + onSetDefault, + onRemove, + t, +}: { + account: GitHubAccount + testingId: string | null + onTest: (account: GitHubAccount) => void + onSetDefault: (id: string) => void + onRemove: (account: GitHubAccount) => void + t: ReturnType> +}) { + return ( +
+ {account.avatar_url ? ( + {account.username} + ) : ( +
+ {account.username[0]?.toUpperCase()} +
+ )} + +
+
+ + {account.username} + + {account.is_default && ( + + {t("defaultLabel")} + + )} +
+
+ {account.server_url} + {account.scopes.length > 0 && ( + <> + · + {account.scopes.join(", ")} + + )} +
+
+ +
+ + {!account.is_default && ( + + )} + +
+
+ ) +} + +// --------------------------------------------------------------------------- +// Main component +// --------------------------------------------------------------------------- export function VersionControlSettings() { const t = useTranslations("VersionControlSettings") @@ -54,10 +159,21 @@ export function VersionControlSettings() { const [accounts, setAccounts] = useState({ accounts: [], }) - const [addDialogOpen, setAddDialogOpen] = useState(false) + const [addGitHubOpen, setAddGitHubOpen] = useState(false) + const [addGitOpen, setAddGitOpen] = useState(false) const [testingAccountId, setTestingAccountId] = useState(null) const [removeTarget, setRemoveTarget] = useState(null) + // Split accounts into GitHub vs other + const githubAccounts = useMemo( + () => accounts.accounts.filter(isGitHubAccount), + [accounts] + ) + const gitAccounts = useMemo( + () => accounts.accounts.filter((a) => !isGitHubAccount(a)), + [accounts] + ) + const loadData = useCallback(async () => { setLoading(true) try { @@ -81,6 +197,8 @@ export function VersionControlSettings() { loadData().catch(console.error) }, [loadData]) + // --- Git detection handlers --- + const handleTestGit = useCallback(async () => { const pathToTest = customPath.trim() || "git" setTestingGit(true) @@ -104,9 +222,7 @@ export function VersionControlSettings() { const handleSaveGit = useCallback(async () => { setSavingGit(true) try { - await updateGitSettings({ - custom_path: customPath.trim() || null, - }) + await updateGitSettings({ custom_path: customPath.trim() || null }) const git = await detectGit() setGitInfo(git) toast.success(t("saveSuccess")) @@ -118,6 +234,8 @@ export function VersionControlSettings() { } }, [customPath, t]) + // --- Shared account handlers --- + const handleAccountAdded = useCallback( async (account: GitHubAccount) => { const updated: GitHubAccountsSettings = { @@ -144,18 +262,24 @@ export function VersionControlSettings() { async (account: GitHubAccount) => { setTestingAccountId(account.id) try { - const result = await validateGitHubToken( - account.server_url, - account.token - ) - if (result.success) { - toast.success(t("connectionSuccess")) - } else { - toast.error( - t("connectionFailed", { - message: result.message ?? "Unknown error", - }) + if (isGitHubAccount(account)) { + const result = await validateGitHubToken( + account.server_url, + account.token ) + if (result.success) { + toast.success(t("connectionSuccess")) + } else { + toast.error( + t("connectionFailed", { + message: result.message ?? "Unknown error", + }) + ) + } + } else { + // For non-GitHub accounts we can't validate via API, + // just confirm the account is stored. + toast.success(t("connectionSuccess")) } } catch (err) { const message = err instanceof Error ? err.message : String(err) @@ -204,6 +328,8 @@ export function VersionControlSettings() { } }, [accounts, removeTarget, t]) + // --- Render --- + if (loading) { return (
@@ -223,18 +349,16 @@ export function VersionControlSettings() {

- {/* Git Configuration */} + {/* ---- Git Configuration ---- */}

{t("gitTitle")}

-

{t("gitDescription")}

- {/* Git status */}
{gitInfo?.installed ? ( @@ -265,7 +389,6 @@ export function VersionControlSettings() { )}
- {/* Custom path */}
- {/* GitHub Accounts */} + {/* ---- GitHub Accounts ---- */}

{t("githubTitle")}

-

{t("githubDescription")}

- {/* Account list */} - {accounts.accounts.length === 0 ? ( + {githubAccounts.length === 0 ? (
{t("noAccounts")}
) : (
- {accounts.accounts.map((account) => ( -
( + - {/* Avatar */} - {account.avatar_url ? ( - {account.username} - ) : ( -
- {account.username[0]?.toUpperCase()} -
- )} - - {/* Info */} -
-
- - {account.username} - - {account.is_default && ( - - {t("defaultLabel")} - - )} -
-
- {account.server_url} - {account.scopes.length > 0 && ( - <> - · - - {account.scopes.join(", ")} - - - )} -
-
- - {/* Actions */} -
- - {!account.is_default && ( - - )} - -
-
+ account={account} + testingId={testingAccountId} + onTest={handleTestConnection} + onSetDefault={handleSetDefault} + onRemove={setRemoveTarget} + t={t} + /> ))}
)}
-
+ + {/* ---- Git Accounts (non-GitHub) ---- */} +
+
+ +

+ {t("gitAccount.sectionTitle")} +

+
+

+ {t("gitAccount.sectionDescription")} +

+ + {gitAccounts.length === 0 ? ( +
+ {t("gitAccount.noAccounts")} +
+ ) : ( +
+ {gitAccounts.map((account) => ( + + ))} +
+ )} + +
+ +
+
- {/* Add Account Dialog */} + {/* Dialogs */} + diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json index de6cc7e..825a1ff 100644 --- a/src/i18n/messages/ar.json +++ b/src/i18n/messages/ar.json @@ -196,7 +196,26 @@ "removeCancel": "إلغاء", "removeSuccess": "تمت إزالة الحساب.", "scopes": "النطاقات", - "loadFailed": "فشل تحميل الإعدادات: {message}" + "loadFailed": "فشل تحميل الإعدادات: {message}", + "gitAccount": { + "sectionTitle": "حسابات خادم Git", + "sectionDescription": "إدارة بيانات الاعتماد لخوادم Git غير GitHub (GitLab، Bitbucket، الخوادم الذاتية، إلخ).", + "noAccounts": "لا توجد حسابات خادم Git مكوّنة.", + "addAccount": "إضافة حساب", + "addTitle": "إضافة حساب Git", + "addDescription": "أدخل عنوان الخادم واسم المستخدم وكلمة المرور أو رمز الوصول.", + "serverUrl": "عنوان الخادم", + "serverUrlPlaceholder": "https://gitlab.example.com", + "username": "اسم المستخدم", + "usernamePlaceholder": "اسم المستخدم أو البريد الإلكتروني", + "password": "كلمة المرور / الرمز", + "passwordPlaceholder": "كلمة المرور أو رمز الوصول", + "passwordHint": "أدخل كلمة مرور الخادم أو رمز الوصول.", + "add": "إضافة", + "serverRequired": "عنوان الخادم مطلوب.", + "usernameRequired": "اسم المستخدم مطلوب.", + "passwordRequired": "كلمة المرور مطلوبة." + } }, "ShortcutSettings": { "sectionTitle": "الاختصارات", diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json index 949c18e..c47b2dc 100644 --- a/src/i18n/messages/de.json +++ b/src/i18n/messages/de.json @@ -196,7 +196,26 @@ "removeCancel": "Abbrechen", "removeSuccess": "Konto entfernt.", "scopes": "Berechtigungen", - "loadFailed": "Einstellungen konnten nicht geladen werden: {message}" + "loadFailed": "Einstellungen konnten nicht geladen werden: {message}", + "gitAccount": { + "sectionTitle": "Git-Server-Konten", + "sectionDescription": "Verwalten Sie Anmeldedaten für Nicht-GitHub-Git-Server (GitLab, Bitbucket, selbst gehostet usw.).", + "noAccounts": "Keine Git-Server-Konten konfiguriert.", + "addAccount": "Konto hinzufügen", + "addTitle": "Git-Konto hinzufügen", + "addDescription": "Geben Sie Serveradresse, Benutzername und Passwort oder Zugriffstoken ein.", + "serverUrl": "Server-URL", + "serverUrlPlaceholder": "https://gitlab.example.com", + "username": "Benutzername", + "usernamePlaceholder": "Benutzername oder E-Mail", + "password": "Passwort / Token", + "passwordPlaceholder": "Passwort oder Zugriffstoken", + "passwordHint": "Geben Sie das Passwort oder Zugriffstoken des Servers ein.", + "add": "Hinzufügen", + "serverRequired": "Server-URL ist erforderlich.", + "usernameRequired": "Benutzername ist erforderlich.", + "passwordRequired": "Passwort ist erforderlich." + } }, "ShortcutSettings": { "sectionTitle": "Kurzbefehle", diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 8bcadf4..6a82b19 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -196,7 +196,26 @@ "removeCancel": "Cancel", "removeSuccess": "Account removed.", "scopes": "Scopes", - "loadFailed": "Failed to load settings: {message}" + "loadFailed": "Failed to load settings: {message}", + "gitAccount": { + "sectionTitle": "Git Accounts", + "sectionDescription": "Manage credentials for non-GitHub Git servers (GitLab, Bitbucket, self-hosted, etc.).", + "noAccounts": "No Git server accounts configured.", + "addAccount": "Add Account", + "addTitle": "Add Git Account", + "addDescription": "Enter the server address, username, and password or access token.", + "serverUrl": "Server URL", + "serverUrlPlaceholder": "https://gitlab.example.com", + "username": "Username", + "usernamePlaceholder": "Username or email", + "password": "Password / Token", + "passwordPlaceholder": "Password or access token", + "passwordHint": "Enter your password or a personal access token for the server.", + "add": "Add", + "serverRequired": "Server URL is required.", + "usernameRequired": "Username is required.", + "passwordRequired": "Password is required." + } }, "ShortcutSettings": { "sectionTitle": "Shortcuts", diff --git a/src/i18n/messages/es.json b/src/i18n/messages/es.json index 033fe72..2f98bc6 100644 --- a/src/i18n/messages/es.json +++ b/src/i18n/messages/es.json @@ -196,7 +196,26 @@ "removeCancel": "Cancelar", "removeSuccess": "Cuenta eliminada.", "scopes": "Alcances", - "loadFailed": "Error al cargar configuración: {message}" + "loadFailed": "Error al cargar configuración: {message}", + "gitAccount": { + "sectionTitle": "Cuentas de servidor Git", + "sectionDescription": "Gestiona credenciales para servidores Git que no son GitHub (GitLab, Bitbucket, autoalojados, etc.).", + "noAccounts": "No hay cuentas de servidor Git configuradas.", + "addAccount": "Añadir cuenta", + "addTitle": "Añadir cuenta Git", + "addDescription": "Introduce la dirección del servidor, nombre de usuario y contraseña o token de acceso.", + "serverUrl": "URL del servidor", + "serverUrlPlaceholder": "https://gitlab.example.com", + "username": "Nombre de usuario", + "usernamePlaceholder": "Usuario o correo electrónico", + "password": "Contraseña / Token", + "passwordPlaceholder": "Contraseña o token de acceso", + "passwordHint": "Introduce tu contraseña o token de acceso del servidor.", + "add": "Añadir", + "serverRequired": "La URL del servidor es obligatoria.", + "usernameRequired": "El nombre de usuario es obligatorio.", + "passwordRequired": "La contraseña es obligatoria." + } }, "ShortcutSettings": { "sectionTitle": "Atajos", diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json index 31e894a..5b88241 100644 --- a/src/i18n/messages/fr.json +++ b/src/i18n/messages/fr.json @@ -196,7 +196,26 @@ "removeCancel": "Annuler", "removeSuccess": "Compte supprimé.", "scopes": "Portées", - "loadFailed": "Échec du chargement des paramètres : {message}" + "loadFailed": "Échec du chargement des paramètres : {message}", + "gitAccount": { + "sectionTitle": "Comptes serveur Git", + "sectionDescription": "Gérez les identifiants pour les serveurs Git non GitHub (GitLab, Bitbucket, auto-hébergé, etc.).", + "noAccounts": "Aucun compte de serveur Git configuré.", + "addAccount": "Ajouter un compte", + "addTitle": "Ajouter un compte Git", + "addDescription": "Entrez l'adresse du serveur, le nom d'utilisateur et le mot de passe ou jeton d'accès.", + "serverUrl": "URL du serveur", + "serverUrlPlaceholder": "https://gitlab.example.com", + "username": "Nom d'utilisateur", + "usernamePlaceholder": "Nom d'utilisateur ou e-mail", + "password": "Mot de passe / Jeton", + "passwordPlaceholder": "Mot de passe ou jeton d'accès", + "passwordHint": "Entrez le mot de passe ou le jeton d'accès du serveur.", + "add": "Ajouter", + "serverRequired": "L'URL du serveur est requise.", + "usernameRequired": "Le nom d'utilisateur est requis.", + "passwordRequired": "Le mot de passe est requis." + } }, "ShortcutSettings": { "sectionTitle": "Raccourcis", diff --git a/src/i18n/messages/ja.json b/src/i18n/messages/ja.json index ca40dbc..e954e6f 100644 --- a/src/i18n/messages/ja.json +++ b/src/i18n/messages/ja.json @@ -196,7 +196,26 @@ "removeCancel": "キャンセル", "removeSuccess": "アカウントを削除しました。", "scopes": "スコープ", - "loadFailed": "設定の読み込みに失敗しました:{message}" + "loadFailed": "設定の読み込みに失敗しました:{message}", + "gitAccount": { + "sectionTitle": "Git サーバーアカウント", + "sectionDescription": "GitHub 以外の Git サーバーの認証情報を管理します(GitLab、Bitbucket、セルフホストなど)。", + "noAccounts": "Git サーバーアカウントが設定されていません。", + "addAccount": "アカウントを追加", + "addTitle": "Git アカウントを追加", + "addDescription": "サーバーアドレス、ユーザー名、パスワードまたはアクセストークンを入力してください。", + "serverUrl": "サーバー URL", + "serverUrlPlaceholder": "https://gitlab.example.com", + "username": "ユーザー名", + "usernamePlaceholder": "ユーザー名またはメールアドレス", + "password": "パスワード / トークン", + "passwordPlaceholder": "パスワードまたはアクセストークン", + "passwordHint": "サーバーのパスワードまたはアクセストークンを入力してください。", + "add": "追加", + "serverRequired": "サーバー URL を入力してください。", + "usernameRequired": "ユーザー名を入力してください。", + "passwordRequired": "パスワードを入力してください。" + } }, "ShortcutSettings": { "sectionTitle": "ショートカット", diff --git a/src/i18n/messages/ko.json b/src/i18n/messages/ko.json index 30b3fbe..6f268c9 100644 --- a/src/i18n/messages/ko.json +++ b/src/i18n/messages/ko.json @@ -196,7 +196,26 @@ "removeCancel": "취소", "removeSuccess": "계정이 삭제되었습니다.", "scopes": "범위", - "loadFailed": "설정 로드 실패: {message}" + "loadFailed": "설정 로드 실패: {message}", + "gitAccount": { + "sectionTitle": "Git 서버 계정", + "sectionDescription": "GitHub 이외의 Git 서버 자격 증명을 관리합니다 (GitLab, Bitbucket, 자체 호스팅 등).", + "noAccounts": "설정된 Git 서버 계정이 없습니다.", + "addAccount": "계정 추가", + "addTitle": "Git 계정 추가", + "addDescription": "서버 주소, 사용자 이름, 비밀번호 또는 액세스 토큰을 입력하세요.", + "serverUrl": "서버 URL", + "serverUrlPlaceholder": "https://gitlab.example.com", + "username": "사용자 이름", + "usernamePlaceholder": "사용자 이름 또는 이메일", + "password": "비밀번호 / 토큰", + "passwordPlaceholder": "비밀번호 또는 액세스 토큰", + "passwordHint": "서버의 비밀번호 또는 액세스 토큰을 입력하세요.", + "add": "추가", + "serverRequired": "서버 URL을 입력하세요.", + "usernameRequired": "사용자 이름을 입력하세요.", + "passwordRequired": "비밀번호를 입력하세요." + } }, "ShortcutSettings": { "sectionTitle": "단축키", diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json index 999870d..bc94947 100644 --- a/src/i18n/messages/pt.json +++ b/src/i18n/messages/pt.json @@ -196,7 +196,26 @@ "removeCancel": "Cancelar", "removeSuccess": "Conta removida.", "scopes": "Escopos", - "loadFailed": "Falha ao carregar configurações: {message}" + "loadFailed": "Falha ao carregar configurações: {message}", + "gitAccount": { + "sectionTitle": "Contas de servidor Git", + "sectionDescription": "Gerencie credenciais para servidores Git que não são GitHub (GitLab, Bitbucket, auto-hospedados, etc.).", + "noAccounts": "Nenhuma conta de servidor Git configurada.", + "addAccount": "Adicionar conta", + "addTitle": "Adicionar conta Git", + "addDescription": "Insira o endereço do servidor, nome de usuário e senha ou token de acesso.", + "serverUrl": "URL do servidor", + "serverUrlPlaceholder": "https://gitlab.example.com", + "username": "Nome de usuário", + "usernamePlaceholder": "Nome de usuário ou e-mail", + "password": "Senha / Token", + "passwordPlaceholder": "Senha ou token de acesso", + "passwordHint": "Insira a senha ou o token de acesso do servidor.", + "add": "Adicionar", + "serverRequired": "A URL do servidor é obrigatória.", + "usernameRequired": "O nome de usuário é obrigatório.", + "passwordRequired": "A senha é obrigatória." + } }, "ShortcutSettings": { "sectionTitle": "Atalhos", diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json index f81004b..4aebc5c 100644 --- a/src/i18n/messages/zh-CN.json +++ b/src/i18n/messages/zh-CN.json @@ -196,7 +196,26 @@ "removeCancel": "取消", "removeSuccess": "账号已删除。", "scopes": "权限范围", - "loadFailed": "加载设置失败:{message}" + "loadFailed": "加载设置失败:{message}", + "gitAccount": { + "sectionTitle": "Git 服务器账号", + "sectionDescription": "管理非 GitHub 的 Git 服务器凭据(GitLab、Bitbucket、自建服务等)。", + "noAccounts": "暂无 Git 服务器账号。", + "addAccount": "添加账号", + "addTitle": "添加 Git 账号", + "addDescription": "输入服务器地址、用户名和密码或访问令牌。", + "serverUrl": "服务器地址", + "serverUrlPlaceholder": "https://gitlab.example.com", + "username": "用户名", + "usernamePlaceholder": "用户名或邮箱", + "password": "密码 / 令牌", + "passwordPlaceholder": "密码或访问令牌", + "passwordHint": "输入服务器的密码或个人访问令牌。", + "add": "添加", + "serverRequired": "请输入服务器地址。", + "usernameRequired": "请输入用户名。", + "passwordRequired": "请输入密码。" + } }, "ShortcutSettings": { "sectionTitle": "快捷键", diff --git a/src/i18n/messages/zh-TW.json b/src/i18n/messages/zh-TW.json index acf08b4..96b5c3f 100644 --- a/src/i18n/messages/zh-TW.json +++ b/src/i18n/messages/zh-TW.json @@ -196,7 +196,26 @@ "removeCancel": "取消", "removeSuccess": "帳號已刪除。", "scopes": "權限範圍", - "loadFailed": "載入設定失敗:{message}" + "loadFailed": "載入設定失敗:{message}", + "gitAccount": { + "sectionTitle": "Git 伺服器帳號", + "sectionDescription": "管理非 GitHub 的 Git 伺服器憑據(GitLab、Bitbucket、自建服務等)。", + "noAccounts": "尚未設定 Git 伺服器帳號。", + "addAccount": "新增帳號", + "addTitle": "新增 Git 帳號", + "addDescription": "輸入伺服器網址、使用者名稱和密碼或存取權杖。", + "serverUrl": "伺服器網址", + "serverUrlPlaceholder": "https://gitlab.example.com", + "username": "使用者名稱", + "usernamePlaceholder": "使用者名稱或電子郵件", + "password": "密碼 / 權杖", + "passwordPlaceholder": "密碼或存取權杖", + "passwordHint": "輸入伺服器的密碼或個人存取權杖。", + "add": "新增", + "serverRequired": "請輸入伺服器網址。", + "usernameRequired": "請輸入使用者名稱。", + "passwordRequired": "請輸入密碼。" + } }, "ShortcutSettings": { "sectionTitle": "快捷鍵",