Refactor environment selector with grouped options and descriptions
This commit is contained in:
parent
269e7024a2
commit
7173ef1149
@ -20,6 +20,8 @@ import {
|
|||||||
getModrinthLoaders,
|
getModrinthLoaders,
|
||||||
getModrinthEnvironments,
|
getModrinthEnvironments,
|
||||||
getCurseForgeMeta,
|
getCurseForgeMeta,
|
||||||
|
type EnvironmentGroup,
|
||||||
|
EnvironmentOption,
|
||||||
} from "@/services/meta";
|
} from "@/services/meta";
|
||||||
import {
|
import {
|
||||||
scanMcPropertiesDir,
|
scanMcPropertiesDir,
|
||||||
@ -327,12 +329,10 @@ function useModrinthLoaders(): {
|
|||||||
|
|
||||||
function useModrinthEnvironments(): {
|
function useModrinthEnvironments(): {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
options: { value: string; label: string }[];
|
options: EnvironmentGroup[];
|
||||||
} {
|
} {
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [options, setOptions] = useState<
|
const [options, setOptions] = useState<EnvironmentGroup[]>([]);
|
||||||
{ value: string; label: string }[]
|
|
||||||
>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
@ -507,8 +507,8 @@ function ConfigModalForm({
|
|||||||
const { token } = theme.useToken();
|
const { token } = theme.useToken();
|
||||||
|
|
||||||
// Each Select loads its own data independently
|
// Each Select loads its own data independently
|
||||||
const mrLoaders = useModrinthLoaders();
|
|
||||||
const mrEnvs = useModrinthEnvironments();
|
const mrEnvs = useModrinthEnvironments();
|
||||||
|
const mrLoaders = useModrinthLoaders();
|
||||||
const cfMeta = useCurseforgeMeta();
|
const cfMeta = useCurseforgeMeta();
|
||||||
|
|
||||||
const sortedMrOptions = useMemo(() => {
|
const sortedMrOptions = useMemo(() => {
|
||||||
@ -551,22 +551,19 @@ function ConfigModalForm({
|
|||||||
filename_format: "modname-${version}-mc${mc_version}.jar",
|
filename_format: "modname-${version}-mc${mc_version}.jar",
|
||||||
source_filename_format: "modname-${version}-mc${mc_version}-sources.jar",
|
source_filename_format: "modname-${version}-mc${mc_version}-sources.jar",
|
||||||
modrinth: {
|
modrinth: {
|
||||||
project_id: "",
|
|
||||||
version_name:
|
version_name:
|
||||||
"ModName v${version} for Minecraft ${mc_version_range}",
|
"ModName v${version} for Minecraft ${mc_version_range}",
|
||||||
version: "${version}-mc${mc_version}",
|
version: "${version}-mc${mc_version}",
|
||||||
loaders: [],
|
loaders: [],
|
||||||
environment: "client_and_server",
|
|
||||||
dependencies: [],
|
dependencies: [],
|
||||||
} as Config["modrinth"],
|
} as Partial<Config["modrinth"]>,
|
||||||
curseforge: {
|
curseforge: {
|
||||||
project_id: 0,
|
|
||||||
version_name: "#{filename_format}",
|
version_name: "#{filename_format}",
|
||||||
environment: [],
|
environment: [],
|
||||||
loaders: [],
|
loaders: [],
|
||||||
relations: { projects: [] },
|
relations: { projects: [] },
|
||||||
} as Config["curseforge"],
|
} as Partial<Config["curseforge"]>,
|
||||||
} as Partial<ConfigValues>);
|
});
|
||||||
}
|
}
|
||||||
}, [mode, initialConfig, form]);
|
}, [mode, initialConfig, form]);
|
||||||
|
|
||||||
@ -755,6 +752,25 @@ function ConfigModalForm({
|
|||||||
placeholder="选择运行环境"
|
placeholder="选择运行环境"
|
||||||
loading={mrEnvs.loading}
|
loading={mrEnvs.loading}
|
||||||
options={mrEnvs.options}
|
options={mrEnvs.options}
|
||||||
|
optionRender={(option) => {
|
||||||
|
const desc = (option.data as unknown as EnvironmentOption).description;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>{option.label}</div>
|
||||||
|
{desc && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: token.fontSizeSM,
|
||||||
|
color: token.colorTextDescription,
|
||||||
|
whiteSpace: "wrap",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{desc}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
notFoundContent={
|
notFoundContent={
|
||||||
mrEnvs.loading ? "加载中…" : "无可用选项"
|
mrEnvs.loading ? "加载中…" : "无可用选项"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,21 +4,118 @@ import { modrinthClient, curseforgeClient } from "./clients";
|
|||||||
import { getLoaders, getGameVersions as getModrinthGameVersions } from "@/lib/modrinth";
|
import { getLoaders, getGameVersions as getModrinthGameVersions } from "@/lib/modrinth";
|
||||||
import { getVersionTypes, getGameVersions, getMinecraftVersions } from "@/lib/curseforge";
|
import { getVersionTypes, getGameVersions, getMinecraftVersions } from "@/lib/curseforge";
|
||||||
import { SemVer } from "@/lib/utils/templates/semver";
|
import { SemVer } from "@/lib/utils/templates/semver";
|
||||||
import { Environment, EnvironmentSchema } from "@/types";
|
import { Environment } from "@/types";
|
||||||
|
|
||||||
const ENVIRONMENT_LABELS: Record<Environment, string> = {
|
const ENVIRONMENT_DESCRIPTIONS: Record<Environment, string> = {
|
||||||
unknown: "Unknown environment",
|
client_only:
|
||||||
client_only: "Client-side only",
|
"All functionality is done client-side and is compatible with vanilla servers.",
|
||||||
server_only: "Server-side only (singleplayer compatible)",
|
server_only:
|
||||||
dedicated_server_only: "Dedicated server only",
|
"All functionality is done server-side and is compatible with vanilla clients. Also works on the internal server in singleplayer.",
|
||||||
client_and_server: "Required on both",
|
dedicated_server_only:
|
||||||
server_only_client_optional: "Optional on client",
|
"All functionality is done server-side and is compatible with vanilla clients. Only works on dedicated servers.",
|
||||||
client_only_server_optional: "Optional on server",
|
client_and_server: "Required on both the client and server.",
|
||||||
client_or_server_prefers_both: "Optional on both (prefers both)",
|
server_only_client_optional:
|
||||||
client_or_server: "Optional on both (either side)",
|
"Most functionality is server-side, but installing it on the client enables enhanced functionality.",
|
||||||
singleplayer_only: "Singleplayer only",
|
client_only_server_optional:
|
||||||
|
"Most functionality is client-side, but installing it on the server enables enhanced functionality.",
|
||||||
|
client_or_server_prefers_both:
|
||||||
|
"Has some functionality on both the client and server, even if only partially. Installing it on both leads to the best experience.",
|
||||||
|
client_or_server:
|
||||||
|
"Has some functionality on both the client and server, even if only partially.",
|
||||||
|
singleplayer_only:
|
||||||
|
"Only functions in Singleplayer or when not connected to a Multiplayer server.",
|
||||||
|
unknown: "The environment for this version could not be determined.",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface EnvironmentOption {
|
||||||
|
value: Environment;
|
||||||
|
label: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EnvironmentGroup {
|
||||||
|
label: string;
|
||||||
|
options: EnvironmentOption[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const ENVIRONMENT_GROUPS: EnvironmentGroup[] = [
|
||||||
|
{
|
||||||
|
label: "Client-side only",
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: "client_only",
|
||||||
|
label: "Client-side only",
|
||||||
|
description: ENVIRONMENT_DESCRIPTIONS.client_only,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Server-side only",
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: "server_only",
|
||||||
|
label: "Works in singleplayer too",
|
||||||
|
description: ENVIRONMENT_DESCRIPTIONS.server_only,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "dedicated_server_only",
|
||||||
|
label: "Dedicated server only",
|
||||||
|
description: ENVIRONMENT_DESCRIPTIONS.dedicated_server_only,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Client and server",
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: "client_and_server",
|
||||||
|
label: "Required on both",
|
||||||
|
description: ENVIRONMENT_DESCRIPTIONS.client_and_server,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "server_only_client_optional",
|
||||||
|
label: "Optional on client",
|
||||||
|
description: ENVIRONMENT_DESCRIPTIONS.server_only_client_optional,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "client_only_server_optional",
|
||||||
|
label: "Optional on server",
|
||||||
|
description: ENVIRONMENT_DESCRIPTIONS.client_only_server_optional,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "client_or_server_prefers_both",
|
||||||
|
label: "Optional on both, works best when installed on both sides",
|
||||||
|
description: ENVIRONMENT_DESCRIPTIONS.client_or_server_prefers_both,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "client_or_server",
|
||||||
|
label: "Optional on both, works the same if installed on either side",
|
||||||
|
description: ENVIRONMENT_DESCRIPTIONS.client_or_server,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Singleplayer only",
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: "singleplayer_only",
|
||||||
|
label: "Singleplayer only",
|
||||||
|
description: ENVIRONMENT_DESCRIPTIONS.singleplayer_only,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Other",
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: "unknown",
|
||||||
|
label: "Unknown environment",
|
||||||
|
description: ENVIRONMENT_DESCRIPTIONS.unknown,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取 Modrinth 支持的加载器名称列表。
|
* 获取 Modrinth 支持的加载器名称列表。
|
||||||
* 用于 ConfigModal 中 loaders 多选下拉框的选项。
|
* 用于 ConfigModal 中 loaders 多选下拉框的选项。
|
||||||
@ -40,14 +137,11 @@ export async function getModrinthMcVersions(): Promise<string[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取 Modrinth 运行环境枚举值,转为 Ant Design Select 的选项格式。
|
* 获取 Modrinth 运行环境枚举值,按 Ant Design OptGroup 分组。
|
||||||
* 前端使用 OptGroup 分组显示(Client / Server / Both / Other)。
|
* 分组来源于 Modrinth 前端 UI 的 environments.ts,英文描述也完全一致。
|
||||||
*/
|
*/
|
||||||
export async function getModrinthEnvironments() {
|
export async function getModrinthEnvironments(): Promise<EnvironmentGroup[]> {
|
||||||
return EnvironmentSchema.options.map((value) => ({
|
return ENVIRONMENT_GROUPS;
|
||||||
value,
|
|
||||||
label: ENVIRONMENT_LABELS[value],
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user