From b275a3668b140d9ce4663de646519d2dbd4297e7 Mon Sep 17 00:00:00 2001 From: 苏向夜 Date: Tue, 24 Feb 2026 22:41:36 +0800 Subject: refactor: rewrite login and settings pages --- packages/ui-new/src/pages/home-view.tsx | 212 +--------------------- packages/ui-new/src/pages/index-old.tsx | 187 +++++++++++++++++++ packages/ui-new/src/pages/index.tsx | 173 ++++-------------- packages/ui-new/src/pages/settings.tsx | 310 ++++++++++++++++++++++++++++++++ 4 files changed, 529 insertions(+), 353 deletions(-) create mode 100644 packages/ui-new/src/pages/index-old.tsx create mode 100644 packages/ui-new/src/pages/settings.tsx (limited to 'packages/ui-new/src/pages') diff --git a/packages/ui-new/src/pages/home-view.tsx b/packages/ui-new/src/pages/home-view.tsx index bcee7e6..4f80cb0 100644 --- a/packages/ui-new/src/pages/home-view.tsx +++ b/packages/ui-new/src/pages/home-view.tsx @@ -1,5 +1,5 @@ -import { Calendar, ExternalLink } from "lucide-react"; import { useEffect, useState } from "react"; +import { BottomBar } from "@/components/bottom-bar"; import type { SaturnEffect } from "@/lib/effects/SaturnEffect"; import { useGameStore } from "../stores/game-store"; import { useReleasesStore } from "../stores/releases-store"; @@ -108,125 +108,6 @@ export function HomeView() { } }; - const formatDate = (dateString: string) => { - return new Date(dateString).toLocaleDateString(undefined, { - year: "numeric", - month: "long", - day: "numeric", - }); - }; - - const escapeHtml = (unsafe: string) => { - return unsafe - .replace(/&/g, "&") - .replace(//g, ">") - .replace(/"/g, """) - .replace(/'/g, "'"); - }; - - const formatBody = (body: string) => { - if (!body) return ""; - - let processed = escapeHtml(body); - - const emojiMap: Record = { - ":tada:": "🎉", - ":sparkles:": "✨", - ":bug:": "🐛", - ":memo:": "📝", - ":rocket:": "🚀", - ":white_check_mark:": "✅", - ":construction:": "🚧", - ":recycle:": "♻️", - ":wrench:": "🔧", - ":package:": "📦", - ":arrow_up:": "⬆️", - ":arrow_down:": "⬇️", - ":warning:": "⚠️", - ":fire:": "🔥", - ":heart:": "❤️", - ":star:": "⭐", - ":zap:": "⚡", - ":art:": "🎨", - ":lipstick:": "💄", - ":globe_with_meridians:": "🌐", - }; - - processed = processed.replace( - /:[a-z0-9_]+:/g, - (match) => emojiMap[match] || match, - ); - - processed = processed.replace(/`([0-9a-f]{7,40})`/g, (_match, hash) => { - return `${hash.substring( - 0, - 7, - )}`; - }); - - processed = processed.replace( - /@([a-zA-Z0-9-]+)/g, - '@$1', - ); - - return processed - .split("\n") - .map((line) => { - line = line.trim(); - - const formatLine = (text: string) => - text - .replace( - /\*\*(.*?)\*\*/g, - '$1', - ) - .replace( - /(?$1', - ) - .replace( - /`([^`]+)`/g, - '$1', - ) - .replace( - /\[(.*?)\]\((.*?)\)/g, - '$1', - ); - - if (line.startsWith("- ") || line.startsWith("* ")) { - return `
  • ${formatLine( - line.substring(2), - )}
  • `; - } - - if (line.startsWith("##")) { - return `

    ${line.replace( - /^#+\s+/, - "", - )}

    `; - } - - if (line.startsWith("#")) { - return `

    ${line.replace( - /^#+\s+/, - "", - )}

    `; - } - - if (line.startsWith("> ")) { - return `
    ${formatLine( - line.substring(2), - )}
    `; - } - - if (line === "") return '
    '; - - return `

    ${formatLine(line)}

    `; - }) - .join(""); - }; - return (
    - {/* Scroll Hint */} - {!releasesStore.isLoading && releasesStore.releases.length > 0 && ( -
    - - Scroll for Updates - - - Scroll for Updates - - -
    - )} - - - {/* Changelog / Updates Section */} -
    -
    -

    - - LATEST UPDATES -

    - - {releasesStore.isLoading ? ( -
    - {Array(3) - .fill(0) - .map((_, i) => ( -
    - ))} -
    - ) : releasesStore.error ? ( -
    - Failed to load updates: {releasesStore.error} -
    - ) : releasesStore.releases.length === 0 ? ( -
    No releases found.
    - ) : ( -
    - {releasesStore.releases.map((release, index) => ( -
    - {/* Timeline Dot */} -
    - -
    -

    - {release.name || release.tagName} -

    -
    - - {formatDate(release.publishedAt)} -
    -
    - - - ))} -
    - )} -
    +
    ); diff --git a/packages/ui-new/src/pages/index-old.tsx b/packages/ui-new/src/pages/index-old.tsx new file mode 100644 index 0000000..a6626c9 --- /dev/null +++ b/packages/ui-new/src/pages/index-old.tsx @@ -0,0 +1,187 @@ +import { useEffect } from "react"; +import { Outlet } from "react-router"; +import { BottomBar } from "@/components/bottom-bar"; +import { DownloadMonitor } from "@/components/download-monitor"; +import { GameConsole } from "@/components/game-console"; +import { LoginModal } from "@/components/login-modal"; +import { ParticleBackground } from "@/components/particle-background"; +import { Sidebar } from "@/components/sidebar"; + +import { useAuthStore } from "@/stores/auth-store"; +import { useGameStore } from "@/stores/game-store"; +import { useInstancesStore } from "@/stores/instances-store"; +import { useLogsStore } from "@/stores/logs-store"; +import { useSettingsStore } from "@/stores/settings-store"; +import { useUIStore } from "@/stores/ui-store"; + +export function IndexPage() { + const authStore = useAuthStore(); + const settingsStore = useSettingsStore(); + const uiStore = useUIStore(); + const instancesStore = useInstancesStore(); + const gameStore = useGameStore(); + const logsStore = useLogsStore(); + useEffect(() => { + // ENFORCE DARK MODE: Always add 'dark' class and attribute + document.documentElement.classList.add("dark"); + document.documentElement.setAttribute("data-theme", "dark"); + document.documentElement.classList.remove("light"); + + // Initialize stores + // Include store functions in the dependency array to satisfy hooks lint. + // These functions are stable in our store implementation, so listing them + // here is safe and prevents lint warnings. + authStore.checkAccount(); + settingsStore.loadSettings(); + logsStore.init(); + settingsStore.detectJava(); + instancesStore.loadInstances(); + gameStore.loadVersions(); + + // Note: getVersion() would need Tauri API setup + // getVersion().then((v) => uiStore.setAppVersion(v)); + }, [ + authStore.checkAccount, + settingsStore.loadSettings, + logsStore.init, + settingsStore.detectJava, + instancesStore.loadInstances, + gameStore.loadVersions, + ]); + + // Refresh versions when active instance changes + useEffect(() => { + if (instancesStore.activeInstanceId) { + gameStore.loadVersions(); + } else { + gameStore.setVersions([]); + } + }, [ + instancesStore.activeInstanceId, + gameStore.loadVersions, + gameStore.setVersions, + ]); + + return ( +
    + {/* Modern Animated Background */} +
    + {settingsStore.settings.customBackgroundPath && ( + Background console.error("Failed to load main background:", e)} + /> + )} + + {/* Dimming Overlay for readability */} + {settingsStore.settings.customBackgroundPath && ( +
    + )} + + {!settingsStore.settings.customBackgroundPath && ( + <> + {settingsStore.settings.theme === "dark" ? ( +
    + ) : ( +
    + )} + + {uiStore.currentView === "home" && } + +
    + + )} + + {/* Subtle Grid Overlay */} +
    +
    + + {/* Content Wrapper */} +
    + {/* Floating Sidebar */} + + + {/* Main Content Area - Transparent & Flat */} +
    + {/* Window Drag Region */} +
    + + {/* App Content */} +
    + {/* Views Container */} +
    + +
    + + {/* Download Monitor Overlay */} +
    +
    + +
    +
    + + {/* Bottom Bar */} + {uiStore.currentView === "home" && } +
    +
    +
    + + {/* Logout Confirmation Dialog */} + {authStore.isLogoutConfirmOpen && ( +
    +
    +

    Logout

    +

    + Are you sure you want to logout{" "} + + {authStore.currentAccount?.username} + + ? +

    +
    + + +
    +
    +
    + )} + + {uiStore.showConsole && ( +
    +
    + +
    +
    + )} +
    + ); +} diff --git a/packages/ui-new/src/pages/index.tsx b/packages/ui-new/src/pages/index.tsx index 2b3c2b2..54cfc1e 100644 --- a/packages/ui-new/src/pages/index.tsx +++ b/packages/ui-new/src/pages/index.tsx @@ -1,94 +1,48 @@ import { useEffect } from "react"; -import { Outlet } from "react-router"; -import { BottomBar } from "@/components/bottom-bar"; -import { DownloadMonitor } from "@/components/download-monitor"; -import { GameConsole } from "@/components/game-console"; -import { LoginModal } from "@/components/login-modal"; +import { Outlet, useLocation } from "react-router"; import { ParticleBackground } from "@/components/particle-background"; import { Sidebar } from "@/components/sidebar"; - -import { useAuthStore } from "@/stores/auth-store"; -import { useGameStore } from "@/stores/game-store"; -import { useInstancesStore } from "@/stores/instances-store"; -import { useLogsStore } from "@/stores/logs-store"; -import { useSettingsStore } from "@/stores/settings-store"; -import { useUIStore } from "@/stores/ui-store"; +import { useAuthStore } from "@/models/auth"; +import { useSettingsStore } from "@/models/settings"; export function IndexPage() { const authStore = useAuthStore(); const settingsStore = useSettingsStore(); - const uiStore = useUIStore(); - const instancesStore = useInstancesStore(); - const gameStore = useGameStore(); - const logsStore = useLogsStore(); - useEffect(() => { - // ENFORCE DARK MODE: Always add 'dark' class and attribute - document.documentElement.classList.add("dark"); - document.documentElement.setAttribute("data-theme", "dark"); - document.documentElement.classList.remove("light"); - // Initialize stores - // Include store functions in the dependency array to satisfy hooks lint. - // These functions are stable in our store implementation, so listing them - // here is safe and prevents lint warnings. - authStore.checkAccount(); - settingsStore.loadSettings(); - logsStore.init(); - settingsStore.detectJava(); - instancesStore.loadInstances(); - gameStore.loadVersions(); + const location = useLocation(); - // Note: getVersion() would need Tauri API setup - // getVersion().then((v) => uiStore.setAppVersion(v)); - }, [ - authStore.checkAccount, - settingsStore.loadSettings, - logsStore.init, - settingsStore.detectJava, - instancesStore.loadInstances, - gameStore.loadVersions, - ]); - - // Refresh versions when active instance changes useEffect(() => { - if (instancesStore.activeInstanceId) { - gameStore.loadVersions(); - } else { - gameStore.setVersions([]); - } - }, [ - instancesStore.activeInstanceId, - gameStore.loadVersions, - gameStore.setVersions, - ]); + authStore.init(); + settingsStore.refresh(); + }, [authStore.init, settingsStore.refresh]); return ( -
    - {/* Modern Animated Background */} +
    - {settingsStore.settings.customBackgroundPath && ( - Background console.error("Failed to load main background:", e)} - /> - )} - - {/* Dimming Overlay for readability */} - {settingsStore.settings.customBackgroundPath && ( -
    + {settingsStore.config?.customBackgroundPath && ( + <> + Background + console.error("Failed to load main background:", e) + } + /> + {/* Dimming Overlay for readability */} +
    + )} - {!settingsStore.settings.customBackgroundPath && ( + {!settingsStore.config?.customBackgroundPath && ( <> - {settingsStore.settings.theme === "dark" ? ( + {settingsStore.theme === "dark" ? (
    ) : (
    )} - {uiStore.currentView === "home" && } + {location.pathname === "/" && }
    @@ -99,91 +53,24 @@ export function IndexPage() { className="absolute inset-0 z-0 dark:opacity-10 opacity-30 pointer-events-none" style={{ backgroundImage: `linear-gradient(${ - settingsStore.settings.theme === "dark" ? "#ffffff" : "#000000" + settingsStore.config?.theme === "dark" ? "#ffffff" : "#000000" } 1px, transparent 1px), linear-gradient(90deg, ${ - settingsStore.settings.theme === "dark" ? "#ffffff" : "#000000" + settingsStore.config?.theme === "dark" ? "#ffffff" : "#000000" } 1px, transparent 1px)`, backgroundSize: "40px 40px", maskImage: "radial-gradient(circle at 50% 50%, black 30%, transparent 70%)", }} - >
    + />
    - {/* Content Wrapper */} -
    - {/* Floating Sidebar */} +
    - {/* Main Content Area - Transparent & Flat */} -
    - {/* Window Drag Region */} -
    - - {/* App Content */} -
    - {/* Views Container */} -
    - -
    - - {/* Download Monitor Overlay */} -
    -
    - -
    -
    - - {/* Bottom Bar */} - {uiStore.currentView === "home" && } -
    +
    +
    - - - - {/* Logout Confirmation Dialog */} - {authStore.isLogoutConfirmOpen && ( -
    -
    -

    Logout

    -

    - Are you sure you want to logout{" "} - - {authStore.currentAccount?.username} - - ? -

    -
    - - -
    -
    -
    - )} - - {uiStore.showConsole && ( -
    -
    - -
    -
    - )}
    ); } diff --git a/packages/ui-new/src/pages/settings.tsx b/packages/ui-new/src/pages/settings.tsx new file mode 100644 index 0000000..440a5dc --- /dev/null +++ b/packages/ui-new/src/pages/settings.tsx @@ -0,0 +1,310 @@ +import { toNumber } from "es-toolkit/compat"; +import { FileJsonIcon } from "lucide-react"; +import { useEffect, useState } from "react"; +import { migrateSharedCaches } from "@/client"; +import { ConfigEditor } from "@/components/config-editor"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { + Field, + FieldContent, + FieldDescription, + FieldGroup, + FieldLabel, + FieldLegend, + FieldSet, +} from "@/components/ui/field"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Spinner } from "@/components/ui/spinner"; +import { Switch } from "@/components/ui/switch"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { useSettingsStore } from "@/models/settings"; + +export type SettingsTab = "general" | "appearance" | "advanced"; + +export function SettingsPage() { + const { config, ...settings } = useSettingsStore(); + const [showConfigEditor, setShowConfigEditor] = useState(false); + const [activeTab, setActiveTab] = useState("general"); + + useEffect(() => { + if (!config) settings.refresh(); + }, [config, settings.refresh]); + + const renderScrollArea = () => { + if (!config) { + return ( +
    + +
    + ); + } + return ( + + + + + General + + + +
    + Window Options + + May not work on some platforms like Linux Niri. + + +
    + + + Window Default Width + + { + settings.merge({ + width: toNumber(e.target.value), + }); + }} + onBlur={() => { + settings.save(); + }} + min={800} + max={3840} + /> + + + + Window Default Height + + { + settings.merge({ + height: toNumber(e.target.value), + }); + }} + onBlur={() => { + settings.save(); + }} + min={600} + max={2160} + /> + +
    + + + + GPU Acceleration + + + Enable GPU acceleration for the interface. + + + { + settings.merge({ + enableGpuAcceleration: checked, + }); + settings.save(); + }} + /> + +
    +
    +
    + Network Options + + + { + settings.merge({ + downloadThreads: toNumber(e.target.value), + }); + }} + onBlur={() => { + settings.save(); + }} + min={1} + max={64} + /> + +
    +
    +
    +
    +
    + + + + + Java Installations + + + + + + + + + Appearance + + + + + + Theme + + Select your prefered theme. + + + + + + + + + + + + Advanced + + + +
    + Advanced Options + + + + + Use Shared Caches + + + Share downloaded assets between instances. + + + { + checked && (await migrateSharedCaches()); + settings.merge({ + useSharedCaches: checked, + }); + settings.save(); + }} + /> + + + + + Keep Legacy Per-Instance Storage + + + Maintain separate cache folders for compatibility. + + + { + settings.merge({ + keepLegacyPerInstanceStorage: checked, + }); + settings.save(); + }} + /> + + +
    +
    +
    +
    +
    +
    + ); + }; + + return ( +
    +
    +

    + Settings +

    + + +
    + + + + General + Java + Appearance + Advanced + + {renderScrollArea()} + + + setShowConfigEditor(false)} + /> +
    + ); +} -- cgit v1.2.3-70-g09d2