Use theme tokens for spacing and sizing and update PublishModal

This commit is contained in:
CPTProgrammer 2026-07-16 14:34:56 +08:00
parent 5f6cab6529
commit 7bb932910a
No known key found for this signature in database
3 changed files with 136 additions and 85 deletions

View File

@ -1,7 +1,7 @@
"use client";
import { useState, useCallback } from "react";
import { Typography, Space, Card, Button } from "antd";
import { Typography, Space, Card, Button, theme } from "antd";
import { CloudUploadOutlined } from "@ant-design/icons";
import { ConfigSelector } from "./ConfigSelector";
import { VersionTable } from "./VersionTable";
@ -16,6 +16,7 @@ interface MainPageProps {
}
export function MainPage({ initialConfigs }: MainPageProps) {
const { token } = theme.useToken();
const [configs, setConfigs] =
useState<{ name: string; file: string }[]>(initialConfigs);
const [selectedConfigFile, setSelectedConfigFile] = useState<string | null>(
@ -67,11 +68,11 @@ export function MainPage({ initialConfigs }: MainPageProps) {
style={{
maxWidth: 900,
margin: "40px auto",
padding: "0 24px",
padding: `0 ${token.paddingLG}px`,
}}
>
{/* Page header */}
<Title level={2} style={{ marginBottom: 24 }}>
<Title level={2} style={{ marginBottom: token.marginLG }}>
Mod Releaser
</Title>
@ -99,7 +100,7 @@ export function MainPage({ initialConfigs }: MainPageProps) {
style={{
display: "flex",
justifyContent: "flex-end",
marginTop: 24,
marginTop: token.marginLG,
}}
>
<Button

View File

@ -14,6 +14,7 @@ import {
Progress,
Typography,
Empty,
theme,
} from "antd";
import { ReloadOutlined } from "@ant-design/icons";
import type { BaseVersion, Config, UploadReleaseType } from "@/types";
@ -68,6 +69,7 @@ export function PublishModal({
project,
onClose,
}: PublishModalProps) {
const { token } = theme.useToken();
const [currentStep, setCurrentStep] = useState(0);
// Version selection
@ -81,14 +83,19 @@ export function PublishModal({
"release",
);
// Progress
// Progress & result
const [publishing, setPublishing] = useState(false);
const [publishDone, setPublishDone] = useState(false);
const [progress, setProgress] = useState<
Record<typeof platforms[number], { done: number; total: number }>
>({
modrinth: { done: 0, total: 0 },
curseforge: { done: 0, total: 0 }
curseforge: { done: 0, total: 0 },
});
const [publishResult, setPublishResult] = useState<{
success: number;
failed: number;
} | null>(null);
// Platform existing versions — loaded when modal opens
const [loadingExisting, setLoadingExisting] = useState(false);
@ -133,7 +140,7 @@ export function PublishModal({
}
setSelectedVersions(preselected);
}
setCurrentStep((prev) => Math.min(prev + 1, 1));
setCurrentStep((prev) => Math.min(prev + 1, 2));
}, [currentStep, publishVersions]);
const handlePrev = useCallback(() => {
@ -142,7 +149,12 @@ export function PublishModal({
const handleConfirmPublish = useCallback(async () => {
setPublishing(true);
// TODO: Call tRPC mutation to publish
setPublishDone(false);
setPublishResult(null);
setCurrentStep(2);
// TODO: Call tRPC mutation to publish, update progress & result
setPublishResult({ success: 0, failed: 0 });
setPublishDone(true);
setPublishing(false);
}, []);
@ -152,6 +164,8 @@ export function PublishModal({
setSelectedVersions(new Set());
setChangelog("");
setVersionType("release");
setPublishResult(null);
setPublishDone(false);
onClose();
}, [publishing, onClose]);
@ -233,7 +247,7 @@ export function PublishModal({
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 16,
marginBottom: token.margin,
}}
>
<Text strong></Text>
@ -250,9 +264,9 @@ export function PublishModal({
{loadingExisting ? (
<div style={{ textAlign: "center", padding: 40 }}></div>
) : (
<Row gutter={16}>
<Row gutter={token.margin}>
<Col span={12}>
<Text strong style={{ display: "block", marginBottom: 8 }}>
<Text strong style={{ display: "block", marginBottom: token.marginXS }}>
Modrinth
</Text>
{publishVersions.length > 0 ? (
@ -268,7 +282,7 @@ export function PublishModal({
)}
</Col>
<Col span={12}>
<Text strong style={{ display: "block", marginBottom: 8 }}>
<Text strong style={{ display: "block", marginBottom: token.marginXS }}>
CurseForge
</Text>
{publishVersions.length > 0 ? (
@ -292,6 +306,49 @@ export function PublishModal({
// --------------- Render: Step 2 — Publish Settings ---------------
const renderStep2 = () => {
return (
<div>
{/* Changelog */}
<div style={{ marginBottom: token.marginLG }}>
<Text strong style={{ display: "block", marginBottom: token.marginSM }}>
Changelog (Markdown)
</Text>
{/* TODO: Replace with @uiw/react-md-editor */}
<textarea
style={{
width: "100%",
minHeight: 160,
border: `1px solid ${token.colorBorder}`,
borderRadius: token.borderRadius,
padding: token.paddingSM,
fontFamily: token.fontFamilyCode ?? "monospace",
fontSize: token.fontSizeSM,
}}
placeholder="输入 Markdown 格式的更新日志…"
value={changelog}
onChange={(e) => setChangelog(e.target.value)}
/>
</div>
{/* Version type */}
<div>
<Text strong style={{ display: "block", marginBottom: token.marginSM }}>
</Text>
<Select
value={versionType}
onChange={(v) => setVersionType(v)}
style={{ width: 160 }}
options={RELEASE_TYPES}
/>
</div>
</div>
);
};
// --------------- Render: Step 3 — Progress & Result ---------------
const renderStep3 = () => {
const hasModrinth = publishVersions.some(
(v) =>
!v.existsModrinth &&
@ -305,75 +362,58 @@ export function PublishModal({
return (
<div>
{/* Changelog */}
<div style={{ marginBottom: 24 }}>
<Text strong style={{ display: "block", marginBottom: 8 }}>
Changelog (Markdown)
</Text>
{/* TODO: Replace with @uiw/react-md-editor */}
<textarea
{/* Progress bars */}
<div style={{ marginBottom: token.marginLG }}>
{hasModrinth && (
<div style={{ marginBottom: token.marginSM }}>
<Text>Modrinth: </Text>
<Progress
percent={Math.round(
(progress.modrinth.done /
Math.max(progress.modrinth.total, 1)) *
100,
)}
format={() =>
`${progress.modrinth.done} / ${progress.modrinth.total}`
}
style={{ width: "80%" }}
/>
</div>
)}
{hasCurseforge && (
<div>
<Text>CurseForge: </Text>
<Progress
percent={Math.round(
(progress.curseforge.done /
Math.max(progress.curseforge.total, 1)) *
100,
)}
format={() =>
`${progress.curseforge.done} / ${progress.curseforge.total}`
}
style={{ width: "80%" }}
/>
</div>
)}
</div>
{/* Result */}
{publishDone && publishResult && (
<div
style={{
width: "100%",
minHeight: 160,
border: "1px solid #d9d9d9",
borderRadius: 6,
padding: 8,
fontFamily: "monospace",
fontSize: 13,
background: token.colorSuccessBg,
border: `1px solid ${token.colorSuccessBorder}`,
borderRadius: token.borderRadius,
padding: token.padding,
}}
placeholder="输入 Markdown 格式的更新日志…"
value={changelog}
onChange={(e) => setChangelog(e.target.value)}
/>
</div>
{/* Version type */}
<div style={{ marginBottom: 24 }}>
<Text strong style={{ display: "block", marginBottom: 8 }}>
</Text>
<Select
value={versionType}
onChange={(v) => setVersionType(v)}
style={{ width: 160 }}
options={RELEASE_TYPES}
/>
</div>
{/* Publishing progress */}
{publishing && (
<div style={{ marginBottom: 24 }}>
{hasModrinth && (
<div style={{ marginBottom: 12 }}>
<Text>Modrinth: </Text>
<Progress
percent={Math.round(
(progress.modrinth.done / Math.max(progress.modrinth.total, 1)) *
100,
)}
format={() =>
`${progress.modrinth.done} / ${progress.modrinth.total}`
}
style={{ width: "80%" }}
/>
</div>
)}
{hasCurseforge && (
<div>
<Text>CurseForge: </Text>
<Progress
percent={Math.round(
(progress.curseforge.done /
Math.max(progress.curseforge.total, 1)) *
100,
)}
format={() =>
`${progress.curseforge.done} / ${progress.curseforge.total}`
}
style={{ width: "80%" }}
/>
</div>
)}
>
<Text strong></Text>
<div style={{ marginTop: token.marginSM }}>
<Text>
{publishResult.success} {publishResult.failed}
</Text>
</div>
</div>
)}
</div>
@ -392,17 +432,21 @@ export function PublishModal({
footer={
publishing ? null : (
<Space>
{currentStep > 0 && (
{currentStep > 0 && currentStep < 2 && (
<Button onClick={handlePrev}></Button>
)}
{currentStep < 1 ? (
{currentStep === 0 ? (
<Button type="primary" onClick={handleNext}>
</Button>
) : (
) : currentStep === 1 ? (
<Button type="primary" onClick={handleConfirmPublish}>
</Button>
) : (
<Button type="primary" onClick={handleClose}>
</Button>
)}
</Space>
)
@ -415,10 +459,15 @@ export function PublishModal({
items={[
{ title: "版本选择" },
{ title: "发布设置" },
{ title: "发布进度" },
]}
/>
{currentStep === 0 ? renderStep1() : renderStep2()}
{currentStep === 0
? renderStep1()
: currentStep === 1
? renderStep2()
: renderStep3()}
</Modal>
);
}

View File

@ -1,7 +1,7 @@
"use client";
import { useEffect, useState, useMemo, useCallback } from "react";
import { Table, Empty, Tag, Typography, Select, Flex } from "antd";
import { Table, Empty, Tag, Typography, Select, Flex, theme } from "antd";
import type { ParsedProject } from "@/services/project";
import { computeVersionRanges, collectAutoRange, type McVersionEntry } from "@/lib/utils/mcVersion";
import { getMcVersionUnion } from "@/services/meta";
@ -24,6 +24,7 @@ export function VersionTable({
loading,
onLoadingChange,
}: VersionTableProps) {
const { token } = theme.useToken();
// ── MC version union from both platforms ──
const [allMcVersions, setAllMcVersions] = useState<string[]>([]);
@ -164,7 +165,7 @@ export function VersionTable({
<Tag color="blue">{entry.wildcard}</Tag>
<Typography.Text
type="secondary"
style={{ fontSize: 12, whiteSpace: "nowrap" }}
style={{ fontSize: token.fontSizeSM, whiteSpace: "nowrap" }}
>
</Typography.Text>