diff --git a/package.json b/package.json index 9db5c3a..5a1ea6f 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "homepage": "https://example.org", "scripts": { "dev": "vite", - "build": "vite build", + "build": "node scripts/generate-about-content.mjs && vite build", "tauri": "tauri", "tauri:dev": "node scripts/tauri-dev.cjs", "tauri:build": "tauri build", diff --git a/scripts/ci/apply-version.mjs b/scripts/ci/apply-version.mjs index 0154627..c2da0e3 100644 --- a/scripts/ci/apply-version.mjs +++ b/scripts/ci/apply-version.mjs @@ -70,3 +70,13 @@ const pkgPath = path.join(process.cwd(), "package.json") const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8")) pkg.version = version fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf-8") + +const tauriConfPath = path.join(process.cwd(), "src-tauri", "tauri.conf.json") +const tauriConf = JSON.parse(fs.readFileSync(tauriConfPath, "utf-8")) +tauriConf.version = version +fs.writeFileSync(tauriConfPath, `${JSON.stringify(tauriConf, null, 2)}\n`, "utf-8") + +const cargoPath = path.join(process.cwd(), "src-tauri", "Cargo.toml") +let cargoContent = fs.readFileSync(cargoPath, "utf-8") +cargoContent = cargoContent.replace(/^version = "[^"]+"/m, `version = "${version}"`) +fs.writeFileSync(cargoPath, cargoContent, "utf-8") diff --git a/scripts/generate-about-content.mjs b/scripts/generate-about-content.mjs new file mode 100644 index 0000000..8c4d01d --- /dev/null +++ b/scripts/generate-about-content.mjs @@ -0,0 +1,69 @@ +import fs from "fs" +import path from "path" + +const cwd = process.cwd() +const readmePath = path.join(cwd, "README.md") +const outputPath = path.join(cwd, "public", "about-content.json") +const pkgPath = path.join(cwd, "package.json") + +if (!fs.existsSync(readmePath)) { + console.error("README.md not found") + process.exit(1) +} + +const readmeContent = fs.readFileSync(readmePath, "utf-8") + +// 提取 README 的主要内容(跳过徽章部分) +const lines = readmeContent.split("\n") +let startIndex = 0 + +// 找到第一个非空行且不是徽章的行 +for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim() + // 跳过徽章行、空行、标题行 + if (line && !line.startsWith("<") && !line.startsWith("[") && !line.startsWith("!")) { + startIndex = i + break + } +} + +// 提取主要内容 +const mainContent = lines.slice(startIndex).join("\n") + +// 简单的 markdown 转 HTML(保留基本格式) +function markdownToHtml(md) { + return md + .replace(/^### (.*$)/gim, "

$1

") + .replace(/^## (.*$)/gim, "

$1

") + .replace(/^# (.*$)/gim, "

$1

") + .replace(/\*\*(.*)\*\*/gim, "$1") + .replace(/\*(.*)\*/gim, "$1") + .replace(/`([^`]+)`/gim, "$1") + .replace(/^- (.*$)/gim, "
  • $1
  • ") + .replace(/(
  • .*<\/li>\n?)+/gim, "") + .replace(/\n/gim, "
    ") +} + +// 读取 package.json 获取版本号 +let version = "1.0.0" +if (fs.existsSync(pkgPath)) { + const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8")) + version = pkg.version || version +} + +const aboutData = { + title: "SecScore", + description: "教育积分管理软件", + version: `v${version}`, + content: markdownToHtml(mainContent), + rawMarkdown: mainContent, +} + +// 确保 public 目录存在 +const publicDir = path.join(cwd, "public") +if (!fs.existsSync(publicDir)) { + fs.mkdirSync(publicDir, { recursive: true }) +} + +fs.writeFileSync(outputPath, JSON.stringify(aboutData, null, 2), "utf-8") +console.log(`About content generated: ${outputPath}`) diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx index 0d7c71d..5e9e1d8 100644 --- a/src/components/Settings.tsx +++ b/src/components/Settings.tsx @@ -77,6 +77,8 @@ export const Settings: React.FC<{ const [recoveryDialogString, setRecoveryDialogString] = useState("") const [recoveryDialogFilename, setRecoveryDialogFilename] = useState("") + const [aboutContent, setAboutContent] = useState<{ title: string; description: string; content: string; rawMarkdown: string } | null>(null) + const [logsDialogVisible, setLogsDialogVisible] = useState(false) const [logsText, setLogsText] = useState("") const [logsLoading, setLogsLoading] = useState(false) @@ -165,8 +167,21 @@ export const Settings: React.FC<{ await loadMcpStatus() } + const loadAboutContent = async () => { + try { + const res = await fetch("/about-content.json") + if (res.ok) { + const data = await res.json() + setAboutContent(data) + } + } catch { + // ignore if about-content.json not found + } + } + useEffect(() => { loadAll() + loadAboutContent() const api = (window as any).api if (!api) return @@ -1122,17 +1137,19 @@ export const Settings: React.FC<{ label: t("settings.about.title"), children: ( -
    SecScore
    +
    + {aboutContent?.title || "SecScore"} +
    - {t("settings.about.appName")} + {aboutContent?.description || t("settings.about.appName")}
    {t("settings.about.version")}
    -
    v1.0.0
    +
    {aboutContent?.version || "v1.0.0"}
    {t("settings.about.copyright")}
    {"CopyRight © 2025-" + currentYear + " SECTL"}
    -
    Electron
    +
    Tauri
    {(window as any).electron?.process?.versions?.electron || "-"}
    Chromium
    {(window as any).electron?.process?.versions?.chrome || "-"}
    @@ -1165,6 +1182,21 @@ export const Settings: React.FC<{
    + {aboutContent?.content && ( + <> + +
    + + )}
    {t("settings.mcp.title")}