diff --git a/.gitignore b/.gitignore index 7c2a053..2b71ff9 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.exe *.exe~ *.dll +*.syso *.so *.dylib diff --git a/CLAUDE.md b/CLAUDE.md index e61d96d..b2f8b0f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,7 +10,7 @@ model.go 顶层 model 定义,page 枚举,Init/Update/View 路由 pages.go 4 个页面的 update/view + resetExecState + describeAction scan.go 扫描 /*/.minecraft/versions/* 目录 api.go HTTP 客户端 + Action/StepResponse 类型定义 -actions.go action 执行引擎(download/unzip/delete/copy/move/backup) +actions.go action 执行引擎(download/unzip/delete/copy/move/new/backup) items.go list.Item 实现:versionItem, menuItem, mirrorItem styles.go lipgloss 样式常量 backend/ Python FastAPI 后端(独立进程,端口 3131) @@ -92,7 +92,8 @@ pageVersionSelect ──Enter──▶ pageMainMenu ──"输入数字码"─ {"type": "add", "path": "相对路径", "unzip": false, "url": "https://...", "mirrors": ["https://..."]}, {"type": "delete", "path": "相对路径"}, {"type": "copy", "path": "源相对路径", "new_path": "目标相对路径"}, - {"type": "move", "path": "源相对路径", "new_path": "目标相对路径"} + {"type": "move", "path": "源相对路径", "new_path": "目标相对路径"}, + {"type": "new", "path": "相对路径", "is_dir": false} ] } ``` @@ -109,6 +110,7 @@ pageVersionSelect ──Enter──▶ pageMainMenu ──"输入数字码"─ | `delete` | 删除 path | 删前备份 | | `copy` | 复制 path→new_path | new_path 已存在则备份 | | `move` | os.Rename,失败则 copy+delete | new_path 已存在则备份 | +| `new` | is_dir=true 创建目录,否则创建空文件(父目录自动创建)。目标已存在则跳过 | 无 | ### 备份 diff --git a/Makefile b/Makefile index 1c2cf5d..7fbb184 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,8 @@ -.PHONY: all backend +.PHONY: all backend manager -all: dist/amt.exe - ./dist/amt.exe - -dist/amt.exe: $(wildcard *.go) go.mod go.sum - @mkdir -p dist - go build -o dist/amt.exe . +all: $(wildcard *.go) go.mod go.sum + @if not exist dist mkdir dist + go build -o dist/amt.exe -ldflags "-w -s" backend: cd backend && uv run main.py diff --git a/actions.go b/actions.go index 1c30e57..f70e703 100644 --- a/actions.go +++ b/actions.go @@ -64,6 +64,28 @@ func executeAction(versionDir string, action Action, index int, backupDir string } return actionCompleteMsg{index: index} } + case "new": + return func() tea.Msg { + absPath := filepath.Join(versionDir, action.Path) + if _, err := os.Stat(absPath); err == nil { + return actionCompleteMsg{index: index} + } + if action.IsDir { + if err := os.MkdirAll(absPath, 0o755); err != nil { + return actionErrorMsg{index: index, err: fmt.Errorf("create dir failed: %w", err)} + } + } else { + if err := os.MkdirAll(filepath.Dir(absPath), 0o755); err != nil { + return actionErrorMsg{index: index, err: fmt.Errorf("mkdir failed: %w", err)} + } + f, err := os.Create(absPath) + if err != nil { + return actionErrorMsg{index: index, err: fmt.Errorf("create file failed: %w", err)} + } + f.Close() + } + return actionCompleteMsg{index: index} + } default: return func() tea.Msg { return actionErrorMsg{index: index, err: fmt.Errorf("unknown action type: %s", action.Type)} diff --git a/api.go b/api.go index 11a7110..913eee7 100644 --- a/api.go +++ b/api.go @@ -22,6 +22,7 @@ type Action struct { Unzip bool `json:"unzip,omitempty"` URL string `json:"url,omitempty"` Mirrors []string `json:"mirrors,omitempty"` + IsDir bool `json:"is_dir,omitempty"` } type actionsReceivedMsg struct{ actions []Action } diff --git a/backend/data.db b/backend/data.db index 0606bb6..3a728be 100644 Binary files a/backend/data.db and b/backend/data.db differ diff --git a/pages.go b/pages.go index fd0271a..5267ca0 100644 --- a/pages.go +++ b/pages.go @@ -326,6 +326,11 @@ func describeAction(a Action, idx, total int) string { return subtleStyle.Render(fmt.Sprintf("%s 复制 %s -> %s ...", prefix, a.Path, a.NewPath)) case "move": return subtleStyle.Render(fmt.Sprintf("%s 移动 %s -> %s ...", prefix, a.Path, a.NewPath)) + case "new": + if a.IsDir { + return subtleStyle.Render(fmt.Sprintf("%s 新建文件夹 %s ...", prefix, a.Path)) + } + return subtleStyle.Render(fmt.Sprintf("%s 新建文件 %s ...", prefix, a.Path)) default: return subtleStyle.Render(fmt.Sprintf("%s %s %s ...", prefix, a.Type, a.Path)) } diff --git a/winres/favicon.png b/winres/favicon.png new file mode 100644 index 0000000..7ee4700 Binary files /dev/null and b/winres/favicon.png differ diff --git a/winres/winres.json b/winres/winres.json new file mode 100644 index 0000000..7a33264 --- /dev/null +++ b/winres/winres.json @@ -0,0 +1,58 @@ +{ + "RT_GROUP_ICON": { + "APP": { + "0000": ["favicon.png"] + } + }, + "RT_MANIFEST": { + "#1": { + "2052": { + "identity": { + "name": "", + "version": "" + }, + "description": "ARinera Minecraft Tool", + "minimum-os": "win7", + "execution-level": "highest", + "ui-access": false, + "auto-elevate": false, + "dpi-awareness": "system", + "disable-theming": false, + "disable-window-filtering": false, + "high-resolution-scrolling-aware": false, + "ultra-high-resolution-scrolling-aware": false, + "long-path-aware": false, + "printer-driver-isolation": false, + "gdi-scaling": false, + "segment-heap": false, + "use-common-controls-v6": false + } + } + }, + "RT_VERSION": { + "#1": { + "0000": { + "fixed": { + "file_version": "1.0.0", + "product_version": "1.0.0" + }, + "info": { + "2052": { + "Comments": "", + "CompanyName": "", + "FileDescription": "", + "FileVersion": "", + "InternalName": "", + "LegalCopyright": "", + "LegalTrademarks": "", + "OriginalFilename": "", + "PrivateBuild": "", + "ProductName": "", + "ProductVersion": "", + "SpecialBuild": "" + } + } + } + } + } +}