Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 892ea44c17 | |||
| ca612797ee | |||
| 787962c6d0 | |||
| 09434fcc5b | |||
| edbe04b9fc | |||
| a6b49bef24 | |||
| dcdb3863ca | |||
| d5710c9102 | |||
| 100c93e8da | |||
| 1e17f76340 | |||
| 1f9c95d6ac | |||
| d58e76d821 | |||
| a4de02bc18 | |||
| a021658fa9 | |||
| 6b222bfa70 |
3
.gitignore
vendored
@@ -1,9 +1,10 @@
|
|||||||
.task
|
.task
|
||||||
bin
|
bin/
|
||||||
tests/
|
tests/
|
||||||
server/
|
server/
|
||||||
proxy/
|
proxy/
|
||||||
temp/
|
temp/
|
||||||
|
.history/
|
||||||
frontend/dist
|
frontend/dist
|
||||||
frontend/node_modules
|
frontend/node_modules
|
||||||
build/linux/appimage/build
|
build/linux/appimage/build
|
||||||
|
|||||||
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Firefly Shelter
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
39
Makefile
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
PROTO_SRC=./proto
|
||||||
|
GEN_DEST=./pkg
|
||||||
|
|
||||||
|
proto:
|
||||||
|
@echo Compiling Protobuf files...
|
||||||
|
protoc --go_out=${GEN_DEST} --go-grpc_out=${GEN_DEST} ${PROTO_SRC}/*.proto
|
||||||
|
@echo Done!
|
||||||
|
|
||||||
|
icon:
|
||||||
|
@echo Updating icons...
|
||||||
|
magick build/appicon.png -define icon:auto-resize=16,24,32,48,64,128,256 build/windows/icon.ico
|
||||||
|
magick build/appicon.png -define icon:auto-resize=16,24,32,48,64,128,256,512 build/windows/icons.ico
|
||||||
|
@echo Done!
|
||||||
|
|
||||||
|
dev:
|
||||||
|
@echo Starting development server...
|
||||||
|
wails3 dev
|
||||||
|
@echo Done!
|
||||||
|
|
||||||
|
build:
|
||||||
|
@echo Building application...
|
||||||
|
wails3 build
|
||||||
|
@echo Done!
|
||||||
|
|
||||||
|
generate:
|
||||||
|
@echo Generating bindings...
|
||||||
|
wails3 generate bindings -ts
|
||||||
|
@echo Done!
|
||||||
|
|
||||||
|
release:
|
||||||
|
@echo Building release application...
|
||||||
|
wails3 package
|
||||||
|
@echo Done!
|
||||||
|
|
||||||
|
update:
|
||||||
|
@echo Updating build assets...
|
||||||
|
wails3 update build-assets -name "firefly-launcher" -binaryname "firefly-launcher" -config build/config.yml
|
||||||
|
@echo Done!
|
||||||
|
|
||||||
BIN
assets/appicon.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
assets/hpatchz.exe
Normal file
@@ -22,15 +22,19 @@ tasks:
|
|||||||
- npm install
|
- npm install
|
||||||
|
|
||||||
build:frontend:
|
build:frontend:
|
||||||
|
label: build:frontend (PRODUCTION={{.PRODUCTION}})
|
||||||
summary: Build the frontend project
|
summary: Build the frontend project
|
||||||
dir: frontend
|
dir: frontend
|
||||||
sources:
|
sources:
|
||||||
- "**/*"
|
- "**/*"
|
||||||
generates:
|
generates:
|
||||||
- dist/*
|
- dist/**/*
|
||||||
deps:
|
deps:
|
||||||
- task: install:frontend:deps
|
- task: install:frontend:deps
|
||||||
- task: generate:bindings
|
- task: generate:bindings
|
||||||
|
vars:
|
||||||
|
BUILD_FLAGS:
|
||||||
|
ref: .BUILD_FLAGS
|
||||||
cmds:
|
cmds:
|
||||||
- npm run {{.BUILD_COMMAND}} -q
|
- npm run {{.BUILD_COMMAND}} -q
|
||||||
env:
|
env:
|
||||||
@@ -40,17 +44,21 @@ tasks:
|
|||||||
|
|
||||||
|
|
||||||
generate:bindings:
|
generate:bindings:
|
||||||
|
label: generate:bindings (BUILD_FLAGS={{.BUILD_FLAGS}})
|
||||||
summary: Generates bindings for the frontend
|
summary: Generates bindings for the frontend
|
||||||
deps:
|
deps:
|
||||||
- task: go:mod:tidy
|
- task: go:mod:tidy
|
||||||
sources:
|
sources:
|
||||||
|
- "**/*.[jt]s"
|
||||||
|
- exclude: frontend/**/*
|
||||||
|
- frontend/bindings/**/* # Rerun when switching between dev/production mode causes changes in output
|
||||||
- "**/*.go"
|
- "**/*.go"
|
||||||
- go.mod
|
- go.mod
|
||||||
- go.sum
|
- go.sum
|
||||||
generates:
|
generates:
|
||||||
- "frontend/bindings/**/*"
|
- frontend/bindings/**/*
|
||||||
cmds:
|
cmds:
|
||||||
- wails3 generate bindings -f '{{.BUILD_FLAGS}}' -clean=true
|
- wails3 generate bindings -f '{{.BUILD_FLAGS}}' -clean=true
|
||||||
|
|
||||||
generate:icons:
|
generate:icons:
|
||||||
summary: Generates Windows `.ico` and Mac `.icns` files from an image
|
summary: Generates Windows `.ico` and Mac `.icns` files from an image
|
||||||
@@ -58,10 +66,10 @@ tasks:
|
|||||||
sources:
|
sources:
|
||||||
- "appicon.png"
|
- "appicon.png"
|
||||||
generates:
|
generates:
|
||||||
- "icons.icns"
|
- "darwin/icons.icns"
|
||||||
- "icons.ico"
|
- "windows/icon.ico"
|
||||||
cmds:
|
cmds:
|
||||||
- wails3 generate icons -input appicon.png -macfilename darwin/icons.icns -windowsfilename windows/icons.ico
|
- wails3 generate icons -input appicon.png -macfilename darwin/icons.icns -windowsfilename windows/icon.ico
|
||||||
|
|
||||||
dev:frontend:
|
dev:frontend:
|
||||||
summary: Runs the frontend in development mode
|
summary: Runs the frontend in development mode
|
||||||
@@ -75,4 +83,4 @@ tasks:
|
|||||||
summary: Updates the build assets
|
summary: Updates the build assets
|
||||||
dir: build
|
dir: build
|
||||||
cmds:
|
cmds:
|
||||||
- wails3 update build-assets -name "{{.APP_NAME}}" -binaryname "{{.APP_NAME}}" -config config.yml -dir .
|
- wails3 update build-assets -name "{{.APP_NAME}}" -binaryname "{{.APP_NAME}}" -config config.yml -dir .
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 304 KiB |
@@ -8,10 +8,10 @@ info:
|
|||||||
companyName: "Firefly Shelter" # The name of the company
|
companyName: "Firefly Shelter" # The name of the company
|
||||||
productName: "Firefly Launcher" # The name of the application
|
productName: "Firefly Launcher" # The name of the application
|
||||||
productIdentifier: "com.fireflyshelter.fireflylauncher" # The unique product identifier
|
productIdentifier: "com.fireflyshelter.fireflylauncher" # The unique product identifier
|
||||||
description: "Custom game launcher built for convenience and quick access" # The application description
|
description: "Firefly Launcher" # The application description
|
||||||
copyright: "@ 2025, Firefly Shelter" # Copyright text
|
copyright: "@ 2025, Firefly Shelter" # Copyright text
|
||||||
comments: "Custom game launcher built for convenience and quick access" # Comments
|
comments: "Firefly Launcher" # Comments
|
||||||
version: "0.1.0" # The application version
|
version: "1.0.0" # The application version
|
||||||
|
|
||||||
# Dev mode configuration
|
# Dev mode configuration
|
||||||
dev_mode:
|
dev_mode:
|
||||||
@@ -44,9 +44,19 @@ dev_mode:
|
|||||||
type: primary
|
type: primary
|
||||||
|
|
||||||
# File Associations
|
# File Associations
|
||||||
# More information at: https://v3alpha.wails.io/noit/done/yet
|
# More information at: https://v3.wails.io/noit/done/yet
|
||||||
fileAssociations:
|
fileAssociations:
|
||||||
|
# - ext: wails
|
||||||
|
# name: Wails
|
||||||
|
# description: Wails Application File
|
||||||
|
# iconName: wailsFileIcon
|
||||||
|
# role: Editor
|
||||||
|
# - ext: jpg
|
||||||
|
# name: JPEG
|
||||||
|
# description: Image File
|
||||||
|
# iconName: jpegFileIcon
|
||||||
|
# role: Editor
|
||||||
|
# mimeType: image/jpeg # (optional)
|
||||||
|
|
||||||
# Other data
|
# Other data
|
||||||
other:
|
other:
|
||||||
|
|||||||
@@ -2,27 +2,27 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>Firefly Launcher</string>
|
<string>Firefly Launcher</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>firefly-launcher</string>
|
<string>firefly-launcher</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>com.fireflyshelter.fireflylauncher</string>
|
<string>com.fireflyshelter.fireflylauncher</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>0.1.0</string>
|
<string>1.0.0</string>
|
||||||
<key>CFBundleGetInfoString</key>
|
<key>CFBundleGetInfoString</key>
|
||||||
<string>Custom game launcher built for convenience and quick access</string>
|
<string>Firefly Launcher</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.1.0</string>
|
<string>1.0.0</string>
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>icons</string>
|
<string>icons</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>10.13.0</string>
|
<string>10.15.0</string>
|
||||||
<key>NSHighResolutionCapable</key>
|
<key>NSHighResolutionCapable</key>
|
||||||
<string>true</string>
|
<string>true</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>@ 2025, Firefly Shelter</string>
|
<string>@ 2025, Firefly Shelter</string>
|
||||||
<key>NSAppTransportSecurity</key>
|
<key>NSAppTransportSecurity</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSAllowsLocalNetworking</key>
|
<key>NSAllowsLocalNetworking</key>
|
||||||
|
|||||||
@@ -2,26 +2,26 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>Firefly Launcher</string>
|
<string>Firefly Launcher</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>firefly-launcher</string>
|
<string>firefly-launcher</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>com.fireflyshelter.fireflylauncher</string>
|
<string>com.fireflyshelter.fireflylauncher</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>0.1.0</string>
|
<string>1.0.0</string>
|
||||||
<key>CFBundleGetInfoString</key>
|
<key>CFBundleGetInfoString</key>
|
||||||
<string>Custom game launcher built for convenience and quick access</string>
|
<string>Firefly Launcher</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.1.0</string>
|
<string>1.0.0</string>
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>icons</string>
|
<string>icons</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>10.13.0</string>
|
<string>10.15.0</string>
|
||||||
<key>NSHighResolutionCapable</key>
|
<key>NSHighResolutionCapable</key>
|
||||||
<string>true</string>
|
<string>true</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>@ 2025, Firefly Shelter</string>
|
<string>@ 2025, Firefly Shelter</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
@@ -9,6 +9,11 @@ tasks:
|
|||||||
deps:
|
deps:
|
||||||
- task: common:go:mod:tidy
|
- task: common:go:mod:tidy
|
||||||
- task: common:build:frontend
|
- task: common:build:frontend
|
||||||
|
vars:
|
||||||
|
BUILD_FLAGS:
|
||||||
|
ref: .BUILD_FLAGS
|
||||||
|
PRODUCTION:
|
||||||
|
ref: .PRODUCTION
|
||||||
- task: common:generate:icons
|
- task: common:generate:icons
|
||||||
cmds:
|
cmds:
|
||||||
- go build {{.BUILD_FLAGS}} -o {{.OUTPUT}}
|
- go build {{.BUILD_FLAGS}} -o {{.OUTPUT}}
|
||||||
@@ -64,7 +69,13 @@ tasks:
|
|||||||
- cp build/darwin/icons.icns {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/Resources
|
- cp build/darwin/icons.icns {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/Resources
|
||||||
- cp {{.BIN_DIR}}/{{.APP_NAME}} {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/MacOS
|
- cp {{.BIN_DIR}}/{{.APP_NAME}} {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/MacOS
|
||||||
- cp build/darwin/Info.plist {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents
|
- cp build/darwin/Info.plist {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents
|
||||||
|
- codesign --force --deep --sign - {{.BIN_DIR}}/{{.APP_NAME}}.app
|
||||||
|
|
||||||
run:
|
run:
|
||||||
cmds:
|
cmds:
|
||||||
- '{{.BIN_DIR}}/{{.APP_NAME}}'
|
- mkdir -p {{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/{MacOS,Resources}
|
||||||
|
- cp build/darwin/icons.icns {{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/Resources
|
||||||
|
- cp {{.BIN_DIR}}/{{.APP_NAME}} {{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/MacOS
|
||||||
|
- cp build/darwin/Info.dev.plist {{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/Info.plist
|
||||||
|
- codesign --force --deep --sign - {{.BIN_DIR}}/{{.APP_NAME}}.dev.app
|
||||||
|
- '{{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/MacOS/{{.APP_NAME}}'
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ tasks:
|
|||||||
deps:
|
deps:
|
||||||
- task: common:go:mod:tidy
|
- task: common:go:mod:tidy
|
||||||
- task: common:build:frontend
|
- task: common:build:frontend
|
||||||
|
vars:
|
||||||
|
BUILD_FLAGS:
|
||||||
|
ref: .BUILD_FLAGS
|
||||||
|
PRODUCTION:
|
||||||
|
ref: .PRODUCTION
|
||||||
- task: common:generate:icons
|
- task: common:generate:icons
|
||||||
cmds:
|
cmds:
|
||||||
- go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/{{.APP_NAME}}
|
- go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/{{.APP_NAME}}
|
||||||
@@ -83,17 +88,17 @@ tasks:
|
|||||||
|
|
||||||
generate:deb:
|
generate:deb:
|
||||||
summary: Creates a deb package
|
summary: Creates a deb package
|
||||||
cmds:
|
cmds:
|
||||||
- wails3 tool package -name {{.APP_NAME}} -format deb -config ./build/linux/nfpm/nfpm.yaml -out {{.ROOT_DIR}}/bin
|
- wails3 tool package -name {{.APP_NAME}} -format deb -config ./build/linux/nfpm/nfpm.yaml -out {{.ROOT_DIR}}/bin
|
||||||
|
|
||||||
generate:rpm:
|
generate:rpm:
|
||||||
summary: Creates a rpm package
|
summary: Creates a rpm package
|
||||||
cmds:
|
cmds:
|
||||||
- wails3 tool package -name {{.APP_NAME}} -format rpm -config ./build/linux/nfpm/nfpm.yaml -out {{.ROOT_DIR}}/bin
|
- wails3 tool package -name {{.APP_NAME}} -format rpm -config ./build/linux/nfpm/nfpm.yaml -out {{.ROOT_DIR}}/bin
|
||||||
|
|
||||||
generate:aur:
|
generate:aur:
|
||||||
summary: Creates a arch linux packager package
|
summary: Creates a arch linux packager package
|
||||||
cmds:
|
cmds:
|
||||||
- wails3 tool package -name {{.APP_NAME}} -format archlinux -config ./build/linux/nfpm/nfpm.yaml -out {{.ROOT_DIR}}/bin
|
- wails3 tool package -name {{.APP_NAME}} -format archlinux -config ./build/linux/nfpm/nfpm.yaml -out {{.ROOT_DIR}}/bin
|
||||||
|
|
||||||
generate:dotdesktop:
|
generate:dotdesktop:
|
||||||
@@ -105,7 +110,7 @@ tasks:
|
|||||||
vars:
|
vars:
|
||||||
APP_NAME: '{{.APP_NAME}}'
|
APP_NAME: '{{.APP_NAME}}'
|
||||||
EXEC: '{{.APP_NAME}}'
|
EXEC: '{{.APP_NAME}}'
|
||||||
ICON: 'appicon'
|
ICON: '{{.APP_NAME}}'
|
||||||
CATEGORIES: 'Development;'
|
CATEGORIES: 'Development;'
|
||||||
OUTPUTFILE: '{{.ROOT_DIR}}/build/linux/{{.APP_NAME}}.desktop'
|
OUTPUTFILE: '{{.ROOT_DIR}}/build/linux/{{.APP_NAME}}.desktop'
|
||||||
|
|
||||||
|
|||||||
@@ -14,12 +14,21 @@ cp -r "${APP_BINARY}" "${APP_DIR}/usr/bin/"
|
|||||||
cp "${ICON_PATH}" "${APP_DIR}/"
|
cp "${ICON_PATH}" "${APP_DIR}/"
|
||||||
cp "${DESKTOP_FILE}" "${APP_DIR}/"
|
cp "${DESKTOP_FILE}" "${APP_DIR}/"
|
||||||
|
|
||||||
# Download linuxdeploy and make it executable
|
if [[ $(uname -m) == *x86_64* ]]; then
|
||||||
wget -q -4 -N https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
# Download linuxdeploy and make it executable
|
||||||
chmod +x linuxdeploy-x86_64.AppImage
|
wget -q -4 -N https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||||
|
chmod +x linuxdeploy-x86_64.AppImage
|
||||||
|
|
||||||
# Run linuxdeploy to bundle the application
|
# Run linuxdeploy to bundle the application
|
||||||
./linuxdeploy-x86_64.AppImage --appdir "${APP_DIR}" --output appimage
|
./linuxdeploy-x86_64.AppImage --appdir "${APP_DIR}" --output appimage
|
||||||
|
else
|
||||||
|
# Download linuxdeploy and make it executable (arm64)
|
||||||
|
wget -q -4 -N https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-aarch64.AppImage
|
||||||
|
chmod +x linuxdeploy-aarch64.AppImage
|
||||||
|
|
||||||
|
# Run linuxdeploy to bundle the application (arm64)
|
||||||
|
./linuxdeploy-aarch64.AppImage --appdir "${APP_DIR}" --output appimage
|
||||||
|
fi
|
||||||
|
|
||||||
# Rename the generated AppImage
|
# Rename the generated AppImage
|
||||||
mv "${APP_NAME}*.AppImage" "${APP_NAME}.AppImage"
|
mv "${APP_NAME}*.AppImage" "${APP_NAME}.AppImage"
|
||||||
|
|||||||
13
build/linux/desktop
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Version=1.0
|
||||||
|
Name=Firefly Launcher
|
||||||
|
Comment=Firefly Launcher
|
||||||
|
# The Exec line includes %u to pass the URL to the application
|
||||||
|
Exec=/usr/local/bin/firefly-launcher %u
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
Icon=firefly-launcher
|
||||||
|
Categories=Utility;
|
||||||
|
StartupWMClass=firefly-launcher
|
||||||
|
|
||||||
|
|
||||||
@@ -6,11 +6,11 @@
|
|||||||
name: "firefly-launcher"
|
name: "firefly-launcher"
|
||||||
arch: ${GOARCH}
|
arch: ${GOARCH}
|
||||||
platform: "linux"
|
platform: "linux"
|
||||||
version: "0.1.0"
|
version: "1.0.0"
|
||||||
section: "default"
|
section: "default"
|
||||||
priority: "extra"
|
priority: "extra"
|
||||||
maintainer: ${GIT_COMMITTER_NAME} <${GIT_COMMITTER_EMAIL}>
|
maintainer: ${GIT_COMMITTER_NAME} <${GIT_COMMITTER_EMAIL}>
|
||||||
description: "Custom game launcher built for convenience and quick access"
|
description: "Firefly Launcher"
|
||||||
vendor: "Firefly Shelter"
|
vendor: "Firefly Shelter"
|
||||||
homepage: "https://wails.io"
|
homepage: "https://wails.io"
|
||||||
license: "MIT"
|
license: "MIT"
|
||||||
@@ -24,9 +24,31 @@ contents:
|
|||||||
- src: "./build/linux/firefly-launcher.desktop"
|
- src: "./build/linux/firefly-launcher.desktop"
|
||||||
dst: "/usr/share/applications/firefly-launcher.desktop"
|
dst: "/usr/share/applications/firefly-launcher.desktop"
|
||||||
|
|
||||||
|
# Default dependencies for Debian 12/Ubuntu 22.04+ with WebKit 4.1
|
||||||
depends:
|
depends:
|
||||||
- gtk3
|
- libgtk-3-0
|
||||||
- libwebkit2gtk
|
- libwebkit2gtk-4.1-0
|
||||||
|
|
||||||
|
# Distribution-specific overrides for different package formats and WebKit versions
|
||||||
|
overrides:
|
||||||
|
# RPM packages for RHEL/CentOS/AlmaLinux/Rocky Linux (WebKit 4.0)
|
||||||
|
rpm:
|
||||||
|
depends:
|
||||||
|
- gtk3
|
||||||
|
- webkit2gtk4.1
|
||||||
|
|
||||||
|
# Arch Linux packages (WebKit 4.1)
|
||||||
|
archlinux:
|
||||||
|
depends:
|
||||||
|
- gtk3
|
||||||
|
- webkit2gtk-4.1
|
||||||
|
|
||||||
|
# scripts section to ensure desktop database is updated after install
|
||||||
|
scripts:
|
||||||
|
postinstall: "./build/linux/nfpm/scripts/postinstall.sh"
|
||||||
|
# You can also add preremove, postremove if needed
|
||||||
|
# preremove: "./build/linux/nfpm/scripts/preremove.sh"
|
||||||
|
# postremove: "./build/linux/nfpm/scripts/postremove.sh"
|
||||||
|
|
||||||
# replaces:
|
# replaces:
|
||||||
# - foobar
|
# - foobar
|
||||||
@@ -43,8 +65,3 @@ depends:
|
|||||||
# - not-foo
|
# - not-foo
|
||||||
# - not-bar
|
# - not-bar
|
||||||
# changelog: "changelog.yaml"
|
# changelog: "changelog.yaml"
|
||||||
# scripts:
|
|
||||||
# preinstall: ./build/linux/nfpm/scripts/preinstall.sh
|
|
||||||
# postinstall: ./build/linux/nfpm/scripts/postinstall.sh
|
|
||||||
# preremove: ./build/linux/nfpm/scripts/preremove.sh
|
|
||||||
# postremove: ./build/linux/nfpm/scripts/postremove.sh
|
|
||||||
|
|||||||
@@ -1 +1,21 @@
|
|||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Update desktop database for .desktop file changes
|
||||||
|
# This makes the application appear in application menus and registers its capabilities.
|
||||||
|
if command -v update-desktop-database >/dev/null 2>&1; then
|
||||||
|
echo "Updating desktop database..."
|
||||||
|
update-desktop-database -q /usr/share/applications
|
||||||
|
else
|
||||||
|
echo "Warning: update-desktop-database command not found. Desktop file may not be immediately recognized." >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update MIME database for custom URL schemes (x-scheme-handler)
|
||||||
|
# This ensures the system knows how to handle your custom protocols.
|
||||||
|
if command -v update-mime-database >/dev/null 2>&1; then
|
||||||
|
echo "Updating MIME database..."
|
||||||
|
update-mime-database -n /usr/share/mime
|
||||||
|
else
|
||||||
|
echo "Warning: update-mime-database command not found. Custom URL schemes may not be immediately recognized." >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ tasks:
|
|||||||
- task: common:go:mod:tidy
|
- task: common:go:mod:tidy
|
||||||
- task: common:build:frontend
|
- task: common:build:frontend
|
||||||
vars:
|
vars:
|
||||||
PRODUCTION: '{{.PRODUCTION}}'
|
BUILD_FLAGS:
|
||||||
|
ref: .BUILD_FLAGS
|
||||||
|
PRODUCTION:
|
||||||
|
ref: .PRODUCTION
|
||||||
- task: common:generate:icons
|
- task: common:generate:icons
|
||||||
cmds:
|
cmds:
|
||||||
- task: generate:syso
|
- task: generate:syso
|
||||||
@@ -28,9 +31,11 @@ tasks:
|
|||||||
PRODUCTION: '{{.PRODUCTION | default "false"}}'
|
PRODUCTION: '{{.PRODUCTION | default "false"}}'
|
||||||
|
|
||||||
package:
|
package:
|
||||||
summary: Packages a production build of the application into a `.exe` bundle
|
summary: Packages a production build of the application
|
||||||
|
vars:
|
||||||
|
FORMAT: '{{.FORMAT | default "nsis"}}'
|
||||||
cmds:
|
cmds:
|
||||||
- task: create:nsis:installer
|
- task: '{{if eq .FORMAT "msix"}}create:msix:package{{else}}create:nsis:installer{{end}}'
|
||||||
|
|
||||||
generate:syso:
|
generate:syso:
|
||||||
summary: Generates Windows `.syso` file
|
summary: Generates Windows `.syso` file
|
||||||
@@ -49,12 +54,42 @@ tasks:
|
|||||||
PRODUCTION: "true"
|
PRODUCTION: "true"
|
||||||
cmds:
|
cmds:
|
||||||
# Create the Microsoft WebView2 bootstrapper if it doesn't exist
|
# Create the Microsoft WebView2 bootstrapper if it doesn't exist
|
||||||
- wails3 generate webview2bootstrapper -dir "{{.ROOT_DIR}}\build\windows\nsis"
|
- wails3 generate webview2bootstrapper -dir "{{.ROOT_DIR}}/build/windows/nsis"
|
||||||
- makensis -DARG_WAILS_{{.ARG_FLAG}}_BINARY="{{.ROOT_DIR}}\{{.BIN_DIR}}\{{.APP_NAME}}.exe" project.nsi
|
- cmd: >
|
||||||
|
powershell -NoProfile -Command "& makensis ('-DARG_WAILS_{{.ARG_FLAG}}_BINARY=' + (\"{{.ROOT_DIR}}/{{.BIN_DIR}}/{{.APP_NAME}}.exe\" -replace '/', '\\')) 'project.nsi'"
|
||||||
|
platforms: [windows]
|
||||||
vars:
|
vars:
|
||||||
ARCH: '{{.ARCH | default ARCH}}'
|
ARCH: '{{.ARCH | default ARCH}}'
|
||||||
ARG_FLAG: '{{if eq .ARCH "amd64"}}AMD64{{else}}ARM64{{end}}'
|
ARG_FLAG: '{{if eq .ARCH "amd64"}}AMD64{{else}}ARM64{{end}}'
|
||||||
|
|
||||||
|
create:msix:package:
|
||||||
|
summary: Creates an MSIX package
|
||||||
|
deps:
|
||||||
|
- task: build
|
||||||
|
vars:
|
||||||
|
PRODUCTION: "true"
|
||||||
|
cmds:
|
||||||
|
- |-
|
||||||
|
wails3 tool msix \
|
||||||
|
--config "{{.ROOT_DIR}}/wails.json" \
|
||||||
|
--name "{{.APP_NAME}}" \
|
||||||
|
--executable "{{.ROOT_DIR}}/{{.BIN_DIR}}/{{.APP_NAME}}.exe" \
|
||||||
|
--arch "{{.ARCH}}" \
|
||||||
|
--out "{{.ROOT_DIR}}/{{.BIN_DIR}}/{{.APP_NAME}}-{{.ARCH}}.msix" \
|
||||||
|
{{if .CERT_PATH}}--cert "{{.CERT_PATH}}"{{end}} \
|
||||||
|
{{if .PUBLISHER}}--publisher "{{.PUBLISHER}}"{{end}} \
|
||||||
|
{{if .USE_MSIX_TOOL}}--use-msix-tool{{else}}--use-makeappx{{end}}
|
||||||
|
vars:
|
||||||
|
ARCH: '{{.ARCH | default ARCH}}'
|
||||||
|
CERT_PATH: '{{.CERT_PATH | default ""}}'
|
||||||
|
PUBLISHER: '{{.PUBLISHER | default ""}}'
|
||||||
|
USE_MSIX_TOOL: '{{.USE_MSIX_TOOL | default "false"}}'
|
||||||
|
|
||||||
|
install:msix:tools:
|
||||||
|
summary: Installs tools required for MSIX packaging
|
||||||
|
cmds:
|
||||||
|
- wails3 tool msix-install-tools
|
||||||
|
|
||||||
run:
|
run:
|
||||||
cmds:
|
cmds:
|
||||||
- '{{.BIN_DIR}}\\{{.APP_NAME}}.exe'
|
- '{{.BIN_DIR}}/{{.APP_NAME}}.exe'
|
||||||
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 157 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 592 KiB |
@@ -1,15 +1,15 @@
|
|||||||
{
|
{
|
||||||
"fixed": {
|
"fixed": {
|
||||||
"file_version": "0.1.0"
|
"file_version": "1.0.0"
|
||||||
},
|
},
|
||||||
"info": {
|
"info": {
|
||||||
"0000": {
|
"0000": {
|
||||||
"ProductVersion": "0.1.0",
|
"ProductVersion": "1.0.0",
|
||||||
"CompanyName": "Firefly Shelter",
|
"CompanyName": "Firefly Shelter",
|
||||||
"FileDescription": "Custom game launcher built for convenience and quick access",
|
"FileDescription": "Firefly Launcher",
|
||||||
"LegalCopyright": "@ 2025, Firefly Shelter",
|
"LegalCopyright": "@ 2025, Firefly Shelter",
|
||||||
"ProductName": "Firefly Launcher",
|
"ProductName": "Firefly Launcher",
|
||||||
"Comments": "Custom game launcher built for convenience and quick access"
|
"Comments": "Firefly Launcher"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
52
build/windows/msix/app_manifest.xml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Package
|
||||||
|
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||||
|
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||||
|
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||||
|
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10">
|
||||||
|
|
||||||
|
<Identity
|
||||||
|
Name="com.wails.firefly-launcher"
|
||||||
|
Publisher="CN=My Company"
|
||||||
|
Version="0.1.0.0"
|
||||||
|
ProcessorArchitecture="x64" />
|
||||||
|
|
||||||
|
<Properties>
|
||||||
|
<DisplayName>My Product</DisplayName>
|
||||||
|
<PublisherDisplayName>My Company</PublisherDisplayName>
|
||||||
|
<Description>My Product Description</Description>
|
||||||
|
<Logo>Assets\StoreLogo.png</Logo>
|
||||||
|
</Properties>
|
||||||
|
|
||||||
|
<Dependencies>
|
||||||
|
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||||
|
</Dependencies>
|
||||||
|
|
||||||
|
<Resources>
|
||||||
|
<Resource Language="en-us" />
|
||||||
|
</Resources>
|
||||||
|
|
||||||
|
<Applications>
|
||||||
|
<Application Id="com.wails.firefly-launcher" Executable="firefly-launcher.exe" EntryPoint="Windows.FullTrustApplication">
|
||||||
|
<uap:VisualElements
|
||||||
|
DisplayName="My Product"
|
||||||
|
Description="My Product Description"
|
||||||
|
BackgroundColor="transparent"
|
||||||
|
Square150x150Logo="Assets\Square150x150Logo.png"
|
||||||
|
Square44x44Logo="Assets\Square44x44Logo.png">
|
||||||
|
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
|
||||||
|
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
||||||
|
</uap:VisualElements>
|
||||||
|
|
||||||
|
<Extensions>
|
||||||
|
<desktop:Extension Category="windows.fullTrustProcess" Executable="firefly-launcher.exe" />
|
||||||
|
|
||||||
|
</Extensions>
|
||||||
|
</Application>
|
||||||
|
</Applications>
|
||||||
|
|
||||||
|
<Capabilities>
|
||||||
|
<rescap:Capability Name="runFullTrust" />
|
||||||
|
|
||||||
|
</Capabilities>
|
||||||
|
</Package>
|
||||||
54
build/windows/msix/template.xml
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<MsixPackagingToolTemplate
|
||||||
|
xmlns="http://schemas.microsoft.com/msix/packaging/msixpackagingtool/template/2022">
|
||||||
|
<Settings
|
||||||
|
AllowTelemetry="false"
|
||||||
|
ApplyACLsToPackageFiles="true"
|
||||||
|
GenerateCommandLineFile="true"
|
||||||
|
AllowPromptForPassword="false">
|
||||||
|
</Settings>
|
||||||
|
<Installer
|
||||||
|
Path="firefly-launcher.exe"
|
||||||
|
Arguments=""
|
||||||
|
InstallLocation="C:\Program Files\My Company\My Product">
|
||||||
|
</Installer>
|
||||||
|
<PackageInformation
|
||||||
|
PackageName="My Product"
|
||||||
|
PackageDisplayName="My Product"
|
||||||
|
PublisherName="CN=My Company"
|
||||||
|
PublisherDisplayName="My Company"
|
||||||
|
Version="0.1.0.0"
|
||||||
|
PackageDescription="My Product Description">
|
||||||
|
<Capabilities>
|
||||||
|
<Capability Name="runFullTrust" />
|
||||||
|
|
||||||
|
</Capabilities>
|
||||||
|
<Applications>
|
||||||
|
<Application
|
||||||
|
Id="com.wails.firefly-launcher"
|
||||||
|
Description="My Product Description"
|
||||||
|
DisplayName="My Product"
|
||||||
|
ExecutableName="firefly-launcher.exe"
|
||||||
|
EntryPoint="Windows.FullTrustApplication">
|
||||||
|
|
||||||
|
</Application>
|
||||||
|
</Applications>
|
||||||
|
<Resources>
|
||||||
|
<Resource Language="en-us" />
|
||||||
|
</Resources>
|
||||||
|
<Dependencies>
|
||||||
|
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||||
|
</Dependencies>
|
||||||
|
<Properties>
|
||||||
|
<Framework>false</Framework>
|
||||||
|
<DisplayName>My Product</DisplayName>
|
||||||
|
<PublisherDisplayName>My Company</PublisherDisplayName>
|
||||||
|
<Description>My Product Description</Description>
|
||||||
|
<Logo>Assets\AppIcon.png</Logo>
|
||||||
|
</Properties>
|
||||||
|
</PackageInformation>
|
||||||
|
<SaveLocation PackagePath="firefly-launcher.msix" />
|
||||||
|
<PackageIntegrity>
|
||||||
|
<CertificatePath></CertificatePath>
|
||||||
|
</PackageIntegrity>
|
||||||
|
</MsixPackagingToolTemplate>
|
||||||
@@ -20,10 +20,10 @@ Unicode true
|
|||||||
## The following information is taken from the wails_tools.nsh file, but they can be overwritten here.
|
## The following information is taken from the wails_tools.nsh file, but they can be overwritten here.
|
||||||
####
|
####
|
||||||
## !define INFO_PROJECTNAME "my-project" # Default "firefly-launcher"
|
## !define INFO_PROJECTNAME "my-project" # Default "firefly-launcher"
|
||||||
## !define INFO_COMPANYNAME "My Company" # Default "FireflyShelter"
|
## !define INFO_COMPANYNAME "My Company" # Default "My Company"
|
||||||
## !define INFO_PRODUCTNAME "My Product Name" # Default "Firefly Launcher"
|
## !define INFO_PRODUCTNAME "My Product Name" # Default "My Product"
|
||||||
## !define INFO_PRODUCTVERSION "1.0.0" # Default "0.1.0"
|
## !define INFO_PRODUCTVERSION "1.0.0" # Default "0.1.0"
|
||||||
## !define INFO_COPYRIGHT "(c) Now, My Company" # Default "@ 2025, FireflyShelter"
|
## !define INFO_COPYRIGHT "(c) Now, My Company" # Default "© now, My Company"
|
||||||
###
|
###
|
||||||
## !define PRODUCT_EXECUTABLE "Application.exe" # Default "${INFO_PROJECTNAME}.exe"
|
## !define PRODUCT_EXECUTABLE "Application.exe" # Default "${INFO_PROJECTNAME}.exe"
|
||||||
## !define UNINST_KEY_NAME "UninstKeyInRegistry" # Default "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}"
|
## !define UNINST_KEY_NAME "UninstKeyInRegistry" # Default "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}"
|
||||||
@@ -92,7 +92,8 @@ Section
|
|||||||
CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
|
CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
|
||||||
|
|
||||||
!insertmacro wails.associateFiles
|
!insertmacro wails.associateFiles
|
||||||
|
!insertmacro wails.associateCustomProtocols
|
||||||
|
|
||||||
!insertmacro wails.writeUninstaller
|
!insertmacro wails.writeUninstaller
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|
||||||
@@ -107,6 +108,7 @@ Section "uninstall"
|
|||||||
Delete "$DESKTOP\${INFO_PRODUCTNAME}.lnk"
|
Delete "$DESKTOP\${INFO_PRODUCTNAME}.lnk"
|
||||||
|
|
||||||
!insertmacro wails.unassociateFiles
|
!insertmacro wails.unassociateFiles
|
||||||
|
!insertmacro wails.unassociateCustomProtocols
|
||||||
|
|
||||||
!insertmacro wails.deleteUninstaller
|
!insertmacro wails.deleteUninstaller
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
!define INFO_PRODUCTNAME "Firefly Launcher"
|
!define INFO_PRODUCTNAME "Firefly Launcher"
|
||||||
!endif
|
!endif
|
||||||
!ifndef INFO_PRODUCTVERSION
|
!ifndef INFO_PRODUCTVERSION
|
||||||
!define INFO_PRODUCTVERSION "0.1.0"
|
!define INFO_PRODUCTVERSION "1.0.0"
|
||||||
!endif
|
!endif
|
||||||
!ifndef INFO_COPYRIGHT
|
!ifndef INFO_COPYRIGHT
|
||||||
!define INFO_COPYRIGHT "@ 2025, Firefly Shelter"
|
!define INFO_COPYRIGHT "@ 2025, Firefly Shelter"
|
||||||
@@ -158,7 +158,7 @@ RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}"
|
|||||||
|
|
||||||
${If} ${REQUEST_EXECUTION_LEVEL} == "user"
|
${If} ${REQUEST_EXECUTION_LEVEL} == "user"
|
||||||
# If the installer is run in user level, check the user specific key exists and is not empty then webview2 is already installed
|
# If the installer is run in user level, check the user specific key exists and is not empty then webview2 is already installed
|
||||||
ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
|
ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
|
||||||
${If} $0 != ""
|
${If} $0 != ""
|
||||||
Goto ok
|
Goto ok
|
||||||
${EndIf}
|
${EndIf}
|
||||||
@@ -209,4 +209,28 @@ RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}"
|
|||||||
!macro wails.unassociateFiles
|
!macro wails.unassociateFiles
|
||||||
; Delete app associations
|
; Delete app associations
|
||||||
|
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro CUSTOM_PROTOCOL_ASSOCIATE PROTOCOL DESCRIPTION ICON COMMAND
|
||||||
|
DeleteRegKey SHELL_CONTEXT "Software\Classes\${PROTOCOL}"
|
||||||
|
WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}" "" "${DESCRIPTION}"
|
||||||
|
WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}" "URL Protocol" ""
|
||||||
|
WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\DefaultIcon" "" "${ICON}"
|
||||||
|
WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\shell" "" ""
|
||||||
|
WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\shell\open" "" ""
|
||||||
|
WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\shell\open\command" "" "${COMMAND}"
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro CUSTOM_PROTOCOL_UNASSOCIATE PROTOCOL
|
||||||
|
DeleteRegKey SHELL_CONTEXT "Software\Classes\${PROTOCOL}"
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro wails.associateCustomProtocols
|
||||||
|
; Create custom protocols associations
|
||||||
|
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro wails.unassociateCustomProtocols
|
||||||
|
; Delete app custom protocol associations
|
||||||
|
|
||||||
!macroend
|
!macroend
|
||||||
@@ -1,13 +1,17 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||||
<assemblyIdentity type="win32" name="com.fireflyshelter.fireflylauncher" version="0.1.0" processorArchitecture="*"/>
|
<assemblyIdentity type="win32" name="com.fireflyshelter.fireflylauncher" version="1.0.0" processorArchitecture="*"/>
|
||||||
<dependency>
|
<dependency>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
|
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<asmv3:application>
|
||||||
<!-- Yêu cầu quyền admin -->
|
<asmv3:windowsSettings>
|
||||||
|
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> <!-- fallback for Windows 7 and 8 -->
|
||||||
|
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness> <!-- falls back to per-monitor if per-monitor v2 is not supported -->
|
||||||
|
</asmv3:windowsSettings>
|
||||||
|
</asmv3:application>
|
||||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
<security>
|
<security>
|
||||||
<requestedPrivileges>
|
<requestedPrivileges>
|
||||||
@@ -15,11 +19,4 @@
|
|||||||
</requestedPrivileges>
|
</requestedPrivileges>
|
||||||
</security>
|
</security>
|
||||||
</trustInfo>
|
</trustInfo>
|
||||||
|
</assembly>
|
||||||
<asmv3:application>
|
|
||||||
<asmv3:windowsSettings>
|
|
||||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
|
|
||||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness>
|
|
||||||
</asmv3:windowsSettings>
|
|
||||||
</asmv3:application>
|
|
||||||
</assembly>
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "@wailsio/runtime";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function CloseApp() {
|
||||||
|
return $Call.ByID(3422460836);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} timeout
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function CloseAppAfterTimeout(timeout) {
|
||||||
|
return $Call.ByID(1705931481, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function GetCurrentLauncherVersion() {
|
||||||
|
return $Call.ByID(3575133982);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function HideApp() {
|
||||||
|
return $Call.ByID(88003266);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function MaximizeApp() {
|
||||||
|
return $Call.ByID(1257306588);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function MinimizeApp() {
|
||||||
|
return $Call.ByID(3434614194);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function RestoreApp() {
|
||||||
|
return $Call.ByID(3115625834);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
import * as AppService from "./appservice.js";
|
||||||
|
export {
|
||||||
|
AppService
|
||||||
|
};
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
// @ts-check
|
|
||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
|
||||||
// This file is automatically generated. DO NOT EDIT
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore: Unused imports
|
|
||||||
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {number} timeout
|
|
||||||
* @returns {Promise<[boolean, string]> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function CloseAppAfterTimeout(timeout) {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(982751559, timeout));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Promise<[boolean, string]> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function GetCurrentLauncherVersion() {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(2542090372));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "@wailsio/runtime";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} patchPath
|
||||||
|
* @returns {$CancellablePromise<[boolean, string, string]>}
|
||||||
|
*/
|
||||||
|
export function CheckTypeHDiff(patchPath) {
|
||||||
|
return $Call.ByID(3717449114, patchPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} gamePath
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function CutData(gamePath) {
|
||||||
|
return $Call.ByID(2019290107, gamePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} gamePath
|
||||||
|
* @param {string} patchPath
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function DataExtract(gamePath, patchPath) {
|
||||||
|
return $Call.ByID(2161622254, gamePath, patchPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} gamePath
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function DeleteFiles(gamePath) {
|
||||||
|
return $Call.ByID(1103091613, gamePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} gamePath
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function HDiffPatchData(gamePath) {
|
||||||
|
return $Call.ByID(3944051994, gamePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} gamePath
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function LDiffPatchData(gamePath) {
|
||||||
|
return $Call.ByID(360123238, gamePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} gamePath
|
||||||
|
* @param {string} patchPath
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function VersionValidate(gamePath, patchPath) {
|
||||||
|
return $Call.ByID(2105077257, gamePath, patchPath);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
import * as DiffService from "./diffservice.js";
|
||||||
|
export {
|
||||||
|
DiffService
|
||||||
|
};
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "@wailsio/runtime";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} path
|
||||||
|
* @returns {$CancellablePromise<boolean>}
|
||||||
|
*/
|
||||||
|
export function DirExists(path) {
|
||||||
|
return $Call.ByID(1772289644, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} path
|
||||||
|
* @returns {$CancellablePromise<boolean>}
|
||||||
|
*/
|
||||||
|
export function FileExists(path) {
|
||||||
|
return $Call.ByID(1782610747, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} archivePath
|
||||||
|
* @param {string} fileInside
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function FileExistsInZip(archivePath, fileInside) {
|
||||||
|
return $Call.ByID(2509699047, archivePath, fileInside);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} path
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function OpenFolder(path) {
|
||||||
|
return $Call.ByID(1635714001, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} filter
|
||||||
|
* @returns {$CancellablePromise<string>}
|
||||||
|
*/
|
||||||
|
export function PickFile(filter) {
|
||||||
|
return $Call.ByID(3756474934, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {$CancellablePromise<string>}
|
||||||
|
*/
|
||||||
|
export function PickFolder() {
|
||||||
|
return $Call.ByID(3654471460);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} path
|
||||||
|
* @returns {$CancellablePromise<boolean>}
|
||||||
|
*/
|
||||||
|
export function StartApp(path) {
|
||||||
|
return $Call.ByID(1267568402, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} path
|
||||||
|
* @returns {$CancellablePromise<boolean>}
|
||||||
|
*/
|
||||||
|
export function StartWithConsole(path) {
|
||||||
|
return $Call.ByID(3249271428, path);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
import * as FSService from "./fsservice.js";
|
||||||
|
export {
|
||||||
|
FSService
|
||||||
|
};
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
// @ts-check
|
|
||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
|
||||||
// This file is automatically generated. DO NOT EDIT
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore: Unused imports
|
|
||||||
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} path
|
|
||||||
* @returns {Promise<boolean> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function DirExists(path) {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(1291974398, path));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} path
|
|
||||||
* @returns {Promise<boolean> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function FileExists(path) {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(3373618865, path));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} archivePath
|
|
||||||
* @param {string} fileInside
|
|
||||||
* @returns {Promise<[boolean, string]> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function FileExistsInZip(archivePath, fileInside) {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(1737012553, archivePath, fileInside));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} path
|
|
||||||
* @returns {Promise<[boolean, string]> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function OpenFolder(path) {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(2733319263, path));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Promise<string> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function PickFile() {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(2181012076));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Promise<string> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function PickFolder() {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(3906046322));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} path
|
|
||||||
* @returns {Promise<boolean> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function StartApp(path) {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(3825990132, path));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} path
|
|
||||||
* @returns {Promise<boolean> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function StartWithConsole(path) {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(2364569062, path));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "@wailsio/runtime";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} version
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function DownloadProxyProgress(version) {
|
||||||
|
return $Call.ByID(3559275334, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} version
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function DownloadServerProgress(version) {
|
||||||
|
return $Call.ByID(1954767259, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {$CancellablePromise<[boolean, string, string]>}
|
||||||
|
*/
|
||||||
|
export function GetLatestLauncherVersion() {
|
||||||
|
return $Call.ByID(262637602);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {$CancellablePromise<[boolean, string, string]>}
|
||||||
|
*/
|
||||||
|
export function GetLatestProxyVersion() {
|
||||||
|
return $Call.ByID(289488362);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {$CancellablePromise<[boolean, string, string]>}
|
||||||
|
*/
|
||||||
|
export function GetLatestServerVersion() {
|
||||||
|
return $Call.ByID(2918980975);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {$CancellablePromise<void>}
|
||||||
|
*/
|
||||||
|
export function UnzipProxy() {
|
||||||
|
return $Call.ByID(2563246729);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {$CancellablePromise<void>}
|
||||||
|
*/
|
||||||
|
export function UnzipServer() {
|
||||||
|
return $Call.ByID(1126363284);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} version
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function UpdateLauncherProgress(version) {
|
||||||
|
return $Call.ByID(97272861, version);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
import * as GitService from "./gitservice.js";
|
||||||
|
export {
|
||||||
|
GitService
|
||||||
|
};
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
// @ts-check
|
|
||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
|
||||||
// This file is automatically generated. DO NOT EDIT
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore: Unused imports
|
|
||||||
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} version
|
|
||||||
* @returns {Promise<[boolean, string]> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function DownloadProxyProgress(version) {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(1951249093, version));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} version
|
|
||||||
* @returns {Promise<[boolean, string]> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function DownloadServerProgress(version) {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(314135954, version));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Promise<[boolean, string, string]> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function GetLatestLauncherVersion() {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(3191972855));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Promise<[boolean, string, string]> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function GetLatestProxyVersion() {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(1462362449));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Promise<[boolean, string, string]> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function GetLatestServerVersion() {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(1447982978));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Promise<void> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function UnzipProxy() {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(4071181044));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Promise<void> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function UnzipServer() {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(4110296071));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} version
|
|
||||||
* @returns {Promise<[boolean, string]> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function UpdateLauncherProgress(version) {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(2648938152, version));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
// @ts-check
|
|
||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
|
||||||
// This file is automatically generated. DO NOT EDIT
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore: Unused imports
|
|
||||||
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} patchPath
|
|
||||||
* @returns {Promise<[boolean, string, string]> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function CheckTypeHDiff(patchPath) {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(1068035136, patchPath));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} gamePath
|
|
||||||
* @returns {Promise<[boolean, string]> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function CutData(gamePath) {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(3671642725, gamePath));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} gamePath
|
|
||||||
* @param {string} patchPath
|
|
||||||
* @param {boolean} isSkipVerify
|
|
||||||
* @returns {Promise<[boolean, string]> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function DataExtract(gamePath, patchPath, isSkipVerify) {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(1843136452, gamePath, patchPath, isSkipVerify));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} gamePath
|
|
||||||
* @returns {Promise<[boolean, string]> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function DeleteFiles(gamePath) {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(989019003, gamePath));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} gamePath
|
|
||||||
* @returns {Promise<[boolean, string]> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function PatchData(gamePath) {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(3608591627, gamePath));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} gamePath
|
|
||||||
* @param {string} patchPath
|
|
||||||
* @returns {Promise<[boolean, string]> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function VersionValidate(gamePath, patchPath) {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(3916254383, gamePath, patchPath));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
// @ts-check
|
|
||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
|
||||||
// This file is automatically generated. DO NOT EDIT
|
|
||||||
|
|
||||||
import * as AppService from "./appservice.js";
|
|
||||||
import * as FSService from "./fsservice.js";
|
|
||||||
import * as GitService from "./gitservice.js";
|
|
||||||
import * as HdiffzService from "./hdiffzservice.js";
|
|
||||||
import * as LanguageService from "./languageservice.js";
|
|
||||||
export {
|
|
||||||
AppService,
|
|
||||||
FSService,
|
|
||||||
GitService,
|
|
||||||
HdiffzService,
|
|
||||||
LanguageService
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
import * as LanguageService from "./languageservice.js";
|
||||||
|
export {
|
||||||
|
LanguageService
|
||||||
|
};
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "@wailsio/runtime";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} path
|
||||||
|
* @returns {$CancellablePromise<[boolean, string, string, string]>}
|
||||||
|
*/
|
||||||
|
export function GetLanguage(path) {
|
||||||
|
return $Call.ByID(3574191687, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} path
|
||||||
|
* @param {string} text
|
||||||
|
* @param {string} voice
|
||||||
|
* @returns {$CancellablePromise<[boolean, string]>}
|
||||||
|
*/
|
||||||
|
export function SetLanguage(path, text, voice) {
|
||||||
|
return $Call.ByID(2768939795, path, text, voice);
|
||||||
|
}
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
// @ts-check
|
|
||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
|
||||||
// This file is automatically generated. DO NOT EDIT
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore: Unused imports
|
|
||||||
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} path
|
|
||||||
* @returns {Promise<[boolean, string, string, string]> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function GetLanguage(path) {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(3450750492, path));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} path
|
|
||||||
* @param {string} text
|
|
||||||
* @param {string} voice
|
|
||||||
* @returns {Promise<[boolean, string]> & { cancel(): void }}
|
|
||||||
*/
|
|
||||||
export function SetLanguage(path, text, voice) {
|
|
||||||
let $resultPromise = /** @type {any} */($Call.ByID(2793672496, path, text, voice));
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,16 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html data-theme="cupcake" lang="en">
|
<html lang="en" data-theme="dracula">
|
||||||
<head>
|
|
||||||
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/wails.png" />
|
<link rel="icon" type="image/svg+xml" href="/appicon.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Wails + React + TS</title>
|
<title>Firefly Launcher</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
|
||||||
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
|
||||||
|
</html>
|
||||||
1347
frontend/package-lock.json
generated
@@ -11,7 +11,7 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/vite": "^4.1.12",
|
"@tailwindcss/vite": "^4.1.14",
|
||||||
"@tanstack/react-router": "^1.131.27",
|
"@tanstack/react-router": "^1.131.27",
|
||||||
"@tanstack/react-router-devtools": "^1.131.27",
|
"@tanstack/react-router-devtools": "^1.131.27",
|
||||||
"lucide-react": "^0.541.0",
|
"lucide-react": "^0.541.0",
|
||||||
@@ -19,8 +19,9 @@
|
|||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
|
"react-easy-crop": "^5.5.3",
|
||||||
"react-toastify": "^11.0.5",
|
"react-toastify": "^11.0.5",
|
||||||
"tailwindcss": "^4.1.12",
|
"tailwindcss": "^4.1.14",
|
||||||
"zustand": "^5.0.8"
|
"zustand": "^5.0.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -33,7 +34,7 @@
|
|||||||
"@typescript-eslint/parser": "^8.40.0",
|
"@typescript-eslint/parser": "^8.40.0",
|
||||||
"@vitejs/plugin-react": "^5.0.1",
|
"@vitejs/plugin-react": "^5.0.1",
|
||||||
"@wailsio/runtime": "^3.0.0-alpha.66",
|
"@wailsio/runtime": "^3.0.0-alpha.66",
|
||||||
"daisyui": "^5.0.50",
|
"daisyui": "^5.1.27",
|
||||||
"eslint": "^9.34.0",
|
"eslint": "^9.34.0",
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.20",
|
"eslint-plugin-react-refresh": "^0.4.20",
|
||||||
|
|||||||
BIN
frontend/public/appicon.png
Normal file
|
After Width: | Height: | Size: 304 KiB |
BIN
frontend/public/bg-1.jpeg
Normal file
|
After Width: | Height: | Size: 196 KiB |
BIN
frontend/public/bg-10.jpg
Normal file
|
After Width: | Height: | Size: 195 KiB |
BIN
frontend/public/bg-11.jpeg
Normal file
|
After Width: | Height: | Size: 386 KiB |
BIN
frontend/public/bg-12.jpg
Normal file
|
After Width: | Height: | Size: 355 KiB |
BIN
frontend/public/bg-13.jpg
Normal file
|
After Width: | Height: | Size: 179 KiB |
BIN
frontend/public/bg-16.jpg
Normal file
|
After Width: | Height: | Size: 270 KiB |
BIN
frontend/public/bg-2.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
frontend/public/bg-3.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
frontend/public/bg-5.jpeg
Normal file
|
After Width: | Height: | Size: 300 KiB |
BIN
frontend/public/bg-6.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
frontend/public/bg-7.jpeg
Normal file
|
After Width: | Height: | Size: 349 KiB |
BIN
frontend/public/bg-8.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
frontend/public/bg-9.jpeg
Normal file
|
After Width: | Height: | Size: 378 KiB |
|
Before Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 1.1 MiB |
BIN
frontend/public/heart-hsr.gif
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
180
frontend/src/components/backgroudModal/index.tsx
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useState, useRef } from 'react'
|
||||||
|
import { X, Image as ImageIcon, Plus, Upload, Check } from 'lucide-react'
|
||||||
|
import useSettingStore from '@/stores/settingStore'
|
||||||
|
import Cropper from 'react-easy-crop'
|
||||||
|
import getCroppedImg from '@/utils/cropImage'
|
||||||
|
|
||||||
|
const initialImages = {
|
||||||
|
"bg-1": "bg-1.jpeg",
|
||||||
|
"bg-2": "bg-2.png",
|
||||||
|
"bg-3": "bg-3.png",
|
||||||
|
"bg-6": "bg-6.png",
|
||||||
|
"bg-7": "bg-7.jpeg",
|
||||||
|
"bg-8": "bg-8.png",
|
||||||
|
"bg-9": "bg-9.jpeg",
|
||||||
|
"bg-10": "bg-10.jpg",
|
||||||
|
"bg-11": "bg-11.jpeg",
|
||||||
|
"bg-12": "bg-12.jpg",
|
||||||
|
"bg-13": "bg-13.jpg",
|
||||||
|
"bg-16": "bg-16.jpg",
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BackgroundSelector = () => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
|
const [newUrl, setNewUrl] = useState('')
|
||||||
|
const [croppingImage, setCroppingImage] = useState<string | null>(null)
|
||||||
|
const [crop, setCrop] = useState({ x: 0, y: 0 })
|
||||||
|
const [zoom, setZoom] = useState(1)
|
||||||
|
const [croppedAreaPixels, setCroppedAreaPixels] = useState<any>(null)
|
||||||
|
const { background, setBackground, extraBackgrounds, setExtraBackgrounds } = useSettingStore()
|
||||||
|
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
|
const handleSelect = (img: string) => {
|
||||||
|
setIsOpen(false)
|
||||||
|
setBackground(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAddUrl = () => {
|
||||||
|
if (!newUrl.trim()) return setCroppingImage(newUrl)
|
||||||
|
setNewUrl('')
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRemoveExtra = (url: string) => {
|
||||||
|
setExtraBackgrounds(extraBackgrounds.filter(bg => bg !== url))
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUploadFile = (file: File) => {
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.onload = () => setCroppingImage(reader.result as string)
|
||||||
|
reader.readAsDataURL(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCropComplete = async () => {
|
||||||
|
if (!croppingImage || !croppedAreaPixels) return
|
||||||
|
const croppedBase64 = await getCroppedImg(croppingImage, croppedAreaPixels)
|
||||||
|
setExtraBackgrounds([croppedBase64, ...extraBackgrounds])
|
||||||
|
setCroppingImage(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const allBackgrounds = [...extraBackgrounds, ...Object.values(initialImages)]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center justify-center gap-4">
|
||||||
|
<div className="tooltip tooltip-right" data-tip="Select Background">
|
||||||
|
<button
|
||||||
|
className="group btn btn-primary btn-circle flex items-center justify-center shadow-md transition-all duration-300 hover:scale-110 hover:shadow-lg hover:bg-primary/80"
|
||||||
|
onClick={() => setIsOpen(true)}
|
||||||
|
>
|
||||||
|
<ImageIcon size={22} className="text-white transition-all duration-300 group-hover:rotate-6 group-hover:scale-110 group-hover:text-yellow-300" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isOpen && (
|
||||||
|
<div className="fixed inset-0 z-40 flex items-center justify-center bg-base-200/60 pt-10">
|
||||||
|
<div className="bg-base-200 text-white rounded-xl shadow-xl p-6 w-[90%] max-w-2xl relative">
|
||||||
|
<button className="btn btn-ghost btn-circle absolute top-3 right-3" onClick={() => setIsOpen(false)}>
|
||||||
|
<X size={20} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<h2 className="text-lg font-semibold mb-4">Choose Background</h2>
|
||||||
|
|
||||||
|
{/* Add via URL */}
|
||||||
|
<div className="flex gap-2 mb-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Paste image URL (https://...)"
|
||||||
|
className="input input-bordered w-full text-info"
|
||||||
|
value={newUrl}
|
||||||
|
onChange={(e) => setNewUrl(e.target.value)}
|
||||||
|
/>
|
||||||
|
<button className="btn btn-success flex items-center gap-1" onClick={handleAddUrl}>
|
||||||
|
<Plus size={16} /> Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Upload from computer */}
|
||||||
|
<div className="flex mb-4">
|
||||||
|
<button
|
||||||
|
className="btn btn-warning flex items-center gap-1"
|
||||||
|
onClick={() => fileInputRef.current?.click()}
|
||||||
|
>
|
||||||
|
<Upload size={16} /> Upload from computer
|
||||||
|
</button>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
className="hidden"
|
||||||
|
ref={fileInputRef}
|
||||||
|
onChange={(e) => {
|
||||||
|
const file = e.target.files?.[0]
|
||||||
|
if (file) handleUploadFile(file)
|
||||||
|
e.target.value = ''
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Crop Modal */}
|
||||||
|
{croppingImage && (
|
||||||
|
<div className="fixed inset-0 z-60 flex flex-col items-center justify-center bg-black/70 p-4">
|
||||||
|
<div className="relative w-full max-w-3xl h-[400px] bg-gray-800 rounded-lg">
|
||||||
|
<Cropper
|
||||||
|
image={croppingImage}
|
||||||
|
crop={crop}
|
||||||
|
zoom={zoom}
|
||||||
|
aspect={16 / 9}
|
||||||
|
onCropChange={setCrop}
|
||||||
|
onZoomChange={setZoom}
|
||||||
|
onCropComplete={(_, croppedAreaPixels) => setCroppedAreaPixels(croppedAreaPixels)}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className="absolute bottom-4 left-1/2 -translate-x-1/2 btn btn-success"
|
||||||
|
onClick={handleCropComplete}
|
||||||
|
>
|
||||||
|
<Check size={20} /> Done
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="absolute top-2 right-2 btn btn-ghost btn-circle"
|
||||||
|
onClick={() => setCroppingImage(null)}
|
||||||
|
>
|
||||||
|
<X size={20} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="grid grid-cols-3 gap-4 max-h-[60vh] overflow-y-auto">
|
||||||
|
{allBackgrounds.map((value, i) => {
|
||||||
|
const isExtra = i < extraBackgrounds.length
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className={`relative rounded-lg overflow-hidden cursor-pointer border-2 transition-all duration-200 ${
|
||||||
|
value === background ? 'border-blue-500' : 'border-transparent hover:border-gray-500'
|
||||||
|
}`}
|
||||||
|
onClick={() => handleSelect(value)}
|
||||||
|
>
|
||||||
|
<img src={value} alt={`bg-${i}`} loading="lazy" className="w-full h-28 object-cover" />
|
||||||
|
{isExtra && (
|
||||||
|
<button
|
||||||
|
className="absolute top-1 right-1 bg-black/50 hover:bg-black/70 text-white rounded-full p-1"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
handleRemoveExtra(value)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<X size={14} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
83
frontend/src/components/closeModal/index.tsx
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import { motion } from "motion/react"
|
||||||
|
import { AppService } from "@bindings/firefly-launcher/internal/app-service"
|
||||||
|
import { toast } from "react-toastify"
|
||||||
|
import useSettingStore from "@/stores/settingStore"
|
||||||
|
|
||||||
|
export default function CloseModal({
|
||||||
|
isOpen,
|
||||||
|
onClose
|
||||||
|
}: {
|
||||||
|
isOpen: boolean
|
||||||
|
onClose: () => void
|
||||||
|
}) {
|
||||||
|
if (!isOpen) return null
|
||||||
|
const { closingOption, setClosingOption } = useSettingStore()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-50 h-full flex items-center justify-center bg-black/40 backdrop-blur-sm">
|
||||||
|
<div className="relative w-[90%] max-w-2xl bg-base-100 text-base-content rounded-xl border border-purple-500/50 shadow-lg shadow-purple-500/20">
|
||||||
|
<div className="border-b border-purple-500/30 px-6 py-4 mb-4 flex justify-between items-center">
|
||||||
|
<h3 className="font-bold text-xl text-transparent bg-clip-text bg-gradient-to-r from-pink-400 to-cyan-600">
|
||||||
|
Confirm Action
|
||||||
|
</h3>
|
||||||
|
<motion.button
|
||||||
|
whileHover={{ scale: 1.1, rotate: 90 }}
|
||||||
|
transition={{ duration: 0.2 }}
|
||||||
|
className="btn btn-circle btn-md btn-error absolute right-3 top-3"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</motion.button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="px-6 pt-2 pb-6">
|
||||||
|
<p className="mb-4 text-lg">
|
||||||
|
Do you want to minimize the application to the system tray or close the application?
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex items-center mb-4">
|
||||||
|
<input
|
||||||
|
id="dontAskAgain"
|
||||||
|
type="checkbox"
|
||||||
|
className="checkbox checkbox-sm mr-2"
|
||||||
|
checked={!closingOption.isAsk}
|
||||||
|
onChange={(e) => setClosingOption({ isMinimize: closingOption.isMinimize, isAsk: !e.target.checked })}
|
||||||
|
/>
|
||||||
|
<label htmlFor="dontAskAgain" className="text-sm font-semibold text-accent">
|
||||||
|
Do not ask me again
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 justify-end gap-3">
|
||||||
|
<button
|
||||||
|
className="btn btn-warning"
|
||||||
|
onClick={async () => {
|
||||||
|
onClose()
|
||||||
|
const [success, message] = await AppService.HideApp()
|
||||||
|
if (!success) toast.error(message)
|
||||||
|
if (!closingOption.isAsk) {
|
||||||
|
setClosingOption({ isMinimize: true, isAsk: false })
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Minimize
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="btn btn-error btn-outline"
|
||||||
|
onClick={async () => {
|
||||||
|
onClose()
|
||||||
|
const [success, message] = await AppService.CloseApp()
|
||||||
|
if (!success) toast.error(message)
|
||||||
|
if (!closingOption.isAsk) {
|
||||||
|
setClosingOption({ isMinimize: false, isAsk: false })
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
163
frontend/src/components/header/index.tsx
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
import { Link } from "@tanstack/react-router";
|
||||||
|
import useModalStore from "@/stores/modalStore";
|
||||||
|
import { Blend, BookOpen, Diff, Home, Info, Languages, Minus, Puzzle, Settings, TrendingUpDown, Wrench, X } from "lucide-react";
|
||||||
|
import { AppService } from "@bindings/firefly-launcher/internal/app-service";
|
||||||
|
|
||||||
|
export default function Header() {
|
||||||
|
const { setIsOpenSettingModal } = useModalStore()
|
||||||
|
|
||||||
|
const controlButtons = [
|
||||||
|
{
|
||||||
|
icon: <Settings className="w-5 h-5" />,
|
||||||
|
action: () => setIsOpenSettingModal(true),
|
||||||
|
tip: "Settings",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <Minus className="w-5 h-5" />,
|
||||||
|
action: () => AppService.MinimizeApp(),
|
||||||
|
tip: "Minimize",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <X className="w-5 h-5" />,
|
||||||
|
action: () => AppService.CloseApp(),
|
||||||
|
tip: "Close",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="navbar sticky top-0 z-50 px-3" style={{ '--wails-draggable': 'drag' } as any}>
|
||||||
|
<div className="navbar-start">
|
||||||
|
<div className="dropdown">
|
||||||
|
<div tabIndex={0} role="button" className="btn btn-ghost md:hidden">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h8m-8 6h16" /> </svg>
|
||||||
|
</div>
|
||||||
|
<ul
|
||||||
|
tabIndex={0}
|
||||||
|
className="menu menu-sm dropdown-content bg-black/50 backdrop-blur-md rounded-box z-1 mt-3 w-52 p-2 shadow">
|
||||||
|
<li><Link to="/">Home</Link></li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a>Tools</a>
|
||||||
|
<ul className="p-2">
|
||||||
|
<li><Link to="/language">Language</Link></li>
|
||||||
|
<li><Link to="/diff">Diff</Link></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a>Plugins</a>
|
||||||
|
<ul className="p-2">
|
||||||
|
<li><Link to="/analysis">Analysis (Veritas)</Link></li>
|
||||||
|
<li><Link to="/srtools">SrTools</Link></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><Link to="/howto">How to?</Link></li>
|
||||||
|
<li><Link to="/about">About</Link></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<Link to="/" className="grid grid-cols-1 items-start text-left gap-0">
|
||||||
|
<div className="flex items-center justify-center">
|
||||||
|
<img src="/appicon.png" alt="Logo" className='w-13 h-13 rounded-lg mx-2' />
|
||||||
|
<div className="flex flex-col justify-center items-start">
|
||||||
|
<h1 className="text-xl font-bold">
|
||||||
|
<span className="text-emerald-500"
|
||||||
|
style={{
|
||||||
|
textShadow: '0 1px 2px rgba(255, 255, 255, 0.2)',
|
||||||
|
}}
|
||||||
|
>Firefly </span>
|
||||||
|
<span className="bg-clip-text text-transparent bg-gradient-to-r from-emerald-400 via-orange-500 to-red-500"
|
||||||
|
style={{
|
||||||
|
textShadow: '0 1px 2px rgba(255, 255, 255, 0.2)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Launcher
|
||||||
|
</span>
|
||||||
|
</h1>
|
||||||
|
<p
|
||||||
|
className="text-white text-sm"
|
||||||
|
style={{
|
||||||
|
textShadow: '0 1px 2px rgba(0, 0, 0, 0.8)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
By Kain
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<div className="navbar-center hidden md:flex bg-black/40 backdrop-blur-sm rounded-lg shadow-lg">
|
||||||
|
<ul className="menu menu-horizontal px-1 gap-4 text-white">
|
||||||
|
<li>
|
||||||
|
<Link to="/" className="flex items-center gap-2 hover:text-cyan-300 transition-colors">
|
||||||
|
<Home size={18} /> Home
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<details>
|
||||||
|
<summary className="flex items-center gap-2 cursor-pointer hover:text-cyan-300 transition-colors">
|
||||||
|
<Wrench size={18} /> Tools
|
||||||
|
</summary>
|
||||||
|
<ul className="p-2 bg-black/75 text-white rounded-lg">
|
||||||
|
<li>
|
||||||
|
<Link to="/language" className="flex items-center gap-2 hover:text-cyan-300">
|
||||||
|
<Languages size={18} /> Language
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link to="/diff" className="flex items-center gap-2 hover:text-cyan-300">
|
||||||
|
<Diff size={18} /> Client update
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<details>
|
||||||
|
<summary className="flex items-center gap-2 cursor-pointer hover:text-cyan-300 transition-colors">
|
||||||
|
<Puzzle size={18} /> Plugins
|
||||||
|
</summary>
|
||||||
|
<ul className="p-2 bg-black/75 text-white rounded-lg">
|
||||||
|
<li>
|
||||||
|
<Link to="/analysis" className="flex items-center gap-2 hover:text-cyan-300">
|
||||||
|
<TrendingUpDown size={18} /> Analysis (Veritas)
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link to="/srtools" className="flex items-center gap-2 hover:text-cyan-300">
|
||||||
|
<Blend size={18} /> Firefly Tools
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link to="/howto" className="flex items-center gap-2 hover:text-cyan-300 transition-colors">
|
||||||
|
<BookOpen size={18} /> How to?
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link to="/about" className="flex items-center gap-2 hover:text-cyan-300 transition-colors">
|
||||||
|
<Info size={18} /> About
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="navbar-end flex gap-2 z-52">
|
||||||
|
<div className="flex items-center gap-2 bg-black/40 backdrop-blur-sm rounded-lg">
|
||||||
|
{controlButtons.map((btn, i) => (
|
||||||
|
<div key={i} className="tooltip tooltip-bottom" data-tip={btn.tip}>
|
||||||
|
<button
|
||||||
|
onClick={btn.action}
|
||||||
|
className="btn btn-ghost btn-circle bg-transparent border-none flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<div className="hover:text-cyan-300 transition-colors">
|
||||||
|
{btn.icon}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
117
frontend/src/components/settingModal/index.tsx
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
import { CheckUpdateLauncher } from "@/helper"
|
||||||
|
import useModalStore from "@/stores/modalStore"
|
||||||
|
import useSettingStore from "@/stores/settingStore"
|
||||||
|
import useLauncherStore from "@/stores/launcherStore"
|
||||||
|
import { toast } from "react-toastify"
|
||||||
|
|
||||||
|
export default function SettingModal({
|
||||||
|
isOpen,
|
||||||
|
onClose
|
||||||
|
}: {
|
||||||
|
isOpen: boolean
|
||||||
|
onClose: () => void
|
||||||
|
}) {
|
||||||
|
if (!isOpen) return null
|
||||||
|
|
||||||
|
const { setIsOpenSelfUpdateModal } = useModalStore()
|
||||||
|
const { closingOption, setClosingOption, serverVersion,
|
||||||
|
proxyVersion, } = useSettingStore()
|
||||||
|
const { setUpdateData, updateData, launcherVersion } = useLauncherStore()
|
||||||
|
const CheckUpdate = async () => {
|
||||||
|
const launcherData = await CheckUpdateLauncher()
|
||||||
|
if (!launcherData.isUpdate) {
|
||||||
|
toast.success("Launcher is already up to date")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setUpdateData({
|
||||||
|
server: updateData.server,
|
||||||
|
proxy: updateData.proxy,
|
||||||
|
launcher: launcherData
|
||||||
|
})
|
||||||
|
|
||||||
|
setIsOpenSelfUpdateModal(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-10 flex items-center justify-center bg-black/40 backdrop-blur-sm">
|
||||||
|
<div className="relative w-[90%] max-w-md bg-base-100 text-base-content rounded-2xl border border-purple-500/30 shadow-2xl shadow-purple-500/30 p-6">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex justify-between items-center mb-6">
|
||||||
|
<h3 className="font-extrabold text-2xl text-transparent bg-clip-text bg-gradient-to-r from-pink-400 to-cyan-500">
|
||||||
|
Settings
|
||||||
|
</h3>
|
||||||
|
<button
|
||||||
|
className="btn btn-circle btn-sm bg-red-600 hover:bg-red-700 text-white border-none shadow-lg"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<div className="flex flex-col gap-6">
|
||||||
|
{/* Section 1: Launcher Update */}
|
||||||
|
<div className="p-4 bg-base-200 rounded-xl border border-purple-300 shadow-sm">
|
||||||
|
<h4 className="font-bold text-lg mb-2">Launcher Update</h4>
|
||||||
|
<p className="text-sm text-info mb-3">
|
||||||
|
Check if your launcher is up to date.
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
className="btn btn-primary bg-gradient-to-r from-orange-500 to-red-500 hover:from-orange-400 hover:to-red-500 text-white shadow-md hover:shadow-lg transition-all duration-200"
|
||||||
|
onClick={CheckUpdate}
|
||||||
|
>
|
||||||
|
Check for Launcher Updates
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Section 2: Closing Option */}
|
||||||
|
<div className="p-4 bg-base-200 rounded-xl border border-purple-300 shadow-sm">
|
||||||
|
<h4 className="font-bold text-lg mb-2">Closing Options</h4>
|
||||||
|
<label className="flex items-start gap-3 cursor-pointer select-none">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="checkbox checkbox-primary w-5 h-5 mt-1"
|
||||||
|
checked={!closingOption.isAsk}
|
||||||
|
onChange={(e) => {
|
||||||
|
setClosingOption({
|
||||||
|
isMinimize: closingOption.isMinimize,
|
||||||
|
isAsk: !e.target.checked
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="text-base font-medium text-info">
|
||||||
|
Set do not ask again
|
||||||
|
</span>
|
||||||
|
<span className="text-sm text-accent">
|
||||||
|
Next time you close the app, it will automatically{" "}
|
||||||
|
{closingOption.isMinimize ? "minimize to system tray" : "quit the app"}{" "}
|
||||||
|
without asking.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{/* Section 3: Launcher Version */}
|
||||||
|
<div className="p-4 bg-base-200 rounded-xl border border-purple-300 shadow-sm">
|
||||||
|
<h4 className="font-bold text-lg mb-2">Version</h4>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
<p className="text-base text-info">
|
||||||
|
Server: {serverVersion}
|
||||||
|
</p>
|
||||||
|
<p className="text-base text-info">
|
||||||
|
Proxy: {proxyVersion}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p className="text-base text-info">
|
||||||
|
Launcher: {launcherVersion}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { useEffect, useState } from "react";
|
|
||||||
|
|
||||||
export default function ThemeController() {
|
|
||||||
const [theme, setTheme] = useState(localStorage.getItem("theme") ?? "light");
|
|
||||||
const handleToggle = (e: any) => {
|
|
||||||
if (e.target.checked) {
|
|
||||||
setTheme("cupcake");
|
|
||||||
} else {
|
|
||||||
setTheme("night");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
localStorage.setItem('theme', theme!)
|
|
||||||
const localTheme = localStorage.getItem('theme')
|
|
||||||
document.querySelector('html')?.setAttribute('data-theme', localTheme!)
|
|
||||||
}, [theme]);
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<label className="toggle text-base-content">
|
|
||||||
<input type="checkbox" onChange={handleToggle} className="theme-controller" />
|
|
||||||
|
|
||||||
<svg aria-label="moon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g strokeLinejoin="round" strokeLinecap="round" strokeWidth="2" fill="none" stroke="currentColor"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"></path></g></svg>
|
|
||||||
<svg aria-label="sun" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g strokeLinejoin="round" strokeLinecap="round" strokeWidth="2" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="4"></circle><path d="M12 2v2"></path><path d="M12 20v2"></path><path d="m4.93 4.93 1.41 1.41"></path><path d="m17.66 17.66 1.41 1.41"></path><path d="M2 12h2"></path><path d="M20 12h2"></path><path d="m6.34 17.66-1.41 1.41"></path><path d="m19.07 4.93-1.41 1.41"></path></g></svg>
|
|
||||||
|
|
||||||
</label>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
64
frontend/src/components/updateModal/index.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { motion } from "framer-motion"
|
||||||
|
|
||||||
|
interface UpdateModalProps {
|
||||||
|
isOpen: boolean
|
||||||
|
title: string
|
||||||
|
message: string
|
||||||
|
buttons: {
|
||||||
|
text: string
|
||||||
|
onClick: () => Promise<void> | void
|
||||||
|
variant?: "primary" | "error" | "outline"
|
||||||
|
}[]
|
||||||
|
onClose: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function UpdateModal({ isOpen, title, message, buttons, onClose }: UpdateModalProps) {
|
||||||
|
if (!isOpen) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
|
||||||
|
<div className="relative w-[90%] max-w-5xl bg-base-100 text-base-content rounded-xl border border-purple-500/50 shadow-lg shadow-purple-500/20">
|
||||||
|
<motion.button
|
||||||
|
whileHover={{ scale: 1.1, rotate: 90 }}
|
||||||
|
transition={{ duration: 0.2 }}
|
||||||
|
className="btn btn-circle btn-md btn-error absolute right-3 top-3"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</motion.button>
|
||||||
|
|
||||||
|
<div className="border-b border-purple-500/30 px-6 py-4 mb-4">
|
||||||
|
<h3 className="font-bold text-2xl text-transparent bg-clip-text bg-gradient-to-r from-pink-400 to-cyan-400">
|
||||||
|
{title}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="px-6 pb-6">
|
||||||
|
<div className="mb-6">
|
||||||
|
<p className="text-accent text-lg">{message}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-end gap-3">
|
||||||
|
{buttons.map((btn, idx) => (
|
||||||
|
<motion.button
|
||||||
|
key={idx}
|
||||||
|
whileHover={{ scale: 1.05 }}
|
||||||
|
whileTap={{ scale: 0.95 }}
|
||||||
|
className={`btn ${
|
||||||
|
btn.variant === "primary"
|
||||||
|
? "btn-primary bg-gradient-to-r from-orange-200 to-red-400 border-none"
|
||||||
|
: btn.variant === "error"
|
||||||
|
? "btn-error"
|
||||||
|
: "btn-outline btn-error"
|
||||||
|
}`}
|
||||||
|
onClick={btn.onClick}
|
||||||
|
>
|
||||||
|
{btn.text}
|
||||||
|
</motion.button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import useLauncherStore from "@/stores/launcherStore";
|
import useLauncherStore from "@/stores/launcherStore";
|
||||||
import { AppService, GitService } from "@bindings/firefly-launcher/internal";
|
import { AppService, } from "@bindings/firefly-launcher/internal/app-service";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import { sleep } from "./sleep";
|
import { sleep } from "./sleep";
|
||||||
|
import { GitService } from "@bindings/firefly-launcher/internal/git-service";
|
||||||
|
|
||||||
export async function CheckUpdateLauncher(): Promise<{ isUpdate: boolean; isExists: boolean; version: string }> {
|
export async function CheckUpdateLauncher(): Promise<{ isUpdate: boolean; isExists: boolean; version: string }> {
|
||||||
const [currentOk, currentVersion] = await AppService.GetCurrentLauncherVersion()
|
const [currentOk, currentVersion] = await AppService.GetCurrentLauncherVersion()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import useLauncherStore from "@/stores/launcherStore";
|
import useLauncherStore from "@/stores/launcherStore";
|
||||||
import useSettingStore from "@/stores/settingStore";
|
import useSettingStore from "@/stores/settingStore";
|
||||||
import { FSService, GitService } from "@bindings/firefly-launcher/internal";
|
import { FSService } from "@bindings/firefly-launcher/internal/fs-service";
|
||||||
|
import { GitService } from "@bindings/firefly-launcher/internal/git-service";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
|
|
||||||
export async function CheckUpdateProxy(proxyPath: string, proxyVersion: string) : Promise<{isUpdate: boolean, isExists: boolean, version: string}> {
|
export async function CheckUpdateProxy(proxyPath: string, proxyVersion: string) : Promise<{isUpdate: boolean, isExists: boolean, version: string}> {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import useLauncherStore from '@/stores/launcherStore';
|
import useLauncherStore from '@/stores/launcherStore';
|
||||||
import useSettingStore from '@/stores/settingStore';
|
import useSettingStore from '@/stores/settingStore';
|
||||||
import { FSService, GitService } from '@bindings/firefly-launcher/internal';
|
import { FSService } from '@bindings/firefly-launcher/internal/fs-service';
|
||||||
|
import { GitService } from '@bindings/firefly-launcher/internal/git-service';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
export async function CheckUpdateServer(
|
export async function CheckUpdateServer(
|
||||||
|
|||||||
@@ -2,27 +2,17 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { Events } from "@wailsio/runtime";
|
import { Events } from "@wailsio/runtime";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
|
import useSettingStore from "@/stores/settingStore";
|
||||||
|
import { AppService } from "@bindings/firefly-launcher/internal/app-service";
|
||||||
|
import useModalStore from "@/stores/modalStore";
|
||||||
|
import useDiffStore from "@/stores/diffStore";
|
||||||
|
import useLauncherStore from "@/stores/launcherStore";
|
||||||
|
|
||||||
export function useGlobalEvents({
|
export function useGlobalEvents() {
|
||||||
setGameRunning,
|
const { setIsOpenCloseModal } = useModalStore()
|
||||||
setServerRunning,
|
const { setGameRunning, setServerRunning, setProxyRunning, setProgressDownload, setDownloadSpeed } = useLauncherStore()
|
||||||
setProxyRunning,
|
const { setProgressUpdate, setMaxProgressUpdate, setMessageUpdate, setStageType } = useDiffStore()
|
||||||
setProgressUpdate,
|
|
||||||
setMaxProgressUpdate,
|
|
||||||
setProgressDownload,
|
|
||||||
setDownloadSpeed,
|
|
||||||
setMessageUpdate,
|
|
||||||
|
|
||||||
}: {
|
|
||||||
setGameRunning: (v: boolean) => void;
|
|
||||||
setServerRunning: (v: boolean) => void;
|
|
||||||
setProxyRunning: (v: boolean) => void;
|
|
||||||
setProgressUpdate: (v: number) => void;
|
|
||||||
setMaxProgressUpdate: (v: number) => void;
|
|
||||||
setProgressDownload: (v: number) => void;
|
|
||||||
setDownloadSpeed: (v: number) => void;
|
|
||||||
setMessageUpdate: (v: string) => void;
|
|
||||||
}) {
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onGameExit = () => setGameRunning(false);
|
const onGameExit = () => setGameRunning(false);
|
||||||
const onServerExit = () => setServerRunning(false);
|
const onServerExit = () => setServerRunning(false);
|
||||||
@@ -31,7 +21,7 @@ export function useGlobalEvents({
|
|||||||
const onDownload = (event: any) => {
|
const onDownload = (event: any) => {
|
||||||
const { percent, speed } = event.data[0];
|
const { percent, speed } = event.data[0];
|
||||||
setProgressDownload(Number(percent));
|
setProgressDownload(Number(percent));
|
||||||
setDownloadSpeed(Number(speed));
|
setDownloadSpeed(speed);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onUpdateProgress = (event: any) => {
|
const onUpdateProgress = (event: any) => {
|
||||||
@@ -45,17 +35,37 @@ export function useGlobalEvents({
|
|||||||
setMessageUpdate(message);
|
setMessageUpdate(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onStageUpdate = (event: any) => {
|
||||||
|
const { stage } = event.data[0];
|
||||||
|
setStageType(stage);
|
||||||
|
};
|
||||||
|
|
||||||
Events.On("download:server", onDownload);
|
Events.On("download:server", onDownload);
|
||||||
Events.On("download:proxy", onDownload);
|
Events.On("download:proxy", onDownload);
|
||||||
Events.On("game:exit", onGameExit);
|
Events.On("game:exit", onGameExit);
|
||||||
Events.On("server:exit", onServerExit);
|
Events.On("server:exit", onServerExit);
|
||||||
Events.On("proxy:exit", onProxyExit);
|
Events.On("proxy:exit", onProxyExit);
|
||||||
Events.On("hdiffz:progress", onUpdateProgress);
|
Events.On("diff:progress", onUpdateProgress);
|
||||||
Events.On("hdiffz:message", onMessageUpdate);
|
Events.On("diff:message", onMessageUpdate);
|
||||||
Events.On("hdiffz:error", (event: any) => {
|
Events.On("diff:stage", onStageUpdate);
|
||||||
|
Events.On("diff:error", (event: any) => {
|
||||||
const { message } = event.data[0];
|
const { message } = event.data[0];
|
||||||
toast.error(message);
|
toast.error(message);
|
||||||
});
|
});
|
||||||
|
Events.On("window:close", async () => {
|
||||||
|
const option = useSettingStore.getState().closingOption
|
||||||
|
if (option.isAsk) {
|
||||||
|
setIsOpenCloseModal(true);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (option.isMinimize) {
|
||||||
|
const [success, message] = await AppService.MinimizeApp()
|
||||||
|
if (!success) toast.error(message)
|
||||||
|
} else {
|
||||||
|
const [success, message] = await AppService.CloseApp()
|
||||||
|
if (!success) toast.error(message)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
Events.Off("download:server");
|
Events.Off("download:server");
|
||||||
@@ -63,9 +73,11 @@ export function useGlobalEvents({
|
|||||||
Events.Off("game:exit");
|
Events.Off("game:exit");
|
||||||
Events.Off("server:exit");
|
Events.Off("server:exit");
|
||||||
Events.Off("proxy:exit");
|
Events.Off("proxy:exit");
|
||||||
Events.Off("hdiffz:progress");
|
Events.Off("diff:progress")
|
||||||
Events.Off("hdiffz:message");
|
Events.Off("diff:message");
|
||||||
|
Events.Off("diff:stage");
|
||||||
Events.Off("version:check");
|
Events.Off("version:check");
|
||||||
|
Events.Off("window:close");
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,20 +4,15 @@ import { RouterProvider, createRouter } from '@tanstack/react-router'
|
|||||||
import './styles/index.css'
|
import './styles/index.css'
|
||||||
import "../runtime.js"
|
import "../runtime.js"
|
||||||
import "@wailsio/runtime";
|
import "@wailsio/runtime";
|
||||||
// Import the generated route tree
|
|
||||||
import { routeTree } from './routeTree.gen.js'
|
import { routeTree } from './routeTree.gen.js'
|
||||||
|
|
||||||
// Create a new router instance
|
|
||||||
const router = createRouter({ routeTree })
|
const router = createRouter({ routeTree })
|
||||||
|
|
||||||
// Register the router instance for type safety
|
|
||||||
declare module '@tanstack/react-router' {
|
declare module '@tanstack/react-router' {
|
||||||
interface Register {
|
interface Register {
|
||||||
router: typeof router
|
router: typeof router
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render the app
|
|
||||||
const rootElement = document.getElementById('root')!
|
const rootElement = document.getElementById('root')!
|
||||||
if (!rootElement.innerHTML) {
|
if (!rootElement.innerHTML) {
|
||||||
const root = ReactDOM.createRoot(rootElement)
|
const root = ReactDOM.createRoot(rootElement)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Link } from "@tanstack/react-router";
|
|||||||
export default function AboutPage() {
|
export default function AboutPage() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-base-200 flex items-center justify-center p-6">
|
<div className="min-h-screen bg-base-200 flex items-center justify-center p-6">
|
||||||
<div className="max-w-3xl w-full bg-base-100 shadow-xl rounded-2xl p-8 space-y-6">
|
<div className="w-full bg-base-100 shadow-xl rounded-2xl p-8 space-y-6">
|
||||||
<h1 className="text-4xl font-bold text-primary text-center">About</h1>
|
<h1 className="text-4xl font-bold text-primary text-center">About</h1>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
@@ -14,7 +14,7 @@ export default function AboutPage() {
|
|||||||
I created a lightweight and modern <span className="font-semibold text-success">Game Launcher</span> to help users easily launch and manage their games with better performance and simplicity.
|
I created a lightweight and modern <span className="font-semibold text-success">Game Launcher</span> to help users easily launch and manage their games with better performance and simplicity.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-lg leading-relaxed">
|
<p className="text-lg leading-relaxed">
|
||||||
The launcher is built using <span className="font-mono text-info">Go + Wails3</span>, with a clean and responsive interface styled with <span className="text-warning">Tailwind CSS</span> and <span className="text-warning">DaisyUI</span>.
|
The launcher is built using <span className="font-mono text-info">Go + Wails3</span>, with a clean and responsive interface styled with <span className="text-accent">Tailwind CSS</span> and <span className="text-accent">DaisyUI</span>.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-lg leading-relaxed">
|
<p className="text-lg leading-relaxed">
|
||||||
My goal is to make tools that are fast, efficient, and enjoyable to use — and this launcher is just the beginning.
|
My goal is to make tools that are fast, efficient, and enjoyable to use — and this launcher is just the beginning.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Link } from '@tanstack/react-router';
|
|||||||
export default function AnalysisPage() {
|
export default function AnalysisPage() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-base-200 flex items-center justify-center p-6">
|
<div className="min-h-screen bg-base-200 flex items-center justify-center p-6">
|
||||||
<div className="max-w-4xl w-full bg-base-100 shadow-xl rounded-2xl p-8 space-y-8">
|
<div className="w-full bg-base-100 shadow-xl rounded-2xl p-8 space-y-8">
|
||||||
<h1 className="text-4xl font-bold text-primary text-center">
|
<h1 className="text-4xl font-bold text-primary text-center">
|
||||||
Firefly Analysis & Veritas Plugin
|
Firefly Analysis & Veritas Plugin
|
||||||
</h1>
|
</h1>
|
||||||
@@ -64,7 +64,7 @@ export default function AnalysisPage() {
|
|||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
href="https://sranalysis.kain.id.vn/"
|
href="https://sranalysis.kain.id.vn/"
|
||||||
className="link link-warning font-mono text-sm break-all"
|
className="link link-accent font-mono text-sm break-all"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
@@ -79,7 +79,7 @@ export default function AnalysisPage() {
|
|||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
href="https://firefly-sranalysis.vercel.app/"
|
href="https://firefly-sranalysis.vercel.app/"
|
||||||
className="link link-warning font-mono text-sm break-all"
|
className="link link-accent font-mono text-sm break-all"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
@@ -144,7 +144,7 @@ export default function AnalysisPage() {
|
|||||||
<div className="space-y-2 text-purple-700">
|
<div className="space-y-2 text-purple-700">
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
<span className="font-medium min-w-[20px] text-purple-600">1.</span>
|
<span className="font-medium min-w-[20px] text-purple-600">1.</span>
|
||||||
<p>Launch the <span className="font-semibold">game</span> and your <span className="font-semibold">Firefly Private Server (PS)</span>.</p>
|
<p>Launch the <span className="font-semibold">game</span> and your <span className="font-semibold">Firefly GO Server (PS)</span>.</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
<span className="font-medium min-w-[20px] text-purple-600">2.</span>
|
<span className="font-medium min-w-[20px] text-purple-600">2.</span>
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ import useSettingStore from "@/stores/settingStore"
|
|||||||
import { Check, Folder, File, X, Settings } from "lucide-react"
|
import { Check, Folder, File, X, Settings } from "lucide-react"
|
||||||
import { useEffect } from "react"
|
import { useEffect } from "react"
|
||||||
import { toast } from "react-toastify"
|
import { toast } from "react-toastify"
|
||||||
import { FSService, HdiffzService} from "@bindings/firefly-launcher/internal"
|
import { DiffService} from "@bindings/firefly-launcher/internal/diff-service"
|
||||||
|
import { FSService } from "@bindings/firefly-launcher/internal/fs-service"
|
||||||
import { motion } from "motion/react"
|
import { motion } from "motion/react"
|
||||||
import useHdiffzStore from "@/stores/hdiffzStore"
|
import useDiffStore from "@/stores/diffStore"
|
||||||
|
|
||||||
export default function HdiffzPage() {
|
export default function DiffPage() {
|
||||||
const { gameDir, setGameDir } = useSettingStore()
|
const { gameDir, setGameDir } = useSettingStore()
|
||||||
const {
|
const {
|
||||||
isLoading,
|
isLoading,
|
||||||
@@ -27,7 +28,7 @@ export default function HdiffzPage() {
|
|||||||
setStageType,
|
setStageType,
|
||||||
messageUpdate,
|
messageUpdate,
|
||||||
setMessageUpdate
|
setMessageUpdate
|
||||||
} = useHdiffzStore()
|
} = useDiffStore()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getLanguage = async () => {
|
const getLanguage = async () => {
|
||||||
@@ -78,7 +79,7 @@ export default function HdiffzPage() {
|
|||||||
const handlePickDiffFile = async () => {
|
const handlePickDiffFile = async () => {
|
||||||
try {
|
try {
|
||||||
setIsLoading({game: false, diff: true})
|
setIsLoading({game: false, diff: true})
|
||||||
const basePath = await FSService.PickFile()
|
const basePath = await FSService.PickFile("")
|
||||||
if (basePath) {
|
if (basePath) {
|
||||||
if (!basePath.endsWith(".7z") && !basePath.endsWith(".zip") && !basePath.endsWith(".rar")) {
|
if (!basePath.endsWith(".7z") && !basePath.endsWith(".zip") && !basePath.endsWith(".rar")) {
|
||||||
toast.error('Not valid file type')
|
toast.error('Not valid file type')
|
||||||
@@ -86,13 +87,6 @@ export default function HdiffzPage() {
|
|||||||
setDiffDir('')
|
setDiffDir('')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const [exists, error] = await FSService.FileExistsInZip(basePath, "StarRail_Data\\StreamingAssets\\BinaryVersion.bytes")
|
|
||||||
if (!exists) {
|
|
||||||
toast.error(error)
|
|
||||||
setDiffCheckResult('error')
|
|
||||||
setDiffDir('')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
setDiffDir(basePath)
|
setDiffDir(basePath)
|
||||||
setDiffCheckResult('success')
|
setDiffCheckResult('success')
|
||||||
} else {
|
} else {
|
||||||
@@ -109,78 +103,78 @@ export default function HdiffzPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleUpdateGame = async () => {
|
const handleUpdateGame = async () => {
|
||||||
|
const handleResult = (ok: boolean, error: string) => {
|
||||||
|
if (!ok) {
|
||||||
|
toast.error(error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setIsDiffLoading(true)
|
setIsDiffLoading(true)
|
||||||
|
|
||||||
if (!gameDir || !diffDir) {
|
if (!gameDir || !diffDir) {
|
||||||
toast.error('Please select game directory and diff file')
|
toast.error('Please select game directory and diff file')
|
||||||
setIsDiffLoading(false)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setStageType('Check Type HDiff')
|
setStageType('Check Type HDiff')
|
||||||
setProgressUpdate(0)
|
setProgressUpdate(0)
|
||||||
setMaxProgressUpdate(1)
|
setMaxProgressUpdate(1)
|
||||||
const [isOk, validType, errorType] = await HdiffzService.CheckTypeHDiff(diffDir)
|
|
||||||
if (!isOk) {
|
const [isOk, validType, errorType] = await DiffService.CheckTypeHDiff(diffDir)
|
||||||
toast.error(errorType)
|
if (!handleResult(isOk, errorType)) return
|
||||||
setIsDiffLoading(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
setProgressUpdate(1)
|
setProgressUpdate(1)
|
||||||
|
|
||||||
if (validType === 'hdiffmap.json') {
|
if (['hdiffmap.json', 'hdifffiles.txt', 'hdifffiles.json'].includes(validType)) {
|
||||||
setStageType('Version Validate')
|
setStageType('Version Validate')
|
||||||
setProgressUpdate(0)
|
setProgressUpdate(0)
|
||||||
setMaxProgressUpdate(1)
|
setMaxProgressUpdate(1)
|
||||||
const [validVersion, errorVersion] = await HdiffzService.VersionValidate(gameDir, diffDir)
|
const [validVersion, errorVersion] = await DiffService.VersionValidate(gameDir, diffDir)
|
||||||
if (!validVersion) {
|
if (!handleResult(validVersion, errorVersion)) return
|
||||||
toast.error(errorVersion)
|
|
||||||
setIsDiffLoading(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
setProgressUpdate(1)
|
setProgressUpdate(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
setStageType('Data Extract')
|
setStageType('Data Extract')
|
||||||
const [validData, errorData] = await HdiffzService.DataExtract(gameDir, diffDir, validType === 'hdifffiles.txt')
|
const [validData, errorData] = await DiffService.DataExtract(gameDir, diffDir)
|
||||||
if (!validData) {
|
if (!handleResult(validData, errorData)) return
|
||||||
toast.error(errorData)
|
|
||||||
setIsDiffLoading(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setStageType('Cut Data')
|
setStageType('Cut Data')
|
||||||
setMessageUpdate('')
|
setMessageUpdate('')
|
||||||
const [validCut, errorCut] = await HdiffzService.CutData(gameDir)
|
const [validCut, errorCut] = await DiffService.CutData(gameDir)
|
||||||
if (!validCut) {
|
if (!handleResult(validCut, errorCut)) return
|
||||||
toast.error(errorCut)
|
|
||||||
setIsDiffLoading(false)
|
switch (validType) {
|
||||||
return
|
case 'hdifffiles.txt':
|
||||||
}
|
case 'hdiffmap.json':
|
||||||
|
case 'hdifffiles.json': {
|
||||||
setStageType('Patch Data')
|
setStageType('Patch Data')
|
||||||
const [validPatch, errorPatch] = await HdiffzService.PatchData(gameDir)
|
const [validPatch, errorPatch] = await DiffService.HDiffPatchData(gameDir)
|
||||||
if (!validPatch) {
|
if (!handleResult(validPatch, errorPatch)) return
|
||||||
toast.error(errorPatch)
|
|
||||||
setIsDiffLoading(false)
|
setStageType('Delete old files')
|
||||||
return
|
const [validDelete, errorDelete] = await DiffService.DeleteFiles(gameDir)
|
||||||
}
|
if (!handleResult(validDelete, errorDelete)) return
|
||||||
|
break
|
||||||
setStageType('Delete old files')
|
}
|
||||||
const [validDelete, errorDelete] = await HdiffzService.DeleteFiles(gameDir)
|
case 'manifest': {
|
||||||
if (!validDelete) {
|
setStageType('Patch Data')
|
||||||
toast.error(errorDelete)
|
const [validPatch, errorPatch] = await DiffService.LDiffPatchData(gameDir)
|
||||||
setIsDiffLoading(false)
|
if (!handleResult(validPatch, errorPatch)) return
|
||||||
return
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toast.success('Update game completed')
|
toast.success('Update game completed')
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
toast.error('PickFile error:', err)
|
toast.error(`PickFile error: ${err}`)
|
||||||
setIsDiffLoading(false)
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsDiffLoading(false)
|
setIsDiffLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-2 mx-4">
|
<div className="p-2 mx-4">
|
||||||
@@ -315,7 +309,7 @@ export default function HdiffzPage() {
|
|||||||
<div className="w-full p-4">
|
<div className="w-full p-4">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex justify-center items-center text-sm text-white/80">
|
<div className="flex justify-center items-center text-sm text-white/80">
|
||||||
<span className="font-bold text-lg text-warning">{stageType}:</span>
|
<span className="font-bold text-lg text-accent">{stageType}:</span>
|
||||||
<div className="flex items-center gap-4 ml-2">
|
<div className="flex items-center gap-4 ml-2">
|
||||||
{stageType !== 'Cut Data' && <span className="text-white font-bold">{progressUpdate.toFixed(0)} / {maxProgressUpdate.toFixed(0)}</span>}
|
{stageType !== 'Cut Data' && <span className="text-white font-bold">{progressUpdate.toFixed(0)} / {maxProgressUpdate.toFixed(0)}</span>}
|
||||||
{stageType === 'Cut Data' && <span className="text-white font-bold truncate max-w-full overflow-hidden whitespace-nowrap">{messageUpdate}</span>}
|
{stageType === 'Cut Data' && <span className="text-white font-bold truncate max-w-full overflow-hidden whitespace-nowrap">{messageUpdate}</span>}
|
||||||
158
frontend/src/pages/fireflytools/index.tsx
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
import { Link } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
export default function FireflyToolsPage() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-base-200 flex items-center justify-center p-6">
|
||||||
|
<div className="w-full bg-base-100 shadow-xl rounded-2xl p-8 space-y-8">
|
||||||
|
<h1 className="text-4xl font-bold text-primary text-center">Firefly Tools</h1>
|
||||||
|
|
||||||
|
{/* Section 1: About SR Tools */}
|
||||||
|
<div className="bg-blue-50 border-l-4 border-blue-400 p-6 rounded-r-lg">
|
||||||
|
<h2 className="text-2xl font-bold text-blue-800 flex items-center gap-2 mb-4">
|
||||||
|
<span>ℹ️</span>
|
||||||
|
<span>About Firefly Tools</span>
|
||||||
|
</h2>
|
||||||
|
<div className="space-y-3 text-blue-700">
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div className="text-blue-600 text-lg">🏠</div>
|
||||||
|
<p>
|
||||||
|
This site is a another version of {" "}
|
||||||
|
<span className="font-semibold text-success">Firefly Tools {" "}</span>
|
||||||
|
developed by {" "}
|
||||||
|
<span className="font-semibold text-accent">Me {"(Kain)"}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="grid md:grid-cols-2 gap-4">
|
||||||
|
<div className="bg-white border border-blue-200 rounded-lg p-4">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<span className="text-blue-600 text-lg">🏆</span>
|
||||||
|
<span className="font-semibold text-blue-800">Master Website</span>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href="https://srtools.kain.id.vn/"
|
||||||
|
className="link link-accent font-mono text-sm break-all"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
https://srtools.kain.id.vn/
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white border border-blue-200 rounded-lg p-4">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<span className="text-blue-600 text-lg">🔄</span>
|
||||||
|
<span className="font-semibold text-blue-800">Backup Website</span>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href="https://firefly-srtools.vercel.app/"
|
||||||
|
className="link link-accent font-mono text-sm break-all"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
https://firefly-srtools.vercel.app/
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div className="text-blue-600 text-lg">👨💻</div>
|
||||||
|
<p>The original tool was created by a third-party developer named <span className="font-semibold text-accent">Amazing</span>. This version is directly based on that work, without modification to core logic.</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div className="text-blue-600 text-lg">🔗</div>
|
||||||
|
<p>There is also a more modern version by the same author available at{" "}
|
||||||
|
<a
|
||||||
|
href="https://srtools.neonteam.dev/"
|
||||||
|
className="link link-accent"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
srtools.neonteam.dev
|
||||||
|
</a>
|
||||||
|
{" "}and the original version at{" "}
|
||||||
|
<a
|
||||||
|
href="https://srtools.pages.dev/"
|
||||||
|
className="link link-accent"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
srtools.pages.dev
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Section 2: Main Features */}
|
||||||
|
<div className="bg-green-50 border-l-4 border-green-400 p-6 rounded-r-lg">
|
||||||
|
<h2 className="text-2xl font-bold text-green-800 flex items-center gap-2 mb-4">
|
||||||
|
<span>🔧</span>
|
||||||
|
<span>Main Features</span>
|
||||||
|
</h2>
|
||||||
|
<div className="space-y-3 text-green-700">
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div className="text-green-600 text-lg">⚙️</div>
|
||||||
|
<p>Configure characters, light cones, relics, traces, and eidolons easily in your browser.</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div className="text-green-600 text-lg">🔌</div>
|
||||||
|
<p>Instantly apply setups to <span className="font-semibold text-accent">Firefly GO Server</span> using <span className="font-semibold">Connect PS</span> — no manual file uploads required.</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div className="text-green-600 text-2xl">✨</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="font-semibold text-green-800 text-lg">Extra Settings</h4>
|
||||||
|
<p className="text-green-700 mt-1">
|
||||||
|
Enhance your <span className="font-semibold text-accent">Firefly GO Server</span> experience with extra features:
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc list-inside mt-2 space-y-1 text-green-700">
|
||||||
|
<li>🎭 <span className="font-medium">Hidden Game UI</span> — remove the entire game interface.</li>
|
||||||
|
<li>🚫 <span className="font-medium">Disable Censorship</span> — get rid of Lens Flare censor 💀.</li>
|
||||||
|
<li>🧪 <span className="font-medium">Theorycraft Mode</span> — configure HP, cycles, and more via the web.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div className="text-green-600 text-lg">📂</div>
|
||||||
|
<p>Export and import full builds using <code className="bg-gray-200 px-2 py-1 rounded text-sm">freesr-data.json</code>.</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div className="text-green-600 text-lg">⚡</div>
|
||||||
|
<p>Fast testing workflow — no sync cooldowns, instant in-game updates.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Section 3: Getting Started */}
|
||||||
|
<div className="bg-purple-50 border-l-4 border-purple-400 p-6 rounded-r-lg">
|
||||||
|
<h2 className="text-2xl font-bold text-purple-800 flex items-center gap-2 mb-4">
|
||||||
|
<span>🚀</span>
|
||||||
|
<span>Getting Started</span>
|
||||||
|
</h2>
|
||||||
|
<div className="space-y-3 text-purple-700">
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div className="text-purple-600 text-lg">1️⃣</div>
|
||||||
|
<p>Access the tool through your browser at the self-hosted instance.</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div className="text-purple-600 text-lg">2️⃣</div>
|
||||||
|
<p>Configure your character builds with the intuitive web interface.</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div className="text-purple-600 text-lg">3️⃣</div>
|
||||||
|
<p>Use <span className="font-semibold">Connect PS</span> feature to instantly sync with your private server.</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div className="text-purple-600 text-lg">4️⃣</div>
|
||||||
|
<p>Test your builds in-game with real-time updates and modifications.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center pt-4">
|
||||||
|
<Link to="/" className="btn btn-primary btn-wide">Back to Home</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ import { Link } from '@tanstack/react-router';
|
|||||||
export default function HowToPage() {
|
export default function HowToPage() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-base-200 flex items-center justify-center p-6">
|
<div className="min-h-screen bg-base-200 flex items-center justify-center p-6">
|
||||||
<div className="max-w-4xl w-full bg-base-100 shadow-xl rounded-2xl p-8 space-y-8">
|
<div className=" w-full bg-base-100 shadow-xl rounded-2xl p-8 space-y-8">
|
||||||
<h1 className="text-4xl font-bold text-primary text-center">How to Use</h1>
|
<h1 className="text-4xl font-bold text-primary text-center">How to Use</h1>
|
||||||
|
|
||||||
{/* Section 1: Launcher Features */}
|
{/* Section 1: Launcher Features */}
|
||||||
@@ -28,12 +28,22 @@ export default function HowToPage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="text-green-600 text-lg">📦</div>
|
<div className="text-green-600 text-2xl">📦</div>
|
||||||
<p>Patch and update game files using{" "}
|
<div>
|
||||||
<a href="/hdiff" className="link link-info font-mono">Hdiffz Tool</a>
|
<p className="text-green-800 font-semibold">
|
||||||
{" "}(HDiffPatch) — fast & lightweight incremental updates.
|
Patch & Update Game Files
|
||||||
</p>
|
</p>
|
||||||
|
<p className="text-green-700">
|
||||||
|
Use the{" "}
|
||||||
|
<a href="/diff" className="link link-info font-mono">Diff Tool</a>{" "}
|
||||||
|
(<span className="font-medium">DiffPatch</span>) for fast & lightweight incremental updates.
|
||||||
|
</p>
|
||||||
|
<p className="text-green-700 mt-1">
|
||||||
|
Supports <span className="font-semibold">Hdiff</span>, <span className="font-semibold">Ldiff</span>, and custom diff formats.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -46,7 +56,7 @@ export default function HowToPage() {
|
|||||||
|
|
||||||
<p className="text-blue-700 mb-4">
|
<p className="text-blue-700 mb-4">
|
||||||
Below are in-game chat commands you can use. Some commands require you to enable{" "}
|
Below are in-game chat commands you can use. Some commands require you to enable{" "}
|
||||||
<span className="font-semibold text-warning">Theorycraft Mode</span>.
|
<span className="font-semibold text-accent">Theorycraft Mode</span>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Theorycraft Mode Warning */}
|
{/* Theorycraft Mode Warning */}
|
||||||
@@ -65,8 +75,60 @@ export default function HowToPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white border border-blue-200 rounded-lg p-4">
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div className="text-blue-600 text-lg">✨</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h4 className="font-semibold text-blue-800 mb-1">Extra Settings</h4>
|
||||||
|
|
||||||
|
<div className="space-y-4 text-blue-700 text-sm">
|
||||||
|
|
||||||
|
<div className="bg-blue-50 border border-blue-200 rounded-lg p-3">
|
||||||
|
<h5 className="font-semibold text-blue-800 flex items-center gap-2">
|
||||||
|
🎭 Hidden UI
|
||||||
|
</h5>
|
||||||
|
<p className="mt-1">
|
||||||
|
Instantly hides the entire game UI — often used in DIM showcase videos.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div className="bg-blue-50 border border-blue-200 rounded-lg p-3">
|
||||||
|
<h5 className="font-semibold text-blue-800 flex items-center gap-2">
|
||||||
|
🚫 Disable Censorship
|
||||||
|
</h5>
|
||||||
|
<p className="mt-1">
|
||||||
|
Remove the Lens Flare censor effect 💀 for a cleaner experience.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-blue-50 border border-blue-200 rounded-lg p-3">
|
||||||
|
<h5 className="font-semibold text-blue-800 flex items-center gap-2">
|
||||||
|
🧪 Theorycraft Mode
|
||||||
|
</h5>
|
||||||
|
<p className="mt-1">
|
||||||
|
No need to type chat commands anymore — configure everything through the
|
||||||
|
web: adjust monster HP, set cycles, view logs, and more.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div className="mt-4 aspect-w-16 aspect-h-9">
|
||||||
|
<iframe
|
||||||
|
src="https://www.youtube.com/embed/uiKdFrvn9NQ"
|
||||||
|
title="Extra Settings Tutorial"
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||||
|
allowFullScreen
|
||||||
|
className="rounded-lg w-full h-[300px]"
|
||||||
|
></iframe>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Commands List */}
|
{/* Commands List */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4 mt-4">
|
||||||
<h3 className="text-lg font-semibold text-blue-800">Available Commands:</h3>
|
<h3 className="text-lg font-semibold text-blue-800">Available Commands:</h3>
|
||||||
|
|
||||||
{/* Theorycraft Toggle */}
|
{/* Theorycraft Toggle */}
|
||||||
@@ -103,16 +165,28 @@ export default function HowToPage() {
|
|||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="text-blue-600 text-lg">❤️</div>
|
<div className="text-blue-600 text-lg">❤️</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<h4 className="font-semibold text-blue-800 mb-1">HP Override <span className="text-red-600 text-sm">(Theorycraft only)</span></h4>
|
<h4 className="font-semibold text-blue-800 mb-1">
|
||||||
<div className="space-y-1 text-blue-700">
|
HP Override <span className="text-red-600 text-sm">(Theorycraft only)</span>
|
||||||
<p><code className="bg-blue-100 px-1 py-0.5 rounded text-sm">/hp WAVE HP</code> — Set monster HP for a specific wave</p>
|
</h4>
|
||||||
<p className="text-sm">Example: <code className="bg-blue-100 px-1 py-0.5 rounded text-sm">/hp 1 2000000</code> sets Wave 1 monster HP to 2,000,000</p>
|
<div className="space-y-2 text-blue-700 text-sm">
|
||||||
<p className="text-sm"><code className="bg-blue-100 px-1 py-0.5 rounded text-sm">/hp 0</code> disables HP override</p>
|
<p>
|
||||||
|
<code className="bg-blue-100 px-1 py-0.5 rounded text-sm">/hp N</code> — Set monster HP (only available in Theorycraft mode)
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<code className="bg-blue-100 px-1 py-0.5 rounded text-sm">/hp 0</code> — Disable the set HP feature
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<code className="bg-blue-100 px-1 py-0.5 rounded text-sm">/hp Wave V1 V2 ...</code> — Set HP for each monster in a specific wave
|
||||||
|
</p>
|
||||||
|
<p className="ml-4">
|
||||||
|
Example: <code className="bg-blue-100 px-1 py-0.5 rounded text-sm">/hp 1 2000000 3000000</code> sets wave 1 monster1 HP=2,000,000 and monster2 HP=3,000,000
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{/* Log Command */}
|
{/* Log Command */}
|
||||||
<div className="bg-white border border-blue-200 rounded-lg p-4">
|
<div className="bg-white border border-blue-200 rounded-lg p-4">
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Folder, Settings, Check, X, Globe, Mic } from 'lucide-react'
|
import { Folder, Settings, Check, X, Globe, Mic } from 'lucide-react'
|
||||||
import { FSService } from '@bindings/firefly-launcher/internal'
|
import { FSService } from '@bindings/firefly-launcher/internal/fs-service'
|
||||||
import { LanguageService } from '@bindings/firefly-launcher/internal'
|
import { LanguageService } from '@bindings/firefly-launcher/internal/language-service'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import useSettingStore from '@/stores/settingStore'
|
import useSettingStore from '@/stores/settingStore'
|
||||||
|
|
||||||
@@ -208,10 +208,10 @@ export default function LanguagePage() {
|
|||||||
|
|
||||||
<div className="bg-warning/5 rounded-lg p-2 border border-warning/30">
|
<div className="bg-warning/5 rounded-lg p-2 border border-warning/30">
|
||||||
<div className="flex items-center gap-2 mb-1">
|
<div className="flex items-center gap-2 mb-1">
|
||||||
<Mic size={20} className="text-warning" />
|
<Mic size={20} className="text-accent" />
|
||||||
<span className="font-bold text-warning">Voice Language</span>
|
<span className="font-bold text-accent">Voice Language</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-2xl font-bold text-warning">
|
<p className="text-2xl font-bold text-accent">
|
||||||
{getLanguageLabel(voiceLang)}
|
{getLanguageLabel(voiceLang)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -250,7 +250,7 @@ export default function LanguagePage() {
|
|||||||
|
|
||||||
{/* Voice Language */}
|
{/* Voice Language */}
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<label className="flex text-sm font-medium text-warning items-center gap-2">
|
<label className="flex text-sm font-medium text-accent items-center gap-2">
|
||||||
<Mic size={16} />
|
<Mic size={16} />
|
||||||
Voice Language
|
Voice Language
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { Play, Menu, FolderOpen, MessageCircleQuestionMark } from 'lucide-react';
|
import { Play, Menu, FolderOpen, MessageCircleQuestionMark } from 'lucide-react';
|
||||||
import { FSService, AppService } from '@bindings/firefly-launcher/internal';
|
import { AppService } from '@bindings/firefly-launcher/internal/app-service';
|
||||||
|
import { FSService } from '@bindings/firefly-launcher/internal/fs-service';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import path from 'path-browserify'
|
import path from 'path-browserify'
|
||||||
import useSettingStore from '@/stores/settingStore';
|
import useSettingStore from '@/stores/settingStore';
|
||||||
@@ -9,9 +10,14 @@ import useLauncherStore from '@/stores/launcherStore';
|
|||||||
import { motion } from 'motion/react';
|
import { motion } from 'motion/react';
|
||||||
import { Link } from '@tanstack/react-router';
|
import { Link } from '@tanstack/react-router';
|
||||||
import { CheckUpdateLauncher, CheckUpdateProxy, CheckUpdateServer, sleep, UpdateLauncher, UpdateProxy, UpdateServer } from '@/helper';
|
import { CheckUpdateLauncher, CheckUpdateProxy, CheckUpdateServer, sleep, UpdateLauncher, UpdateProxy, UpdateServer } from '@/helper';
|
||||||
|
import UpdateModal from '@/components/updateModal';
|
||||||
|
import { BackgroundSelector } from '@/components/backgroudModal';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function LauncherPage() {
|
export default function LauncherPage() {
|
||||||
const { gamePath,
|
const {
|
||||||
|
gamePath,
|
||||||
setGamePath,
|
setGamePath,
|
||||||
setGameDir,
|
setGameDir,
|
||||||
serverPath,
|
serverPath,
|
||||||
@@ -19,7 +25,9 @@ export default function LauncherPage() {
|
|||||||
gameDir,
|
gameDir,
|
||||||
serverVersion,
|
serverVersion,
|
||||||
proxyVersion,
|
proxyVersion,
|
||||||
|
background
|
||||||
} = useSettingStore()
|
} = useSettingStore()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOpenDownloadDataModal,
|
isOpenDownloadDataModal,
|
||||||
isOpenUpdateDataModal,
|
isOpenUpdateDataModal,
|
||||||
@@ -28,6 +36,7 @@ export default function LauncherPage() {
|
|||||||
setIsOpenUpdateDataModal,
|
setIsOpenUpdateDataModal,
|
||||||
setIsOpenSelfUpdateModal
|
setIsOpenSelfUpdateModal
|
||||||
} = useModalStore()
|
} = useModalStore()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isLoading,
|
isLoading,
|
||||||
downloadType,
|
downloadType,
|
||||||
@@ -40,7 +49,7 @@ export default function LauncherPage() {
|
|||||||
progressDownload,
|
progressDownload,
|
||||||
downloadSpeed,
|
downloadSpeed,
|
||||||
updateData,
|
updateData,
|
||||||
launcherVersion,
|
// launcherVersion,
|
||||||
setLauncherVersion,
|
setLauncherVersion,
|
||||||
setIsLoading,
|
setIsLoading,
|
||||||
setDownloadType,
|
setDownloadType,
|
||||||
@@ -53,6 +62,33 @@ export default function LauncherPage() {
|
|||||||
setUpdateData,
|
setUpdateData,
|
||||||
} = useLauncherStore()
|
} = 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(() => {
|
useEffect(() => {
|
||||||
const check = async () => {
|
const check = async () => {
|
||||||
if (!serverVersion || !proxyVersion) {
|
if (!serverVersion || !proxyVersion) {
|
||||||
@@ -120,7 +156,7 @@ export default function LauncherPage() {
|
|||||||
const handlePickFile = async () => {
|
const handlePickFile = async () => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
const basePath = await FSService.PickFile()
|
const basePath = await FSService.PickFile("exe")
|
||||||
if (basePath.endsWith("StarRail.exe") || basePath.endsWith("launcher.exe")) {
|
if (basePath.endsWith("StarRail.exe") || basePath.endsWith("launcher.exe")) {
|
||||||
const normalized = basePath.replace(/\\/g, '/')
|
const normalized = basePath.replace(/\\/g, '/')
|
||||||
const folderPath = path.dirname(normalized)
|
const folderPath = path.dirname(normalized)
|
||||||
@@ -199,18 +235,18 @@ export default function LauncherPage() {
|
|||||||
setIsDownloading(true)
|
setIsDownloading(true)
|
||||||
if (updateData.launcher.isUpdate) {
|
if (updateData.launcher.isUpdate) {
|
||||||
await UpdateLauncher(updateData.launcher.version)
|
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)
|
setIsOpenSelfUpdateModal(true)
|
||||||
}
|
}
|
||||||
if (updateData.server.isUpdate || !updateData.server.isExists) {
|
if (updateData.server.isUpdate || !updateData.server.isExists) {
|
||||||
await UpdateServer(updateData.server.version)
|
await UpdateServer(updateData.server.version)
|
||||||
setServerReady(true)
|
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) {
|
if (updateData.proxy.isUpdate || !updateData.proxy.isExists) {
|
||||||
await UpdateProxy(updateData.proxy.version)
|
await UpdateProxy(updateData.proxy.version)
|
||||||
setProxyReady(true)
|
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("")
|
setDownloadType("")
|
||||||
@@ -234,78 +270,39 @@ export default function LauncherPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative min-h-fit overflow-hidden">
|
<div className="relative min-h-fit overflow-hidden">
|
||||||
<div
|
<img
|
||||||
className="fixed inset-0 z-0 w-full h-full"
|
src={background}
|
||||||
style={{
|
alt="background"
|
||||||
backgroundImage: "url('/bg.jpg')",
|
className="fixed inset-0 w-full h-full object-cover z-0"
|
||||||
backgroundSize: "cover",
|
onError={(e) => {
|
||||||
backgroundPosition: "center",
|
const target = e.currentTarget as HTMLImageElement
|
||||||
backgroundRepeat: "no-repeat"
|
target.src = "bg-12.jpg"
|
||||||
}}
|
}}
|
||||||
></div>
|
/>
|
||||||
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<header className="hidden sm:flex fixed z-10 items-center justify-between p-6">
|
<header className="hidden sm:flex fixed z-10 items-center justify-between py-6 px-4 ">
|
||||||
<div className="text-2xl font-bold text-white">Firefly GO</div>
|
<div className="text-2xl font-bold text-white bg-gray-500/5 rounded-full p-1">Firefly GO</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div className="hidden sm:flex fixed top-1/4 left-4 z-10 flex-col space-y-3 bg-black/30 backdrop-blur-md rounded-xl p-3 shadow-lg">
|
<div className="hidden sm:flex fixed top-1/4 right-4 z-10 flex-col space-y-3 bg-white/5 rounded-xl p-3 shadow-lg">
|
||||||
|
{widgetLinks.map((link, idx) => (
|
||||||
|
<div key={idx} className="tooltip tooltip-left" data-tip={link.tooltip}>
|
||||||
|
<a
|
||||||
|
className={`btn btn-circle ${link.btnClass}`}
|
||||||
|
target="_blank"
|
||||||
|
href={link.href}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={link.img}
|
||||||
|
alt={link.tooltip}
|
||||||
|
className="w-8 h-8 rounded-full"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
<div className="tooltip tooltip-right" data-tip="Firefly SRAnalysis">
|
<div className="tooltip tooltip-left" data-tip="How to use all tools & commands">
|
||||||
<a
|
|
||||||
className="btn btn-circle btn-primary"
|
|
||||||
target="_blank"
|
|
||||||
href="https://sranalysis.kain.id.vn/"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="https://sranalysis.kain.id.vn/ff-sranalysis.png"
|
|
||||||
alt="SRAnalysis Logo"
|
|
||||||
className="w-8 h-8 rounded-full"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="tooltip tooltip-right" data-tip="Firefly SRTools">
|
|
||||||
<a
|
|
||||||
className="btn btn-circle btn-secondary"
|
|
||||||
target="_blank"
|
|
||||||
href="https://srtools.kain.id.vn/"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="https://srtools.kain.id.vn/ff-srtool.png"
|
|
||||||
alt="SRTools Logo"
|
|
||||||
className="w-8 h-8 rounded-full"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div className="tooltip tooltip-right" data-tip="Amazing's SRTools (Original UI)">
|
|
||||||
<a
|
|
||||||
className="btn btn-circle btn-primary"
|
|
||||||
target="_blank"
|
|
||||||
href="https://srtools.pages.dev/"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="https://icons.duckduckgo.com/ip3/srtools.pages.dev.ico"
|
|
||||||
alt="SRTools Logo"
|
|
||||||
className="w-8 h-8 rounded-full"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div className="tooltip tooltip-right" data-tip="Amazing's SRTools (Modern UI)">
|
|
||||||
<a
|
|
||||||
className="btn btn-circle btn-secondary"
|
|
||||||
target="_blank"
|
|
||||||
href="https://srtools.neonteam.dev/"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="https://icons.duckduckgo.com/ip3/srtools.neonteam.dev.ico"
|
|
||||||
alt="SRTools Logo"
|
|
||||||
className="w-8 h-8 rounded-full"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="tooltip tooltip-right" data-tip="How to use all tools & commands">
|
|
||||||
<Link
|
<Link
|
||||||
to="/howto"
|
to="/howto"
|
||||||
className="btn btn-warning btn-circle"
|
className="btn btn-warning btn-circle"
|
||||||
@@ -315,6 +312,9 @@ export default function LauncherPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="hidden sm:flex fixed top-1/2 left-5 z-10 ">
|
||||||
|
<BackgroundSelector />
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Bottom Panel */}
|
{/* Bottom Panel */}
|
||||||
{serverReady && proxyReady && !isDownloading && (
|
{serverReady && proxyReady && !isDownloading && (
|
||||||
@@ -344,32 +344,33 @@ export default function LauncherPage() {
|
|||||||
</div>
|
</div>
|
||||||
<ul tabIndex={0} className="dropdown-content menu bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm">
|
<ul tabIndex={0} className="dropdown-content menu bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm">
|
||||||
<li><button onClick={handlePickFile}>Change Game Path</button></li>
|
<li><button onClick={handlePickFile}>Change Game Path</button></li>
|
||||||
<li><button
|
<li>
|
||||||
onClick={async () => {
|
<button
|
||||||
const serverData = await CheckUpdateServer(serverPath, serverVersion)
|
onClick={async () => {
|
||||||
const proxyData = await CheckUpdateProxy(proxyPath, proxyVersion)
|
const serverData = await CheckUpdateServer(serverPath, serverVersion)
|
||||||
const launcherData = await CheckUpdateLauncher()
|
const proxyData = await CheckUpdateProxy(proxyPath, proxyVersion)
|
||||||
setUpdateData({
|
setUpdateData({
|
||||||
server: serverData,
|
server: serverData,
|
||||||
proxy: proxyData,
|
proxy: proxyData,
|
||||||
launcher: launcherData
|
launcher: updateData.launcher
|
||||||
})
|
})
|
||||||
if (launcherData.isUpdate) {
|
|
||||||
setIsOpenSelfUpdateModal(true)
|
if (!serverData.isExists || !proxyData.isExists) {
|
||||||
return
|
setIsOpenDownloadDataModal(true)
|
||||||
}
|
return
|
||||||
if (!serverData.isExists || !proxyData.isExists) {
|
}
|
||||||
setIsOpenDownloadDataModal(true)
|
if (serverData.isUpdate || proxyData.isUpdate) {
|
||||||
return
|
setIsOpenUpdateDataModal(true)
|
||||||
}
|
return
|
||||||
if (serverData.isUpdate || proxyData.isUpdate) {
|
}
|
||||||
setIsOpenUpdateDataModal(true)
|
toast.success("No updates available")
|
||||||
return
|
}}>
|
||||||
}
|
Check for Updates Server & Proxy
|
||||||
toast.success("No updates available")
|
</button>
|
||||||
}}>
|
</li>
|
||||||
Check for Updates
|
<li>
|
||||||
</button></li>
|
|
||||||
|
</li>
|
||||||
<li><button disabled={!serverPath} onClick={() => {
|
<li><button disabled={!serverPath} onClick={() => {
|
||||||
if (serverPath) {
|
if (serverPath) {
|
||||||
FSService.OpenFolder("./server")
|
FSService.OpenFolder("./server")
|
||||||
@@ -394,34 +395,35 @@ export default function LauncherPage() {
|
|||||||
|
|
||||||
{/* Downloading */}
|
{/* Downloading */}
|
||||||
{isDownloading && (
|
{isDownloading && (
|
||||||
updateData.proxy.isUpdate
|
updateData.proxy.isUpdate
|
||||||
|| updateData.server.isUpdate
|
|| updateData.server.isUpdate
|
||||||
|| !updateData.proxy.isExists
|
|| !updateData.proxy.isExists
|
||||||
|| !updateData.server.isExists
|
|| !updateData.server.isExists
|
||||||
) && (
|
) && (
|
||||||
<div className="fixed bottom-4 left-1/2 transform -translate-x-1/2 z-10 w-[60vw] bg-black/20 backdrop-blur-sm rounded-lg p-4 shadow-lg">
|
<div className="fixed bottom-4 left-1/2 transform -translate-x-1/2 z-10 w-[60vw] bg-black/20 backdrop-blur-sm rounded-lg p-4 shadow-lg">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex justify-center items-center text-sm text-white/80">
|
<div className="flex justify-center items-center text-sm text-white/80">
|
||||||
<span>{downloadType}</span>
|
<span>{downloadType}</span>
|
||||||
<div className="flex items-center gap-4 ml-4">
|
<div className="flex items-center gap-4 ml-4">
|
||||||
<span className="text-cyan-400 font-semibold">{downloadSpeed.toFixed(1)} MB/s</span>
|
<span className="text-cyan-400 font-semibold">{downloadSpeed}</span>
|
||||||
<span className="text-white font-bold">{progressDownload.toFixed(1)}%</span>
|
<span className="text-white font-bold">{progressDownload.toFixed(1)}%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="w-full bg-white/20 rounded-full h-2 overflow-hidden">
|
||||||
|
<motion.div
|
||||||
|
className="h-full bg-gradient-to-r from-cyan-400 to-blue-500 rounded-full"
|
||||||
|
initial={{ width: 0 }}
|
||||||
|
animate={{ width: `${progressDownload}%` }}
|
||||||
|
transition={{ type: "tween", ease: "linear", duration: 0.03 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="text-center text-xs text-white/60">
|
||||||
|
{progressDownload < 100 ? 'Please wait...' : 'Complete!'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full bg-white/20 rounded-full h-2 overflow-hidden">
|
|
||||||
<motion.div
|
|
||||||
className="h-full bg-gradient-to-r from-cyan-400 to-blue-500 rounded-full"
|
|
||||||
initial={{ width: 0 }}
|
|
||||||
animate={{ width: `${progressDownload}%` }}
|
|
||||||
transition={{ duration: 0.3 }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="text-center text-xs text-white/60">
|
|
||||||
{progressDownload < 100 ? 'Please wait...' : 'Complete!'}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
|
||||||
{isDownloading && updateData.launcher.isUpdate && (
|
{isDownloading && updateData.launcher.isUpdate && (
|
||||||
<div className="fixed bottom-4 left-1/2 transform -translate-x-1/2 z-10 w-[60vw] bg-black/20 backdrop-blur-sm rounded-lg p-4 shadow-lg">
|
<div className="fixed bottom-4 left-1/2 transform -translate-x-1/2 z-10 w-[60vw] bg-black/20 backdrop-blur-sm rounded-lg p-4 shadow-lg">
|
||||||
<div className="space-y-3 text-sm text-white/80 text-center">
|
<div className="space-y-3 text-sm text-white/80 text-center">
|
||||||
@@ -429,10 +431,10 @@ export default function LauncherPage() {
|
|||||||
<div className="flex justify-center items-center gap-4 ml-4">
|
<div className="flex justify-center items-center gap-4 ml-4">
|
||||||
<span
|
<span
|
||||||
className={`font-bold ${downloadType === "update:launcher:downloading"
|
className={`font-bold ${downloadType === "update:launcher:downloading"
|
||||||
? "text-yellow-200 text-2xl"
|
? "text-yellow-200 text-2xl"
|
||||||
: downloadType === "update:launcher:success"
|
: downloadType === "update:launcher:success"
|
||||||
? "text-emerald-200 text-xl"
|
? "text-emerald-200 text-xl"
|
||||||
: "text-red-200 text-xl"
|
: "text-red-200 text-xl"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{downloadType === "update:launcher:downloading" && "Updating launcher"}
|
{downloadType === "update:launcher:downloading" && "Updating launcher"}
|
||||||
@@ -465,151 +467,44 @@ export default function LauncherPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Version Info */}
|
<div className="hidden md:block fixed bottom-4 left-5 z-10">
|
||||||
{serverReady && proxyReady && !isDownloading && (
|
|
||||||
<div className="hidden md:block fixed bottom-4 left-4 z-10 text-sm font-bold bg-black/20 backdrop-blur-sm rounded-lg p-2 shadow">
|
|
||||||
<p className="text-primary">Version server: {serverVersion}</p>
|
|
||||||
<p className="mt-2 text-secondary">Version proxy: {proxyVersion}</p>
|
|
||||||
<p className="mt-2 text-success">Version launcher: {launcherVersion}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
|
<img src="/heart-hsr.gif" alt="firefly animation" className="rounded-lg w-24 h-24" />
|
||||||
|
|
||||||
|
</div>
|
||||||
{/* Modal */}
|
{/* Modal */}
|
||||||
{isOpenUpdateDataModal && (
|
<UpdateModal
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
|
isOpen={isOpenUpdateDataModal}
|
||||||
<div className="relative w-[90%] max-w-5xl bg-base-100 text-base-content rounded-xl border border-purple-500/50 shadow-lg shadow-purple-500/20">
|
onClose={() => setIsOpenUpdateDataModal(false)}
|
||||||
<motion.button
|
title="Update Data"
|
||||||
whileHover={{ scale: 1.1, rotate: 90 }}
|
message="Do you want to update data server and proxy?"
|
||||||
transition={{ duration: 0.2 }}
|
buttons={[
|
||||||
className="btn btn-circle btn-md btn-error absolute right-3 top-3"
|
{ text: "No", onClick: () => setIsOpenUpdateDataModal(false), variant: "outline" },
|
||||||
onClick={() => setIsOpenUpdateDataModal(false)}
|
{ text: "Yes", onClick: async () => { setIsOpenUpdateDataModal(false); await handlerUpdateData() }, variant: "primary" }
|
||||||
>
|
]}
|
||||||
✕
|
/>
|
||||||
</motion.button>
|
|
||||||
|
|
||||||
<div className="border-b border-purple-500/30 px-6 py-4 mb-4">
|
<UpdateModal
|
||||||
<h3 className="font-bold text-2xl text-transparent bg-clip-text bg-gradient-to-r from-pink-400 to-cyan-400">
|
isOpen={isOpenDownloadDataModal}
|
||||||
Update Data
|
onClose={() => setIsOpenDownloadDataModal(false)}
|
||||||
</h3>
|
title="Download Data"
|
||||||
</div>
|
message="Data server and proxy download required"
|
||||||
|
buttons={[
|
||||||
|
{ text: "Download", onClick: async () => { setIsOpenDownloadDataModal(false); await handlerUpdateData() }, variant: "primary" }
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="px-6 pb-6">
|
<UpdateModal
|
||||||
<div className="mb-6">
|
isOpen={isOpenSelfUpdateModal}
|
||||||
<p className="text-warning text-lg">
|
onClose={() => setIsOpenSelfUpdateModal(false)}
|
||||||
Do you want to update data server and proxy?
|
title="Update Launcher"
|
||||||
</p>
|
message="Do you want to update launcher?"
|
||||||
</div>
|
buttons={[
|
||||||
|
{ text: "No", onClick: () => setIsOpenSelfUpdateModal(false), variant: "outline" },
|
||||||
|
{ text: "Yes", onClick: async () => { setIsOpenSelfUpdateModal(false); await handlerUpdateData() }, variant: "primary" }
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="flex justify-end gap-3">
|
|
||||||
<motion.button
|
|
||||||
whileHover={{ scale: 1.05 }}
|
|
||||||
whileTap={{ scale: 0.95 }}
|
|
||||||
className="btn btn-outline btn-error"
|
|
||||||
onClick={() => setIsOpenUpdateDataModal(false)}
|
|
||||||
>
|
|
||||||
No
|
|
||||||
</motion.button>
|
|
||||||
<motion.button
|
|
||||||
whileHover={{ scale: 1.05 }}
|
|
||||||
whileTap={{ scale: 0.95 }}
|
|
||||||
className="btn btn-primary bg-gradient-to-r from-orange-200 to-red-400 border-none"
|
|
||||||
onClick={async () => {
|
|
||||||
setIsOpenUpdateDataModal(false)
|
|
||||||
await handlerUpdateData()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Yes
|
|
||||||
</motion.button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isOpenDownloadDataModal && (
|
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
|
|
||||||
<div className="relative w-[90%] max-w-5xl bg-base-100 text-base-content rounded-xl border border-purple-500/50 shadow-lg shadow-purple-500/20">
|
|
||||||
<div className="border-b border-purple-500/30 px-6 py-4 mb-4">
|
|
||||||
<h3 className="font-bold text-2xl text-transparent bg-clip-text bg-gradient-to-r from-pink-400 to-cyan-400">
|
|
||||||
Download Data
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="px-6 pb-6">
|
|
||||||
<div className="mb-6">
|
|
||||||
<p className="text-warning text-lg">
|
|
||||||
Data server and proxy download required
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-end gap-3">
|
|
||||||
<motion.button
|
|
||||||
whileHover={{ scale: 1.05 }}
|
|
||||||
whileTap={{ scale: 0.95 }}
|
|
||||||
className="btn btn-primary bg-gradient-to-r from-orange-200 to-red-400 border-none"
|
|
||||||
onClick={async () => {
|
|
||||||
setIsOpenDownloadDataModal(false)
|
|
||||||
await handlerUpdateData()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Download
|
|
||||||
</motion.button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isOpenSelfUpdateModal && (
|
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
|
|
||||||
<div className="relative w-[90%] max-w-5xl bg-base-100 text-base-content rounded-xl border border-purple-500/50 shadow-lg shadow-purple-500/20">
|
|
||||||
<motion.button
|
|
||||||
whileHover={{ scale: 1.1, rotate: 90 }}
|
|
||||||
transition={{ duration: 0.2 }}
|
|
||||||
className="btn btn-circle btn-md btn-error absolute right-3 top-3"
|
|
||||||
onClick={() => setIsOpenSelfUpdateModal(false)}
|
|
||||||
>
|
|
||||||
✕
|
|
||||||
</motion.button>
|
|
||||||
|
|
||||||
<div className="border-b border-purple-500/30 px-6 py-4 mb-4">
|
|
||||||
<h3 className="font-bold text-2xl text-transparent bg-clip-text bg-gradient-to-r from-pink-400 to-cyan-400">
|
|
||||||
Update Launcher
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="px-6 pb-6">
|
|
||||||
<div className="mb-6">
|
|
||||||
<p className="text-warning text-lg">
|
|
||||||
Do you want to update launcher?
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-end gap-3">
|
|
||||||
<motion.button
|
|
||||||
whileHover={{ scale: 1.05 }}
|
|
||||||
whileTap={{ scale: 0.95 }}
|
|
||||||
className="btn btn-outline btn-error"
|
|
||||||
onClick={() => setIsOpenSelfUpdateModal(false)}
|
|
||||||
>
|
|
||||||
No
|
|
||||||
</motion.button>
|
|
||||||
<motion.button
|
|
||||||
whileHover={{ scale: 1.05 }}
|
|
||||||
whileTap={{ scale: 0.95 }}
|
|
||||||
className="btn btn-primary bg-gradient-to-r from-orange-200 to-red-400 border-none"
|
|
||||||
onClick={async () => {
|
|
||||||
setIsOpenSelfUpdateModal(false)
|
|
||||||
await handlerUpdateData()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Yes
|
|
||||||
</motion.button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,143 +0,0 @@
|
|||||||
import { Link } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
export default function SrToolsPage() {
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-base-200 flex items-center justify-center p-6">
|
|
||||||
<div className="max-w-4xl w-full bg-base-100 shadow-xl rounded-2xl p-8 space-y-8">
|
|
||||||
<h1 className="text-4xl font-bold text-primary text-center">SR Tools</h1>
|
|
||||||
|
|
||||||
{/* Section 1: About SR Tools */}
|
|
||||||
<div className="bg-blue-50 border-l-4 border-blue-400 p-6 rounded-r-lg">
|
|
||||||
<h2 className="text-2xl font-bold text-blue-800 flex items-center gap-2 mb-4">
|
|
||||||
<span>ℹ️</span>
|
|
||||||
<span>About SR Tools</span>
|
|
||||||
</h2>
|
|
||||||
<div className="space-y-3 text-blue-700">
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<div className="text-blue-600 text-lg">🏠</div>
|
|
||||||
<p>
|
|
||||||
This site is a another version of {" "}
|
|
||||||
<span className="font-semibold text-success">SR Tools {" "}</span>
|
|
||||||
developed by {" "}
|
|
||||||
<span className="font-semibold text-warning">Me {"(Kain)"}</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="grid md:grid-cols-2 gap-4">
|
|
||||||
<div className="bg-white border border-blue-200 rounded-lg p-4">
|
|
||||||
<div className="flex items-center gap-2 mb-2">
|
|
||||||
<span className="text-blue-600 text-lg">🏆</span>
|
|
||||||
<span className="font-semibold text-blue-800">Master Website</span>
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
href="https://srtools.kain.id.vn/"
|
|
||||||
className="link link-warning font-mono text-sm break-all"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
https://srtools.kain.id.vn/
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-white border border-blue-200 rounded-lg p-4">
|
|
||||||
<div className="flex items-center gap-2 mb-2">
|
|
||||||
<span className="text-blue-600 text-lg">🔄</span>
|
|
||||||
<span className="font-semibold text-blue-800">Backup Website</span>
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
href="https://firefly-srtools.vercel.app/"
|
|
||||||
className="link link-warning font-mono text-sm break-all"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
https://firefly-srtools.vercel.app/
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<div className="text-blue-600 text-lg">👨💻</div>
|
|
||||||
<p>The original tool was created by a third-party developer named <span className="font-semibold text-warning">Amazing</span>. This version is directly based on that work, without modification to core logic.</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<div className="text-blue-600 text-lg">🔗</div>
|
|
||||||
<p>There is also a more modern version by the same author available at{" "}
|
|
||||||
<a
|
|
||||||
href="https://srtools.neonteam.dev/"
|
|
||||||
className="link link-warning"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
srtools.neonteam.dev
|
|
||||||
</a>
|
|
||||||
{" "}and the original version at{" "}
|
|
||||||
<a
|
|
||||||
href="https://srtools.pages.dev/"
|
|
||||||
className="link link-warning"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
srtools.pages.dev
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Section 2: Main Features */}
|
|
||||||
<div className="bg-green-50 border-l-4 border-green-400 p-6 rounded-r-lg">
|
|
||||||
<h2 className="text-2xl font-bold text-green-800 flex items-center gap-2 mb-4">
|
|
||||||
<span>🔧</span>
|
|
||||||
<span>Main Features</span>
|
|
||||||
</h2>
|
|
||||||
<div className="space-y-3 text-green-700">
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<div className="text-green-600 text-lg">⚙️</div>
|
|
||||||
<p>Configure characters, light cones, relics, traces, and eidolons easily in your browser.</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<div className="text-green-600 text-lg">🔌</div>
|
|
||||||
<p>Instantly apply setups to <span className="font-semibold text-warning">Firefly Private Server</span> using <span className="font-semibold">Connect PS</span> — no manual file uploads required.</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<div className="text-green-600 text-lg">📂</div>
|
|
||||||
<p>Export and import full builds using <code className="bg-gray-200 px-2 py-1 rounded text-sm">freesr-data.json</code>.</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<div className="text-green-600 text-lg">⚡</div>
|
|
||||||
<p>Fast testing workflow — no sync cooldowns, instant in-game updates.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Section 3: Getting Started */}
|
|
||||||
<div className="bg-purple-50 border-l-4 border-purple-400 p-6 rounded-r-lg">
|
|
||||||
<h2 className="text-2xl font-bold text-purple-800 flex items-center gap-2 mb-4">
|
|
||||||
<span>🚀</span>
|
|
||||||
<span>Getting Started</span>
|
|
||||||
</h2>
|
|
||||||
<div className="space-y-3 text-purple-700">
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<div className="text-purple-600 text-lg">1️⃣</div>
|
|
||||||
<p>Access the tool through your browser at the self-hosted instance.</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<div className="text-purple-600 text-lg">2️⃣</div>
|
|
||||||
<p>Configure your character builds with the intuitive web interface.</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<div className="text-purple-600 text-lg">3️⃣</div>
|
|
||||||
<p>Use <span className="font-semibold">Connect PS</span> feature to instantly sync with your private server.</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<div className="text-purple-600 text-lg">4️⃣</div>
|
|
||||||
<p>Test your builds in-game with real-time updates and modifications.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="text-center pt-4">
|
|
||||||
<Link to="/" className="btn btn-primary btn-wide">Back to Home</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,7 @@ import { Route as rootRouteImport } from './routes/__root'
|
|||||||
import { Route as SrtoolsRouteImport } from './routes/srtools'
|
import { Route as SrtoolsRouteImport } from './routes/srtools'
|
||||||
import { Route as LanguageRouteImport } from './routes/language'
|
import { Route as LanguageRouteImport } from './routes/language'
|
||||||
import { Route as HowtoRouteImport } from './routes/howto'
|
import { Route as HowtoRouteImport } from './routes/howto'
|
||||||
import { Route as HdiffzRouteImport } from './routes/hdiffz'
|
import { Route as DiffRouteImport } from './routes/diff'
|
||||||
import { Route as AnalysisRouteImport } from './routes/analysis'
|
import { Route as AnalysisRouteImport } from './routes/analysis'
|
||||||
import { Route as AboutRouteImport } from './routes/about'
|
import { Route as AboutRouteImport } from './routes/about'
|
||||||
import { Route as IndexRouteImport } from './routes/index'
|
import { Route as IndexRouteImport } from './routes/index'
|
||||||
@@ -32,9 +32,9 @@ const HowtoRoute = HowtoRouteImport.update({
|
|||||||
path: '/howto',
|
path: '/howto',
|
||||||
getParentRoute: () => rootRouteImport,
|
getParentRoute: () => rootRouteImport,
|
||||||
} as any)
|
} as any)
|
||||||
const HdiffzRoute = HdiffzRouteImport.update({
|
const DiffRoute = DiffRouteImport.update({
|
||||||
id: '/hdiffz',
|
id: '/diff',
|
||||||
path: '/hdiffz',
|
path: '/diff',
|
||||||
getParentRoute: () => rootRouteImport,
|
getParentRoute: () => rootRouteImport,
|
||||||
} as any)
|
} as any)
|
||||||
const AnalysisRoute = AnalysisRouteImport.update({
|
const AnalysisRoute = AnalysisRouteImport.update({
|
||||||
@@ -57,7 +57,7 @@ export interface FileRoutesByFullPath {
|
|||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/about': typeof AboutRoute
|
'/about': typeof AboutRoute
|
||||||
'/analysis': typeof AnalysisRoute
|
'/analysis': typeof AnalysisRoute
|
||||||
'/hdiffz': typeof HdiffzRoute
|
'/diff': typeof DiffRoute
|
||||||
'/howto': typeof HowtoRoute
|
'/howto': typeof HowtoRoute
|
||||||
'/language': typeof LanguageRoute
|
'/language': typeof LanguageRoute
|
||||||
'/srtools': typeof SrtoolsRoute
|
'/srtools': typeof SrtoolsRoute
|
||||||
@@ -66,7 +66,7 @@ export interface FileRoutesByTo {
|
|||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/about': typeof AboutRoute
|
'/about': typeof AboutRoute
|
||||||
'/analysis': typeof AnalysisRoute
|
'/analysis': typeof AnalysisRoute
|
||||||
'/hdiffz': typeof HdiffzRoute
|
'/diff': typeof DiffRoute
|
||||||
'/howto': typeof HowtoRoute
|
'/howto': typeof HowtoRoute
|
||||||
'/language': typeof LanguageRoute
|
'/language': typeof LanguageRoute
|
||||||
'/srtools': typeof SrtoolsRoute
|
'/srtools': typeof SrtoolsRoute
|
||||||
@@ -76,7 +76,7 @@ export interface FileRoutesById {
|
|||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/about': typeof AboutRoute
|
'/about': typeof AboutRoute
|
||||||
'/analysis': typeof AnalysisRoute
|
'/analysis': typeof AnalysisRoute
|
||||||
'/hdiffz': typeof HdiffzRoute
|
'/diff': typeof DiffRoute
|
||||||
'/howto': typeof HowtoRoute
|
'/howto': typeof HowtoRoute
|
||||||
'/language': typeof LanguageRoute
|
'/language': typeof LanguageRoute
|
||||||
'/srtools': typeof SrtoolsRoute
|
'/srtools': typeof SrtoolsRoute
|
||||||
@@ -87,7 +87,7 @@ export interface FileRouteTypes {
|
|||||||
| '/'
|
| '/'
|
||||||
| '/about'
|
| '/about'
|
||||||
| '/analysis'
|
| '/analysis'
|
||||||
| '/hdiffz'
|
| '/diff'
|
||||||
| '/howto'
|
| '/howto'
|
||||||
| '/language'
|
| '/language'
|
||||||
| '/srtools'
|
| '/srtools'
|
||||||
@@ -96,7 +96,7 @@ export interface FileRouteTypes {
|
|||||||
| '/'
|
| '/'
|
||||||
| '/about'
|
| '/about'
|
||||||
| '/analysis'
|
| '/analysis'
|
||||||
| '/hdiffz'
|
| '/diff'
|
||||||
| '/howto'
|
| '/howto'
|
||||||
| '/language'
|
| '/language'
|
||||||
| '/srtools'
|
| '/srtools'
|
||||||
@@ -105,7 +105,7 @@ export interface FileRouteTypes {
|
|||||||
| '/'
|
| '/'
|
||||||
| '/about'
|
| '/about'
|
||||||
| '/analysis'
|
| '/analysis'
|
||||||
| '/hdiffz'
|
| '/diff'
|
||||||
| '/howto'
|
| '/howto'
|
||||||
| '/language'
|
| '/language'
|
||||||
| '/srtools'
|
| '/srtools'
|
||||||
@@ -115,7 +115,7 @@ export interface RootRouteChildren {
|
|||||||
IndexRoute: typeof IndexRoute
|
IndexRoute: typeof IndexRoute
|
||||||
AboutRoute: typeof AboutRoute
|
AboutRoute: typeof AboutRoute
|
||||||
AnalysisRoute: typeof AnalysisRoute
|
AnalysisRoute: typeof AnalysisRoute
|
||||||
HdiffzRoute: typeof HdiffzRoute
|
DiffRoute: typeof DiffRoute
|
||||||
HowtoRoute: typeof HowtoRoute
|
HowtoRoute: typeof HowtoRoute
|
||||||
LanguageRoute: typeof LanguageRoute
|
LanguageRoute: typeof LanguageRoute
|
||||||
SrtoolsRoute: typeof SrtoolsRoute
|
SrtoolsRoute: typeof SrtoolsRoute
|
||||||
@@ -144,11 +144,11 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof HowtoRouteImport
|
preLoaderRoute: typeof HowtoRouteImport
|
||||||
parentRoute: typeof rootRouteImport
|
parentRoute: typeof rootRouteImport
|
||||||
}
|
}
|
||||||
'/hdiffz': {
|
'/diff': {
|
||||||
id: '/hdiffz'
|
id: '/diff'
|
||||||
path: '/hdiffz'
|
path: '/diff'
|
||||||
fullPath: '/hdiffz'
|
fullPath: '/diff'
|
||||||
preLoaderRoute: typeof HdiffzRouteImport
|
preLoaderRoute: typeof DiffRouteImport
|
||||||
parentRoute: typeof rootRouteImport
|
parentRoute: typeof rootRouteImport
|
||||||
}
|
}
|
||||||
'/analysis': {
|
'/analysis': {
|
||||||
@@ -179,7 +179,7 @@ const rootRouteChildren: RootRouteChildren = {
|
|||||||
IndexRoute: IndexRoute,
|
IndexRoute: IndexRoute,
|
||||||
AboutRoute: AboutRoute,
|
AboutRoute: AboutRoute,
|
||||||
AnalysisRoute: AnalysisRoute,
|
AnalysisRoute: AnalysisRoute,
|
||||||
HdiffzRoute: HdiffzRoute,
|
DiffRoute: DiffRoute,
|
||||||
HowtoRoute: HowtoRoute,
|
HowtoRoute: HowtoRoute,
|
||||||
LanguageRoute: LanguageRoute,
|
LanguageRoute: LanguageRoute,
|
||||||
SrtoolsRoute: SrtoolsRoute,
|
SrtoolsRoute: SrtoolsRoute,
|
||||||
|
|||||||
@@ -1,106 +1,31 @@
|
|||||||
import { createRootRoute, Link, Outlet } from '@tanstack/react-router'
|
import { createRootRoute, Outlet } from '@tanstack/react-router'
|
||||||
import ThemeController from '../components/themeController'
|
|
||||||
import { ToastContainer } from 'react-toastify'
|
import { ToastContainer } from 'react-toastify'
|
||||||
import { useGlobalEvents } from '@/hooks';
|
import { useGlobalEvents } from '@/hooks';
|
||||||
import useLauncherStore from '@/stores/launcherStore';
|
import useModalStore from '@/stores/modalStore';;
|
||||||
import useHdiffzStore from '@/stores/hdiffzStore';
|
import SettingModal from '@/components/settingModal';
|
||||||
|
import CloseModal from '@/components/closeModal';
|
||||||
|
import Header from '@/components/header';
|
||||||
|
|
||||||
export const Route = createRootRoute({
|
export const Route = createRootRoute({
|
||||||
component: RootLayout
|
component: RootLayout
|
||||||
})
|
})
|
||||||
|
|
||||||
function RootLayout() {
|
function RootLayout() {
|
||||||
const { setGameRunning, setServerRunning, setProxyRunning, setProgressDownload, setDownloadSpeed } = useLauncherStore()
|
const { setIsOpenCloseModal, isOpenCloseModal, isOpenSettingModal, setIsOpenSettingModal } = useModalStore()
|
||||||
const { setProgressUpdate, setMaxProgressUpdate, setMessageUpdate } = useHdiffzStore()
|
|
||||||
useGlobalEvents({
|
useGlobalEvents();
|
||||||
setGameRunning,
|
|
||||||
setServerRunning,
|
|
||||||
setProxyRunning,
|
|
||||||
setProgressUpdate,
|
|
||||||
setMaxProgressUpdate,
|
|
||||||
setProgressDownload,
|
|
||||||
setDownloadSpeed,
|
|
||||||
setMessageUpdate,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="navbar bg-base-100 shadow-sm sticky top-0 z-50 px-3">
|
<Header />
|
||||||
<div className="navbar-start">
|
|
||||||
<div className="dropdown">
|
|
||||||
<div tabIndex={0} role="button" className="btn btn-ghost md:hidden">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h8m-8 6h16" /> </svg>
|
|
||||||
</div>
|
|
||||||
<ul
|
|
||||||
tabIndex={0}
|
|
||||||
className="menu menu-sm dropdown-content bg-base-100 rounded-box z-1 mt-3 w-52 p-2 shadow">
|
|
||||||
<li><Link to="/">Home</Link></li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<a>Tools</a>
|
|
||||||
<ul className="p-2">
|
|
||||||
<li><Link to="/language">Language</Link></li>
|
|
||||||
<li><Link to="/hdiffz">Hdiffz</Link></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a>Plugins</a>
|
|
||||||
<ul className="p-2">
|
|
||||||
<li><Link to="/analysis">Analysis (Veritas)</Link></li>
|
|
||||||
<li><Link to="/srtools">SrTools</Link></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><Link to="/howto">How to?</Link></li>
|
|
||||||
<li><Link to="/about">About</Link></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<Link to="/" className="grid grid-cols-1 items-start text-left gap-0 hover:scale-105 px-2">
|
|
||||||
<div className="flex items-center justify-center">
|
|
||||||
<img src="/ff-launcher.png" alt="Logo" className='w-13 h-13' />
|
|
||||||
<div className="flex flex-col justify-center items-start">
|
|
||||||
<h1 className="text-xl font-bold">
|
|
||||||
<span className="text-emerald-500">Firefly </span>
|
|
||||||
<span className="bg-clip-text text-transparent bg-gradient-to-r from-emerald-400 via-orange-500 to-red-500">
|
|
||||||
Launcher
|
|
||||||
</span>
|
|
||||||
</h1>
|
|
||||||
<p className="text-sm text-gray-500">By Kain</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
<div className="navbar-center hidden md:flex">
|
|
||||||
<ul className="menu menu-horizontal px-1">
|
|
||||||
<li><Link to="/">Home</Link></li>
|
|
||||||
<li>
|
|
||||||
<details>
|
|
||||||
<summary>Tools</summary>
|
|
||||||
<ul className="p-2">
|
|
||||||
<li><Link to="/language">Language</Link></li>
|
|
||||||
<li><Link to="/hdiffz">Hdiffz</Link></li>
|
|
||||||
</ul>
|
|
||||||
</details>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<details>
|
|
||||||
<summary>Plugins</summary>
|
|
||||||
<ul className="p-2">
|
|
||||||
<li><Link to="/analysis">Analysis (Veritas)</Link></li>
|
|
||||||
<li><Link to="/srtools">SrTools</Link></li>
|
|
||||||
</ul>
|
|
||||||
</details>
|
|
||||||
</li>
|
|
||||||
<li><Link to="/howto">How to?</Link></li>
|
|
||||||
<li><Link to="/about">About</Link></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div className="navbar-end">
|
|
||||||
<ThemeController />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="min-h-[78vh]">
|
<div className="min-h-[78vh]">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<CloseModal isOpen={isOpenCloseModal} onClose={() => setIsOpenCloseModal(false)} />
|
||||||
|
<SettingModal isOpen={isOpenSettingModal} onClose={() => setIsOpenSettingModal(false)} />
|
||||||
<ToastContainer />
|
<ToastContainer />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
7
frontend/src/routes/diff.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import DiffPage from '@/pages/diff'
|
||||||
|
import { createFileRoute } from '@tanstack/react-router'
|
||||||
|
|
||||||
|
export const Route = createFileRoute('/diff')({
|
||||||
|
component: DiffPage,
|
||||||
|
})
|
||||||
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import HdiffzPage from '@/pages/hdiffz'
|
|
||||||
import { createFileRoute } from '@tanstack/react-router'
|
|
||||||
|
|
||||||
export const Route = createFileRoute('/hdiffz')({
|
|
||||||
component: HdiffzPage,
|
|
||||||
})
|
|
||||||
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import SrToolsPage from '@/pages/srtools'
|
import FireflyToolsPage from '@/pages/fireflytools'
|
||||||
import { createFileRoute } from '@tanstack/react-router'
|
import { createFileRoute } from '@tanstack/react-router'
|
||||||
|
|
||||||
export const Route = createFileRoute('/srtools')({
|
export const Route = createFileRoute('/srtools')({
|
||||||
component: SrToolsPage,
|
component: FireflyToolsPage,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import { create } from 'zustand'
|
import { create } from 'zustand'
|
||||||
|
|
||||||
interface LauncherState {
|
interface DiffState {
|
||||||
folderCheckResult: 'success' | 'error' | null,
|
folderCheckResult: 'success' | 'error' | null,
|
||||||
isLoading: {game: boolean, diff: boolean},
|
isLoading: {game: boolean, diff: boolean},
|
||||||
diffDir: string,
|
diffDir: string,
|
||||||
@@ -22,7 +22,7 @@ interface LauncherState {
|
|||||||
setStageType: (value: string) => void,
|
setStageType: (value: string) => void,
|
||||||
}
|
}
|
||||||
|
|
||||||
const useLauncherStore = create<LauncherState>((set, get) => ({
|
const useDiffStore = create<DiffState>((set, get) => ({
|
||||||
isLoading: {game: false, diff: false},
|
isLoading: {game: false, diff: false},
|
||||||
folderCheckResult: null,
|
folderCheckResult: null,
|
||||||
diffDir: "",
|
diffDir: "",
|
||||||
@@ -43,4 +43,4 @@ const useLauncherStore = create<LauncherState>((set, get) => ({
|
|||||||
setStageType: (value: string) => set({ stageType: value }),
|
setStageType: (value: string) => set({ stageType: value }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default useLauncherStore;
|
export default useDiffStore;
|
||||||
@@ -11,7 +11,7 @@ interface LauncherState {
|
|||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
gameRunning: boolean;
|
gameRunning: boolean;
|
||||||
progressDownload: number;
|
progressDownload: number;
|
||||||
downloadSpeed: number;
|
downloadSpeed: string;
|
||||||
launcherVersion: string;
|
launcherVersion: string;
|
||||||
updateData: Record<'server' | 'proxy' | 'launcher', { isUpdate: boolean, isExists: boolean, version: string }>;
|
updateData: Record<'server' | 'proxy' | 'launcher', { isUpdate: boolean, isExists: boolean, version: string }>;
|
||||||
setDownloadType: (value: string) => void;
|
setDownloadType: (value: string) => void;
|
||||||
@@ -24,7 +24,7 @@ interface LauncherState {
|
|||||||
setGameRunning: (value: boolean) => void;
|
setGameRunning: (value: boolean) => void;
|
||||||
setProgressDownload: (value: number) => void;
|
setProgressDownload: (value: number) => void;
|
||||||
setLauncherVersion: (value: string) => void;
|
setLauncherVersion: (value: string) => void;
|
||||||
setDownloadSpeed: (value: number) => void;
|
setDownloadSpeed: (value: string) => void;
|
||||||
setUpdateData: (value: Record<'server' | 'proxy' | 'launcher', { isUpdate: boolean, isExists: boolean, version: string }>) => void;
|
setUpdateData: (value: Record<'server' | 'proxy' | 'launcher', { isUpdate: boolean, isExists: boolean, version: string }>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ const useLauncherStore = create<LauncherState>((set, get) => ({
|
|||||||
proxyRunning: false,
|
proxyRunning: false,
|
||||||
gameRunning: false,
|
gameRunning: false,
|
||||||
progressDownload: 0,
|
progressDownload: 0,
|
||||||
downloadSpeed: 0,
|
downloadSpeed: "",
|
||||||
launcherVersion: "",
|
launcherVersion: "",
|
||||||
updateData: {
|
updateData: {
|
||||||
server: { isUpdate: false, isExists: false, version: "" },
|
server: { isUpdate: false, isExists: false, version: "" },
|
||||||
@@ -55,7 +55,7 @@ const useLauncherStore = create<LauncherState>((set, get) => ({
|
|||||||
setGameRunning: (value: boolean) => set({ gameRunning: value }),
|
setGameRunning: (value: boolean) => set({ gameRunning: value }),
|
||||||
setProgressDownload: (value: number) => set({ progressDownload: value }),
|
setProgressDownload: (value: number) => set({ progressDownload: value }),
|
||||||
setLauncherVersion: (value: string) => set({ launcherVersion: value }),
|
setLauncherVersion: (value: string) => set({ launcherVersion: value }),
|
||||||
setDownloadSpeed: (value: number) => set({ downloadSpeed: value }),
|
setDownloadSpeed: (value: string) => set({ downloadSpeed: value }),
|
||||||
setUpdateData: (value: Record<'server' | 'proxy' | 'launcher', { isUpdate: boolean, isExists: boolean, version: string }>) => set({ updateData: value }),
|
setUpdateData: (value: Record<'server' | 'proxy' | 'launcher', { isUpdate: boolean, isExists: boolean, version: string }>) => set({ updateData: value }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -5,18 +5,26 @@ interface ModalState {
|
|||||||
isOpenDownloadDataModal: boolean;
|
isOpenDownloadDataModal: boolean;
|
||||||
isOpenUpdateDataModal: boolean;
|
isOpenUpdateDataModal: boolean;
|
||||||
isOpenSelfUpdateModal: boolean;
|
isOpenSelfUpdateModal: boolean;
|
||||||
|
isOpenCloseModal: boolean;
|
||||||
|
isOpenSettingModal: boolean;
|
||||||
setIsOpenDownloadDataModal: (modal: boolean) => void;
|
setIsOpenDownloadDataModal: (modal: boolean) => void;
|
||||||
setIsOpenUpdateDataModal: (modal: boolean) => void;
|
setIsOpenUpdateDataModal: (modal: boolean) => void;
|
||||||
setIsOpenSelfUpdateModal: (modal: boolean) => void;
|
setIsOpenSelfUpdateModal: (modal: boolean) => void;
|
||||||
|
setIsOpenCloseModal: (modal: boolean) => void;
|
||||||
|
setIsOpenSettingModal: (modal: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useModalStore = create<ModalState>((set, get) => ({
|
const useModalStore = create<ModalState>((set, get) => ({
|
||||||
isOpenDownloadDataModal: false,
|
isOpenDownloadDataModal: false,
|
||||||
isOpenUpdateDataModal: false,
|
isOpenUpdateDataModal: false,
|
||||||
isOpenSelfUpdateModal: false,
|
isOpenSelfUpdateModal: false,
|
||||||
|
isOpenCloseModal: false,
|
||||||
|
isOpenSettingModal: false,
|
||||||
setIsOpenDownloadDataModal: (modal: boolean) => set({ isOpenDownloadDataModal: modal }),
|
setIsOpenDownloadDataModal: (modal: boolean) => set({ isOpenDownloadDataModal: modal }),
|
||||||
setIsOpenUpdateDataModal: (modal: boolean) => set({ isOpenUpdateDataModal: modal }),
|
setIsOpenUpdateDataModal: (modal: boolean) => set({ isOpenUpdateDataModal: modal }),
|
||||||
setIsOpenSelfUpdateModal: (modal: boolean) => set({ isOpenSelfUpdateModal: modal }),
|
setIsOpenSelfUpdateModal: (modal: boolean) => set({ isOpenSelfUpdateModal: modal }),
|
||||||
|
setIsOpenCloseModal: (modal: boolean) => set({ isOpenCloseModal: modal }),
|
||||||
|
setIsOpenSettingModal: (modal: boolean) => set({ isOpenSettingModal: modal }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default useModalStore;
|
export default useModalStore;
|
||||||
@@ -10,6 +10,15 @@ interface SettingState {
|
|||||||
proxyPath: string;
|
proxyPath: string;
|
||||||
serverVersion: string;
|
serverVersion: string;
|
||||||
proxyVersion: string;
|
proxyVersion: string;
|
||||||
|
closingOption: {
|
||||||
|
isMinimize: boolean;
|
||||||
|
isAsk: boolean;
|
||||||
|
}
|
||||||
|
background: string;
|
||||||
|
extraBackgrounds: string[];
|
||||||
|
setExtraBackgrounds: (newExtraBackgrounds: string[]) => void;
|
||||||
|
setBackground: (newBackground: string) => void;
|
||||||
|
setClosingOption: (newClosingOption: { isMinimize: boolean; isAsk: boolean }) => void;
|
||||||
setLocale: (newLocale: string) => void;
|
setLocale: (newLocale: string) => void;
|
||||||
setGamePath: (newGamePath: string) => void;
|
setGamePath: (newGamePath: string) => void;
|
||||||
setGameDir: (newGameDir: string) => void;
|
setGameDir: (newGameDir: string) => void;
|
||||||
@@ -29,6 +38,15 @@ const useSettingStore = create<SettingState>()(
|
|||||||
proxyPath: "",
|
proxyPath: "",
|
||||||
serverVersion: "",
|
serverVersion: "",
|
||||||
proxyVersion: "",
|
proxyVersion: "",
|
||||||
|
closingOption: {
|
||||||
|
isMinimize: false,
|
||||||
|
isAsk: true,
|
||||||
|
},
|
||||||
|
background: "bg-12.jpg",
|
||||||
|
extraBackgrounds: [],
|
||||||
|
setExtraBackgrounds: (newExtraBackgrounds: string[]) => set({ extraBackgrounds: newExtraBackgrounds }),
|
||||||
|
setBackground: (newBackground: string) => set({ background: newBackground }),
|
||||||
|
setClosingOption: (newClosingOption: { isMinimize: boolean; isAsk: boolean }) => set({ closingOption: newClosingOption }),
|
||||||
setLocale: (newLocale: string) => set({ locale: newLocale }),
|
setLocale: (newLocale: string) => set({ locale: newLocale }),
|
||||||
setGamePath: (newGamePath: string) => set({ gamePath: newGamePath }),
|
setGamePath: (newGamePath: string) => set({ gamePath: newGamePath }),
|
||||||
setGameDir: (newGameDir: string) => set({ gameDir: newGameDir }),
|
setGameDir: (newGameDir: string) => set({ gameDir: newGameDir }),
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
@plugin "daisyui";
|
@plugin "daisyui"{
|
||||||
@plugin "daisyui" {
|
themes: dracula --default;
|
||||||
themes: night --default, night --prefersdark, cupcake;
|
}
|
||||||
}
|
|
||||||
|
|||||||
25
frontend/src/utils/cropImage.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
export default function getCroppedImg(imageSrc: string, crop: any): Promise<string> {
|
||||||
|
const image = new Image()
|
||||||
|
image.src = imageSrc
|
||||||
|
const canvas = document.createElement('canvas')
|
||||||
|
const ctx = canvas.getContext('2d')!
|
||||||
|
canvas.width = crop.width
|
||||||
|
canvas.height = crop.height
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
image.onload = () => {
|
||||||
|
ctx.drawImage(
|
||||||
|
image,
|
||||||
|
crop.x,
|
||||||
|
crop.y,
|
||||||
|
crop.width,
|
||||||
|
crop.height,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
crop.width,
|
||||||
|
crop.height
|
||||||
|
)
|
||||||
|
resolve(canvas.toDataURL('image/jpeg'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ export default defineConfig({
|
|||||||
base: '/',
|
base: '/',
|
||||||
build: {
|
build: {
|
||||||
outDir: 'dist',
|
outDir: 'dist',
|
||||||
|
chunkSizeWarningLimit: 2000,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
tanstackRouter({
|
tanstackRouter({
|
||||||
|
|||||||