From 960f6392221dc336522b1bd5f67cc1120fe3794c Mon Sep 17 00:00:00 2001 From: Decrabbit <99632363+Decrabbityyy@users.noreply.github.com> Date: Fri, 28 Aug 2026 16:59:13 +0800 Subject: [PATCH 1/3] fix(console): improve log opening and titlebar controls --- src-tauri/src/backend.rs | 27 ++++++++++++++++--- .../src/components/WindowTitlebar.tsx | 9 ++++--- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/backend.rs b/src-tauri/src/backend.rs index 5886def..65a042e 100644 --- a/src-tauri/src/backend.rs +++ b/src-tauri/src/backend.rs @@ -4422,10 +4422,29 @@ fn get_mod_cache_status() -> Result { } #[tauri::command] -fn show_log_window() { - if let Err(error) = open::that(crate::logging::path()) { - crate::logging::error(format_args!("Failed to open CeleMod log: {error}")); - } +fn show_log_window() -> Result<(), String> { + let path = crate::logging::path(); + #[cfg(windows)] + let result = match open::that(path) { + Ok(()) => Ok(()), + Err(default_error) => std::process::Command::new("notepad.exe") + .arg(path) + .spawn() + .map(|_| ()) + .map_err(|notepad_error| { + format!( + "default opener failed: {default_error}; Notepad fallback failed: {notepad_error}" + ) + }), + }; + #[cfg(not(windows))] + let result = open::that(path).map_err(|error| error.to_string()); + + result.map_err(|error| { + let message = format!("Failed to open CeleMod log {}: {error}", path.display()); + crate::logging::error(format_args!("{message}")); + message + }) } #[tauri::command] diff --git a/src/celemod-ui/src/components/WindowTitlebar.tsx b/src/celemod-ui/src/components/WindowTitlebar.tsx index 4d8bf3d..1294e0e 100644 --- a/src/celemod-ui/src/components/WindowTitlebar.tsx +++ b/src/celemod-ui/src/components/WindowTitlebar.tsx @@ -59,15 +59,16 @@ export function WindowTitlebar() { + {gamePath && newKeyboardInputEnabled === true ? ( diff --git a/src/celemod-ui/src/routes/Settings.tsx b/src/celemod-ui/src/routes/Settings.tsx index 8058fd4..91eefb6 100644 --- a/src/celemod-ui/src/routes/Settings.tsx +++ b/src/celemod-ui/src/routes/Settings.tsx @@ -11,6 +11,7 @@ import { useMirror, useUseMultiThread, } from "../states"; +import { clearInMemoryModCatalog, loadModCatalog } from "../api/modCatalog"; import { callRemote } from "../utils"; import "./Settings.scss"; @@ -166,6 +167,7 @@ export const Settings = () => { const [useMultiThread, setUseMultiThread] = useUseMultiThread(); const [downloadAdvanced, setDownloadAdvanced] = useState(false); const [cacheStatus, setCacheStatus] = useState(null); + const [cacheBusy, setCacheBusy] = useState(false); const [cacheError, setCacheError] = useState(""); const downloadDefaultEnabled = useAppStore( @@ -249,13 +251,17 @@ export const Settings = () => { return "advanced"; }, [downloadDefaultEnabled, downloadTypeDefaults]); - const refreshCacheStatus = () => { - void callRemote("get_mod_cache_status") - .then(setCacheStatus) - .catch((error) => setCacheError(String(error))); + const refreshCacheStatus = async () => { + try { + setCacheStatus(await callRemote("get_mod_cache_status")); + } catch (error) { + setCacheError(String(error)); + } }; - useEffect(refreshCacheStatus, []); + useEffect(() => { + void refreshCacheStatus(); + }, []); const formatCacheTime = (timestamp: number) => { if (!timestamp) return _i18n.t("未知"); @@ -510,6 +516,30 @@ export const Settings = () => { +
+ + {cacheStatus?.path || _i18n.t("缓存尚未建立")} + + +
{cacheError &&
{cacheError}
} diff --git a/src/celemod-ui/src/vite-env.d.ts b/src/celemod-ui/src/vite-env.d.ts index dfcd921..cd944ae 100644 --- a/src/celemod-ui/src/vite-env.d.ts +++ b/src/celemod-ui/src/vite-env.d.ts @@ -1,7 +1,7 @@ /// interface Window { - _checkUpdate(): Promise; + _checkUpdate(forceRefresh?: boolean): Promise; env: any; isMaximizable: boolean; storage: any; From 34e70102fc1567d9048205e06e08861fd58d26b9 Mon Sep 17 00:00:00 2001 From: Decrabbit <99632363+Decrabbityyy@users.noreply.github.com> Date: Thu, 3 Sep 2026 18:09:28 +0800 Subject: [PATCH 3/3] feat(manage): add duplicate Mod filter --- src/celemod-ui/locales/de-DE.json | 1 + src/celemod-ui/locales/en-US.json | 1 + src/celemod-ui/locales/fr-FR.json | 1 + src/celemod-ui/locales/pt-BR.json | 1 + src/celemod-ui/locales/ru-RU.json | 1 + src/celemod-ui/locales/zh-CN.json | 1 + src/celemod-ui/src/manageDependencies.test.ts | 35 +++++++++++++++++-- src/celemod-ui/src/manageDisplayNames.test.ts | 2 ++ src/celemod-ui/src/routes/Manage.tsx | 26 ++++++++++++++ src/celemod-ui/src/stores/manage.ts | 9 +++++ 10 files changed, 76 insertions(+), 2 deletions(-) diff --git a/src/celemod-ui/locales/de-DE.json b/src/celemod-ui/locales/de-DE.json index 6286fb8..8492a44 100644 --- a/src/celemod-ui/locales/de-DE.json +++ b/src/celemod-ui/locales/de-DE.json @@ -361,6 +361,7 @@ "重置": "重置", "启用状态": "启用状态", "依赖状态": "依赖状态", + "仅显示重复 Mod": "Nur doppelte Mods anzeigen", "正常": "正常", "有问题": "有问题", "缺失依赖": "缺失依赖", diff --git a/src/celemod-ui/locales/en-US.json b/src/celemod-ui/locales/en-US.json index dd26b33..122d70c 100644 --- a/src/celemod-ui/locales/en-US.json +++ b/src/celemod-ui/locales/en-US.json @@ -300,6 +300,7 @@ "有问题": "Something went wrong", "缺失依赖": "Missing dependencies", "仅显示可更新": "Show updatable only", + "仅显示重复 Mod": "Show duplicate Mods only", "显示设置中隐藏的类型": "Types hidden in display settings", "操作": "Actions", "展开全部": "Expand all", diff --git a/src/celemod-ui/locales/fr-FR.json b/src/celemod-ui/locales/fr-FR.json index 9ed6bbc..ba5f51d 100644 --- a/src/celemod-ui/locales/fr-FR.json +++ b/src/celemod-ui/locales/fr-FR.json @@ -361,6 +361,7 @@ "重置": "重置", "启用状态": "启用状态", "依赖状态": "依赖状态", + "仅显示重复 Mod": "Afficher uniquement les mods en double", "正常": "正常", "有问题": "有问题", "缺失依赖": "缺失依赖", diff --git a/src/celemod-ui/locales/pt-BR.json b/src/celemod-ui/locales/pt-BR.json index 5fae21c..17d2dc0 100644 --- a/src/celemod-ui/locales/pt-BR.json +++ b/src/celemod-ui/locales/pt-BR.json @@ -292,6 +292,7 @@ "有问题": "Tem problema", "缺失依赖": "Dependência ausente", "仅显示可更新": "Mostrar apenas atualizáveis", + "仅显示重复 Mod": "Mostrar apenas mods duplicados", "显示设置中隐藏的类型": "Tipos ocultos nas configurações de exibição", "操作": "Ações", "展开全部": "Expandir tudo", diff --git a/src/celemod-ui/locales/ru-RU.json b/src/celemod-ui/locales/ru-RU.json index 945b43d..ea30775 100644 --- a/src/celemod-ui/locales/ru-RU.json +++ b/src/celemod-ui/locales/ru-RU.json @@ -292,6 +292,7 @@ "有问题": "Есть проблема", "缺失依赖": "Отсутствующая зависимость", "仅显示可更新": "Показывать только обновляемые", + "仅显示重复 Mod": "Показывать только дублирующиеся моды", "显示设置中隐藏的类型": "Типы, скрытые в настройках", "操作": "Действия", "展开全部": "Развернуть все", diff --git a/src/celemod-ui/locales/zh-CN.json b/src/celemod-ui/locales/zh-CN.json index 25f276e..798c2ea 100644 --- a/src/celemod-ui/locales/zh-CN.json +++ b/src/celemod-ui/locales/zh-CN.json @@ -300,6 +300,7 @@ "有问题": "有问题", "缺失依赖": "缺失依赖", "仅显示可更新": "仅显示可更新", + "仅显示重复 Mod": "仅显示重复 Mod", "显示设置中隐藏的类型": "显示设置中隐藏的类型", "操作": "操作", "展开全部": "展开全部", diff --git a/src/celemod-ui/src/manageDependencies.test.ts b/src/celemod-ui/src/manageDependencies.test.ts index 240e294..2423d0c 100644 --- a/src/celemod-ui/src/manageDependencies.test.ts +++ b/src/celemod-ui/src/manageDependencies.test.ts @@ -4,6 +4,7 @@ import { collectOrphanDependencyNames, collectSwitchNames, normalizeManageDependencies, + selectVisibleRootNames, type ManageNode, } from "./stores/manage"; @@ -94,8 +95,6 @@ test("keeps a categorized dependency excluded by the orphan type setting", () => ["Root"], ); }); - - test("does not treat optional dependencies as orphan deletion candidates", () => { const nodes = { Root: node("Root", { @@ -135,3 +134,35 @@ test("collects transitive required orphan dependencies", () => { ["RequiredMod", "NestedMod"], ); }); + +test("shows only Mods with duplicate installed files", () => { + const nodes = { + Duplicate: node("Duplicate", { + duplicateFiles: [ + { file: "Duplicate.zip", version: "1.0.0", size: 1, modifiedAt: 1, isDirectory: false }, + { file: "Duplicate-old.zip", version: "0.9.0", size: 1, modifiedAt: 0, isDirectory: false }, + ], + }), + Single: node("Single"), + }; + + assert.deepEqual( + selectVisibleRootNames({ + nodes, + filters: { + query: "", + enabled: "all", + health: "all", + types: [], + updateOnly: false, + duplicateOnly: true, + showHiddenTypes: false, + }, + rootOnly: false, + includeOptional: false, + hiddenTypes: [], + updateNames: new Set(), + }), + ["Duplicate"], + ); +}); diff --git a/src/celemod-ui/src/manageDisplayNames.test.ts b/src/celemod-ui/src/manageDisplayNames.test.ts index 901c6a9..12799b6 100644 --- a/src/celemod-ui/src/manageDisplayNames.test.ts +++ b/src/celemod-ui/src/manageDisplayNames.test.ts @@ -73,6 +73,7 @@ test("sorts the management list by the displayed primary name", () => { health: "all", types: [], updateOnly: false, + duplicateOnly: false, showHiddenTypes: false, }, rootOnly: false, @@ -116,6 +117,7 @@ test("filters management mods by their internal name", () => { health: "all", types: [], updateOnly: false, + duplicateOnly: false, showHiddenTypes: false, }, rootOnly: false, diff --git a/src/celemod-ui/src/routes/Manage.tsx b/src/celemod-ui/src/routes/Manage.tsx index 0e4067b..90ad8e5 100644 --- a/src/celemod-ui/src/routes/Manage.tsx +++ b/src/celemod-ui/src/routes/Manage.tsx @@ -1134,6 +1134,10 @@ export const Manage = () => { const setEnabledFilter = useManageStore((state) => state.setEnabledFilter); const setHealthFilter = useManageStore((state) => state.setHealthFilter); const toggleType = useManageStore((state) => state.toggleType); + const setUpdateOnly = useManageStore((state) => state.setUpdateOnly); + const setDuplicateOnly = useManageStore( + (state) => state.setDuplicateOnly, + ); const setShowHiddenTypes = useManageStore( (state) => state.setShowHiddenTypes, ); @@ -1297,6 +1301,7 @@ export const Manage = () => { health: "all" as const, types: [], updateOnly: false, + duplicateOnly: false, showHiddenTypes: true, }), [filters], @@ -2021,6 +2026,7 @@ export const Manage = () => { Number(filters.enabled !== "all") + Number(filters.health !== "all") + Number(filters.updateOnly) + + Number(filters.duplicateOnly) + Number(filters.showHiddenTypes) + Number(rootOnly) + Number(fullTree); @@ -2112,6 +2118,26 @@ export const Manage = () => {
+ +