Skip to content

Commit ca67114

Browse files
committed
feat: add CurseForge support during management refactor (WIP)
1 parent 8e5f2c3 commit ca67114

33 files changed

+3700
-390
lines changed

frontend/package-lock.json

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"dependencies": {
1313
"@heroui/react": "2.7.2",
1414
"@react-stately/utils": "^3.10.5",
15+
"@wailsio/runtime": "latest",
1516
"framer-motion": "^11.3.22",
1617
"i18next": "^23.12.7",
1718
"i18next-browser-languagedetector": "^8.0.0",
@@ -23,16 +24,16 @@
2324
"react-dom": "^18.3.1",
2425
"react-i18next": "^15.0.1",
2526
"react-icons": "^5.2.1",
27+
"react-markdown": "^9.0.0",
2628
"react-router-dom": "^6.26.0",
2729
"react-sage": "^0.3.16",
30+
"remark-gfm": "^4.0.0",
2831
"tailwind-merge": "^3.0.2",
29-
"uuid": "^10.0.0",
30-
"@wailsio/runtime": "latest",
31-
"react-markdown": "^9.0.0",
32-
"remark-gfm": "^4.0.0"
32+
"uuid": "^10.0.0"
3333
},
3434
"devDependencies": {
3535
"@iconify/react": "^5.0.2",
36+
"@tailwindcss/typography": "^0.5.19",
3637
"@types/react": "^18.0.17",
3738
"@types/react-dom": "^18.0.6",
3839
"@types/uuid": "^10.0.0",

frontend/src/App.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ import ReactMarkdown from "react-markdown";
5252
import remarkGfm from "remark-gfm";
5353
import AboutPage from "./pages/AboutPage";
5454
import OnboardingPage from "./pages/OnboardingPage";
55+
import CurseForgePage from "./pages/CurseForgePage";
56+
import CurseForgeModPage from "./pages/CurseForgeModPage";
5557

5658
function App() {
5759
const [splashVisible, setSplashVisible] = useState(true);
@@ -543,6 +545,8 @@ function App() {
543545
element={<VersionSettingsPage />}
544546
/>
545547
<Route path="/mods" element={<ModsPage />} />
548+
<Route path="/curseforge" element={<CurseForgePage />} />
549+
<Route path="/curseforge/mod/:id" element={<CurseForgeModPage />} />
546550
<Route path="/updating" element={<UpdatingPage />} />
547551
<Route path="/onboarding" element={<OnboardingPage />} />
548552
<Route path="/filemanager" element={<FileManagerPage />} />

frontend/src/assets/locales/en_US.json

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -800,5 +800,29 @@
800800
"cancel": "Cancel",
801801
"save": "Save and Finish"
802802
}
803+
},
804+
"curseforgecard": {
805+
"title": "CurseForge",
806+
"content": "Browse and download mods",
807+
"browse": "Browse CurseForge"
808+
},
809+
"curseforge": {
810+
"title": "CurseForge",
811+
"search_placeholder": "Search mods...",
812+
"search": "Search",
813+
"minecraft_version": "Minecraft Version",
814+
"select_version": "Select version",
815+
"all_versions": "All Versions",
816+
"mod_loader": "Mod Loader",
817+
"select_loader": "Select loader",
818+
"class": "Type",
819+
"select_class": "Select type",
820+
"all_classes": "All Types",
821+
"category": "Category",
822+
"select_category": "Select category",
823+
"sort_by": "Sort By",
824+
"select_sort": "Select sort",
825+
"no_results": "No results found",
826+
"view_details": "View Details"
803827
}
804-
}
828+
}

frontend/src/assets/locales/ru_RU.json

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -799,5 +799,29 @@
799799
"cancel": "Отмена",
800800
"save": "Сохранить и завершить"
801801
}
802+
},
803+
"curseforgecard": {
804+
"title": "CurseForge",
805+
"content": "Просмотр и загрузка модов",
806+
"browse": "Просмотреть CurseForge"
807+
},
808+
"curseforge": {
809+
"title": "CurseForge",
810+
"search_placeholder": "Поиск модов...",
811+
"search": "Поиск",
812+
"minecraft_version": "Версия Minecraft",
813+
"select_version": "Выберите версию",
814+
"all_versions": "Все версии",
815+
"mod_loader": "Загрузчик модов",
816+
"select_loader": "Выберите загрузчик",
817+
"class": "Тип",
818+
"select_class": "Выберите тип",
819+
"all_classes": "Все типы",
820+
"category": "Категория",
821+
"select_category": "Выберите категорию",
822+
"sort_by": "Сортировка",
823+
"select_sort": "Выберите сортировку",
824+
"no_results": "Результаты не найдены",
825+
"view_details": "Подробнее"
802826
}
803-
}
827+
}

