From 5afc3c04183b1fdb7527007422817f5a90594569 Mon Sep 17 00:00:00 2001 From: /home/neo <158327205+neoapps-dev@users.noreply.github.com> Date: Fri, 3 Apr 2026 21:59:57 +0300 Subject: [PATCH] feat: devtools menu placeholder (#35) --- src/components/common/AchievementToast.tsx | 16 ++- src/components/views/DevtoolsView.tsx | 86 +++++++++++ src/components/views/HomeView.tsx | 22 ++- src/components/views/ThemesView.tsx | 157 --------------------- src/hooks/useDiscordRPC.ts | 25 +++- src/pages/App.tsx | 121 ++++++++++------ src/services/TauriService.ts | 83 +++++------ 7 files changed, 251 insertions(+), 259 deletions(-) create mode 100644 src/components/views/DevtoolsView.tsx delete mode 100644 src/components/views/ThemesView.tsx diff --git a/src/components/common/AchievementToast.tsx b/src/components/common/AchievementToast.tsx index 91a77c8..a6427dc 100644 --- a/src/components/common/AchievementToast.tsx +++ b/src/components/common/AchievementToast.tsx @@ -13,8 +13,8 @@ export function AchievementToast({ message, onClose, onClick, - title = "Error Get!", - variant = "error" + title = "Error!", + variant = "error", }: AchievementToastProps) { useEffect(() => { if (message) { @@ -69,10 +69,14 @@ export function AchievementToast({ animate={{ x: 0, opacity: 1 }} exit={{ x: 400, opacity: 0 }} transition={{ type: "spring", damping: 20, stiffness: 100 }} - onClick={onClick ? () => { - onClick(); - onClose(); - } : undefined} + onClick={ + onClick + ? () => { + onClick(); + onClose(); + } + : undefined + } className={`fixed top-6 right-6 z-[9999] ${onClick ? "cursor-pointer" : ""}`} >
(null); + const containerRef = useRef(null); + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === "Escape" || e.key === "Backspace") { + playBackSound(); + setActiveView("main"); + } + }; + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [playBackSound, setActiveView]); + + useEffect(() => { + if (focusIndex !== null) { + const el = containerRef.current?.querySelector( + `[data-index="${focusIndex}"]`, + ) as HTMLElement; + if (el) el.focus(); + } + }, [focusIndex]); + + const getItemStyle = (index: number) => ({ + backgroundImage: + focusIndex === index + ? "url('/images/button_highlighted.png')" + : "url('/images/Button_Background.png')", + backgroundSize: "100% 100%", + imageRendering: "pixelated" as const, + }); + + return ( + +

+ Developer Tools +

+ +
+ + Developer Tools coming soon... + +
+ + +
+ ); +} diff --git a/src/components/views/HomeView.tsx b/src/components/views/HomeView.tsx index 4fb8fff..1959975 100644 --- a/src/components/views/HomeView.tsx +++ b/src/components/views/HomeView.tsx @@ -1,12 +1,26 @@ import { useState, useEffect, useMemo, memo } from "react"; import { motion } from "framer-motion"; -import { useUI, useConfig, useAudio, useGame } from "../../context/LauncherContext"; +import { + useUI, + useConfig, + useAudio, + useGame, +} from "../../context/LauncherContext"; const HomeView = memo(function HomeView() { - const { setActiveView, setShowCredits, focusSection, onNavigateToSkin } = useUI(); + const { setActiveView, setShowCredits, focusSection, onNavigateToSkin } = + useUI(); const { profile, legacyMode } = useConfig(); const { playClickSound, playSfx } = useAudio(); - const { handleLaunch, isGameRunning, editions, installs, toggleInstall, downloadProgress, downloadingId } = useGame(); + const { + handleLaunch, + isGameRunning, + editions, + installs, + toggleInstall, + downloadProgress, + downloadingId, + } = useGame(); const isFocusedSection = focusSection === "menu"; const selectedEdition = editions.find((e: any) => e.id === profile); @@ -32,7 +46,7 @@ const HomeView = memo(function HomeView() { { label: "Help & Options", action: () => setActiveView("settings") }, { label: "Versions", action: () => setActiveView("versions") }, { label: "Workshop", action: () => setActiveView("workshop") }, - { label: "Themes & Tools", action: () => setActiveView("themes") }, + { label: "Developer Tools", action: () => setActiveView("devtools") }, ], [ isGameRunning, diff --git a/src/components/views/ThemesView.tsx b/src/components/views/ThemesView.tsx deleted file mode 100644 index 993c78a..0000000 --- a/src/components/views/ThemesView.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import { useState, useEffect, useRef, memo } from "react"; -import { motion } from "framer-motion"; -import { TauriService, ThemePalette } from "../../services/TauriService"; -import { useUI, useConfig, useAudio } from "../../context/LauncherContext"; - -const ThemesView = memo(function ThemesView() { - const { setActiveView } = useUI(); - const { theme: currentTheme, setTheme } = useConfig(); - const { playClickSound, playBackSound } = useAudio(); - - const [focusIndex, setFocusIndex] = useState(null); - const [externalPalettes, setExternalPalettes] = useState([]); - const containerRef = useRef(null); - - const baseThemes = ["Default", "Modern"]; - - useEffect(() => { - TauriService.getExternalPalettes().then(setExternalPalettes); - }, []); - - const totalPalettes = [...baseThemes, ...externalPalettes.map((p) => p.name)]; - const ITEM_COUNT = 3; // Theme Cycle, Import Theme, Back - - const handleImport = async () => { - playClickSound(); - try { - const result = await TauriService.importTheme(); - if (result === "success") { - const updated = await TauriService.getExternalPalettes(); - setExternalPalettes(updated); - } - } catch (e) { - console.error(e); - } - }; - - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - if (e.key === "Escape" || e.key === "Backspace") { - playBackSound(); - setActiveView("main"); - return; - } - - if (e.key === "ArrowDown") { - setFocusIndex((prev) => - prev === null || prev >= ITEM_COUNT - 1 ? 0 : prev + 1, - ); - } else if (e.key === "ArrowUp") { - setFocusIndex((prev) => - prev === null || prev <= 0 ? ITEM_COUNT - 1 : prev - 1, - ); - } else if (e.key === "Enter" && focusIndex !== null) { - if (focusIndex === 0) { - playClickSound(); - const currentIndex = totalPalettes.indexOf(currentTheme); - const nextIndex = (currentIndex + 1) % totalPalettes.length; - setTheme(totalPalettes[nextIndex]); - } else if (focusIndex === 1) { - handleImport(); - } else { - playBackSound(); - setActiveView("main"); - } - } - }; - window.addEventListener("keydown", handleKeyDown); - return () => window.removeEventListener("keydown", handleKeyDown); - }, [ - focusIndex, - currentTheme, - playClickSound, - playBackSound, - setActiveView, - setTheme, - totalPalettes, - ]); - - useEffect(() => { - if (focusIndex !== null) { - const el = containerRef.current?.querySelector( - `[data-index="${focusIndex}"]`, - ) as HTMLElement; - if (el) el.focus(); - } - }, [focusIndex]); - - const getItemStyle = (index: number) => ({ - backgroundImage: - focusIndex === index - ? "url('/images/button_highlighted.png')" - : "url('/images/Button_Background.png')", - backgroundSize: "100% 100%", - imageRendering: "pixelated" as const, - }); - - return ( - -

- Themes & Styles -

- -
- - - -
- - -
- ); -}); - -export default ThemesView; diff --git a/src/hooks/useDiscordRPC.ts b/src/hooks/useDiscordRPC.ts index e5ace3f..7aecbce 100644 --- a/src/hooks/useDiscordRPC.ts +++ b/src/hooks/useDiscordRPC.ts @@ -30,24 +30,28 @@ export function useDiscordRPC({ const updateRPC = async () => { if (!rpcEnabled || showIntro || !username) return; - if (!isWindowVisible && !isGameRunning && downloadProgress === null) return; + if (!isWindowVisible && !isGameRunning && downloadProgress === null) + return; const version = editions.find((e) => e.id === profile); const versionName = version ? version.name : "Unknown Version"; let details = "In Menus"; - let state = isGameRunning ? `Playing as ${username}` : `Logged in as ${username}`; + let state = isGameRunning + ? `Playing as ${username}` + : `Logged in as ${username}`; if (isGameRunning) { details = `Playing ${versionName}`; } else if (downloadProgress !== null) { - const downloadingName = editions.find((e) => e.id === downloadingId)?.name || "Game Files"; + const downloadingName = + editions.find((e) => e.id === downloadingId)?.name || "Game Files"; details = `Downloading ${downloadingName} (${downloadProgress.toFixed(0)}%)`; } else { const tabNames: Record = { main: "Main Menu", versions: "Selecting Version", settings: "In Settings", - themes: "Browsing Themes", + devtools: "Developing for LCE", skins: "Browsing Skins", workshop: "Browsing Workshop", }; @@ -58,5 +62,16 @@ export function useDiscordRPC({ }; updateRPC(); - }, [rpcEnabled, showIntro, username, profile, activeView, isGameRunning, isWindowVisible, Math.floor(downloadProgress || 0), downloadingId, editions]); + }, [ + rpcEnabled, + showIntro, + username, + profile, + activeView, + isGameRunning, + isWindowVisible, + Math.floor(downloadProgress || 0), + downloadingId, + editions, + ]); } diff --git a/src/pages/App.tsx b/src/pages/App.tsx index cbeaccf..3de47a0 100644 --- a/src/pages/App.tsx +++ b/src/pages/App.tsx @@ -3,7 +3,7 @@ import { motion, AnimatePresence, MotionConfig } from "framer-motion"; import HomeView from "../components/views/HomeView"; import SettingsView from "../components/views/SettingsView"; import VersionsView from "../components/views/VersionsView"; -import ThemesView from "../components/views/ThemesView"; +import DevtoolsView from "../components/views/DevtoolsView"; import SkinsView from "../components/views/SkinsView"; import WorkshopView from "../components/views/WorkshopView"; import SetupView from "../components/views/SetupView"; @@ -14,7 +14,13 @@ import { ClickParticles } from "../components/common/ClickParticles"; import { AppHeader } from "../components/layout/AppHeader"; import { DownloadOverlay } from "../components/layout/DownloadOverlay"; import { AchievementToast } from "../components/common/AchievementToast"; -import { useUI, useConfig, useAudio, useGame, useSkin } from "../context/LauncherContext"; +import { + useUI, + useConfig, + useAudio, + useGame, + useSkin, +} from "../context/LauncherContext"; import { getCurrentWindow } from "@tauri-apps/api/window"; import { TauriService } from "../services/TauriService"; import pkg from "../../package.json"; @@ -22,10 +28,20 @@ const appWindow = getCurrentWindow(); export default function App() { const { - showIntro, setShowIntro, logoAnimDone, setLogoAnimDone, - activeView, setActiveView, isUiHidden, setIsUiHidden, - showCredits, setShowCredits, focusSection, - onNavigateToMenu, updateMessage, clearUpdateMessage + showIntro, + setShowIntro, + logoAnimDone, + setLogoAnimDone, + activeView, + setActiveView, + isUiHidden, + setIsUiHidden, + showCredits, + setShowCredits, + focusSection, + onNavigateToMenu, + updateMessage, + clearUpdateMessage, } = useUI(); const config = useConfig(); @@ -40,30 +56,25 @@ export default function App() { setDisplayIsDay(config.isDayTime); }, [config.isDayTime]); - const selectedEdition = game.editions.find((e: any) => e.id === config.profile); + const selectedEdition = game.editions.find( + (e: any) => e.id === config.profile, + ); const selectedVersionName = selectedEdition?.name || ""; const titleImage = selectedEdition?.titleImage || "/images/MenuTitle.png"; useEffect(() => { if (config.isLoaded) { - // Check localStorage directly to ensure accurate setup state - const setupCompleted = localStorage.getItem('lce-setup-completed') === 'true'; + const setupCompleted = + localStorage.getItem("lce-setup-completed") === "true"; setShowSetup(!setupCompleted); } }, [config.isLoaded]); useEffect(() => { appWindow.show(); - // Only start intro timing if setup is not shown - if (!showSetup) { - setTimeout(() => setShowIntro(false), 2400); - setTimeout(() => setLogoAnimDone(true), 3400); - } else if (showSetup) { - // Skip intro entirely if setup is shown - setShowIntro(false); - setLogoAnimDone(true); - } + setTimeout(() => setShowIntro(false), 2400); + setTimeout(() => setLogoAnimDone(true), 3400); }, [showSetup]); useEffect(() => { @@ -76,21 +87,21 @@ export default function App() { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, - transition: { duration: config.animationsEnabled ? 0.5 : 0 } + transition: { duration: config.animationsEnabled ? 0.5 : 0 }, }; const backgroundFade = { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, - transition: { duration: config.animationsEnabled ? 0.8 : 0 } + transition: { duration: config.animationsEnabled ? 0.8 : 0 }, }; return (