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 (