diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 6831e57a..8222fccd 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -30,12 +30,12 @@ runs: with: node-version: 22 cache: 'pnpm' - cache-dependency-path: frontend + cache-dependency-path: frontend/pnpm-lock.yaml - name: Install Wails if: ${{ inputs.backend == 'true' && inputs.skip-wails != 'true' }} shell: bash - run: go install github.com/wailsapp/wails/v2/cmd/wails@v2.10.2 + run: go install github.com/wailsapp/wails/v2/cmd/wails@v2.15.0 # Any go operation on linux will require these - name: Install additional dependencies @@ -43,4 +43,7 @@ runs: shell: bash run: | sudo apt update - sudo apt -y install libgtk-3-dev libwebkit2gtk-4.1-dev patchelf librsvg2-dev + if ! pkg-config --exists webkit2gtk-4.1; then + sudo apt -y install libwebkit2gtk-4.1-dev + fi + sudo apt -y install libgtk-3-dev patchelf librsvg2-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav diff --git a/.github/docker/Dockerfile.builder b/.github/docker/Dockerfile.builder new file mode 100644 index 00000000..8a661fa5 --- /dev/null +++ b/.github/docker/Dockerfile.builder @@ -0,0 +1,64 @@ +FROM ubuntu:22.04 AS builder + +ENV DEBIAN_FRONTEND=noninteractive + +# Pull webkit2gtk-4.1 build dependencies +RUN sed -i 's/^#\s*deb-src/deb-src/' /etc/apt/sources.list && \ + apt-get update && \ + apt-get install -y --no-install-recommends build-essential curl ca-certificates && \ + apt-get build-dep -y libwebkit2gtk-4.1-dev + +# Build freetype 2.13 +ARG FREETYPE_VERSION=2.13.3 +RUN mkdir -p /build/freetype && cd /build/freetype && \ + curl -sSL "https://download.savannah.gnu.org/releases/freetype/freetype-${FREETYPE_VERSION}.tar.xz" | tar -xJ --strip-components=1 && \ + ./configure --prefix=/usr/local --enable-shared --disable-static && \ + make -j"$(nproc)" && \ + make install && \ + ldconfig && \ + mkdir -p /etc/dpkg && echo "libfreetype 6 libfreetype6" >> /etc/dpkg/shlibs.override + +# Build webkit2gtk-4.1 +RUN mkdir -p /build/webkit && cd /build/webkit && \ + apt-get source webkit2gtk && \ + cd webkit2gtk-*/ && \ + sed -i 's/ENABLE_SOUP2=YES/ENABLE_SOUP2=NO/' debian/rules && \ + sed -i 's/ENABLE_GTK4=YES/ENABLE_GTK4=NO/' debian/rules && \ + DEB_BUILD_OPTIONS="nocheck nodoc parallel=$(nproc)" dpkg-buildpackage -b -uc -us + +FROM ubuntu:22.04 + +ENV DEBIAN_FRONTEND=noninteractive + +# Used in the cache key of actions/setup-go, would be "undefined" if missing +ENV ImageOS=ubuntu22 + +# Install packages normally installed on GHA +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + git \ + sudo \ + build-essential \ + pkg-config \ + file + +RUN curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - && \ + sudo apt-get install -y nodejs + +RUN (type -p wget >/dev/null || (sudo apt update && sudo apt install wget -y)) \ + && sudo mkdir -p -m 755 /etc/apt/keyrings \ + && out=$(mktemp) && wget -nv -O$out https://cli.github.com/packages/githubcli-archive-keyring.gpg \ + && cat $out | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \ + && sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \ + && sudo mkdir -p -m 755 /etc/apt/sources.list.d \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ + && sudo apt update \ + && sudo apt install gh -y + +# Install our build of webkit2gtk-4.1 +COPY --from=builder /build/webkit/*.deb /tmp/debs/ + +RUN apt-get install -y --no-install-recommends /tmp/debs/*.deb && \ + rm -rf /tmp/debs diff --git a/.github/workflows/build-base.yml b/.github/workflows/build-base.yml index 5e61960c..f22bd159 100644 --- a/.github/workflows/build-base.yml +++ b/.github/workflows/build-base.yml @@ -20,6 +20,11 @@ jobs: # the oldest non-EOL distro version which has webkit2gtk-4.1, so it will have to do, and we can hope it covers # all distros and versions people would reasonably install SMM on runs-on: ubuntu-22.04 + # We also use a custom webkit2gtk-4.1 built against freetype 2.13 + # as a fix for appimage crashes on distros using versions newer than that when displaying emoji + # Running webkit2gtk-4.1 built against this newer version should be safe on older distros too, + # since the struct causing the crash is larger in newer versions + container: ghcr.io/${{ github.repository_owner }}/smm-builder:ubuntu22.04 - os: windows # Using windows-2022 for now because NSIS is missing on windows-2025 runners runs-on: windows-2022 @@ -28,10 +33,16 @@ jobs: runs-on: macos-15 goreleaser_args: "-p 1" # Wails packaging for darwin cannot handle parallel builds runs-on: ${{ matrix.platform.runs-on }} + container: ${{ matrix.platform.container || null }} steps: - uses: actions/checkout@v6 with: fetch-depth: 0 + + # When running in a container, the user is root, but the workspace is mounted from the host, so git commands will fail unless we set the safe directory + # https://github.com/actions/checkout/issues/2031 + - name: Set safe directory + run: git config --system --add safe.directory "$GITHUB_WORKSPACE" - uses: ./.github/actions/setup diff --git a/.github/workflows/builder-image.yml b/.github/workflows/builder-image.yml new file mode 100644 index 00000000..d3027121 --- /dev/null +++ b/.github/workflows/builder-image.yml @@ -0,0 +1,30 @@ +name: Build Builder Image + +on: + workflow_dispatch: + push: + paths: + - '.github/docker/**' + - '.github/workflows/builder-image.yml' + +jobs: + build-image: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v7 + + - uses: docker/setup-buildx-action@v4 + + - uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - uses: docker/build-push-action@v7 + with: + context: . + file: .github/docker/Dockerfile.builder + push: true + tags: ghcr.io/${{ github.repository_owner }}/smm-builder:ubuntu22.04 diff --git a/backend/autoupdate/autoupdate.go b/backend/autoupdate/autoupdate.go index 6700acbf..e356d9f5 100644 --- a/backend/autoupdate/autoupdate.go +++ b/backend/autoupdate/autoupdate.go @@ -40,8 +40,9 @@ func Init() { slog.Info("update found", slog.Any("version", update.Version)) if common.AppContext != nil { wailsRuntime.EventsEmit(common.AppContext, "updateAvailable", &PendingUpdate{ - Version: update.Version.String(), - Changelogs: update.Changelogs, + Version: update.Version.String(), + Changelogs: update.Changelogs, + CanAutoUpdate: Updater.Updater.CanApplyUpdates(), }) } }) @@ -85,8 +86,9 @@ func makeUpdaterConfig() updater.Config { } type PendingUpdate struct { - Version string `json:"version"` - Changelogs map[string]string `json:"changelogs"` + Version string `json:"version"` + Changelogs map[string]string `json:"changelogs"` + CanAutoUpdate bool `json:"canAutoUpdate"` } func (u *autoUpdate) PendingUpdate() *PendingUpdate { @@ -97,8 +99,9 @@ func (u *autoUpdate) PendingUpdate() *PendingUpdate { return nil } return &PendingUpdate{ - Version: u.Updater.PendingUpdate.Version.String(), - Changelogs: u.Updater.PendingUpdate.Changelogs, + Version: u.Updater.PendingUpdate.Version.String(), + Changelogs: u.Updater.PendingUpdate.Changelogs, + CanAutoUpdate: u.Updater.CanApplyUpdates(), } } diff --git a/backend/autoupdate/updater/updater.go b/backend/autoupdate/updater/updater.go index 5782f5ad..3fb7c53e 100644 --- a/backend/autoupdate/updater/updater.go +++ b/backend/autoupdate/updater/updater.go @@ -44,6 +44,10 @@ func MakeUpdater(config Config) *Updater { } } +func (u *Updater) CanApplyUpdates() bool { + return u.config.File != "" && u.config.Apply != nil +} + func (u *Updater) OnExit(restart bool) error { u.lock.Lock() defer u.lock.Unlock() 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/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, }) } 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 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 diff --git a/backend/installfinders/launchers/lutris/lutris_linux.go b/backend/installfinders/launchers/lutris/lutris_linux.go index be6e7923..e7e196c5 100644 --- a/backend/installfinders/launchers/lutris/lutris_linux.go +++ b/backend/installfinders/launchers/lutris/lutris_linux.go @@ -5,7 +5,6 @@ import ( "fmt" "os" "os/exec" - "strings" "github.com/satisfactorymodding/SatisfactoryModManager/backend/installfinders/common" "github.com/satisfactorymodding/SatisfactoryModManager/backend/installfinders/launchers" @@ -34,21 +33,7 @@ func findInstallations(lutrisCmd []string, launcher string) ([]*common.Installat lutrisLj := exec.Command(lutrisLjCmd[0], lutrisLjCmd[1:]...) lutrisLj.Env = os.Environ() lutrisLj.Env = append(lutrisLj.Env, "LUTRIS_SKIP_INIT=1") - if os.Getenv("APPIMAGE") != "" { - // Must clear the APPIMAGE added entries of LD_LIBRARY_PATH, as well as PYTHONHOME and PYTHONPATH - // otherwise lutris will load some appimage libraries and some system libraries, which are incompatible - lutrisLj.Env = append(lutrisLj.Env, "PYTHONHOME=", "PYTHONPATH=") - appdir := os.Getenv("APPDIR") - ldLibraryPathEntries := strings.Split(os.Getenv("LD_LIBRARY_PATH"), ":") - newLdLibraryPathEntries := []string{} - for _, entry := range ldLibraryPathEntries { - if !strings.HasPrefix(entry, appdir) { - newLdLibraryPathEntries = append(newLdLibraryPathEntries, entry) - } - } - lutrisLj.Env = append(lutrisLj.Env, "LD_LIBRARY_PATH="+strings.Join(newLdLibraryPathEntries, ":")) - } outputBytes, err := lutrisLj.Output() if err != nil { return nil, []error{ diff --git a/build/linux/AppRun b/build/linux/AppRun new file mode 100644 index 00000000..3f35578b --- /dev/null +++ b/build/linux/AppRun @@ -0,0 +1,10 @@ +#!/bin/bash + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +# The GTK plugin patches webkit2gtk's hardcoded path to an executable with a string replacement, +# turning /usr/lib into ././/lib, so the workdir needs to be /usr + +cd $SCRIPT_DIR/usr + +exec ./bin/SatisfactoryModManager diff --git a/build/linux/appimage.sh b/build/linux/appimage.sh index da1ca220..516f0ddc 100644 --- a/build/linux/appimage.sh +++ b/build/linux/appimage.sh @@ -1,5 +1,8 @@ #!/bin/bash +# abort on all errors +set -e + APPNAME="SatisfactoryModManager" export ARCH="x86_64" # Export because linuxdeploy gtk plugin copies i386 libraries too, so linuxdeploy can't decide on architecture SCRIPT_DIR=$(dirname "$0") @@ -11,62 +14,57 @@ OUTPUT=$2 TMPDIR=$(mktemp -d) APPDIR="$SCRIPT_DIR/../bin/$APPNAME.AppDir" +# The input binary name is appimage-bin-tmp, so we need to fix that +cp "$BINARY" "$TMPDIR/$APPNAME" +BINARY="$TMPDIR/$APPNAME" + if [ -d "$APPDIR" ]; then rm -rf "$APPDIR" fi mkdir -p "$APPDIR" -mkdir -p "$APPDIR/usr/bin" -mkdir -p "$APPDIR/usr/lib" -mkdir -p "$APPDIR/usr/lib64" - +# We still copy icons manually instead of using linuxdeploy +# because the icons are not square, and linuxdeploy checks that ( cd "$APPDIR" || exit -cp "$BINARY" "usr/bin/$APPNAME" -cp "$BUILD_DIR/appicon.png" "$APPNAME.png" -cp "$BUILD_DIR/appicon.png" ".DirIcon" icons=(16 32 64 128 256 512) for i in "${icons[@]}"; do mkdir -p "usr/share/icons/hicolor/${i}x${i}/apps" cp "$BUILD_DIR/icons/${i}x${i}.png" "usr/share/icons/hicolor/${i}x${i}/apps/$APPNAME.png" done - -mkdir -p "usr/share/applications" -cp "$SCRIPT_DIR/$APPNAME.desktop" "usr/share/applications/$APPNAME.desktop" -ln -sf "usr/share/applications/$APPNAME.desktop" "$APPNAME.desktop" -) - -( -cd "$APPDIR" || exit - -# Copy webkit2gtk libraries -find -L /usr/lib* -name WebKitNetworkProcess -exec mkdir -p "$(dirname '{}')" \; -exec cp --parents '{}' "." \; || true -find -L /usr/lib* -name WebKitWebProcess -exec mkdir -p "$(dirname '{}')" \; -exec cp --parents '{}' "." \; || true -find -L /usr/lib* -name libwebkit2gtkinjectedbundle.so -exec mkdir -p "$(dirname '{}')" \; -exec cp --parents '{}' "." \; || true - -# Download AppRun -wget -O AppRun https://github.com/AppImage/AppImageKit/releases/download/continuous/AppRun-${ARCH} -chmod +x AppRun ) ( cd "$TMPDIR" || exit -wget https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh +wget https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh chmod +x linuxdeploy-plugin-gtk.sh -wget https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gstreamer/master/linuxdeploy-plugin-gstreamer.sh +wget https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gstreamer/master/linuxdeploy-plugin-gstreamer.sh chmod +x linuxdeploy-plugin-gstreamer.sh +cp "$SCRIPT_DIR/linuxdeploy-plugin-webkitgtk.sh" . +chmod +x linuxdeploy-plugin-webkitgtk.sh + wget -O linuxdeploy.AppImage https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-${ARCH}.AppImage chmod +x linuxdeploy.AppImage ) mkdir -p "$(dirname "$OUTPUT")" -LDAI_OUTPUT="$OUTPUT" DEPLOY_GTK_VERSION="3" "$TMPDIR/linuxdeploy.AppImage" --appimage-extract-and-run --appdir "$APPDIR" --plugin gtk --plugin gstreamer --output appimage +LDAI_OUTPUT="$OUTPUT" DEPLOY_GTK_VERSION="3" "$TMPDIR/linuxdeploy.AppImage" --appimage-extract-and-run \ + --executable "$BINARY" \ + --desktop-file "$SCRIPT_DIR/$APPNAME.desktop" \ + "${ICON_FILES[@]}" \ + --icon-filename "$APPNAME" \ + --custom-apprun "$SCRIPT_DIR/AppRun" \ + --appdir "$APPDIR" \ + --plugin gtk \ + --plugin webkitgtk \ + --plugin gstreamer \ + --output appimage rm -rf "$TMPDIR" rm -rf "$APPDIR" diff --git a/build/linux/linuxdeploy-plugin-webkitgtk.sh b/build/linux/linuxdeploy-plugin-webkitgtk.sh new file mode 100644 index 00000000..7236d58e --- /dev/null +++ b/build/linux/linuxdeploy-plugin-webkitgtk.sh @@ -0,0 +1,295 @@ +#! /bin/bash + +# abort on all errors +set -e + +SCRIPT="$(basename "$(readlink -f "$0")")" + +show_usage() { + echo "Usage: $SCRIPT --appdir " + echo + echo "Bundles resources for applications that use WebKitGTK into an AppDir" + echo + echo "Required variables:" + echo " LINUXDEPLOY=\".../linuxdeploy\" path to linuxdeploy (e.g., AppImage); set automatically when plugin is run directly by linuxdeploy" + echo + echo "Optional variables:" + echo " DEPLOY_WEBKITGTK_VERSION (complete version name of WebKitGTK to deploy, e.g., webkit2gtk-4.1; auto-detect by default)" +} + +variable_is_true() { + local var="$1" + + if [ -n "$var" ] && { [ "$var" == "true" ] || [ "$var" -gt 0 ]; } 2> /dev/null; then + return 0 # true + else + return 1 # false + fi +} + +get_pkgconf_variable() { + local variable="$1" + local library="$2" + local default_value="$3" + + pkgconfig_ret="$("$PKG_CONFIG" --variable="$variable" "$library")" + if [ -n "$pkgconfig_ret" ]; then + echo "$pkgconfig_ret" + elif [ -n "$default_value" ]; then + echo "$default_value" + else + echo "$0: there is no '$variable' variable for '$library' library." > /dev/stderr + echo "Please check the '$library.pc' file is present in \$PKG_CONFIG_PATH (you may need to install the appropriate -dev/-devel package)." > /dev/stderr + exit 1 + fi +} + +copy_tree() { + local src=("${@:1:$#-1}") + local dst="${*:$#}" + + for elem in "${src[@]}"; do + mkdir -p "${dst::-1}$elem" + cp "$elem" --archive --parents --target-directory="$dst" $verbose + done +} + +copy_lib_tree() { + # The source lib directory could be /usr/lib, /usr/lib64, or /usr/lib/x86_64-linux-gnu + # Therefore, when copying lib directories, we need to transform that target path + # to a consistent /usr/lib + local src=("${@:1:$#-1}") + local dst="${*:$#}" + + for elem in "${src[@]}"; do + mkdir -p "${dst::-1}${elem/$LD_GTK_LIBRARY_PATH//usr/lib}" + pushd "$LD_GTK_LIBRARY_PATH" + cp "$(realpath --relative-to="$LD_GTK_LIBRARY_PATH" "$elem")" --archive --parents --target-directory="$dst/usr/lib" $verbose + popd + done +} + +get_triplet_path() { + if command -v dpkg-architecture > /dev/null; then + echo "/usr/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)" + fi +} + + + +search_library_path() { + PATH_ARRAY=( + "$(get_triplet_path)" + "/usr/lib64" + "/usr/lib" + ) + + for path in "${PATH_ARRAY[@]}"; do + if [ -d "$path" ]; then + echo "$path" + return 0 + fi + done +} + +search_tool() { + local tool="$1" + local directory="$2" + + if command -v "$tool"; then + return 0 + fi + + PATH_ARRAY=( + "$(get_triplet_path)/$directory/$tool" + "/usr/lib64/$directory/$tool" + "/usr/lib/$directory/$tool" + "/usr/bin/$tool" + "/usr/bin/$tool-64" + "/usr/bin/$tool-32" + ) + + for path in "${PATH_ARRAY[@]}"; do + if [ -x "$path" ]; then + echo "$path" + return 0 + fi + done +} + +find_all() { + local -n find_array="$1" + shift + local -n result_array="$1" + shift + + for (( i=0; i<${#find_array[@]}; i+=2 )); do + directory=${find_array[i]} + file=${find_array[i+1]} + while IFS= read -r -d '' file; do + result_array+=( "$file" ) + done < <(find "$directory" \( -type l -o -type f \) -name "$file" -print0) + done +} + +DEPLOY_WEBKITGTK_VERSION="${DEPLOY_WEBKITGTK_VERSION:-0}" # When not set by user, this variable use the integer '0' as a sentinel value +APPDIR="" + +while [ "$1" != "" ]; do + case "$1" in + --plugin-api-version) + echo "0" + exit 0 + ;; + --appdir) + APPDIR="$2" + shift + shift + ;; + *) + echo "Invalid argument: $1" + echo + show_usage + exit 2 + esac +done + +if [[ "$APPDIR" == "" ]]; then + show_usage + exit 2 +fi + +APPDIR="$(realpath "$APPDIR")" +mkdir -p "$APPDIR" + +if command -v pkgconf > /dev/null; then + PKG_CONFIG="pkgconf" +elif command -v pkg-config > /dev/null; then + PKG_CONFIG="pkg-config" +else + echo "$0: pkg-config/pkgconf not found in PATH, aborting" + exit 1 +fi + +if ! command -v find &>/dev/null && ! type find &>/dev/null; then + echo -e "$0: find not found.\nInstall findutils then re-run the plugin." + exit 1 +fi + +if [ -z "$LINUXDEPLOY" ]; then + echo -e "$0: LINUXDEPLOY environment variable is not set.\nDownload a suitable linuxdeploy AppImage, set the environment variable and re-run the plugin." + exit 1 +fi + +webkitgtk_versions=0 # Count versions of WebKitGTK when auto-detecting WebKitGTK version +if [ "$DEPLOY_WEBKITGTK_VERSION" = "0" ]; then + echo "Determining which WebKitGTK version to deploy" + while IFS= read -r -d '' file; do + if [ "$DEPLOY_WEBKITGTK_VERSION" != "webkit2gtk-4.0" ] && ldd "$file" | grep -q "libwebkit2gtk-4.0.so"; then + DEPLOY_WEBKITGTK_VERSION="webkit2gtk-4.0" + webkitgtk_versions="$((webkitgtk_versions+1))" + fi + if [ "$DEPLOY_WEBKITGTK_VERSION" != "webkit2gtk-4.1" ] && ldd "$file" | grep -q "libwebkit2gtk-4.1.so"; then + DEPLOY_WEBKITGTK_VERSION="webkit2gtk-4.1" + webkitgtk_versions="$((webkitgtk_versions+1))" + fi + if [ "$DEPLOY_WEBKITGTK_VERSION" != "webkitgtk-6.0" ] && ldd "$file" | grep -q "libwebkitgtk-6.0.so"; then + DEPLOY_WEBKITGTK_VERSION="webkitgtk-6.0" + webkitgtk_versions="$((webkitgtk_versions+1))" + fi + done < <(find "$APPDIR/usr/bin" -executable -type f -print0) +fi + +if [ "$webkitgtk_versions" -gt 1 ]; then + echo "$0: can not deploy multiple WebKitGTK versions at the same time." + echo "Please set DEPLOY_WEBKITGTK_VERSION to {webkit2gtk-4.0, webkit2gtk-4.1, webkitgtk-6.0}." + exit 1 +elif [ "$DEPLOY_WEBKITGTK_VERSION" = "0" ]; then + echo "$0: failed to auto-detect WebKitGTK version." + echo "Please set DEPLOY_WEBKITGTK_VERSION to {webkit2gtk-4.0, webkit2gtk-4.1, webkitgtk-6.0}." + exit 1 +fi + + + +echo "Installing AppRun hook" +HOOKSDIR="$APPDIR/apprun-hooks" +HOOKFILE="$HOOKSDIR/linuxdeploy-plugin-webkitgtk.sh" +mkdir -p "$HOOKSDIR" +cat > "$HOOKFILE" <> "$HOOKFILE" < - - - -
diff --git a/frontend/package.json b/frontend/package.json index 6e65d86f..638c5b54 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", @@ -22,7 +23,8 @@ }, "dependencies": { "@floating-ui/dom": "^1.7.4", - "@fontsource/fira-mono": "^5.2.7", + "@fontsource/roboto": "^5.3.0", + "@fontsource/roboto-mono": "^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..85953cc3 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -11,9 +11,12 @@ importers: '@floating-ui/dom': specifier: ^1.7.4 version: 1.7.4 - '@fontsource/fira-mono': - specifier: ^5.2.7 - version: 5.2.7 + '@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 +177,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 +926,11 @@ packages: '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} - '@fontsource/fira-mono@5.2.7': - resolution: {integrity: sha512-wYrAn6i3nH6luqQBZxtWUpl4UTUvs9AEbEeZxksPMwIqyjRRaxHTNW3c2VfM50gabS2IS7pT8lVWS2USB4ukYA==} + '@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 +1326,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 +1615,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 +2469,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 +2579,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 +2771,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 +2907,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 +2924,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 +3375,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 +3528,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 +3542,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 +3603,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 +4091,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 +5019,9 @@ snapshots: '@floating-ui/utils@0.2.10': {} - '@fontsource/fira-mono@5.2.7': {} + '@fontsource/roboto-mono@5.3.0': {} + + '@fontsource/roboto@5.3.0': {} '@graphql-codegen/add@5.0.3(graphql@16.12.0)': dependencies: @@ -7176,8 +7162,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 +7372,6 @@ snapshots: dependencies: tslib: 2.8.1 - is-url@1.2.4: {} - is-weakmap@2.0.2: {} is-weakref@1.1.1: @@ -7503,8 +7485,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 +7495,6 @@ snapshots: lodash.sortby@4.7.0: {} - lodash.trim@4.5.1: {} - lodash@4.17.21: {} log-symbols@4.1.0: @@ -7961,15 +7939,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 +8125,6 @@ snapshots: pstree.remy@1.1.8: {} - punycode@1.3.2: {} - punycode@1.4.1: {} punycode@2.3.1: {} @@ -8168,8 +8135,6 @@ snapshots: pvutils@1.1.5: {} - querystring@0.2.0: {} - queue-microtask@1.2.3: {} react@18.3.1: @@ -8240,10 +8205,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 +8779,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/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 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/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 diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index 83089140..6c583d76 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -132,23 +132,25 @@ $: if ($smmUpdateInit && checkStart) { checkStart = false; if ($smmUpdate) { - $isUpdateOnStart = true; - if ($smmUpdateReady) { - modalStore.trigger({ - type: 'component', - component: 'smmUpdateReady', - meta: { - persistent: true, - }, - }); - } else { - modalStore.trigger({ - type: 'component', - component: 'smmUpdateDownload', - meta: { - persistent: true, - }, - }); + if ($smmUpdate.canAutoUpdate) { + $isUpdateOnStart = true; + if ($smmUpdateReady) { + modalStore.trigger({ + type: 'component', + component: 'smmUpdateReady', + meta: { + persistent: true, + }, + }); + } else { + modalStore.trigger({ + type: 'component', + component: 'smmUpdateDownload', + meta: { + persistent: true, + }, + }); + } } } } diff --git a/frontend/src/_global.postcss b/frontend/src/_global.postcss index 55aa2348..4dfe73d5 100644 --- a/frontend/src/_global.postcss +++ b/frontend/src/_global.postcss @@ -1,4 +1,14 @@ -@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"; + +/* @fontsource/noto-color-emoji doesn't include COLRv1, so it is broken pretty much everywhere */ +@font-face { + font-family: 'Noto Color Emoji'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(/NotoColorEmoji-Regular.woff2) format('woff2'); +} @tailwind base; @tailwind components; @@ -6,7 +16,7 @@ html, body { - font-family: 'Roboto', Helvetica Neue, sans-serif, 'Noto Color Emoji'; + font-family: 'Roboto', 'Noto Color Emoji', Helvetica Neue, sans-serif; margin: 0; padding: 0; overflow: hidden; @@ -18,11 +28,6 @@ html { @apply text-sm; } -html.accessibility, -body.accessibility { - font-family: 'Atkinson Hyperlegible', Helvetica Neue, sans-serif; -} - button { @apply tracking-wider; } @@ -215,4 +220,4 @@ button { ::-ms-reveal { display: none; -} \ No newline at end of file +} diff --git a/frontend/src/gql/mods/modDetails.graphql b/frontend/src/gql/mods/modDetails.graphql index e3565bc8..0dd97beb 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 @@ -21,6 +25,11 @@ query GetModDetails($modReference: ModReference!) { note } } + ai_use_disclosure { + disclosure_type + message + } + network_use_disclosure authors { user { id diff --git a/frontend/src/lib/components/mod-details/ModDetails.svelte b/frontend/src/lib/components/mod-details/ModDetails.svelte index 18ea5392..e809261d 100644 --- a/frontend/src/lib/components/mod-details/ModDetails.svelte +++ b/frontend/src/lib/components/mod-details/ModDetails.svelte @@ -1,6 +1,6 @@