+
+
Firefly Tools
- {/* Section 1: About SR Tools */}
-
-
- ℹ️
- About Firefly Tools
-
-
-
-
🏠
-
- This site is a another version of {" "}
- Firefly Tools {" "}
- developed by {" "}
- Me {"(Kain)"}
-
-
-
+ {/* Section 1: About SR Tools */}
+
+
+ ℹ️
+ About Firefly Tools
+
+
+
+
🏠
+
+ This site is a another version of {" "}
+ Firefly Tools {" "}
+ developed by {" "}
+ Me {"(Kain)"}
+
+
+
🏆
@@ -37,7 +37,7 @@ export default function FireflyToolsPage() {
https://srtools.kain.id.vn/
-
+
🔄
@@ -53,106 +53,106 @@ export default function FireflyToolsPage() {
-
-
👨💻
-
The original tool was created by a third-party developer named Amazing . This version is directly based on that work, without modification to core logic.
-
-
-
-
+
+
👨💻
+
The original tool was created by a third-party developer named Amazing . This version is directly based on that work, without modification to core logic.
+
+
+
+
- {/* Section 2: Main Features */}
-
-
- 🔧
- Main Features
-
-
-
-
⚙️
-
Configure characters, light cones, relics, traces, and eidolons easily in your browser.
-
-
-
🔌
-
Instantly apply setups to Firefly GO Server using Connect PS — no manual file uploads required.
-
-
-
✨
-
-
Extra Settings
-
- Enhance your Firefly GO Server experience with extra features:
-
-
- 🎭 Hidden Game UI — remove the entire game interface.
- 🚫 Disable Censorship — get rid of Lens Flare censor 💀.
- 🧪 Theorycraft Mode — configure HP, cycles, and more via the web.
-
-
-
+ {/* Section 2: Main Features */}
+
+
+ 🔧
+ Main Features
+
+
+
+
⚙️
+
Configure characters, light cones, relics, traces, and eidolons easily in your browser.
+
+
+
🔌
+
Instantly apply setups to Firefly GO Server using Connect PS — no manual file uploads required.
+
+
+
✨
+
+
Extra Settings
+
+ Enhance your Firefly GO Server experience with extra features:
+
+
+ 🎭 Hidden Game UI — remove the entire game interface.
+ 🚫 Disable Censorship — get rid of Lens Flare censor 💀.
+ 🧪 Theorycraft Mode — configure HP, cycles, and more via the web.
+
+
+
-
-
📂
-
Export and import full builds using freesr-data.json.
-
-
-
⚡
-
Fast testing workflow — no sync cooldowns, instant in-game updates.
-
-
-
+
+
📂
+
Export and import full builds using freesr-data.json.
+
+
+
⚡
+
Fast testing workflow — no sync cooldowns, instant in-game updates.
+
+
+
- {/* Section 3: Getting Started */}
-
-
- 🚀
- Getting Started
-
-
-
-
1️⃣
-
Access the tool through your browser at the self-hosted instance.
-
-
-
2️⃣
-
Configure your character builds with the intuitive web interface.
-
-
-
3️⃣
-
Use Connect PS feature to instantly sync with your private server.
-
-
-
4️⃣
-
Test your builds in-game with real-time updates and modifications.
-
-
-
+ {/* Section 3: Getting Started */}
+
+
+ 🚀
+ Getting Started
+
+
+
+
1️⃣
+
Access the tool through your browser at the self-hosted instance.
+
+
+
2️⃣
+
Configure your character builds with the intuitive web interface.
+
+
+
3️⃣
+
Use Connect PS feature to instantly sync with your private server.
+
+
+
4️⃣
+
Test your builds in-game with real-time updates and modifications.
+
+
+
-
- Back to Home
-
-
-
- );
+
+ Back to Home
+
+
+
+ );
}
\ No newline at end of file
diff --git a/frontend/src/pages/launcher/index.tsx b/frontend/src/pages/launcher/index.tsx
index 616e1a6..472035b 100644
--- a/frontend/src/pages/launcher/index.tsx
+++ b/frontend/src/pages/launcher/index.tsx
@@ -10,6 +10,7 @@ import useLauncherStore from '@/stores/launcherStore';
import { motion } from 'motion/react';
import { Link } from '@tanstack/react-router';
import { CheckUpdateLauncher, CheckUpdateProxy, CheckUpdateServer, sleep, UpdateLauncher, UpdateProxy, UpdateServer } from '@/helper';
+import UpdateModal from '@/components/updateModal';
export default function LauncherPage() {
const { gamePath,
@@ -21,6 +22,7 @@ export default function LauncherPage() {
serverVersion,
proxyVersion,
} = useSettingStore()
+
const {
isOpenDownloadDataModal,
isOpenUpdateDataModal,
@@ -29,6 +31,7 @@ export default function LauncherPage() {
setIsOpenUpdateDataModal,
setIsOpenSelfUpdateModal
} = useModalStore()
+
const {
isLoading,
downloadType,
@@ -54,6 +57,33 @@ export default function LauncherPage() {
setUpdateData,
} = useLauncherStore()
+ const widgetLinks = [
+ {
+ tooltip: "Firefly SRAnalysis",
+ href: "https://sranalysis.kain.id.vn/",
+ img: "https://sranalysis.kain.id.vn/ff-sranalysis.png",
+ btnClass: "btn-primary"
+ },
+ {
+ tooltip: "Firefly SRTools",
+ href: "https://srtools.kain.id.vn/",
+ img: "https://srtools.kain.id.vn/ff-srtool.png",
+ btnClass: "btn-secondary"
+ },
+ {
+ tooltip: "Amazing's SRTools (Original UI)",
+ href: "https://srtools.pages.dev/",
+ img: "https://icons.duckduckgo.com/ip3/srtools.pages.dev.ico",
+ btnClass: "btn-primary"
+ },
+ {
+ tooltip: "Amazing's SRTools (Modern UI)",
+ href: "https://srtools.neonteam.dev/",
+ img: "https://icons.duckduckgo.com/ip3/srtools.neonteam.dev.ico",
+ btnClass: "btn-secondary"
+ }
+ ]
+
useEffect(() => {
const check = async () => {
if (!serverVersion || !proxyVersion) {
@@ -200,18 +230,18 @@ export default function LauncherPage() {
setIsDownloading(true)
if (updateData.launcher.isUpdate) {
await UpdateLauncher(updateData.launcher.version)
- setUpdateData({...updateData, launcher: { isUpdate: false, isExists: true, version: updateData.launcher.version }})
+ setUpdateData({ ...updateData, launcher: { isUpdate: false, isExists: true, version: updateData.launcher.version } })
setIsOpenSelfUpdateModal(true)
}
if (updateData.server.isUpdate || !updateData.server.isExists) {
await UpdateServer(updateData.server.version)
setServerReady(true)
- setUpdateData({...updateData, server: { isUpdate: false, isExists: true, version: updateData.server.version }})
+ setUpdateData({ ...updateData, server: { isUpdate: false, isExists: true, version: updateData.server.version } })
}
if (updateData.proxy.isUpdate || !updateData.proxy.isExists) {
await UpdateProxy(updateData.proxy.version)
setProxyReady(true)
- setUpdateData({...updateData, proxy: { isUpdate: false, isExists: true, version: updateData.proxy.version }})
+ setUpdateData({ ...updateData, proxy: { isUpdate: false, isExists: true, version: updateData.proxy.version } })
}
setDownloadType("")
@@ -251,60 +281,21 @@ export default function LauncherPage() {
-
-
-
-
-
-
+ {widgetLinks.map((link, idx) => (
+
+ ))}
Change Game Path
- {
- const serverData = await CheckUpdateServer(serverPath, serverVersion)
- const proxyData = await CheckUpdateProxy(proxyPath, proxyVersion)
- const launcherData = await CheckUpdateLauncher()
- setUpdateData({
- server: serverData,
- proxy: proxyData,
- launcher: launcherData
- })
- if (launcherData.isUpdate) {
- setIsOpenSelfUpdateModal(true)
- return
- }
- if (!serverData.isExists || !proxyData.isExists) {
- setIsOpenDownloadDataModal(true)
- return
- }
- if (serverData.isUpdate || proxyData.isUpdate) {
- setIsOpenUpdateDataModal(true)
- return
- }
- toast.success("No updates available")
- }}>
- Check for Updates
-
+
+ {
+ const serverData = await CheckUpdateServer(serverPath, serverVersion)
+ const proxyData = await CheckUpdateProxy(proxyPath, proxyVersion)
+ setUpdateData({
+ server: serverData,
+ proxy: proxyData,
+ launcher: updateData.launcher
+ })
+
+ if (!serverData.isExists || !proxyData.isExists) {
+ setIsOpenDownloadDataModal(true)
+ return
+ }
+ if (serverData.isUpdate || proxyData.isUpdate) {
+ setIsOpenUpdateDataModal(true)
+ return
+ }
+ toast.success("No updates available")
+ }}>
+ Check for Updates Server & Proxy
+
+
+
+
+
{
if (serverPath) {
FSService.OpenFolder("./server")
@@ -395,34 +387,35 @@ export default function LauncherPage() {
{/* Downloading */}
{isDownloading && (
- updateData.proxy.isUpdate
- || updateData.server.isUpdate
- || !updateData.proxy.isExists
+ updateData.proxy.isUpdate
+ || updateData.server.isUpdate
+ || !updateData.proxy.isExists
|| !updateData.server.isExists
) && (
-
-
-
-
{downloadType}
-
-
{downloadSpeed}
-
{progressDownload.toFixed(1)}%
+
+
+
+
{downloadType}
+
+ {downloadSpeed}
+ {progressDownload.toFixed(1)}%
+
+
+
+
+
+
+ {progressDownload < 100 ? 'Please wait...' : 'Complete!'}
-
-
-
-
- {progressDownload < 100 ? 'Please wait...' : 'Complete!'}
-
-
- )}
+ )}
+
{isDownloading && updateData.launcher.isUpdate && (
@@ -430,10 +423,10 @@ export default function LauncherPage() {
{downloadType === "update:launcher:downloading" && "Updating launcher"}
@@ -476,141 +469,38 @@ export default function LauncherPage() {
)}
{/* Modal */}
- {isOpenUpdateDataModal && (
-
-
-
setIsOpenUpdateDataModal(false)}
- >
- ✕
-
+
setIsOpenUpdateDataModal(false)}
+ title="Update Data"
+ message="Do you want to update data server and proxy?"
+ buttons={[
+ { text: "No", onClick: () => setIsOpenUpdateDataModal(false), variant: "outline" },
+ { text: "Yes", onClick: async () => { setIsOpenUpdateDataModal(false); await handlerUpdateData() }, variant: "primary" }
+ ]}
+ />
-
-
- Update Data
-
-
+ setIsOpenDownloadDataModal(false)}
+ title="Download Data"
+ message="Data server and proxy download required"
+ buttons={[
+ { text: "Download", onClick: async () => { setIsOpenDownloadDataModal(false); await handlerUpdateData() }, variant: "primary" }
+ ]}
+ />
-
-
-
- Do you want to update data server and proxy?
-
-
+
setIsOpenSelfUpdateModal(false)}
+ title="Update Launcher"
+ message="Do you want to update launcher?"
+ buttons={[
+ { text: "No", onClick: () => setIsOpenSelfUpdateModal(false), variant: "outline" },
+ { text: "Yes", onClick: async () => { setIsOpenSelfUpdateModal(false); await handlerUpdateData() }, variant: "primary" }
+ ]}
+ />
-
- setIsOpenUpdateDataModal(false)}
- >
- No
-
- {
- setIsOpenUpdateDataModal(false)
- await handlerUpdateData()
- }}
- >
- Yes
-
-
-
-
-
- )}
-
- {isOpenDownloadDataModal && (
-
-
-
-
- Download Data
-
-
-
-
-
-
- Data server and proxy download required
-
-
-
-
- {
- setIsOpenDownloadDataModal(false)
- await handlerUpdateData()
- }}
- >
- Download
-
-
-
-
-
- )}
-
- {isOpenSelfUpdateModal && (
-
-
-
setIsOpenSelfUpdateModal(false)}
- >
- ✕
-
-
-
-
- Update Launcher
-
-
-
-
-
-
- Do you want to update launcher?
-
-
-
-
- setIsOpenSelfUpdateModal(false)}
- >
- No
-
- {
- setIsOpenSelfUpdateModal(false)
- await handlerUpdateData()
- }}
- >
- Yes
-
-
-
-
-
- )}
)
}
diff --git a/frontend/src/routes/__root.tsx b/frontend/src/routes/__root.tsx
index 314aeae..357eeb3 100644
--- a/frontend/src/routes/__root.tsx
+++ b/frontend/src/routes/__root.tsx
@@ -1,107 +1,31 @@
-import { createRootRoute, Link, Outlet } from '@tanstack/react-router'
-import ThemeController from '../components/themeController'
+import { createRootRoute, Outlet } from '@tanstack/react-router'
import { ToastContainer } from 'react-toastify'
import { useGlobalEvents } from '@/hooks';
-import useLauncherStore from '@/stores/launcherStore';
-import useDiffStore from '@/stores/diffStore';
+import useModalStore from '@/stores/modalStore';;
+import SettingModal from '@/components/settingModal';
+import CloseModal from '@/components/closeModal';
+import Header from '@/components/header';
export const Route = createRootRoute({
component: RootLayout
})
function RootLayout() {
- const { setGameRunning, setServerRunning, setProxyRunning, setProgressDownload, setDownloadSpeed } = useLauncherStore()
- const { setProgressUpdate, setMaxProgressUpdate, setMessageUpdate, setStageType } = useDiffStore()
- useGlobalEvents({
- setGameRunning,
- setServerRunning,
- setProxyRunning,
- setProgressUpdate,
- setMaxProgressUpdate,
- setProgressDownload,
- setDownloadSpeed,
- setMessageUpdate,
- setStageType
- });
+ const { setIsOpenCloseModal, isOpenCloseModal, isOpenSettingModal, setIsOpenSettingModal } = useModalStore()
+
+ useGlobalEvents();
+
return (
<>
-
-
-
-
-
- Home
+
-
- Tools
-
-
-
- Plugins
-
- Analysis (Veritas)
- SrTools
-
-
- How to?
- About
-
-
-
-
-
-
-
- Firefly
-
- Launcher
-
-
-
By Kain
-
-
-
-
-
-
- Home
-
-
- Tools
-
-
-
-
-
- Plugins
-
- Analysis (Veritas)
- Firefly Tools
-
-
-
- How to?
- About
-
-
-
-
-
-
+
+
setIsOpenCloseModal(false)} />
+ setIsOpenSettingModal(false)} />
>
)
diff --git a/frontend/src/stores/modalStore.ts b/frontend/src/stores/modalStore.ts
index 1349adb..004be15 100644
--- a/frontend/src/stores/modalStore.ts
+++ b/frontend/src/stores/modalStore.ts
@@ -5,18 +5,26 @@ interface ModalState {
isOpenDownloadDataModal: boolean;
isOpenUpdateDataModal: boolean;
isOpenSelfUpdateModal: boolean;
+ isOpenCloseModal: boolean;
+ isOpenSettingModal: boolean;
setIsOpenDownloadDataModal: (modal: boolean) => void;
setIsOpenUpdateDataModal: (modal: boolean) => void;
setIsOpenSelfUpdateModal: (modal: boolean) => void;
+ setIsOpenCloseModal: (modal: boolean) => void;
+ setIsOpenSettingModal: (modal: boolean) => void;
}
const useModalStore = create((set, get) => ({
isOpenDownloadDataModal: false,
isOpenUpdateDataModal: false,
isOpenSelfUpdateModal: false,
+ isOpenCloseModal: false,
+ isOpenSettingModal: false,
setIsOpenDownloadDataModal: (modal: boolean) => set({ isOpenDownloadDataModal: modal }),
setIsOpenUpdateDataModal: (modal: boolean) => set({ isOpenUpdateDataModal: modal }),
setIsOpenSelfUpdateModal: (modal: boolean) => set({ isOpenSelfUpdateModal: modal }),
+ setIsOpenCloseModal: (modal: boolean) => set({ isOpenCloseModal: modal }),
+ setIsOpenSettingModal: (modal: boolean) => set({ isOpenSettingModal: modal }),
}));
export default useModalStore;
\ No newline at end of file
diff --git a/frontend/src/stores/settingStore.ts b/frontend/src/stores/settingStore.ts
index 0f717ed..41ccb99 100644
--- a/frontend/src/stores/settingStore.ts
+++ b/frontend/src/stores/settingStore.ts
@@ -10,6 +10,11 @@ interface SettingState {
proxyPath: string;
serverVersion: string;
proxyVersion: string;
+ closingOption: {
+ isMinimize: boolean;
+ isAsk: boolean;
+ }
+ setClosingOption: (newClosingOption: { isMinimize: boolean; isAsk: boolean }) => void;
setLocale: (newLocale: string) => void;
setGamePath: (newGamePath: string) => void;
setGameDir: (newGameDir: string) => void;
@@ -29,6 +34,11 @@ const useSettingStore = create()(
proxyPath: "",
serverVersion: "",
proxyVersion: "",
+ closingOption: {
+ isMinimize: false,
+ isAsk: true,
+ },
+ setClosingOption: (newClosingOption: { isMinimize: boolean; isAsk: boolean }) => set({ closingOption: newClosingOption }),
setLocale: (newLocale: string) => set({ locale: newLocale }),
setGamePath: (newGamePath: string) => set({ gamePath: newGamePath }),
setGameDir: (newGameDir: string) => set({ gameDir: newGameDir }),
diff --git a/internal/app-service/app.go b/internal/app-service/app.go
index ebf4350..546d3ae 100644
--- a/internal/app-service/app.go
+++ b/internal/app-service/app.go
@@ -19,4 +19,19 @@ func (a *AppService) CloseAppAfterTimeout(timeout int) (bool, string) {
application.Get().Quit()
}()
return true, ""
-}
\ No newline at end of file
+}
+
+func (a *AppService) CloseApp() (bool, string) {
+ application.Get().Quit()
+ return true, ""
+}
+
+func (a *AppService) MinimizeApp() (bool, string) {
+ window := application.Get().Window.Current()
+ if window == nil {
+ return false, "not found window"
+ }
+ window.Hide()
+ return true, ""
+}
+
diff --git a/main.go b/main.go
index 859cfcb..1060eab 100644
--- a/main.go
+++ b/main.go
@@ -17,6 +17,7 @@ import (
"path/filepath"
"github.com/wailsapp/wails/v3/pkg/application"
+ "github.com/wailsapp/wails/v3/pkg/events"
)
//go:embed all:frontend/dist
@@ -84,7 +85,7 @@ func main() {
})
// Create window
- app.Window.NewWithOptions(application.WebviewWindowOptions{
+ window := app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "Firefly Launcher - Kain",
Mac: application.MacWindow{
InvisibleTitleBarHeight: 50,
@@ -106,13 +107,20 @@ func main() {
// Attach the window to the system tray
menu := application.NewMenu()
menu.Add("Open").OnClick(func(ctx *application.Context) {
- // Handle click
+ window.Show()
})
menu.Add("Quit").OnClick(func(ctx *application.Context) {
app.Quit()
})
systemTray.SetMenu(menu)
+
+
+
+ window.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) {
+ app.Event.Emit("window:close")
+ e.Cancel()
+ })
err = app.Run()
if err != nil {
diff --git a/pkg/constant/constant.go b/pkg/constant/constant.go
index cc815e4..02993ec 100644
--- a/pkg/constant/constant.go
+++ b/pkg/constant/constant.go
@@ -10,7 +10,7 @@ const ProxyZipFile = "64bit.zip"
const LauncherFile = "firefly-launcher.exe"
const TempUrl = "./temp"
-const CurrentLauncherVersion = "2.0.1"
+const CurrentLauncherVersion = "2.1.0"
type ToolFile string