frontend/src/assets/locales/zh_CN.json

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -800,5 +800,29 @@
800800
"cancel": "取消",
801801
"save": "保存并完成"
802802
}
803+
},
804+
"curseforgecard": {
805+
"title": "CurseForge",
806+
"content": "浏览和下载模组",
807+
"browse": "浏览CurseForge"
808+
},
809+
"curseforge": {
810+
"title": "CurseForge",
811+
"search_placeholder": "搜索模组...",
812+
"search": "搜索",
813+
"minecraft_version": "Minecraft版本",
814+
"select_version": "选择版本",
815+
"all_versions": "全部版本",
816+
"mod_loader": "模组加载器",
817+
"select_loader": "选择加载器",
818+
"class": "类型",
819+
"select_class": "选择类型",
820+
"all_classes": "全部类型",
821+
"category": "分类",
822+
"select_category": "选择分类",
823+
"sort_by": "排序",
824+
"select_sort": "选择排序",
825+
"no_results": "未找到结果",
826+
"view_details": "查看详情"
803827
}
804-
}
828+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React from "react";
2+
import { Tooltip, Card, CardBody, Button } from "@heroui/react";
3+
import { useTranslation } from "react-i18next";
4+
import { useNavigate } from "react-router-dom";
5+
import { LuDownload } from "react-icons/lu";
6+
7+
export const CurseForgeCard = () => {
8+
const { t } = useTranslation();
9+
const navigate = useNavigate();
10+
11+
return (
12+
<Card className="rounded-2xl shadow-md h-full min-h-[160px] bg-white/70 dark:bg-black/30 backdrop-blur-md border border-white/30">
13+
<CardBody className="relative p-4 sm:p-5 flex flex-col gap-3 text-left">
14+
<div className="flex items-center gap-2">
15+
<span className="font-bold text-lg">{t("curseforgecard.title")}</span>
16+
</div>
17+
<div className="flex items-center text-base font-semibold">
18+
<LuDownload className="text-blue-500 mr-2" />
19+
<span>{t("curseforgecard.content")}</span>
20+
</div>
21+
<div className="absolute bottom-3 right-3">
22+
<Tooltip
23+
content={
24+
t("curseforgecard.browse", {
25+
defaultValue: "浏览CurseForge",
26+
}) as unknown as string
27+
}
28+
placement="left"
29+
>
30+
<Button
31+
isIconOnly
32+
size="sm"
33+
variant="light"
34+
radius="full"
35+
onPress={() => navigate("/curseforge")}
36+
aria-label={
37+
t("curseforgecard.browse", {
38+
defaultValue: "浏览CurseForge",
39+
}) as unknown as string
40+
}
41+
>
42+
<LuDownload size={20} />
43+
</Button>
44+
</Tooltip>
45+
</div>
46+
</CardBody>
47+
</Card>
48+
);
49+
};

frontend/src/pages/BehaviorPacksPage.tsx

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,11 @@ import { motion } from "framer-motion";
2323
import { useNavigate } from "react-router-dom";
2424
import {
2525
GetContentRoots,
26-
ListDir,
26+
ListPacksForVersion,
2727
OpenPathDir,
2828
} from "../../bindings/github.com/liteldev/LeviLauncher/minecraft";
2929
import * as types from "../../bindings/github.com/liteldev/LeviLauncher/internal/types/models";
3030
import { readCurrentVersionName } from "../utils/currentVersion";
31-
import { listDirectories } from "../utils/fs";
3231
import * as minecraft from "../../bindings/github.com/liteldev/LeviLauncher/minecraft";
3332
import { renderMcText } from "../utils/mcformat";
3433

@@ -119,7 +118,10 @@ export default function BehaviorPacksPage() {
119118
});
120119
setEntries([]);
121120
} else {
122-
const r = await GetContentRoots(name);
121+
const [r, allPacks] = await Promise.all([
122+
GetContentRoots(name),
123+
ListPacksForVersion(name, ""),
124+
]);
123125
const safe = r || {
124126
base: "",
125127
usersRoot: "",
@@ -129,21 +131,27 @@ export default function BehaviorPacksPage() {
129131
isPreview: false,
130132
};
131133
setRoots(safe);
132-
const dirs = await listDirectories(safe.behaviorPacks);
133-
setEntries(dirs);
134+
135+
const filtered = (allPacks || []).filter(
136+
(p) => p.manifest.pack_type === 4
137+
);
138+
139+
setEntries([]);
134140
const basic = await Promise.all(
135-
dirs.map(async (d) => {
141+
filtered.map(async (p) => {
136142
try {
137-
const info = await (minecraft as any)?.GetPackInfo?.(d.path);
138-
return { ...info, path: d.path };
143+
const info = await (minecraft as any)?.GetPackInfo?.(p.path);
144+
return { ...info, path: p.path };
139145
} catch {
140146
return {
141-
name: d.name,
142-
description: "",
143-
version: "",
147+
name: p.manifest.name,
148+
description: p.manifest.description,
149+
version: p.manifest.identity.version
150+
? `${p.manifest.identity.version.major}.${p.manifest.identity.version.minor}.${p.manifest.identity.version.patch}`
151+
: "",
144152
minEngineVersion: "",
145153
iconDataUrl: "",
146-
path: d.path,
154+
path: p.path,
147155
};
148156
}
149157
})

0 commit comments

Comments
 (0)