From 935ec4ae90e4c46337122cdf568f78635d85087e Mon Sep 17 00:00:00 2001 From: mircearoata Date: Sat, 6 Jun 2026 15:47:25 +0200 Subject: [PATCH 01/22] Bump version --- wails.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wails.json b/wails.json index fbad824c..be4acdd5 100644 --- a/wails.json +++ b/wails.json @@ -12,7 +12,7 @@ "email": "mircearoatapalade@gmail.com" }, "info": { - "productVersion": "3.0.7", + "productVersion": "3.1.0", "fileAssociations": [ { "ext": "smmprofile", From de178dbb8a42d44cc4d412e6c467854ada261ba9 Mon Sep 17 00:00:00 2001 From: mircearoata Date: Mon, 6 Jul 2026 19:08:43 +0300 Subject: [PATCH 02/22] Save crash logs using panicwrap --- go.mod | 2 ++ go.sum | 4 ++++ main.go | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index d74e180c..d3415d36 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ toolchain go1.25.2 require ( github.com/Masterminds/semver/v3 v3.4.0 github.com/andygrunwald/vdf v1.1.0 + github.com/bugsnag/panicwrap v1.3.4 github.com/godbus/dbus/v5 v5.1.0 github.com/jezek/xgb v1.1.1 github.com/kbinani/screenshot v0.0.0-20250624051815-089614a94018 @@ -56,6 +57,7 @@ require ( github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 // indirect github.com/jlaffaye/ftp v0.2.0 // indirect + github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/kr/fs v0.1.0 // indirect github.com/labstack/echo/v4 v4.13.4 // indirect github.com/labstack/gommon v0.4.2 // indirect diff --git a/go.sum b/go.sum index 7cafdd43..874d1bea 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,8 @@ github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHS github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= +github.com/bugsnag/panicwrap v1.3.4 h1:A6sXFtDGsgU/4BLf5JT0o5uYg3EeKgGx3Sfs+/uk3pU= +github.com/bugsnag/panicwrap v1.3.4/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY= github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc= @@ -76,6 +78,8 @@ github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4= github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= github.com/jlaffaye/ftp v0.2.0 h1:lXNvW7cBu7R/68bknOX3MrRIIqZ61zELs1P2RAiA3lg= github.com/jlaffaye/ftp v0.2.0/go.mod h1:is2Ds5qkhceAPy2xD6RLI6hmp/qysSoymZ+Z2uTnspI= +github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= +github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kbinani/screenshot v0.0.0-20250624051815-089614a94018 h1:NQYgMY188uWrS+E/7xMVpydsI48PMHcc7SfR4OxkDF4= github.com/kbinani/screenshot v0.0.0-20250624051815-089614a94018/go.mod h1:Pmpz2BLf55auQZ67u3rvyI2vAQvNetkK/4zYUmpauZQ= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= diff --git a/main.go b/main.go index 3bacb6b9..31433494 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( "context" "embed" + "fmt" "log/slog" "net/http" "net/url" @@ -12,6 +13,7 @@ import ( "sync" "time" + "github.com/bugsnag/panicwrap" "github.com/spf13/viper" "github.com/tawesoft/golib/v2/dialog" "github.com/wailsapp/wails/v2" @@ -50,6 +52,15 @@ var ( ) func main() { + exitStatus, err := panicwrap.BasicWrap(panicHandler) + if err != nil { + panic(err) + } + + if exitStatus >= 0 { + os.Exit(exitStatus) + } + logging.Init() slog.Info("starting Satisfactory Mod Manager", slog.String("version", version), slog.String("commit", commit), slog.String("date", date), slog.String("type", updateMode)) @@ -60,7 +71,7 @@ func main() { autoupdate.Init() - err := settings.LoadSettings() + err = settings.LoadSettings() if err != nil { slog.Error("failed to load settings", slog.Any("error", err)) // Cannot use wails message dialogs here yet, because they expect a frontend to exist @@ -328,6 +339,46 @@ func init() { viper.Set("log-file", filepath.Join(smmCacheDir, "logs", "SatisfactoryModManager.log")) } +func panicHandler(output string) { + file, err := dumpCrash(output) + if err != nil { + _ = dialog.Message{ + Title: "Satisfactory Mod Manager Crashed", + Format: "An error occurred while saving the crash log: %s", + Args: []interface{}{err.Error()}, + Icon: dialog.IconError, + }.Raise() + os.Exit(1) + } + + _ = dialog.Message{ + Title: "Satisfactory Mod Manager Crashed", + Format: "Crash log: %s", + Args: []interface{}{file}, + Icon: dialog.IconError, + }.Raise() + os.Exit(1) +} + +func dumpCrash(output string) (string, error) { + crashesDir := filepath.Join(viper.GetString("smm-cache-dir"), "crashes") + + err := os.MkdirAll(crashesDir, 0o755) + if err != nil { + return "", fmt.Errorf("failed to create crashes directory: %w", err) + } + + crashFileName := time.Now().Format("20060102150405") + ".log" + crashFile := filepath.Join(crashesDir, crashFileName) + + err = os.WriteFile(crashFile, []byte(output), 0o644) + if err != nil { + return "", fmt.Errorf("failed to write crash to file: %w", err) + } + + return crashFile, nil +} + type withUserAgent struct { inner http.RoundTripper } From b0a886488269fad9d53a3dd6ce17cb43bf68960d Mon Sep 17 00:00:00 2001 From: mircearoata Date: Thu, 9 Jul 2026 10:43:02 +0300 Subject: [PATCH 03/22] Update ficsit-cli --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d3415d36..c6b55df3 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/puzpuzpuz/xsync/v3 v3.5.1 github.com/samber/lo v1.52.0 github.com/samber/slog-multi v1.5.0 - github.com/satisfactorymodding/ficsit-cli v0.7.0 + github.com/satisfactorymodding/ficsit-cli v0.7.1 github.com/satisfactorymodding/ficsit-resolver v0.0.6 github.com/shirou/gopsutil/v3 v3.24.5 github.com/spf13/viper v1.21.0 diff --git a/go.sum b/go.sum index 874d1bea..c947892b 100644 --- a/go.sum +++ b/go.sum @@ -161,8 +161,8 @@ github.com/samber/slog-common v0.19.0 h1:fNcZb8B2uOLooeYwFpAlKjkQTUafdjfqKcwcC89 github.com/samber/slog-common v0.19.0/go.mod h1:dTz+YOU76aH007YUU0DffsXNsGFQRQllPQh9XyNoA3M= github.com/samber/slog-multi v1.5.0 h1:UDRJdsdb0R5vFQFy3l26rpX3rL3FEPJTJ2yKVjoiT1I= github.com/samber/slog-multi v1.5.0/go.mod h1:im2Zi3mH/ivSY5XDj6LFcKToRIWPw1OcjSVSdXt+2d0= -github.com/satisfactorymodding/ficsit-cli v0.7.0 h1:Z2+0iVdoQjfRTLFRhZ6BBZ6/Sx+KZVlXg3XJceL40zM= -github.com/satisfactorymodding/ficsit-cli v0.7.0/go.mod h1:YYy9ZdDhSSQnSU2agRqwbbYccsXfQvxSvGmIjyXsoOc= +github.com/satisfactorymodding/ficsit-cli v0.7.1 h1:r2I1H5MOt8lkXiebYpqzID36eY62q9kzHw0vBmg7aLo= +github.com/satisfactorymodding/ficsit-cli v0.7.1/go.mod h1:YYy9ZdDhSSQnSU2agRqwbbYccsXfQvxSvGmIjyXsoOc= github.com/satisfactorymodding/ficsit-resolver v0.0.6 h1:4iCIHOg3z+AvwSVeWtu+k9aysLOL9+FIszCbiKOG2oo= github.com/satisfactorymodding/ficsit-resolver v0.0.6/go.mod h1:ckKMmMvDoYbbkEbWXEsMes608uvv6EKphXPhHX8LKSc= github.com/satisfactorymodding/go-webview2 v0.0.0-20241013154424-330566cba2f0 h1:ZiuWkzfqk+7B5mvYb4vaTYTm8fgiGJCgBxt+nMsxD+8= From ca7d87058b079420936ad1168f262e8c88ca2f87 Mon Sep 17 00:00:00 2001 From: mircearoata Date: Sun, 5 Jul 2026 16:31:03 +0300 Subject: [PATCH 04/22] Update pnpm-workspace allowed builds --- frontend/pnpm-workspace.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/pnpm-workspace.yaml b/frontend/pnpm-workspace.yaml index 044b96af..bcd6edd8 100644 --- a/frontend/pnpm-workspace.yaml +++ b/frontend/pnpm-workspace.yaml @@ -1,3 +1,3 @@ -onlyBuiltDependencies: - - esbuild - - svelte-preprocess +allowBuilds: + esbuild: true + svelte-preprocess: true From c0646fa444422f2d634a836edaf6049fb4a70c13 Mon Sep 17 00:00:00 2001 From: mircearoata Date: Wed, 22 Jul 2026 17:53:17 +0300 Subject: [PATCH 05/22] Fix epic appName used in launch command --- backend/installfinders/launchers/epic/epic.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/backend/installfinders/launchers/epic/epic.go b/backend/installfinders/launchers/epic/epic.go index 667b5f90..815ca53c 100644 --- a/backend/installfinders/launchers/epic/epic.go +++ b/backend/installfinders/launchers/epic/epic.go @@ -103,11 +103,12 @@ func FindInstallationsEpic(epicManifestsPath string, launcher string, platform c continue } - branch, err := GetEpicBranch(epicManifest.MainGameAppName) - if err != nil { - // Some Epic installs appear to come in with a null MainGameAppName; fall back to AppName - branch, err = GetEpicBranch(epicManifest.AppName) + appName := epicManifest.MainGameAppName + if appName == "" { + appName = epicManifest.AppName } + + branch, err := GetEpicBranch(appName) if err != nil { findErrors = append(findErrors, common.InstallFindError{ Path: installLocation, @@ -123,7 +124,7 @@ func FindInstallationsEpic(epicManifestsPath string, launcher string, platform c Location: common.LocationTypeLocal, Branch: branch, Launcher: launcher, - LaunchPath: platform.LauncherCommand(epicManifest.MainGameAppName), + LaunchPath: platform.LauncherCommand(appName), SavedPath: savedPath, }) } From e994618755f6f5d17a403ca7494cd5aa477a3394 Mon Sep 17 00:00:00 2001 From: mircearoata Date: Mon, 10 Aug 2026 12:51:18 +0300 Subject: [PATCH 06/22] Remove some unnecessary Legendary config entries --- .../launchers/legendary/legendary.go | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/backend/installfinders/launchers/legendary/legendary.go b/backend/installfinders/launchers/legendary/legendary.go index b40bab24..497a267a 100644 --- a/backend/installfinders/launchers/legendary/legendary.go +++ b/backend/installfinders/launchers/legendary/legendary.go @@ -13,21 +13,14 @@ import ( ) type Game struct { - AppName string `json:"app_name"` - BaseURLs []string `json:"base_urls"` - CanRunOffline bool `json:"can_run_offline"` - EGLGUID string `json:"egl_guid"` - Executable string `json:"executable"` - InstallPath string `json:"install_path"` - InstallSize int `json:"install_size"` - IsDLC bool `json:"is_dlc"` - LaunchParameters string `json:"launch_parameters"` - ManifestPath string `json:"manifest_path"` - NeedsVerification bool `json:"needs_verification"` - RequiresOT bool `json:"requires_ot"` - SavePath string `json:"save_path"` - Title string `json:"title"` - Version string `json:"version"` + AppName string `json:"app_name"` + Executable string `json:"executable"` + InstallPath string `json:"install_path"` + LaunchParameters string `json:"launch_parameters"` + ManifestPath string `json:"manifest_path"` + SavePath string `json:"save_path"` + Title string `json:"title"` + Version string `json:"version"` } type Data = map[string]Game From ae05724b5661cf58fc87f7a7a02539600ae14336 Mon Sep 17 00:00:00 2001 From: mircearoata Date: Mon, 10 Aug 2026 13:22:33 +0300 Subject: [PATCH 07/22] Use bundled fonts --- frontend/index.html | 4 -- frontend/package.json | 5 +- frontend/pnpm-lock.yaml | 108 ++++++++++++----------------------- frontend/postcss.config.cjs | 4 -- frontend/src/_global.postcss | 11 ++-- 5 files changed, 43 insertions(+), 89 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 4cb59b87..4ef2339e 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -3,10 +3,6 @@ - - - -
diff --git a/frontend/package.json b/frontend/package.json index 6e65d86f..7dc2a1e0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,7 +22,9 @@ }, "dependencies": { "@floating-ui/dom": "^1.7.4", - "@fontsource/fira-mono": "^5.2.7", + "@fontsource/roboto": "^5.3.0", + "@fontsource/roboto-mono": "^5.3.0", + "@fontsource/noto-color-emoji": "^5.3.0", "@graphql-typed-document-node/core": "^3.2.0", "@lukeed/uuid": "^2.0.1", "@tailwindcss/container-queries": "^0.1.1", @@ -78,7 +80,6 @@ "nodemon": "^3.1.10", "npm-run-all": "^4.1.5", "postcss": "^8.5.6", - "postcss-import-url": "^7.2.0", "postcss-load-config": "^5.1.0", "postcss-preset-env": "^9.6.0", "prettier": "^3.6.2", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index c4f60bc9..5bdabf63 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -11,9 +11,15 @@ importers: '@floating-ui/dom': specifier: ^1.7.4 version: 1.7.4 - '@fontsource/fira-mono': - specifier: ^5.2.7 - version: 5.2.7 + '@fontsource/noto-color-emoji': + specifier: ^5.3.0 + version: 5.3.0 + '@fontsource/roboto': + specifier: ^5.3.0 + version: 5.3.0 + '@fontsource/roboto-mono': + specifier: ^5.3.0 + version: 5.3.0 '@graphql-typed-document-node/core': specifier: ^3.2.0 version: 3.2.0(graphql@16.12.0) @@ -174,9 +180,6 @@ importers: postcss: specifier: ^8.5.6 version: 8.5.6 - postcss-import-url: - specifier: ^7.2.0 - version: 7.2.0(postcss@8.5.6) postcss-load-config: specifier: ^5.1.0 version: 5.1.0(jiti@1.21.7)(postcss@8.5.6) @@ -926,8 +929,14 @@ packages: '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} - '@fontsource/fira-mono@5.2.7': - resolution: {integrity: sha512-wYrAn6i3nH6luqQBZxtWUpl4UTUvs9AEbEeZxksPMwIqyjRRaxHTNW3c2VfM50gabS2IS7pT8lVWS2USB4ukYA==} + '@fontsource/noto-color-emoji@5.3.0': + resolution: {integrity: sha512-5uVerNQW6zoxRVaMdjgpYSAxpo02WgCPjpVdE5MfrWd8sNwq7uGs3pMnKB1fVsIBwlxH++I2RisvI9XadC6NzA==} + + '@fontsource/roboto-mono@5.3.0': + resolution: {integrity: sha512-yhwplHMHW9dCk+1pZuERfcJe+K3rosGVZEoK0XktkxRSqQ9qsipSwwiuW2/8Qmtp9lzu3Ixa/ie4BMH3i8CkZQ==} + + '@fontsource/roboto@5.3.0': + resolution: {integrity: sha512-BapRJOWYP+LZ21zp+wBQjfpPYKRoxc4LspJ/RLuI+HSMBD5u/X4O+ESDrSvEqDSy0rAl7GwBJ+09mdc16cVQ1Q==} '@graphql-codegen/add@5.0.3': resolution: {integrity: sha512-SxXPmramkth8XtBlAHu4H4jYcYXM/o3p01+psU+0NADQowA8jtYkK6MW5rV6T+CxkEaNZItfSmZRPgIuypcqnA==} @@ -1323,56 +1332,67 @@ packages: resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.52.5': resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.52.5': resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.52.5': resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.52.5': resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-gnu@4.52.5': resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.52.5': resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.52.5': resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.52.5': resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.52.5': resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.52.5': resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openharmony-arm64@4.52.5': resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} @@ -1601,6 +1621,7 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + deprecated: Potential CWE-502 - Update to 1.3.1 or higher '@urql/core@6.0.1': resolution: {integrity: sha512-FZDiQk6jxbj5hixf2rEPv0jI+IZz0EqqGW8mJBEug68/zHTtT+f34guZDmyjJZyiWbj0vL165LoMr/TkeDHaug==} @@ -2454,11 +2475,12 @@ packages: glob@10.4.5: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me globals@13.24.0: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} @@ -2563,9 +2585,6 @@ packages: hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - http-https@1.0.0: - resolution: {integrity: sha512-o0PWwVCSp3O0wS6FvNr6xfBCHgt0m1tvPLFOCc2iFDKTRAXhB7m8klDf7ErowFH8POa6dVdGatKU5I1YYwzUyg==} - http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} @@ -2758,9 +2777,6 @@ packages: is-upper-case@2.0.2: resolution: {integrity: sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ==} - is-url@1.2.4: - resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} - is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -2897,9 +2913,6 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash.assign@4.2.0: - resolution: {integrity: sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==} - lodash.clonedeep@4.5.0: resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} @@ -2917,9 +2930,6 @@ packages: lodash.sortby@4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} - lodash.trim@4.5.1: - resolution: {integrity: sha512-nJAlRl/K+eiOehWKDzoBVrSMhK0K3A3YQsUNXHQa5yIrKBAhsZgSu3KoAFoFT+mEgiyBHddZ0pRk1ITpIp90Wg==} - lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -3371,12 +3381,6 @@ packages: peerDependencies: postcss: ^8.4 - postcss-import-url@7.2.0: - resolution: {integrity: sha512-El61K/5+Rv753G9mBiHyQlOIN2mBfN0YHPMXLlgIo/m1+tPDLM32wd97WoUjc8FHUnC6EyyfVA8RDuKoyuVl0Q==} - engines: {node: '>=10'} - peerDependencies: - postcss: ^8.0.0 - postcss-import@15.1.0: resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} @@ -3530,9 +3534,6 @@ packages: pstree.remy@1.1.8: resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} - punycode@1.3.2: - resolution: {integrity: sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==} - punycode@1.4.1: resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} @@ -3547,11 +3548,6 @@ packages: resolution: {integrity: sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==} engines: {node: '>=16.0.0'} - querystring@0.2.0: - resolution: {integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==} - engines: {node: '>=0.4.x'} - deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. - queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -3613,9 +3609,6 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - resolve-relative-url@1.0.0: - resolution: {integrity: sha512-zpcelQBAmrwckiyRmym9os1goECU3EzuTU/UrYkGzXV0i14n8FkyGUvwkOYA5klqVLq1Hz/EiFZMS7bZQdd+EA==} - resolve@1.22.11: resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} engines: {node: '>= 0.4'} @@ -4104,9 +4097,6 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - url@0.10.3: - resolution: {integrity: sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==} - urlpattern-polyfill@10.1.0: resolution: {integrity: sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==} @@ -5035,7 +5025,11 @@ snapshots: '@floating-ui/utils@0.2.10': {} - '@fontsource/fira-mono@5.2.7': {} + '@fontsource/noto-color-emoji@5.3.0': {} + + '@fontsource/roboto-mono@5.3.0': {} + + '@fontsource/roboto@5.3.0': {} '@graphql-codegen/add@5.0.3(graphql@16.12.0)': dependencies: @@ -7176,8 +7170,6 @@ snapshots: hosted-git-info@2.8.9: {} - http-https@1.0.0: {} - http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 @@ -7388,8 +7380,6 @@ snapshots: dependencies: tslib: 2.8.1 - is-url@1.2.4: {} - is-weakmap@2.0.2: {} is-weakref@1.1.1: @@ -7503,8 +7493,6 @@ snapshots: dependencies: p-locate: 5.0.0 - lodash.assign@4.2.0: {} - lodash.clonedeep@4.5.0: {} lodash.get@4.4.2: {} @@ -7515,8 +7503,6 @@ snapshots: lodash.sortby@4.7.0: {} - lodash.trim@4.5.1: {} - lodash@4.17.21: {} log-symbols@4.1.0: @@ -7961,15 +7947,6 @@ snapshots: postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-import-url@7.2.0(postcss@8.5.6): - dependencies: - http-https: 1.0.0 - is-url: 1.2.4 - lodash.assign: 4.2.0 - lodash.trim: 4.5.1 - postcss: 8.5.6 - resolve-relative-url: 1.0.0 - postcss-import@15.1.0(postcss@8.5.6): dependencies: postcss: 8.5.6 @@ -8156,8 +8133,6 @@ snapshots: pstree.remy@1.1.8: {} - punycode@1.3.2: {} - punycode@1.4.1: {} punycode@2.3.1: {} @@ -8168,8 +8143,6 @@ snapshots: pvutils@1.1.5: {} - querystring@0.2.0: {} - queue-microtask@1.2.3: {} react@18.3.1: @@ -8240,10 +8213,6 @@ snapshots: resolve-from@5.0.0: {} - resolve-relative-url@1.0.0: - dependencies: - url: 0.10.3 - resolve@1.22.11: dependencies: is-core-module: 2.16.1 @@ -8818,11 +8787,6 @@ snapshots: dependencies: punycode: 2.3.1 - url@0.10.3: - dependencies: - punycode: 1.3.2 - querystring: 0.2.0 - urlpattern-polyfill@10.1.0: {} urlpattern-polyfill@8.0.2: {} diff --git a/frontend/postcss.config.cjs b/frontend/postcss.config.cjs index b315cbcd..06e5627d 100644 --- a/frontend/postcss.config.cjs +++ b/frontend/postcss.config.cjs @@ -1,4 +1,3 @@ -const importUrl = require('postcss-import-url'); const postcssPresetEnv = require('postcss-preset-env'); const tailwindCSS = require('tailwindcss'); const tailwindCSSNesting = require('tailwindcss/nesting'); @@ -12,9 +11,6 @@ module.exports = { }, }), - importUrl({ - modernBrowser: true, - }), tailwindCSSNesting(), tailwindCSS(), ], diff --git a/frontend/src/_global.postcss b/frontend/src/_global.postcss index 55aa2348..d47f1123 100644 --- a/frontend/src/_global.postcss +++ b/frontend/src/_global.postcss @@ -1,4 +1,6 @@ -@import 'https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible&family=Roboto&family=Roboto+Mono&family=Material+Icons&family=Noto+Color+Emoji&display=swap'; +@import "@fontsource/roboto"; +@import "@fontsource/roboto-mono"; +@import "@fontsource/noto-color-emoji"; @tailwind base; @tailwind components; @@ -18,11 +20,6 @@ html { @apply text-sm; } -html.accessibility, -body.accessibility { - font-family: 'Atkinson Hyperlegible', Helvetica Neue, sans-serif; -} - button { @apply tracking-wider; } @@ -215,4 +212,4 @@ button { ::-ms-reveal { display: none; -} \ No newline at end of file +} From a688af1a2acbec68dd309f2e28b6be0ea1fdffb1 Mon Sep 17 00:00:00 2001 From: mircearoata Date: Mon, 10 Aug 2026 14:15:21 +0300 Subject: [PATCH 08/22] Fix tag filter list not scrolling --- frontend/src/lib/components/mods-list/ModsListFilters.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/components/mods-list/ModsListFilters.svelte b/frontend/src/lib/components/mods-list/ModsListFilters.svelte index a48c66b1..b72f3510 100644 --- a/frontend/src/lib/components/mods-list/ModsListFilters.svelte +++ b/frontend/src/lib/components/mods-list/ModsListFilters.svelte @@ -97,7 +97,7 @@ -
+
{#if availableTags.length > 0}
{#each availableTags as tag} From 24ee8334645056ec9835de6666c70684948be249 Mon Sep 17 00:00:00 2001 From: mircearoata Date: Thu, 13 Aug 2026 14:26:14 +0300 Subject: [PATCH 09/22] Add faugus launcher support --- backend/installfinders/launchers/all/all.go | 1 + .../launchers/faugus/faugus_linux.go | 76 +++++++++++++++++++ .../launchers/faugus/faugus_noop.go | 1 + 3 files changed, 78 insertions(+) create mode 100644 backend/installfinders/launchers/faugus/faugus_linux.go create mode 100644 backend/installfinders/launchers/faugus/faugus_noop.go diff --git a/backend/installfinders/launchers/all/all.go b/backend/installfinders/launchers/all/all.go index 477291dc..ce22e915 100644 --- a/backend/installfinders/launchers/all/all.go +++ b/backend/installfinders/launchers/all/all.go @@ -3,6 +3,7 @@ package all import ( _ "github.com/satisfactorymodding/SatisfactoryModManager/backend/installfinders/launchers/crossover" // register crossover _ "github.com/satisfactorymodding/SatisfactoryModManager/backend/installfinders/launchers/epic" // register epic + _ "github.com/satisfactorymodding/SatisfactoryModManager/backend/installfinders/launchers/faugus" // register faugus _ "github.com/satisfactorymodding/SatisfactoryModManager/backend/installfinders/launchers/heroic" // register heroic _ "github.com/satisfactorymodding/SatisfactoryModManager/backend/installfinders/launchers/legendary" // register legendary _ "github.com/satisfactorymodding/SatisfactoryModManager/backend/installfinders/launchers/lutris" // register lutris diff --git a/backend/installfinders/launchers/faugus/faugus_linux.go b/backend/installfinders/launchers/faugus/faugus_linux.go new file mode 100644 index 00000000..b3902446 --- /dev/null +++ b/backend/installfinders/launchers/faugus/faugus_linux.go @@ -0,0 +1,76 @@ +package faugus + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + + "github.com/satisfactorymodding/SatisfactoryModManager/backend/installfinders/common" + "github.com/satisfactorymodding/SatisfactoryModManager/backend/installfinders/launchers" + "github.com/satisfactorymodding/SatisfactoryModManager/backend/installfinders/launchers/epic" +) + +type FaugusGame struct { + Gameid string `json:"gameid"` + Title string `json:"title"` + Path string `json:"path"` + Prefix string `json:"prefix"` + LaunchArguments string `json:"launch_arguments"` + GameArguments string `json:"game_arguments"` +} + +var faugusGamesRelativePath = filepath.Join("faugus-launcher", "games.json") + +func init() { + launchers.Add("Faugus", func() ([]*common.Installation, []error) { + dataDir, ok := os.LookupEnv("XDG_DATA_HOME") + if !ok { + homeDir, err := os.UserHomeDir() + if err != nil { + return nil, []error{fmt.Errorf("failed to get user home dir: %w", err)} + } + dataDir = filepath.Join(homeDir, ".local", "share") + } + + return findInstallationsFaugus(dataDir, "Faugus", []string{"faugus-launcher"}) + }) + launchers.Add("Faugus-flatpak", func() ([]*common.Installation, []error) { + homeDir, err := os.UserHomeDir() + if err != nil { + return nil, []error{fmt.Errorf("failed to get user home dir: %w", err)} + } + flatpakXdgDataHome := filepath.Join(homeDir, ".var", "app", "io.github.Faugus.faugus-launcher", "data") + + return findInstallationsFaugus(flatpakXdgDataHome, "Faugus", []string{"flatpak", "run", "io.github.Faugus.faugus-launcher"}) + }) +} + +func findInstallationsFaugus(xdgDataHome string, launcher string, launchCommand []string) ([]*common.Installation, []error) { + faugusGamesJSONPath := filepath.Join(xdgDataHome, faugusGamesRelativePath) + + faugusGamesJSON, err := os.ReadFile(faugusGamesJSONPath) + if err != nil { + return nil, []error{fmt.Errorf("failed to read faugus games.json: %w", err)} + } + + var faugusGames []FaugusGame + err = json.Unmarshal(faugusGamesJSON, &faugusGames) + if err != nil { + return nil, []error{fmt.Errorf("failed to parse faugus games.json: %w", err)} + } + + var epicGamesGame *FaugusGame + for _, game := range faugusGames { + if game.Gameid == "epic-games" { + epicGamesGame = &game + break + } + } + + if epicGamesGame == nil { + return nil, nil + } + + return epic.FindInstallationsWine(epicGamesGame.Prefix, launcher, append(launchCommand, "--game", epicGamesGame.Gameid)) +} diff --git a/backend/installfinders/launchers/faugus/faugus_noop.go b/backend/installfinders/launchers/faugus/faugus_noop.go new file mode 100644 index 00000000..ad24565a --- /dev/null +++ b/backend/installfinders/launchers/faugus/faugus_noop.go @@ -0,0 +1 @@ +package faugus From a171772e08854a38cc729df9263f7d41f41e638a Mon Sep 17 00:00:00 2001 From: mircearoata Date: Thu, 13 Aug 2026 19:44:52 +0300 Subject: [PATCH 10/22] Use local graphql schema --- frontend/.graphqlrc.yml | 74 ++--- frontend/package.json | 1 + frontend/schema.graphql | 623 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 666 insertions(+), 32 deletions(-) create mode 100644 frontend/schema.graphql diff --git a/frontend/.graphqlrc.yml b/frontend/.graphqlrc.yml index 720e5137..13b5f67b 100644 --- a/frontend/.graphqlrc.yml +++ b/frontend/.graphqlrc.yml @@ -1,33 +1,43 @@ -schema: 'https://api.ficsit.app/v2/query' -documents: 'src/**/*.graphql' -extensions: - codegen: - overwrite: true - generates: - ./src/lib/generated/graphql/graphql.ts: - plugins: - - add: - content: '/* eslint-disable */' - - 'typescript' - - 'typescript-operations' - - 'typed-document-node' +projects: + fetch-schema: + schema: 'https://api.ficsit.app/v2/query' + extensions: + codegen: + generates: + ./schema.graphql: + plugins: + - schema-ast + default: + schema: './schema.graphql' + documents: 'src/**/*.graphql' + extensions: + codegen: + overwrite: true + generates: + ./src/lib/generated/graphql/graphql.ts: + plugins: + - add: + content: '/* eslint-disable */' + - 'typescript' + - 'typescript-operations' + - 'typed-document-node' + config: + useTypeImports: true + ./src/lib/generated/graphql/graphql.schema.urql.json: + plugins: + - 'urql-introspection' + config: + module: commonjs + ./graphql.schema.json: + plugins: + - 'introspection' config: - useTypeImports: true - ./src/lib/generated/graphql/graphql.schema.urql.json: - plugins: - - 'urql-introspection' - config: - module: commonjs - ./graphql.schema.json: - plugins: - - 'introspection' - config: - scalars: - ModID: string - ModReference: string - VersionID: string - TagID: string - AnnouncementID: string - BootstrapVersionID: string - GuideID: string - UserID: string + scalars: + ModID: string + ModReference: string + VersionID: string + TagID: string + AnnouncementID: string + BootstrapVersionID: string + GuideID: string + UserID: string diff --git a/frontend/package.json b/frontend/package.json index 7dc2a1e0..a47721da 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,6 +12,7 @@ "check:watch": "svelte-check --tsconfig ./tsconfig.json --watch", "lint": "eslint --ignore-path .gitignore .", "format": "eslint --ignore-path .gitignore --fix .", + "graphql-schema": "graphql-codegen --config .graphqlrc.yml --project fetch-schema", "graphql-codegen": "graphql-codegen --config .graphqlrc.yml", "graphql-codegen:watch": "graphql-codegen --config .graphqlrc.yml --watch", "generate:watch": "run-p graphql-codegen:watch", diff --git a/frontend/schema.graphql b/frontend/schema.graphql new file mode 100644 index 00000000..977a26ef --- /dev/null +++ b/frontend/schema.graphql @@ -0,0 +1,623 @@ +directive @canApproveMods on FIELD_DEFINITION | INPUT_FIELD_DEFINITION + +directive @canApproveVersions on FIELD_DEFINITION | INPUT_FIELD_DEFINITION + +directive @canEditAnnouncements on FIELD_DEFINITION | INPUT_FIELD_DEFINITION + +directive @canEditGuide(field: String!) on FIELD_DEFINITION + +directive @canEditMod(field: String!) on FIELD_DEFINITION + +directive @canEditModCompatibility(field: String) on FIELD_DEFINITION + +directive @canEditSatisfactoryVersions on FIELD_DEFINITION | INPUT_FIELD_DEFINITION + +directive @canEditUser(field: String!, object: Boolean!) on FIELD_DEFINITION + +directive @canEditUsers on FIELD_DEFINITION | INPUT_FIELD_DEFINITION + +directive @canEditVersion(field: String!) on FIELD_DEFINITION + +directive @canManageTags on FIELD_DEFINITION | INPUT_FIELD_DEFINITION + +""" +Directs the executor to defer this fragment when the `if` argument is true or undefined. +""" +directive @defer( + """Deferred when true or undefined.""" + if: Boolean = true + + """Unique name""" + label: String +) on FRAGMENT_SPREAD | INLINE_FRAGMENT + +directive @isLoggedIn on FIELD_DEFINITION | INPUT_FIELD_DEFINITION + +directive @isNotLoggedIn on FIELD_DEFINITION | INPUT_FIELD_DEFINITION + +type AIUseDisclosureInfo { + disclosure_type: AIUseDisclosureType! + message: String +} + +input AIUseDisclosureInput { + disclosure_type: AIUseDisclosureType! + message: String +} + +enum AIUseDisclosureType { + ai_usage + no_ai_usage + runtime_ai_usage +} + +type Announcement { + id: AnnouncementID! + importance: AnnouncementImportance! + message: String! +} + +scalar AnnouncementID + +enum AnnouncementImportance { + Alert + Fix + Info + Warning +} + +type Compatibility { + note: String + state: CompatibilityState! +} + +type CompatibilityInfo { + Controller: ControllerCompatibility! + EA: Compatibility! + EXP: Compatibility! +} + +input CompatibilityInfoInput { + Controller: ControllerCompatibilityInput! + EA: CompatibilityInput! + EXP: CompatibilityInput! +} + +input CompatibilityInput { + note: String + state: CompatibilityState! +} + +enum CompatibilityState { + Broken + Damaged + Works +} + +type ControllerCompatibility { + note: String + state: ControllerCompatibilityState! +} + +input ControllerCompatibilityInput { + note: String + state: ControllerCompatibilityState! +} + +enum ControllerCompatibilityState { + Implicit + Partial + Supported + Unsupported + Untested +} + +type CreateVersionResponse { + auto_approved: Boolean! + version: Version +} + +scalar Date + +type GetGuides { + count: Int! + guides: [Guide!]! +} + +type GetMods { + count: Int! + mods: [Mod!]! +} + +type GetMyMods { + count: Int! + mods: [Mod!]! +} + +type GetMyVersions { + count: Int! + versions: [Version!]! +} + +type GetSMLVersions { + count: Int! + sml_versions: [SMLVersion!]! +} + +type GetVersions { + count: Int! + versions: [Version!]! +} + +type Group { + id: String! + name: String! +} + +type Guide { + created_at: Date! + guide: String! + id: GuideID! + name: String! + short_description: String! + tags: [Tag!]! + updated_at: Date! + user: User! + user_id: UserID! + views: Int! +} + +enum GuideFields { + created_at + name + updated_at + views +} + +input GuideFilter { + ids: [String!] + limit: Int + offset: Int + order: Order + order_by: GuideFields + search: String + tagIDs: [TagID!] +} + +scalar GuideID + +type LatestVersions { + alpha: Version + beta: Version + release: Version +} + +type Mod { + ai_use_disclosure: AIUseDisclosureInfo + approved: Boolean! + authors: [UserMod!]! + compatibility: CompatibilityInfo + created_at: Date! + creator_id: UserID! + downloads: Int! + full_description: String + hidden: Boolean! + hotness: Int! + id: ModID! + last_version_date: Date + latestVersions: LatestVersions! + logo: String + logo_thumbhash: String + mod_reference: ModReference! + name: String! + network_use_disclosure: String + popularity: Int! + short_description: String! + source_url: String + tags: [Tag!] + toggle_explicit_content: Boolean! + updated_at: Date! + version(version: String!): Version + versions(filter: VersionFilter): [Version!]! + views: Int! +} + +enum ModFields { + created_at + downloads + hotness + last_version_date + name + popularity + search + updated_at + views +} + +input ModFilter { + hidden: Boolean + ids: [String!] + limit: Int + offset: Int + order: Order + order_by: ModFields + references: [String!] + search: String + tagIDs: [TagID!] +} + +scalar ModID + +scalar ModReference + +type ModVersion { + id: ModID! + mod_reference: ModReference! + versions: [Version!]! +} + +input ModVersionConstraint { + modIdOrReference: String! + version: String! +} + +type Mutation { + approveMod(modId: ModID!): Boolean! + approveVersion(versionId: VersionID!): Boolean! + createAnnouncement(announcement: NewAnnouncement!): Announcement + createGuide(guide: NewGuide!): Guide + createMod(mod: NewMod!): Mod + createMultipleTags(tagNames: [NewTag!]!): [Tag!]! + createSatisfactoryVersion(input: NewSatisfactoryVersion!): SatisfactoryVersion! + createTag(description: String!, tagName: TagName!): Tag + createVersion(modId: ModID!): VersionID! + deleteAnnouncement(announcementId: AnnouncementID!): Boolean! + deleteGuide(guideId: GuideID!): Boolean! + deleteMod(modId: ModID!): Boolean! + deleteSatisfactoryVersion(id: SatisfactoryVersionID!): Boolean! + deleteTag(tagID: TagID!): Boolean! + deleteVersion(versionId: VersionID!): Boolean! + denyMod(modId: ModID!): Boolean! + denyVersion(versionId: VersionID!): Boolean! + discourseSSO(sig: String!, sso: String!): String + finalizeCreateVersion(modId: ModID!, version: NewVersion!, versionId: VersionID!): Boolean! + logout: Boolean! + oAuthFacebook(code: String!, state: String!): UserSession + oAuthGithub(code: String!, state: String!): UserSession + oAuthGoogle(code: String!, state: String!): UserSession + updateAnnouncement(announcement: UpdateAnnouncement!, announcementId: AnnouncementID!): Announcement! + updateGuide(guide: UpdateGuide!, guideId: GuideID!): Guide! + updateMod(mod: UpdateMod!, modId: ModID!): Mod! + updateModCompatibility(compatibility: CompatibilityInfoInput!, modId: ModID!): Boolean! + updateMultipleModCompatibilities(compatibility: CompatibilityInfoInput!, modIDs: [ModID!]!): Boolean! + updateSatisfactoryVersion(id: SatisfactoryVersionID!, input: UpdateSatisfactoryVersion!): SatisfactoryVersion! + updateTag(NewName: TagName!, description: String!, tagID: TagID!): Tag! + updateUser(input: UpdateUser!, userId: UserID!): User! + updateVersion(version: UpdateVersion!, versionId: VersionID!): Version! + uploadVersionPart(file: Upload!, modId: ModID!, part: Int!, versionId: VersionID!): Boolean! +} + +input NewAnnouncement { + importance: AnnouncementImportance! + message: String! +} + +input NewGuide { + guide: String! + name: String! + short_description: String! + tagIDs: [TagID!] +} + +input NewMod { + full_description: String + hidden: Boolean + logo: Upload + mod_reference: ModReference! + name: String! + short_description: String! + source_url: String + tagIDs: [TagID!] + toggle_explicit_content: Boolean +} + +input NewSatisfactoryVersion { + engine_version: String! + version: Int! +} + +input NewTag { + description: String! + name: TagName! +} + +input NewVersion { + changelog: String! + stability: VersionStabilities! +} + +type OAuthOptions { + facebook: String! + github: String! + google: String! +} + +enum Order { + asc + desc +} + +type Query { + checkVersionUploadState(modId: ModID!, versionId: VersionID!): CreateVersionResponse + getAnnouncement(announcementId: AnnouncementID!): Announcement + getAnnouncements: [Announcement!]! + getAnnouncementsByImportance(importance: AnnouncementImportance!): [Announcement!]! + getGuide(guideId: GuideID!): Guide + getGuides(filter: GuideFilter): GetGuides! + getMe: User + getMod(modId: ModID!): Mod + getModAssetList(modReference: ModID!): [String!]! + getModByIdOrReference(modIdOrReference: String!): Mod + getModByReference(modReference: ModReference!): Mod + getMods(filter: ModFilter): GetMods! + getMyMods(filter: ModFilter): GetMyMods! + getMyUnapprovedMods(filter: ModFilter): GetMyMods! + getMyUnapprovedVersions(filter: VersionFilter): GetMyVersions! + getMyVersions(filter: VersionFilter): GetMyVersions! + getOAuthOptions(callback_url: String!): OAuthOptions! + getSMLVersion(smlVersionID: SMLVersionID!): SMLVersion @deprecated(reason: "SML is now a mod") + getSMLVersions(filter: SMLVersionFilter): GetSMLVersions! @deprecated(reason: "SML is now a mod") + getSatisfactoryVersion(id: SatisfactoryVersionID!): SatisfactoryVersion + getSatisfactoryVersions: [SatisfactoryVersion!]! + getTag(tagID: TagID!): Tag + getTags(filter: TagFilter): [Tag!]! + getUnapprovedMods(filter: ModFilter): GetMods! + getUnapprovedVersions(filter: VersionFilter): GetVersions! + getUser(userId: UserID!): User + getUsers(userIds: [UserID!]!): [User]! + getVersion(versionId: VersionID!): Version + getVersions(filter: VersionFilter): GetVersions! + resolveModVersions(filter: [ModVersionConstraint!]!): [ModVersion!]! +} + +type SMLVersion { + bootstrap_version: String + changelog: String! + created_at: Date! + date: Date! + engine_version: String! + id: SMLVersionID! + link: String! + satisfactory_version: Int! + stability: VersionStabilities! + targets: [SMLVersionTarget]! + updated_at: Date! + version: String! +} + +enum SMLVersionFields { + created_at + date + name + satisfactory_version + updated_at +} + +input SMLVersionFilter { + ids: [String!] + limit: Int + offset: Int + order: Order + order_by: SMLVersionFields + search: String +} + +scalar SMLVersionID + +type SMLVersionTarget { + VersionID: SMLVersionID! + link: String! + targetName: TargetName! +} + +type SatisfactoryVersion { + engine_version: String! + id: SatisfactoryVersionID! + version: Int! +} + +scalar SatisfactoryVersionID + +type Tag { + description: String! + id: TagID! + name: TagName! +} + +input TagFilter { + ids: [TagID!] + limit: Int + offset: Int + order: Order + search: String +} + +scalar TagID + +scalar TagName + +enum TargetName { + LinuxServer + Windows + WindowsServer +} + +input UpdateAnnouncement { + importance: AnnouncementImportance + message: String +} + +input UpdateGuide { + guide: String + name: String + short_description: String + tagIDs: [TagID!] +} + +input UpdateMod { + ai_use_disclosure: AIUseDisclosureInput + authors: [UpdateUserMod!] + compatibility: CompatibilityInfoInput + full_description: String + hidden: Boolean + logo: Upload + mod_reference: ModReference + name: String + network_use_disclosure: String + short_description: String + source_url: String + tagIDs: [TagID!] + toggle_explicit_content: Boolean +} + +input UpdateSatisfactoryVersion { + engine_version: String + version: Int +} + +input UpdateUser { + avatar: Upload + groups: [String!] + username: String +} + +input UpdateUserMod { + role: String! + user_id: UserID! +} + +input UpdateVersion { + changelog: String + stability: VersionStabilities +} + +scalar Upload + +type User { + avatar: String + avatar_thumbhash: String + created_at: Date! + email: String + facebook_id: String + github_id: String + google_id: String + groups: [Group!]! + guides: [Guide!]! + id: UserID! + mods: [UserMod!]! + roles: UserRoles! + username: String! +} + +scalar UserID + +type UserMod { + mod: Mod! + mod_id: ModID! + role: String! + user: User! + user_id: UserID! +} + +type UserRoles { + approveMods: Boolean! + approveVersions: Boolean! + deleteContent: Boolean! + editAnyModCompatibility: Boolean! + editBootstrapVersions: Boolean! + editContent: Boolean! + editSatisfactoryVersions: Boolean! + editUsers: Boolean! +} + +type UserSession { + token: String! +} + +type Version { + approved: Boolean! + changelog: String! + created_at: Date! + dependencies: [VersionDependency!]! + downloads: Int! + game_version: String! + hash: String + id: VersionID! + link: String! + metadata: String + mod: Mod! + mod_id: ModID! + required_on_remote: Boolean! + size: Int + sml_version: String! + stability: VersionStabilities! + targets: [VersionTarget]! + updated_at: Date! + version: String! + virustotal_results: [VirustotalResult!]! +} + +type VersionDependency { + condition: String! + mod: Mod + mod_id: ModID! @deprecated(reason: "soon will return actual mod id instead of reference. use mod_reference field instead!") + mod_reference: String! + optional: Boolean! + version: Version + version_id: VersionID! +} + +enum VersionFields { + created_at + downloads + updated_at +} + +input VersionFilter { + ids: [String!] + limit: Int + offset: Int + order: Order + order_by: VersionFields + search: String +} + +scalar VersionID + +enum VersionStabilities { + alpha + beta + release +} + +type VersionTarget { + VersionID: VersionID! + hash: String + link: String! + size: Int + targetName: TargetName! +} + +scalar VirustotalHash + +scalar VirustotalID + +type VirustotalResult { + created_at: Date! + file_name: String! + hash: VirustotalHash! + id: VirustotalID + safe: Boolean! + updated_at: Date + version_id: String! +} \ No newline at end of file From 407ada632601c1d5022e45c0ca604c176c35c3fb Mon Sep 17 00:00:00 2001 From: Mark Lierman Date: Sat, 12 Sep 2026 08:34:45 -0400 Subject: [PATCH 11/22] Add controller combatibility icon (#365) --- frontend/src/gql/mods/modDetails.graphql | 4 ++ .../components/mod-details/ModDetails.svelte | 41 ++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/frontend/src/gql/mods/modDetails.graphql b/frontend/src/gql/mods/modDetails.graphql index e3565bc8..98b0a891 100644 --- a/frontend/src/gql/mods/modDetails.graphql +++ b/frontend/src/gql/mods/modDetails.graphql @@ -12,6 +12,10 @@ query GetModDetails($modReference: ModReference!) { views hidden compatibility { + Controller { + state + note + } EA { state note diff --git a/frontend/src/lib/components/mod-details/ModDetails.svelte b/frontend/src/lib/components/mod-details/ModDetails.svelte index 18ea5392..74a11fff 100644 --- a/frontend/src/lib/components/mod-details/ModDetails.svelte +++ b/frontend/src/lib/components/mod-details/ModDetails.svelte @@ -1,6 +1,6 @@