在文件树上支持右键添加文件和添加目录操作

This commit is contained in:
xintaofei
2026-03-13 23:33:31 +08:00
parent 874591a473
commit f4f70c438a
14 changed files with 363 additions and 12 deletions

View File

@@ -2555,6 +2555,69 @@ pub async fn delete_file_tree_entry(
Ok(()) Ok(())
} }
#[tauri::command]
pub async fn create_file_tree_entry(
root_path: String,
path: String,
name: String,
kind: String,
) -> Result<String, AppCommandError> {
let root = PathBuf::from(&root_path);
if !root.exists() || !root.is_dir() {
return Err(AppCommandError::not_found("Folder does not exist"));
}
let validated_name = validate_new_name(&name)?;
let parent_dir = if path.is_empty() {
root.clone()
} else {
let resolved = resolve_tree_path(&root, &path)?;
if !resolved.exists() {
return Err(AppCommandError::not_found("Parent path does not exist"));
}
if resolved.is_file() {
resolved
.parent()
.map(|p| p.to_path_buf())
.ok_or_else(|| AppCommandError::invalid_input("Cannot determine parent directory"))?
} else {
resolved
}
};
let target = parent_dir.join(validated_name);
if target.exists() {
return Err(AppCommandError::already_exists(
"A file or directory with this name already exists",
));
}
match kind.as_str() {
"file" => {
std::fs::File::create(&target).map_err(AppCommandError::io)?;
}
"dir" => {
std::fs::create_dir(&target).map_err(AppCommandError::io)?;
}
_ => {
return Err(AppCommandError::invalid_input(
"Kind must be 'file' or 'dir'",
));
}
}
let rel = target
.strip_prefix(&root)
.map_err(|e| {
AppCommandError::invalid_input("Failed to compute relative path")
.with_detail(e.to_string())
})?
.to_string_lossy()
.to_string();
Ok(rel)
}
#[tauri::command] #[tauri::command]
pub async fn git_log( pub async fn git_log(
path: String, path: String,

View File

@@ -218,6 +218,7 @@ pub fn run() {
folders::save_file_copy, folders::save_file_copy,
folders::rename_file_tree_entry, folders::rename_file_tree_entry,
folders::delete_file_tree_entry, folders::delete_file_tree_entry,
folders::create_file_tree_entry,
folders::git_log, folders::git_log,
folders::git_commit_branches, folders::git_commit_branches,
windows::open_folder_window, windows::open_folder_window,

View File

@@ -21,6 +21,7 @@ import { useTabContext } from "@/contexts/tab-context"
import { useTerminalContext } from "@/contexts/terminal-context" import { useTerminalContext } from "@/contexts/terminal-context"
import { useWorkspaceContext } from "@/contexts/workspace-context" import { useWorkspaceContext } from "@/contexts/workspace-context"
import { import {
createFileTreeEntry,
deleteFileTreeEntry, deleteFileTreeEntry,
gitAddFiles, gitAddFiles,
getGitBranch, getGitBranch,
@@ -419,6 +420,7 @@ interface RenderNodeProps {
onOpenDirInTerminal: (dirPath: string, fileName: string) => Promise<void> onOpenDirInTerminal: (dirPath: string, fileName: string) => Promise<void>
onRequestAddToVcs: (target: FileActionTarget) => void onRequestAddToVcs: (target: FileActionTarget) => void
onRequestRename: (target: FileActionTarget) => void onRequestRename: (target: FileActionTarget) => void
onRequestCreate: (parentPath: string, kind: "file" | "dir") => void
onRequestDelete: (target: FileActionTarget) => void onRequestDelete: (target: FileActionTarget) => void
onRefresh: () => void onRefresh: () => void
} }
@@ -441,6 +443,7 @@ function RenderNode({
onRequestRollback, onRequestRollback,
onOpenDirInTerminal, onOpenDirInTerminal,
onRequestAddToVcs, onRequestAddToVcs,
onRequestCreate,
onRequestRename, onRequestRename,
onRequestDelete, onRequestDelete,
onRefresh, onRefresh,
@@ -507,6 +510,21 @@ function RenderNode({
> >
{t("attachToCurrentSession")} {t("attachToCurrentSession")}
</ContextMenuItem> </ContextMenuItem>
<ContextMenuSub>
<ContextMenuSubTrigger>{t("new")}</ContextMenuSubTrigger>
<ContextMenuSubContent>
<ContextMenuItem
onSelect={() => onRequestCreate(node.path, "file")}
>
{t("newFile")}
</ContextMenuItem>
<ContextMenuItem
onSelect={() => onRequestCreate(node.path, "dir")}
>
{t("newDirectory")}
</ContextMenuItem>
</ContextMenuSubContent>
</ContextMenuSub>
<ContextMenuSub> <ContextMenuSub>
<ContextMenuSubTrigger disabled={isGitMenuDisabled}> <ContextMenuSubTrigger disabled={isGitMenuDisabled}>
{t("git")} {t("git")}
@@ -629,6 +647,7 @@ function RenderNode({
onRequestCompareWithBranch={onRequestCompareWithBranch} onRequestCompareWithBranch={onRequestCompareWithBranch}
onRequestRollback={onRequestRollback} onRequestRollback={onRequestRollback}
onOpenDirInTerminal={onOpenDirInTerminal} onOpenDirInTerminal={onOpenDirInTerminal}
onRequestCreate={onRequestCreate}
onRequestAddToVcs={onRequestAddToVcs} onRequestAddToVcs={onRequestAddToVcs}
onRequestRename={onRequestRename} onRequestRename={onRequestRename}
onRequestDelete={onRequestDelete} onRequestDelete={onRequestDelete}
@@ -639,6 +658,19 @@ function RenderNode({
</FileTreeFolder> </FileTreeFolder>
</ContextMenuTrigger> </ContextMenuTrigger>
<ContextMenuContent> <ContextMenuContent>
<ContextMenuSub>
<ContextMenuSubTrigger>{t("new")}</ContextMenuSubTrigger>
<ContextMenuSubContent>
<ContextMenuItem
onSelect={() => onRequestCreate(node.path, "file")}
>
{t("newFile")}
</ContextMenuItem>
<ContextMenuItem onSelect={() => onRequestCreate(node.path, "dir")}>
{t("newDirectory")}
</ContextMenuItem>
</ContextMenuSubContent>
</ContextMenuSub>
<ContextMenuSub> <ContextMenuSub>
<ContextMenuSubTrigger disabled={isGitMenuDisabled}> <ContextMenuSubTrigger disabled={isGitMenuDisabled}>
{t("git")} {t("git")}
@@ -736,6 +768,10 @@ export function FileTreeTab() {
) )
const [renameValue, setRenameValue] = useState("") const [renameValue, setRenameValue] = useState("")
const [renaming, setRenaming] = useState(false) const [renaming, setRenaming] = useState(false)
const [createParentPath, setCreateParentPath] = useState<string | null>(null)
const [createKind, setCreateKind] = useState<"file" | "dir">("file")
const [createName, setCreateName] = useState("")
const [creating, setCreating] = useState(false)
const [deleteTarget, setDeleteTarget] = useState<FileActionTarget | null>( const [deleteTarget, setDeleteTarget] = useState<FileActionTarget | null>(
null null
) )
@@ -1139,6 +1175,15 @@ export function FileTreeTab() {
}) })
}, [folder, t]) }, [folder, t])
const handleRequestCreate = useCallback(
(parentPath: string, kind: "file" | "dir") => {
setCreateParentPath(parentPath)
setCreateKind(kind)
setCreateName("")
},
[]
)
const handleRequestRename = useCallback((target: FileActionTarget) => { const handleRequestRename = useCallback((target: FileActionTarget) => {
setRenameTarget(target) setRenameTarget(target)
setRenameValue(target.name) setRenameValue(target.name)
@@ -1516,6 +1561,33 @@ export function FileTreeTab() {
] ]
) )
const handleCreateConfirm = useCallback(async () => {
if (!folder?.path || createParentPath === null) return
const trimmedName = createName.trim()
if (!trimmedName) {
setCreateParentPath(null)
return
}
setCreating(true)
try {
await createFileTreeEntry(
folder.path,
createParentPath,
trimmedName,
createKind
)
setCreateParentPath(null)
setCreateName("")
await fetchTree()
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
toast.error(t("toasts.createFailed"), { description: message })
} finally {
setCreating(false)
}
}, [createKind, createName, createParentPath, fetchTree, folder?.path, t])
const handleRenameConfirm = useCallback(async () => { const handleRenameConfirm = useCallback(async () => {
if (!folder?.path || !renameTarget) return if (!folder?.path || !renameTarget) return
const nextName = renameValue.trim() const nextName = renameValue.trim()
@@ -2074,6 +2146,7 @@ export function FileTreeTab() {
} }
onRequestRollback={handleRequestRollback} onRequestRollback={handleRequestRollback}
onOpenDirInTerminal={handleOpenDirInTerminal} onOpenDirInTerminal={handleOpenDirInTerminal}
onRequestCreate={handleRequestCreate}
onRequestAddToVcs={handleAddToVcs} onRequestAddToVcs={handleAddToVcs}
onRequestRename={handleRequestRename} onRequestRename={handleRequestRename}
onRequestDelete={handleRequestDelete} onRequestDelete={handleRequestDelete}
@@ -2083,6 +2156,21 @@ export function FileTreeTab() {
</FileTreeFolder> </FileTreeFolder>
</ContextMenuTrigger> </ContextMenuTrigger>
<ContextMenuContent> <ContextMenuContent>
<ContextMenuSub>
<ContextMenuSubTrigger>{t("new")}</ContextMenuSubTrigger>
<ContextMenuSubContent>
<ContextMenuItem
onSelect={() => handleRequestCreate("", "file")}
>
{t("newFile")}
</ContextMenuItem>
<ContextMenuItem
onSelect={() => handleRequestCreate("", "dir")}
>
{t("newDirectory")}
</ContextMenuItem>
</ContextMenuSubContent>
</ContextMenuSub>
<ContextMenuSub> <ContextMenuSub>
<ContextMenuSubTrigger disabled={!gitEnabled}> <ContextMenuSubTrigger disabled={!gitEnabled}>
{t("git")} {t("git")}
@@ -2165,6 +2253,17 @@ export function FileTreeTab() {
</div> </div>
</ContextMenuTrigger> </ContextMenuTrigger>
<ContextMenuContent> <ContextMenuContent>
<ContextMenuSub>
<ContextMenuSubTrigger>{t("new")}</ContextMenuSubTrigger>
<ContextMenuSubContent>
<ContextMenuItem onSelect={() => handleRequestCreate("", "file")}>
{t("newFile")}
</ContextMenuItem>
<ContextMenuItem onSelect={() => handleRequestCreate("", "dir")}>
{t("newDirectory")}
</ContextMenuItem>
</ContextMenuSubContent>
</ContextMenuSub>
<ContextMenuItem <ContextMenuItem
onSelect={() => { onSelect={() => {
void fetchTree() void fetchTree()
@@ -2175,6 +2274,68 @@ export function FileTreeTab() {
</ContextMenuContent> </ContextMenuContent>
</ContextMenu> </ContextMenu>
<Dialog
open={createParentPath !== null}
onOpenChange={(open) => {
if (open) return
setCreateParentPath(null)
setCreateName("")
}}
>
<DialogContent>
<DialogHeader>
<DialogTitle>
{createKind === "dir"
? t("createDialog.newDirectory")
: t("createDialog.newFile")}
</DialogTitle>
<DialogDescription>
{t("createDialog.description", {
kind:
createKind === "dir"
? t("newDirectory").toLowerCase()
: t("newFile").toLowerCase(),
})}
</DialogDescription>
</DialogHeader>
<form
onSubmit={(event) => {
event.preventDefault()
void handleCreateConfirm()
}}
className="space-y-4"
>
<Input
value={createName}
onChange={(event) => setCreateName(event.target.value)}
autoFocus
disabled={creating}
placeholder={
createKind === "dir"
? t("createDialog.placeholderDirectory")
: t("createDialog.placeholderFile")
}
/>
<DialogFooter>
<Button
type="button"
variant="outline"
disabled={creating}
onClick={() => {
setCreateParentPath(null)
setCreateName("")
}}
>
{tCommon("cancel")}
</Button>
<Button type="submit" disabled={creating || !createName.trim()}>
{tCommon("create")}
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
<Dialog <Dialog
open={Boolean(renameTarget)} open={Boolean(renameTarget)}
onOpenChange={(open) => { onOpenChange={(open) => {
@@ -2473,7 +2634,12 @@ export function FileTreeTab() {
<Collapsible key={remoteName}> <Collapsible key={remoteName}>
<CollapsibleTrigger className="flex w-full items-center gap-2.5 rounded-xl px-2 py-1.5 pl-5 text-sm hover:bg-accent hover:text-accent-foreground select-none outline-hidden"> <CollapsibleTrigger className="flex w-full items-center gap-2.5 rounded-xl px-2 py-1.5 pl-5 text-sm hover:bg-accent hover:text-accent-foreground select-none outline-hidden">
<ChevronRight className="h-3 w-3 shrink-0 transition-transform [[data-state=open]>&]:rotate-90" /> <ChevronRight className="h-3 w-3 shrink-0 transition-transform [[data-state=open]>&]:rotate-90" />
{remoteName} ({groupedCompareRemoteBranches[remoteName].length}) {remoteName} (
{
groupedCompareRemoteBranches[remoteName]
.length
}
)
</CollapsibleTrigger> </CollapsibleTrigger>
<CollapsibleContent className="space-y-1 pt-1 pl-3"> <CollapsibleContent className="space-y-1 pt-1 pl-3">
{groupedCompareRemoteBranches[remoteName].map( {groupedCompareRemoteBranches[remoteName].map(
@@ -2489,7 +2655,9 @@ export function FileTreeTab() {
}} }}
disabled={comparing} disabled={comparing}
> >
{branch.substring(remoteName.length + 1)} {branch.substring(
remoteName.length + 1
)}
</Button> </Button>
) )
)} )}

View File

@@ -967,6 +967,9 @@
"attachToCurrentSession": "إرفاق بالجلسة الحالية", "attachToCurrentSession": "إرفاق بالجلسة الحالية",
"compareWithBranch": "المقارنة مع الفرع...", "compareWithBranch": "المقارنة مع الفرع...",
"reloadFromDisk": "إعادة التحميل من القرص", "reloadFromDisk": "إعادة التحميل من القرص",
"new": "جديد",
"newFile": "ملف",
"newDirectory": "مجلد",
"openIn": "فتح في", "openIn": "فتح في",
"openInTerminal": "فتح في الطرفية", "openInTerminal": "فتح في الطرفية",
"actions": { "actions": {
@@ -996,7 +999,15 @@
"rolledBackFiles": "{count, plural, one {تم التراجع عن ملف واحد} other {تم التراجع عن # ملفات}}", "rolledBackFiles": "{count, plural, one {تم التراجع عن ملف واحد} other {تم التراجع عن # ملفات}}",
"savedAsCopy": "تم الحفظ كنسخة", "savedAsCopy": "تم الحفظ كنسخة",
"saveCopyFailed": "فشل الحفظ كنسخة", "saveCopyFailed": "فشل الحفظ كنسخة",
"watchStartFailed": "فشل بدء مراقبة الملفات" "watchStartFailed": "فشل بدء مراقبة الملفات",
"createFailed": "فشل في الإنشاء"
},
"createDialog": {
"newFile": "ملف جديد",
"newDirectory": "مجلد جديد",
"description": "أدخل اسمًا لـ{kind} الجديد.",
"placeholderFile": "file-name.ext",
"placeholderDirectory": "folder-name"
}, },
"renameDialog": { "renameDialog": {
"renameDirectory": "إعادة تسمية المجلد", "renameDirectory": "إعادة تسمية المجلد",

View File

@@ -967,6 +967,9 @@
"attachToCurrentSession": "An aktuelle Sitzung anhängen", "attachToCurrentSession": "An aktuelle Sitzung anhängen",
"compareWithBranch": "Mit Branch vergleichen...", "compareWithBranch": "Mit Branch vergleichen...",
"reloadFromDisk": "Von Datenträger neu laden", "reloadFromDisk": "Von Datenträger neu laden",
"new": "Neu",
"newFile": "Datei",
"newDirectory": "Verzeichnis",
"openIn": "Öffnen in", "openIn": "Öffnen in",
"openInTerminal": "Im Terminal öffnen", "openInTerminal": "Im Terminal öffnen",
"actions": { "actions": {
@@ -996,7 +999,15 @@
"rolledBackFiles": "{count, plural, one {# Datei zurückgesetzt} other {# Dateien zurückgesetzt}}", "rolledBackFiles": "{count, plural, one {# Datei zurückgesetzt} other {# Dateien zurückgesetzt}}",
"savedAsCopy": "Als Kopie gespeichert", "savedAsCopy": "Als Kopie gespeichert",
"saveCopyFailed": "Speichern als Kopie fehlgeschlagen", "saveCopyFailed": "Speichern als Kopie fehlgeschlagen",
"watchStartFailed": "Dateiwatch konnte nicht gestartet werden" "watchStartFailed": "Dateiwatch konnte nicht gestartet werden",
"createFailed": "Erstellen fehlgeschlagen"
},
"createDialog": {
"newFile": "Neue Datei",
"newDirectory": "Neues Verzeichnis",
"description": "Geben Sie einen Namen für das neue {kind} ein.",
"placeholderFile": "file-name.ext",
"placeholderDirectory": "folder-name"
}, },
"renameDialog": { "renameDialog": {
"renameDirectory": "Verzeichnis umbenennen", "renameDirectory": "Verzeichnis umbenennen",

View File

@@ -967,6 +967,9 @@
"attachToCurrentSession": "Attach to current session", "attachToCurrentSession": "Attach to current session",
"compareWithBranch": "Compare with branch...", "compareWithBranch": "Compare with branch...",
"reloadFromDisk": "Reload from disk", "reloadFromDisk": "Reload from disk",
"new": "New",
"newFile": "File",
"newDirectory": "Directory",
"openIn": "Open in", "openIn": "Open in",
"openInTerminal": "Open in terminal", "openInTerminal": "Open in terminal",
"actions": { "actions": {
@@ -996,7 +999,15 @@
"rolledBackFiles": "Rolled back {count, plural, one {# file} other {# files}}", "rolledBackFiles": "Rolled back {count, plural, one {# file} other {# files}}",
"savedAsCopy": "Saved as a copy", "savedAsCopy": "Saved as a copy",
"saveCopyFailed": "Failed to save as copy", "saveCopyFailed": "Failed to save as copy",
"watchStartFailed": "Failed to start file watch" "watchStartFailed": "Failed to start file watch",
"createFailed": "Failed to create"
},
"createDialog": {
"newFile": "New file",
"newDirectory": "New directory",
"description": "Enter a name for the new {kind}.",
"placeholderFile": "file-name.ext",
"placeholderDirectory": "folder-name"
}, },
"renameDialog": { "renameDialog": {
"renameDirectory": "Rename directory", "renameDirectory": "Rename directory",

View File

@@ -967,6 +967,9 @@
"attachToCurrentSession": "Adjuntar a la sesión actual", "attachToCurrentSession": "Adjuntar a la sesión actual",
"compareWithBranch": "Comparar con rama...", "compareWithBranch": "Comparar con rama...",
"reloadFromDisk": "Recargar desde disco", "reloadFromDisk": "Recargar desde disco",
"new": "Nuevo",
"newFile": "Archivo",
"newDirectory": "Directorio",
"openIn": "Abrir en", "openIn": "Abrir en",
"openInTerminal": "Abrir en terminal", "openInTerminal": "Abrir en terminal",
"actions": { "actions": {
@@ -996,7 +999,15 @@
"rolledBackFiles": "{count, plural, one {Se revirtió # archivo} other {Se revirtieron # archivos}}", "rolledBackFiles": "{count, plural, one {Se revirtió # archivo} other {Se revirtieron # archivos}}",
"savedAsCopy": "Guardado como copia", "savedAsCopy": "Guardado como copia",
"saveCopyFailed": "No se pudo guardar como copia", "saveCopyFailed": "No se pudo guardar como copia",
"watchStartFailed": "No se pudo iniciar la vigilancia de archivos" "watchStartFailed": "No se pudo iniciar la vigilancia de archivos",
"createFailed": "Error al crear"
},
"createDialog": {
"newFile": "Nuevo archivo",
"newDirectory": "Nuevo directorio",
"description": "Ingrese un nombre para el nuevo {kind}.",
"placeholderFile": "file-name.ext",
"placeholderDirectory": "folder-name"
}, },
"renameDialog": { "renameDialog": {
"renameDirectory": "Renombrar directorio", "renameDirectory": "Renombrar directorio",

View File

@@ -967,6 +967,9 @@
"attachToCurrentSession": "Attacher à la session actuelle", "attachToCurrentSession": "Attacher à la session actuelle",
"compareWithBranch": "Comparer avec la branche...", "compareWithBranch": "Comparer avec la branche...",
"reloadFromDisk": "Recharger depuis le disque", "reloadFromDisk": "Recharger depuis le disque",
"new": "Nouveau",
"newFile": "Fichier",
"newDirectory": "Répertoire",
"openIn": "Ouvrir dans", "openIn": "Ouvrir dans",
"openInTerminal": "Ouvrir dans le terminal", "openInTerminal": "Ouvrir dans le terminal",
"actions": { "actions": {
@@ -996,7 +999,15 @@
"rolledBackFiles": "{count, plural, one {# fichier annulé} other {# fichiers annulés}}", "rolledBackFiles": "{count, plural, one {# fichier annulé} other {# fichiers annulés}}",
"savedAsCopy": "Enregistré en copie", "savedAsCopy": "Enregistré en copie",
"saveCopyFailed": "Échec de l'enregistrement en copie", "saveCopyFailed": "Échec de l'enregistrement en copie",
"watchStartFailed": "Échec du démarrage de la surveillance de fichiers" "watchStartFailed": "Échec du démarrage de la surveillance de fichiers",
"createFailed": "Échec de la création"
},
"createDialog": {
"newFile": "Nouveau fichier",
"newDirectory": "Nouveau répertoire",
"description": "Entrez un nom pour le nouveau {kind}.",
"placeholderFile": "file-name.ext",
"placeholderDirectory": "folder-name"
}, },
"renameDialog": { "renameDialog": {
"renameDirectory": "Renommer le dossier", "renameDirectory": "Renommer le dossier",

View File

@@ -967,6 +967,9 @@
"attachToCurrentSession": "現在のセッションに添付", "attachToCurrentSession": "現在のセッションに添付",
"compareWithBranch": "ブランチと比較...", "compareWithBranch": "ブランチと比較...",
"reloadFromDisk": "ディスクから再読み込み", "reloadFromDisk": "ディスクから再読み込み",
"new": "新規作成",
"newFile": "ファイル",
"newDirectory": "ディレクトリ",
"openIn": "で開く", "openIn": "で開く",
"openInTerminal": "ターミナルで開く", "openInTerminal": "ターミナルで開く",
"actions": { "actions": {
@@ -996,7 +999,15 @@
"rolledBackFiles": "{count, plural, one {# 件のファイルをロールバックしました} other {# 件のファイルをロールバックしました}}", "rolledBackFiles": "{count, plural, one {# 件のファイルをロールバックしました} other {# 件のファイルをロールバックしました}}",
"savedAsCopy": "コピーとして保存しました", "savedAsCopy": "コピーとして保存しました",
"saveCopyFailed": "コピーとして保存できませんでした", "saveCopyFailed": "コピーとして保存できませんでした",
"watchStartFailed": "ファイル監視の開始に失敗しました" "watchStartFailed": "ファイル監視の開始に失敗しました",
"createFailed": "作成に失敗しました"
},
"createDialog": {
"newFile": "新規ファイル",
"newDirectory": "新規ディレクトリ",
"description": "新しい{kind}の名前を入力してください。",
"placeholderFile": "file-name.ext",
"placeholderDirectory": "folder-name"
}, },
"renameDialog": { "renameDialog": {
"renameDirectory": "ディレクトリ名を変更", "renameDirectory": "ディレクトリ名を変更",

View File

@@ -967,6 +967,9 @@
"attachToCurrentSession": "현재 세션에 연결", "attachToCurrentSession": "현재 세션에 연결",
"compareWithBranch": "브랜치와 비교...", "compareWithBranch": "브랜치와 비교...",
"reloadFromDisk": "디스크에서 다시 불러오기", "reloadFromDisk": "디스크에서 다시 불러오기",
"new": "새로 만들기",
"newFile": "파일",
"newDirectory": "디렉터리",
"openIn": "열기", "openIn": "열기",
"openInTerminal": "터미널에서 열기", "openInTerminal": "터미널에서 열기",
"actions": { "actions": {
@@ -996,7 +999,15 @@
"rolledBackFiles": "{count, plural, one {#개 파일을 롤백했습니다} other {#개 파일을 롤백했습니다}}", "rolledBackFiles": "{count, plural, one {#개 파일을 롤백했습니다} other {#개 파일을 롤백했습니다}}",
"savedAsCopy": "사본으로 저장했습니다", "savedAsCopy": "사본으로 저장했습니다",
"saveCopyFailed": "사본으로 저장하지 못했습니다", "saveCopyFailed": "사본으로 저장하지 못했습니다",
"watchStartFailed": "파일 감시 시작에 실패했습니다" "watchStartFailed": "파일 감시 시작에 실패했습니다",
"createFailed": "생성 실패"
},
"createDialog": {
"newFile": "새 파일",
"newDirectory": "새 디렉터리",
"description": "새 {kind}의 이름을 입력하세요.",
"placeholderFile": "file-name.ext",
"placeholderDirectory": "folder-name"
}, },
"renameDialog": { "renameDialog": {
"renameDirectory": "디렉터리 이름 변경", "renameDirectory": "디렉터리 이름 변경",

View File

@@ -967,6 +967,9 @@
"attachToCurrentSession": "Anexar à sessão atual", "attachToCurrentSession": "Anexar à sessão atual",
"compareWithBranch": "Comparar com branch...", "compareWithBranch": "Comparar com branch...",
"reloadFromDisk": "Recarregar do disco", "reloadFromDisk": "Recarregar do disco",
"new": "Novo",
"newFile": "Arquivo",
"newDirectory": "Diretório",
"openIn": "Abrir em", "openIn": "Abrir em",
"openInTerminal": "Abrir no terminal", "openInTerminal": "Abrir no terminal",
"actions": { "actions": {
@@ -996,7 +999,15 @@
"rolledBackFiles": "{count, plural, one {# arquivo revertido} other {# arquivos revertidos}}", "rolledBackFiles": "{count, plural, one {# arquivo revertido} other {# arquivos revertidos}}",
"savedAsCopy": "Salvo como cópia", "savedAsCopy": "Salvo como cópia",
"saveCopyFailed": "Falha ao salvar como cópia", "saveCopyFailed": "Falha ao salvar como cópia",
"watchStartFailed": "Falha ao iniciar monitoramento de arquivos" "watchStartFailed": "Falha ao iniciar monitoramento de arquivos",
"createFailed": "Falha ao criar"
},
"createDialog": {
"newFile": "Novo arquivo",
"newDirectory": "Novo diretório",
"description": "Digite um nome para o novo {kind}.",
"placeholderFile": "file-name.ext",
"placeholderDirectory": "folder-name"
}, },
"renameDialog": { "renameDialog": {
"renameDirectory": "Renomear diretório", "renameDirectory": "Renomear diretório",

View File

@@ -967,6 +967,9 @@
"attachToCurrentSession": "附加到当前会话", "attachToCurrentSession": "附加到当前会话",
"compareWithBranch": "与分支比较...", "compareWithBranch": "与分支比较...",
"reloadFromDisk": "从磁盘重新加载", "reloadFromDisk": "从磁盘重新加载",
"new": "新建",
"newFile": "文件",
"newDirectory": "目录",
"openIn": "打开于", "openIn": "打开于",
"openInTerminal": "在终端打开", "openInTerminal": "在终端打开",
"actions": { "actions": {
@@ -996,7 +999,15 @@
"rolledBackFiles": "已回滚 {count} 个文件", "rolledBackFiles": "已回滚 {count} 个文件",
"savedAsCopy": "已另存为副本", "savedAsCopy": "已另存为副本",
"saveCopyFailed": "另存为副本失败", "saveCopyFailed": "另存为副本失败",
"watchStartFailed": "文件监听启动失败" "watchStartFailed": "文件监听启动失败",
"createFailed": "创建失败"
},
"createDialog": {
"newFile": "新建文件",
"newDirectory": "新建目录",
"description": "输入新{kind}的名称。",
"placeholderFile": "文件名.ext",
"placeholderDirectory": "文件夹名"
}, },
"renameDialog": { "renameDialog": {
"renameDirectory": "重命名目录", "renameDirectory": "重命名目录",

View File

@@ -967,6 +967,9 @@
"attachToCurrentSession": "附加到目前會話", "attachToCurrentSession": "附加到目前會話",
"compareWithBranch": "與分支比較...", "compareWithBranch": "與分支比較...",
"reloadFromDisk": "從磁碟重新載入", "reloadFromDisk": "從磁碟重新載入",
"new": "新建",
"newFile": "檔案",
"newDirectory": "目錄",
"openIn": "開啟於", "openIn": "開啟於",
"openInTerminal": "在終端開啟", "openInTerminal": "在終端開啟",
"actions": { "actions": {
@@ -996,7 +999,15 @@
"rolledBackFiles": "已回滾 {count} 個檔案", "rolledBackFiles": "已回滾 {count} 個檔案",
"savedAsCopy": "已另存為副本", "savedAsCopy": "已另存為副本",
"saveCopyFailed": "另存為副本失敗", "saveCopyFailed": "另存為副本失敗",
"watchStartFailed": "檔案監聽啟動失敗" "watchStartFailed": "檔案監聽啟動失敗",
"createFailed": "建立失敗"
},
"createDialog": {
"newFile": "新建檔案",
"newDirectory": "新建目錄",
"description": "輸入新{kind}的名稱。",
"placeholderFile": "file-name.ext",
"placeholderDirectory": "folder-name"
}, },
"renameDialog": { "renameDialog": {
"renameDirectory": "重新命名目錄", "renameDirectory": "重新命名目錄",

View File

@@ -850,6 +850,15 @@ export async function deleteFileTreeEntry(
return invoke("delete_file_tree_entry", { rootPath, path }) return invoke("delete_file_tree_entry", { rootPath, path })
} }
export async function createFileTreeEntry(
rootPath: string,
path: string,
name: string,
kind: "file" | "dir"
): Promise<string> {
return invoke("create_file_tree_entry", { rootPath, path, name, kind })
}
export async function gitLog( export async function gitLog(
path: string, path: string,
limit?: number, limit?: number,