From 7635b4ce2191ed2a3b9109d7b7dfecd98886d02c Mon Sep 17 00:00:00 2001 From: Nguyen Duy Date: Wed, 6 May 2026 14:36:18 +0700 Subject: [PATCH 01/13] refactor: update project structure - Move makefile to core/ - Migrate project solution to .slnx format - Move loader/ to packages/hub/ - Move plugins/ to packages/preload/ - Add root package.json - Add an empty app/ for later app hub impl - Add an empty packages/types/ for later shared types --- .gitignore | 4 - app/.gitkeep | 0 core/core.vcxproj | 1 + core/core.vcxproj.filters | 104 ------------------ makefile => core/makefile | 12 +- package.json | 29 +++++ {loader => packages/hub}/.gitignore | 0 {loader => packages/hub}/.npmrc | 0 .../hub}/.vscode/extensions.json | 0 .../hub}/.vscode/settings.json | 0 {loader => packages/hub}/README.md | 0 {loader => packages/hub}/index.html | 0 {loader => packages/hub}/package.json | 0 {loader => packages/hub}/pnpm-lock.yaml | 0 {loader => packages/hub}/src-tauri/.gitignore | 0 {loader => packages/hub}/src-tauri/Cargo.lock | 0 {loader => packages/hub}/src-tauri/Cargo.toml | 0 {loader => packages/hub}/src-tauri/build.rs | 0 .../hub}/src-tauri/icons/128x128.png | Bin .../hub}/src-tauri/icons/128x128@2x.png | Bin .../hub}/src-tauri/icons/32x32.png | Bin .../src-tauri/icons/Square107x107Logo.png | Bin .../src-tauri/icons/Square142x142Logo.png | Bin .../src-tauri/icons/Square150x150Logo.png | Bin .../src-tauri/icons/Square284x284Logo.png | Bin .../hub}/src-tauri/icons/Square30x30Logo.png | Bin .../src-tauri/icons/Square310x310Logo.png | Bin .../hub}/src-tauri/icons/Square44x44Logo.png | Bin .../hub}/src-tauri/icons/Square71x71Logo.png | Bin .../hub}/src-tauri/icons/Square89x89Logo.png | Bin .../hub}/src-tauri/icons/StoreLogo.png | Bin .../hub}/src-tauri/icons/icon.icns | Bin .../hub}/src-tauri/icons/icon.ico | Bin .../hub}/src-tauri/icons/icon.png | Bin .../hub}/src-tauri/src/config.rs | 0 .../hub}/src-tauri/src/macos/core.rs | 0 .../hub}/src-tauri/src/macos/dylib.rs | 0 .../hub}/src-tauri/src/macos/mod.rs | 0 .../hub}/src-tauri/src/macos/socket.rs | 0 .../hub}/src-tauri/src/macos/startup.rs | 0 .../hub}/src-tauri/src/macos/tray.rs | 0 .../hub}/src-tauri/src/macos/utils.rs | 0 .../hub}/src-tauri/src/macos/webview.rs | 0 .../hub}/src-tauri/src/main.rs | 0 .../hub}/src-tauri/src/shell.rs | 0 .../hub}/src-tauri/src/windows/mod.rs | 0 .../hub}/src-tauri/src/windows/mod_ifeo.rs | 0 .../hub}/src-tauri/src/windows/mod_symlink.rs | 0 .../hub}/src-tauri/src/windows/utils.rs | 0 .../hub}/src-tauri/tauri.conf.json | 0 {loader => packages/hub}/src/App.css | 0 {loader => packages/hub}/src/App.tsx | 0 .../hub}/src/assets/bg-mask.svg | 0 .../hub}/src/assets/icon-sm.png | Bin {loader => packages/hub}/src/assets/icon.png | Bin .../hub}/src/components/Activator.tsx | 0 .../hub}/src/components/Appbar.tsx | 0 .../hub}/src/components/Icons.tsx | 0 .../hub}/src/components/PluginGallery.tsx | 0 .../hub}/src/components/PluginStore.tsx | 0 .../src/components/settings/Tab.About.tsx | 0 .../src/components/settings/Tab.Client.tsx | 0 .../src/components/settings/Tab.Pengu.tsx | 0 .../hub}/src/components/settings/index.tsx | 0 .../src/components/settings/templates.tsx | 0 .../hub}/src/components/ui/Button.tsx | 0 .../hub}/src/components/ui/Checkbox.tsx | 0 .../hub}/src/components/ui/ComboBox.tsx | 0 .../hub}/src/components/ui/RadioButton.tsx | 0 .../hub}/src/components/ui/index.ts | 0 {loader => packages/hub}/src/index.tsx | 0 {loader => packages/hub}/src/lib/config.ts | 0 .../hub}/src/lib/core-module.ts | 0 .../hub}/src/lib/datatstore.ts | 0 {loader => packages/hub}/src/lib/i18n.ts | 0 .../hub}/src/lib/league-client.ts | 0 {loader => packages/hub}/src/lib/plugins.ts | 0 {loader => packages/hub}/src/lib/root.ts | 0 {loader => packages/hub}/src/lib/shell.ts | 0 {loader => packages/hub}/src/lib/startup.ts | 0 {loader => packages/hub}/src/lib/store.ts | 0 {loader => packages/hub}/src/lib/utils.ts | 0 .../hub}/src/pages/MainPage.tsx | 0 .../hub}/src/pages/SplashPage.tsx | 0 .../hub}/src/pages/WelcomePage.tsx | 0 {loader => packages/hub}/src/vite-env.d.ts | 0 {loader => packages/hub}/tailwind.config.ts | 0 {loader => packages/hub}/translations.json | 0 {loader => packages/hub}/tsconfig.json | 0 {loader => packages/hub}/tsconfig.node.json | 0 {loader => packages/hub}/vite.config.ts | 0 {plugins => packages/preload}/.gitignore | 0 {plugins => packages/preload}/README.md | 0 {plugins => packages/preload}/index.html | 0 {plugins => packages/preload}/package.json | 0 {plugins => packages/preload}/pnpm-lock.yaml | 0 {plugins => packages/preload}/src/index.ts | 0 .../preload}/src/preload/api/DataStore.ts | 0 .../preload}/src/preload/api/Effect.ts | 0 .../preload}/src/preload/api/index.ts | 0 .../preload}/src/preload/api/native.ts | 0 .../preload}/src/preload/index.ts | 0 .../preload}/src/preload/load-hooks.ts | 0 .../preload}/src/preload/loader.ts | 0 .../preload}/src/preload/polyfills.ts | 0 .../preload}/src/preload/rcp/hooks.ts | 0 .../preload}/src/preload/rcp/index.ts | 0 .../preload}/src/preload/rcp/socket.ts | 0 .../preload}/src/preload/super-potato.ts | 0 {plugins => packages/preload}/src/types.d.ts | 0 .../preload}/src/views/App.tsx | 0 .../preload}/src/views/assets/pengu.jpg | Bin .../preload}/src/views/assets/pengu2.jpg | Bin .../views/components/CommandBar/Animator.tsx | 0 .../views/components/CommandBar/SearchBar.tsx | 0 .../components/CommandBar/SearchItem.tsx | 0 .../components/CommandBar/SearchResults.tsx | 0 .../src/views/components/CommandBar/data.ts | 0 .../src/views/components/CommandBar/index.tsx | 0 .../src/views/components/CommandBar/root.ts | 0 .../src/views/components/CommandBar/utils.ts | 0 .../preload}/src/views/components/Toaster.tsx | 0 .../preload}/src/views/components/Welcome.tsx | 0 .../preload}/src/views/env.d.ts | 0 .../preload}/src/views/index.tsx | 0 .../preload}/src/views/lib/i18n.ts | 0 .../preload}/src/views/lib/updater.ts | 0 .../preload}/src/views/style.css | 0 .../preload}/src/views/trans.json | 0 .../preload}/tailwind.config.ts | 0 {plugins => packages/preload}/tsconfig.json | 0 {plugins => packages/preload}/twind.config.ts | 0 {plugins => packages/preload}/vite.config.ts | 0 packages/types/.gitkeep | 0 pengu.sln | 25 ----- pengu.slnx | 6 + 136 files changed, 42 insertions(+), 139 deletions(-) create mode 100644 app/.gitkeep delete mode 100644 core/core.vcxproj.filters rename makefile => core/makefile (89%) create mode 100644 package.json rename {loader => packages/hub}/.gitignore (100%) rename {loader => packages/hub}/.npmrc (100%) rename {loader => packages/hub}/.vscode/extensions.json (100%) rename {loader => packages/hub}/.vscode/settings.json (100%) rename {loader => packages/hub}/README.md (100%) rename {loader => packages/hub}/index.html (100%) rename {loader => packages/hub}/package.json (100%) rename {loader => packages/hub}/pnpm-lock.yaml (100%) rename {loader => packages/hub}/src-tauri/.gitignore (100%) rename {loader => packages/hub}/src-tauri/Cargo.lock (100%) rename {loader => packages/hub}/src-tauri/Cargo.toml (100%) rename {loader => packages/hub}/src-tauri/build.rs (100%) rename {loader => packages/hub}/src-tauri/icons/128x128.png (100%) rename {loader => packages/hub}/src-tauri/icons/128x128@2x.png (100%) rename {loader => packages/hub}/src-tauri/icons/32x32.png (100%) rename {loader => packages/hub}/src-tauri/icons/Square107x107Logo.png (100%) rename {loader => packages/hub}/src-tauri/icons/Square142x142Logo.png (100%) rename {loader => packages/hub}/src-tauri/icons/Square150x150Logo.png (100%) rename {loader => packages/hub}/src-tauri/icons/Square284x284Logo.png (100%) rename {loader => packages/hub}/src-tauri/icons/Square30x30Logo.png (100%) rename {loader => packages/hub}/src-tauri/icons/Square310x310Logo.png (100%) rename {loader => packages/hub}/src-tauri/icons/Square44x44Logo.png (100%) rename {loader => packages/hub}/src-tauri/icons/Square71x71Logo.png (100%) rename {loader => packages/hub}/src-tauri/icons/Square89x89Logo.png (100%) rename {loader => packages/hub}/src-tauri/icons/StoreLogo.png (100%) rename {loader => packages/hub}/src-tauri/icons/icon.icns (100%) rename {loader => packages/hub}/src-tauri/icons/icon.ico (100%) rename {loader => packages/hub}/src-tauri/icons/icon.png (100%) rename {loader => packages/hub}/src-tauri/src/config.rs (100%) rename {loader => packages/hub}/src-tauri/src/macos/core.rs (100%) rename {loader => packages/hub}/src-tauri/src/macos/dylib.rs (100%) rename {loader => packages/hub}/src-tauri/src/macos/mod.rs (100%) rename {loader => packages/hub}/src-tauri/src/macos/socket.rs (100%) rename {loader => packages/hub}/src-tauri/src/macos/startup.rs (100%) rename {loader => packages/hub}/src-tauri/src/macos/tray.rs (100%) rename {loader => packages/hub}/src-tauri/src/macos/utils.rs (100%) rename {loader => packages/hub}/src-tauri/src/macos/webview.rs (100%) rename {loader => packages/hub}/src-tauri/src/main.rs (100%) rename {loader => packages/hub}/src-tauri/src/shell.rs (100%) rename {loader => packages/hub}/src-tauri/src/windows/mod.rs (100%) rename {loader => packages/hub}/src-tauri/src/windows/mod_ifeo.rs (100%) rename {loader => packages/hub}/src-tauri/src/windows/mod_symlink.rs (100%) rename {loader => packages/hub}/src-tauri/src/windows/utils.rs (100%) rename {loader => packages/hub}/src-tauri/tauri.conf.json (100%) rename {loader => packages/hub}/src/App.css (100%) rename {loader => packages/hub}/src/App.tsx (100%) rename {loader => packages/hub}/src/assets/bg-mask.svg (100%) rename {loader => packages/hub}/src/assets/icon-sm.png (100%) rename {loader => packages/hub}/src/assets/icon.png (100%) rename {loader => packages/hub}/src/components/Activator.tsx (100%) rename {loader => packages/hub}/src/components/Appbar.tsx (100%) rename {loader => packages/hub}/src/components/Icons.tsx (100%) rename {loader => packages/hub}/src/components/PluginGallery.tsx (100%) rename {loader => packages/hub}/src/components/PluginStore.tsx (100%) rename {loader => packages/hub}/src/components/settings/Tab.About.tsx (100%) rename {loader => packages/hub}/src/components/settings/Tab.Client.tsx (100%) rename {loader => packages/hub}/src/components/settings/Tab.Pengu.tsx (100%) rename {loader => packages/hub}/src/components/settings/index.tsx (100%) rename {loader => packages/hub}/src/components/settings/templates.tsx (100%) rename {loader => packages/hub}/src/components/ui/Button.tsx (100%) rename {loader => packages/hub}/src/components/ui/Checkbox.tsx (100%) rename {loader => packages/hub}/src/components/ui/ComboBox.tsx (100%) rename {loader => packages/hub}/src/components/ui/RadioButton.tsx (100%) rename {loader => packages/hub}/src/components/ui/index.ts (100%) rename {loader => packages/hub}/src/index.tsx (100%) rename {loader => packages/hub}/src/lib/config.ts (100%) rename {loader => packages/hub}/src/lib/core-module.ts (100%) rename {loader => packages/hub}/src/lib/datatstore.ts (100%) rename {loader => packages/hub}/src/lib/i18n.ts (100%) rename {loader => packages/hub}/src/lib/league-client.ts (100%) rename {loader => packages/hub}/src/lib/plugins.ts (100%) rename {loader => packages/hub}/src/lib/root.ts (100%) rename {loader => packages/hub}/src/lib/shell.ts (100%) rename {loader => packages/hub}/src/lib/startup.ts (100%) rename {loader => packages/hub}/src/lib/store.ts (100%) rename {loader => packages/hub}/src/lib/utils.ts (100%) rename {loader => packages/hub}/src/pages/MainPage.tsx (100%) rename {loader => packages/hub}/src/pages/SplashPage.tsx (100%) rename {loader => packages/hub}/src/pages/WelcomePage.tsx (100%) rename {loader => packages/hub}/src/vite-env.d.ts (100%) rename {loader => packages/hub}/tailwind.config.ts (100%) rename {loader => packages/hub}/translations.json (100%) rename {loader => packages/hub}/tsconfig.json (100%) rename {loader => packages/hub}/tsconfig.node.json (100%) rename {loader => packages/hub}/vite.config.ts (100%) rename {plugins => packages/preload}/.gitignore (100%) rename {plugins => packages/preload}/README.md (100%) rename {plugins => packages/preload}/index.html (100%) rename {plugins => packages/preload}/package.json (100%) rename {plugins => packages/preload}/pnpm-lock.yaml (100%) rename {plugins => packages/preload}/src/index.ts (100%) rename {plugins => packages/preload}/src/preload/api/DataStore.ts (100%) rename {plugins => packages/preload}/src/preload/api/Effect.ts (100%) rename {plugins => packages/preload}/src/preload/api/index.ts (100%) rename {plugins => packages/preload}/src/preload/api/native.ts (100%) rename {plugins => packages/preload}/src/preload/index.ts (100%) rename {plugins => packages/preload}/src/preload/load-hooks.ts (100%) rename {plugins => packages/preload}/src/preload/loader.ts (100%) rename {plugins => packages/preload}/src/preload/polyfills.ts (100%) rename {plugins => packages/preload}/src/preload/rcp/hooks.ts (100%) rename {plugins => packages/preload}/src/preload/rcp/index.ts (100%) rename {plugins => packages/preload}/src/preload/rcp/socket.ts (100%) rename {plugins => packages/preload}/src/preload/super-potato.ts (100%) rename {plugins => packages/preload}/src/types.d.ts (100%) rename {plugins => packages/preload}/src/views/App.tsx (100%) rename {plugins => packages/preload}/src/views/assets/pengu.jpg (100%) rename {plugins => packages/preload}/src/views/assets/pengu2.jpg (100%) rename {plugins => packages/preload}/src/views/components/CommandBar/Animator.tsx (100%) rename {plugins => packages/preload}/src/views/components/CommandBar/SearchBar.tsx (100%) rename {plugins => packages/preload}/src/views/components/CommandBar/SearchItem.tsx (100%) rename {plugins => packages/preload}/src/views/components/CommandBar/SearchResults.tsx (100%) rename {plugins => packages/preload}/src/views/components/CommandBar/data.ts (100%) rename {plugins => packages/preload}/src/views/components/CommandBar/index.tsx (100%) rename {plugins => packages/preload}/src/views/components/CommandBar/root.ts (100%) rename {plugins => packages/preload}/src/views/components/CommandBar/utils.ts (100%) rename {plugins => packages/preload}/src/views/components/Toaster.tsx (100%) rename {plugins => packages/preload}/src/views/components/Welcome.tsx (100%) rename {plugins => packages/preload}/src/views/env.d.ts (100%) rename {plugins => packages/preload}/src/views/index.tsx (100%) rename {plugins => packages/preload}/src/views/lib/i18n.ts (100%) rename {plugins => packages/preload}/src/views/lib/updater.ts (100%) rename {plugins => packages/preload}/src/views/style.css (100%) rename {plugins => packages/preload}/src/views/trans.json (100%) rename {plugins => packages/preload}/tailwind.config.ts (100%) rename {plugins => packages/preload}/tsconfig.json (100%) rename {plugins => packages/preload}/twind.config.ts (100%) rename {plugins => packages/preload}/vite.config.ts (100%) create mode 100644 packages/types/.gitkeep delete mode 100644 pengu.sln create mode 100644 pengu.slnx diff --git a/.gitignore b/.gitignore index c73dd52f..a864b74e 100644 --- a/.gitignore +++ b/.gitignore @@ -230,9 +230,7 @@ PublishScripts/ # NuGet Symbol Packages *.snupkg # The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* # except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files @@ -456,6 +454,4 @@ Temporary Items ### Project defines ### bin/* -packages/ -include/ *.g.h \ No newline at end of file diff --git a/app/.gitkeep b/app/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/core/core.vcxproj b/core/core.vcxproj index c023ba61..3d82a756 100644 --- a/core/core.vcxproj +++ b/core/core.vcxproj @@ -50,6 +50,7 @@ Win32Proj core 10.0 + true diff --git a/core/core.vcxproj.filters b/core/core.vcxproj.filters deleted file mode 100644 index 7fdaef2b..00000000 --- a/core/core.vcxproj.filters +++ /dev/null @@ -1,104 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {f7fb3482-8bb9-4289-8727-31c4e42d8158} - - - {dfeda954-1c41-4771-8da2-c733492503fa} - - - {d97c7ba6-93de-4f95-81f6-e18b2c0ba005} - - - {e0b1c27d-cfeb-41e1-87e3-5f64c8ddc7fd} - - - - - src - - - src - - - src\browser - - - src\browser - - - src\browser - - - src\browser - - - src - - - src\renderer - - - src - - - src\browser - - - src\renderer - - - src\renderer - - - src\utils - - - src\utils - - - src\utils - - - src\utils - - - src\utils - - - - - res - - - src\utils - - - - - res - - - - - src - - - src\renderer - - - src\browser - - - src - - - src - - - \ No newline at end of file diff --git a/makefile b/core/makefile similarity index 89% rename from makefile rename to core/makefile index d4fb72a5..97da5f07 100644 --- a/makefile +++ b/core/makefile @@ -1,8 +1,8 @@ VERSION := 1.2.0 # Directories -SRC_DIR := core/src -BIN_DIR := bin +SRC_DIR := ./src +BIN_DIR := ../bin OBJ_DIR := $(BIN_DIR)/obj PLUGINS_DIR := $(BIN_DIR)/plugins @@ -23,12 +23,12 @@ TARGET_LIB_PATH := $(TARGET_LIB_DIR)/libEGL.dylib # Compiler CC := clang CXX := clang++ -CXXFLAGS := -std=c++20 -fPIC -arch x86_64 -I./core/cef -I./core/src -fvisibility=hidden -Wno-address-of-temporary -Wno-nonportable-include-path +CXXFLAGS := -std=c++20 -fPIC -arch x86_64 -I./cef -I./src -fvisibility=hidden -Wno-address-of-temporary -Wno-nonportable-include-path # Linker LDFLAGS := -shared -dynamiclib -arch x86_64 -current_version $(VERSION) -compatibility_version 1.0.0 -LDLIBS := -framework cocoa -Lcore/cef/lib/mac -weak-lcef.d -flat_namespace - +LDLIBS := -framework cocoa -L./cef/lib/mac -weak-lcef.d -flat_namespace + # Source files CPP_SRCS := $(wildcard $(SRC_DIR)/*.cc) $(wildcard $(SRC_DIR)/**/*.cc) OBJCXX_SRCS := $(wildcard $(SRC_DIR)/**/*.mm) @@ -67,7 +67,7 @@ $(LIB_OUT_PATH): $(CPP_OBJS) $(OBJCXX_OBJS) # @install_name_tool -change "libcef.d.dylib" "@loader_path/../Chromium Embedded Framework" $@ $(INSERT_DYLIB_PATH): - $(CC) -O2 -o $@ core/insert_dylib.c + $(CC) -O2 -o $@ ./insert_dylib.c install: $(LIB_OUT_PATH) $(INSERT_DYLIB_PATH) cp -n $(TARGET_LIB_PATH) $(TARGET_LIB_PATH).bak || true diff --git a/package.json b/package.json new file mode 100644 index 00000000..9f36ca21 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "pengu-loader", + "version": "1.2.0", + "description": "The ultimate JavaScript plugin loader", + "license": "MIT", + "private": true, + "type": "module", + "workspaces": [ + "packages/hub", + "packages/preload", + "packages/types" + ], + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/PenguLoader/PenguLoader.git" + }, + "keywords": [ + "league-of-legends", + "league-client", + "plugin-loader" + ], + "bugs": { + "url": "https://github.com/PenguLoader/PenguLoader/issues" + }, + "homepage": "https://github.com/PenguLoader/PenguLoader#readme" +} \ No newline at end of file diff --git a/loader/.gitignore b/packages/hub/.gitignore similarity index 100% rename from loader/.gitignore rename to packages/hub/.gitignore diff --git a/loader/.npmrc b/packages/hub/.npmrc similarity index 100% rename from loader/.npmrc rename to packages/hub/.npmrc diff --git a/loader/.vscode/extensions.json b/packages/hub/.vscode/extensions.json similarity index 100% rename from loader/.vscode/extensions.json rename to packages/hub/.vscode/extensions.json diff --git a/loader/.vscode/settings.json b/packages/hub/.vscode/settings.json similarity index 100% rename from loader/.vscode/settings.json rename to packages/hub/.vscode/settings.json diff --git a/loader/README.md b/packages/hub/README.md similarity index 100% rename from loader/README.md rename to packages/hub/README.md diff --git a/loader/index.html b/packages/hub/index.html similarity index 100% rename from loader/index.html rename to packages/hub/index.html diff --git a/loader/package.json b/packages/hub/package.json similarity index 100% rename from loader/package.json rename to packages/hub/package.json diff --git a/loader/pnpm-lock.yaml b/packages/hub/pnpm-lock.yaml similarity index 100% rename from loader/pnpm-lock.yaml rename to packages/hub/pnpm-lock.yaml diff --git a/loader/src-tauri/.gitignore b/packages/hub/src-tauri/.gitignore similarity index 100% rename from loader/src-tauri/.gitignore rename to packages/hub/src-tauri/.gitignore diff --git a/loader/src-tauri/Cargo.lock b/packages/hub/src-tauri/Cargo.lock similarity index 100% rename from loader/src-tauri/Cargo.lock rename to packages/hub/src-tauri/Cargo.lock diff --git a/loader/src-tauri/Cargo.toml b/packages/hub/src-tauri/Cargo.toml similarity index 100% rename from loader/src-tauri/Cargo.toml rename to packages/hub/src-tauri/Cargo.toml diff --git a/loader/src-tauri/build.rs b/packages/hub/src-tauri/build.rs similarity index 100% rename from loader/src-tauri/build.rs rename to packages/hub/src-tauri/build.rs diff --git a/loader/src-tauri/icons/128x128.png b/packages/hub/src-tauri/icons/128x128.png similarity index 100% rename from loader/src-tauri/icons/128x128.png rename to packages/hub/src-tauri/icons/128x128.png diff --git a/loader/src-tauri/icons/128x128@2x.png b/packages/hub/src-tauri/icons/128x128@2x.png similarity index 100% rename from loader/src-tauri/icons/128x128@2x.png rename to packages/hub/src-tauri/icons/128x128@2x.png diff --git a/loader/src-tauri/icons/32x32.png b/packages/hub/src-tauri/icons/32x32.png similarity index 100% rename from loader/src-tauri/icons/32x32.png rename to packages/hub/src-tauri/icons/32x32.png diff --git a/loader/src-tauri/icons/Square107x107Logo.png b/packages/hub/src-tauri/icons/Square107x107Logo.png similarity index 100% rename from loader/src-tauri/icons/Square107x107Logo.png rename to packages/hub/src-tauri/icons/Square107x107Logo.png diff --git a/loader/src-tauri/icons/Square142x142Logo.png b/packages/hub/src-tauri/icons/Square142x142Logo.png similarity index 100% rename from loader/src-tauri/icons/Square142x142Logo.png rename to packages/hub/src-tauri/icons/Square142x142Logo.png diff --git a/loader/src-tauri/icons/Square150x150Logo.png b/packages/hub/src-tauri/icons/Square150x150Logo.png similarity index 100% rename from loader/src-tauri/icons/Square150x150Logo.png rename to packages/hub/src-tauri/icons/Square150x150Logo.png diff --git a/loader/src-tauri/icons/Square284x284Logo.png b/packages/hub/src-tauri/icons/Square284x284Logo.png similarity index 100% rename from loader/src-tauri/icons/Square284x284Logo.png rename to packages/hub/src-tauri/icons/Square284x284Logo.png diff --git a/loader/src-tauri/icons/Square30x30Logo.png b/packages/hub/src-tauri/icons/Square30x30Logo.png similarity index 100% rename from loader/src-tauri/icons/Square30x30Logo.png rename to packages/hub/src-tauri/icons/Square30x30Logo.png diff --git a/loader/src-tauri/icons/Square310x310Logo.png b/packages/hub/src-tauri/icons/Square310x310Logo.png similarity index 100% rename from loader/src-tauri/icons/Square310x310Logo.png rename to packages/hub/src-tauri/icons/Square310x310Logo.png diff --git a/loader/src-tauri/icons/Square44x44Logo.png b/packages/hub/src-tauri/icons/Square44x44Logo.png similarity index 100% rename from loader/src-tauri/icons/Square44x44Logo.png rename to packages/hub/src-tauri/icons/Square44x44Logo.png diff --git a/loader/src-tauri/icons/Square71x71Logo.png b/packages/hub/src-tauri/icons/Square71x71Logo.png similarity index 100% rename from loader/src-tauri/icons/Square71x71Logo.png rename to packages/hub/src-tauri/icons/Square71x71Logo.png diff --git a/loader/src-tauri/icons/Square89x89Logo.png b/packages/hub/src-tauri/icons/Square89x89Logo.png similarity index 100% rename from loader/src-tauri/icons/Square89x89Logo.png rename to packages/hub/src-tauri/icons/Square89x89Logo.png diff --git a/loader/src-tauri/icons/StoreLogo.png b/packages/hub/src-tauri/icons/StoreLogo.png similarity index 100% rename from loader/src-tauri/icons/StoreLogo.png rename to packages/hub/src-tauri/icons/StoreLogo.png diff --git a/loader/src-tauri/icons/icon.icns b/packages/hub/src-tauri/icons/icon.icns similarity index 100% rename from loader/src-tauri/icons/icon.icns rename to packages/hub/src-tauri/icons/icon.icns diff --git a/loader/src-tauri/icons/icon.ico b/packages/hub/src-tauri/icons/icon.ico similarity index 100% rename from loader/src-tauri/icons/icon.ico rename to packages/hub/src-tauri/icons/icon.ico diff --git a/loader/src-tauri/icons/icon.png b/packages/hub/src-tauri/icons/icon.png similarity index 100% rename from loader/src-tauri/icons/icon.png rename to packages/hub/src-tauri/icons/icon.png diff --git a/loader/src-tauri/src/config.rs b/packages/hub/src-tauri/src/config.rs similarity index 100% rename from loader/src-tauri/src/config.rs rename to packages/hub/src-tauri/src/config.rs diff --git a/loader/src-tauri/src/macos/core.rs b/packages/hub/src-tauri/src/macos/core.rs similarity index 100% rename from loader/src-tauri/src/macos/core.rs rename to packages/hub/src-tauri/src/macos/core.rs diff --git a/loader/src-tauri/src/macos/dylib.rs b/packages/hub/src-tauri/src/macos/dylib.rs similarity index 100% rename from loader/src-tauri/src/macos/dylib.rs rename to packages/hub/src-tauri/src/macos/dylib.rs diff --git a/loader/src-tauri/src/macos/mod.rs b/packages/hub/src-tauri/src/macos/mod.rs similarity index 100% rename from loader/src-tauri/src/macos/mod.rs rename to packages/hub/src-tauri/src/macos/mod.rs diff --git a/loader/src-tauri/src/macos/socket.rs b/packages/hub/src-tauri/src/macos/socket.rs similarity index 100% rename from loader/src-tauri/src/macos/socket.rs rename to packages/hub/src-tauri/src/macos/socket.rs diff --git a/loader/src-tauri/src/macos/startup.rs b/packages/hub/src-tauri/src/macos/startup.rs similarity index 100% rename from loader/src-tauri/src/macos/startup.rs rename to packages/hub/src-tauri/src/macos/startup.rs diff --git a/loader/src-tauri/src/macos/tray.rs b/packages/hub/src-tauri/src/macos/tray.rs similarity index 100% rename from loader/src-tauri/src/macos/tray.rs rename to packages/hub/src-tauri/src/macos/tray.rs diff --git a/loader/src-tauri/src/macos/utils.rs b/packages/hub/src-tauri/src/macos/utils.rs similarity index 100% rename from loader/src-tauri/src/macos/utils.rs rename to packages/hub/src-tauri/src/macos/utils.rs diff --git a/loader/src-tauri/src/macos/webview.rs b/packages/hub/src-tauri/src/macos/webview.rs similarity index 100% rename from loader/src-tauri/src/macos/webview.rs rename to packages/hub/src-tauri/src/macos/webview.rs diff --git a/loader/src-tauri/src/main.rs b/packages/hub/src-tauri/src/main.rs similarity index 100% rename from loader/src-tauri/src/main.rs rename to packages/hub/src-tauri/src/main.rs diff --git a/loader/src-tauri/src/shell.rs b/packages/hub/src-tauri/src/shell.rs similarity index 100% rename from loader/src-tauri/src/shell.rs rename to packages/hub/src-tauri/src/shell.rs diff --git a/loader/src-tauri/src/windows/mod.rs b/packages/hub/src-tauri/src/windows/mod.rs similarity index 100% rename from loader/src-tauri/src/windows/mod.rs rename to packages/hub/src-tauri/src/windows/mod.rs diff --git a/loader/src-tauri/src/windows/mod_ifeo.rs b/packages/hub/src-tauri/src/windows/mod_ifeo.rs similarity index 100% rename from loader/src-tauri/src/windows/mod_ifeo.rs rename to packages/hub/src-tauri/src/windows/mod_ifeo.rs diff --git a/loader/src-tauri/src/windows/mod_symlink.rs b/packages/hub/src-tauri/src/windows/mod_symlink.rs similarity index 100% rename from loader/src-tauri/src/windows/mod_symlink.rs rename to packages/hub/src-tauri/src/windows/mod_symlink.rs diff --git a/loader/src-tauri/src/windows/utils.rs b/packages/hub/src-tauri/src/windows/utils.rs similarity index 100% rename from loader/src-tauri/src/windows/utils.rs rename to packages/hub/src-tauri/src/windows/utils.rs diff --git a/loader/src-tauri/tauri.conf.json b/packages/hub/src-tauri/tauri.conf.json similarity index 100% rename from loader/src-tauri/tauri.conf.json rename to packages/hub/src-tauri/tauri.conf.json diff --git a/loader/src/App.css b/packages/hub/src/App.css similarity index 100% rename from loader/src/App.css rename to packages/hub/src/App.css diff --git a/loader/src/App.tsx b/packages/hub/src/App.tsx similarity index 100% rename from loader/src/App.tsx rename to packages/hub/src/App.tsx diff --git a/loader/src/assets/bg-mask.svg b/packages/hub/src/assets/bg-mask.svg similarity index 100% rename from loader/src/assets/bg-mask.svg rename to packages/hub/src/assets/bg-mask.svg diff --git a/loader/src/assets/icon-sm.png b/packages/hub/src/assets/icon-sm.png similarity index 100% rename from loader/src/assets/icon-sm.png rename to packages/hub/src/assets/icon-sm.png diff --git a/loader/src/assets/icon.png b/packages/hub/src/assets/icon.png similarity index 100% rename from loader/src/assets/icon.png rename to packages/hub/src/assets/icon.png diff --git a/loader/src/components/Activator.tsx b/packages/hub/src/components/Activator.tsx similarity index 100% rename from loader/src/components/Activator.tsx rename to packages/hub/src/components/Activator.tsx diff --git a/loader/src/components/Appbar.tsx b/packages/hub/src/components/Appbar.tsx similarity index 100% rename from loader/src/components/Appbar.tsx rename to packages/hub/src/components/Appbar.tsx diff --git a/loader/src/components/Icons.tsx b/packages/hub/src/components/Icons.tsx similarity index 100% rename from loader/src/components/Icons.tsx rename to packages/hub/src/components/Icons.tsx diff --git a/loader/src/components/PluginGallery.tsx b/packages/hub/src/components/PluginGallery.tsx similarity index 100% rename from loader/src/components/PluginGallery.tsx rename to packages/hub/src/components/PluginGallery.tsx diff --git a/loader/src/components/PluginStore.tsx b/packages/hub/src/components/PluginStore.tsx similarity index 100% rename from loader/src/components/PluginStore.tsx rename to packages/hub/src/components/PluginStore.tsx diff --git a/loader/src/components/settings/Tab.About.tsx b/packages/hub/src/components/settings/Tab.About.tsx similarity index 100% rename from loader/src/components/settings/Tab.About.tsx rename to packages/hub/src/components/settings/Tab.About.tsx diff --git a/loader/src/components/settings/Tab.Client.tsx b/packages/hub/src/components/settings/Tab.Client.tsx similarity index 100% rename from loader/src/components/settings/Tab.Client.tsx rename to packages/hub/src/components/settings/Tab.Client.tsx diff --git a/loader/src/components/settings/Tab.Pengu.tsx b/packages/hub/src/components/settings/Tab.Pengu.tsx similarity index 100% rename from loader/src/components/settings/Tab.Pengu.tsx rename to packages/hub/src/components/settings/Tab.Pengu.tsx diff --git a/loader/src/components/settings/index.tsx b/packages/hub/src/components/settings/index.tsx similarity index 100% rename from loader/src/components/settings/index.tsx rename to packages/hub/src/components/settings/index.tsx diff --git a/loader/src/components/settings/templates.tsx b/packages/hub/src/components/settings/templates.tsx similarity index 100% rename from loader/src/components/settings/templates.tsx rename to packages/hub/src/components/settings/templates.tsx diff --git a/loader/src/components/ui/Button.tsx b/packages/hub/src/components/ui/Button.tsx similarity index 100% rename from loader/src/components/ui/Button.tsx rename to packages/hub/src/components/ui/Button.tsx diff --git a/loader/src/components/ui/Checkbox.tsx b/packages/hub/src/components/ui/Checkbox.tsx similarity index 100% rename from loader/src/components/ui/Checkbox.tsx rename to packages/hub/src/components/ui/Checkbox.tsx diff --git a/loader/src/components/ui/ComboBox.tsx b/packages/hub/src/components/ui/ComboBox.tsx similarity index 100% rename from loader/src/components/ui/ComboBox.tsx rename to packages/hub/src/components/ui/ComboBox.tsx diff --git a/loader/src/components/ui/RadioButton.tsx b/packages/hub/src/components/ui/RadioButton.tsx similarity index 100% rename from loader/src/components/ui/RadioButton.tsx rename to packages/hub/src/components/ui/RadioButton.tsx diff --git a/loader/src/components/ui/index.ts b/packages/hub/src/components/ui/index.ts similarity index 100% rename from loader/src/components/ui/index.ts rename to packages/hub/src/components/ui/index.ts diff --git a/loader/src/index.tsx b/packages/hub/src/index.tsx similarity index 100% rename from loader/src/index.tsx rename to packages/hub/src/index.tsx diff --git a/loader/src/lib/config.ts b/packages/hub/src/lib/config.ts similarity index 100% rename from loader/src/lib/config.ts rename to packages/hub/src/lib/config.ts diff --git a/loader/src/lib/core-module.ts b/packages/hub/src/lib/core-module.ts similarity index 100% rename from loader/src/lib/core-module.ts rename to packages/hub/src/lib/core-module.ts diff --git a/loader/src/lib/datatstore.ts b/packages/hub/src/lib/datatstore.ts similarity index 100% rename from loader/src/lib/datatstore.ts rename to packages/hub/src/lib/datatstore.ts diff --git a/loader/src/lib/i18n.ts b/packages/hub/src/lib/i18n.ts similarity index 100% rename from loader/src/lib/i18n.ts rename to packages/hub/src/lib/i18n.ts diff --git a/loader/src/lib/league-client.ts b/packages/hub/src/lib/league-client.ts similarity index 100% rename from loader/src/lib/league-client.ts rename to packages/hub/src/lib/league-client.ts diff --git a/loader/src/lib/plugins.ts b/packages/hub/src/lib/plugins.ts similarity index 100% rename from loader/src/lib/plugins.ts rename to packages/hub/src/lib/plugins.ts diff --git a/loader/src/lib/root.ts b/packages/hub/src/lib/root.ts similarity index 100% rename from loader/src/lib/root.ts rename to packages/hub/src/lib/root.ts diff --git a/loader/src/lib/shell.ts b/packages/hub/src/lib/shell.ts similarity index 100% rename from loader/src/lib/shell.ts rename to packages/hub/src/lib/shell.ts diff --git a/loader/src/lib/startup.ts b/packages/hub/src/lib/startup.ts similarity index 100% rename from loader/src/lib/startup.ts rename to packages/hub/src/lib/startup.ts diff --git a/loader/src/lib/store.ts b/packages/hub/src/lib/store.ts similarity index 100% rename from loader/src/lib/store.ts rename to packages/hub/src/lib/store.ts diff --git a/loader/src/lib/utils.ts b/packages/hub/src/lib/utils.ts similarity index 100% rename from loader/src/lib/utils.ts rename to packages/hub/src/lib/utils.ts diff --git a/loader/src/pages/MainPage.tsx b/packages/hub/src/pages/MainPage.tsx similarity index 100% rename from loader/src/pages/MainPage.tsx rename to packages/hub/src/pages/MainPage.tsx diff --git a/loader/src/pages/SplashPage.tsx b/packages/hub/src/pages/SplashPage.tsx similarity index 100% rename from loader/src/pages/SplashPage.tsx rename to packages/hub/src/pages/SplashPage.tsx diff --git a/loader/src/pages/WelcomePage.tsx b/packages/hub/src/pages/WelcomePage.tsx similarity index 100% rename from loader/src/pages/WelcomePage.tsx rename to packages/hub/src/pages/WelcomePage.tsx diff --git a/loader/src/vite-env.d.ts b/packages/hub/src/vite-env.d.ts similarity index 100% rename from loader/src/vite-env.d.ts rename to packages/hub/src/vite-env.d.ts diff --git a/loader/tailwind.config.ts b/packages/hub/tailwind.config.ts similarity index 100% rename from loader/tailwind.config.ts rename to packages/hub/tailwind.config.ts diff --git a/loader/translations.json b/packages/hub/translations.json similarity index 100% rename from loader/translations.json rename to packages/hub/translations.json diff --git a/loader/tsconfig.json b/packages/hub/tsconfig.json similarity index 100% rename from loader/tsconfig.json rename to packages/hub/tsconfig.json diff --git a/loader/tsconfig.node.json b/packages/hub/tsconfig.node.json similarity index 100% rename from loader/tsconfig.node.json rename to packages/hub/tsconfig.node.json diff --git a/loader/vite.config.ts b/packages/hub/vite.config.ts similarity index 100% rename from loader/vite.config.ts rename to packages/hub/vite.config.ts diff --git a/plugins/.gitignore b/packages/preload/.gitignore similarity index 100% rename from plugins/.gitignore rename to packages/preload/.gitignore diff --git a/plugins/README.md b/packages/preload/README.md similarity index 100% rename from plugins/README.md rename to packages/preload/README.md diff --git a/plugins/index.html b/packages/preload/index.html similarity index 100% rename from plugins/index.html rename to packages/preload/index.html diff --git a/plugins/package.json b/packages/preload/package.json similarity index 100% rename from plugins/package.json rename to packages/preload/package.json diff --git a/plugins/pnpm-lock.yaml b/packages/preload/pnpm-lock.yaml similarity index 100% rename from plugins/pnpm-lock.yaml rename to packages/preload/pnpm-lock.yaml diff --git a/plugins/src/index.ts b/packages/preload/src/index.ts similarity index 100% rename from plugins/src/index.ts rename to packages/preload/src/index.ts diff --git a/plugins/src/preload/api/DataStore.ts b/packages/preload/src/preload/api/DataStore.ts similarity index 100% rename from plugins/src/preload/api/DataStore.ts rename to packages/preload/src/preload/api/DataStore.ts diff --git a/plugins/src/preload/api/Effect.ts b/packages/preload/src/preload/api/Effect.ts similarity index 100% rename from plugins/src/preload/api/Effect.ts rename to packages/preload/src/preload/api/Effect.ts diff --git a/plugins/src/preload/api/index.ts b/packages/preload/src/preload/api/index.ts similarity index 100% rename from plugins/src/preload/api/index.ts rename to packages/preload/src/preload/api/index.ts diff --git a/plugins/src/preload/api/native.ts b/packages/preload/src/preload/api/native.ts similarity index 100% rename from plugins/src/preload/api/native.ts rename to packages/preload/src/preload/api/native.ts diff --git a/plugins/src/preload/index.ts b/packages/preload/src/preload/index.ts similarity index 100% rename from plugins/src/preload/index.ts rename to packages/preload/src/preload/index.ts diff --git a/plugins/src/preload/load-hooks.ts b/packages/preload/src/preload/load-hooks.ts similarity index 100% rename from plugins/src/preload/load-hooks.ts rename to packages/preload/src/preload/load-hooks.ts diff --git a/plugins/src/preload/loader.ts b/packages/preload/src/preload/loader.ts similarity index 100% rename from plugins/src/preload/loader.ts rename to packages/preload/src/preload/loader.ts diff --git a/plugins/src/preload/polyfills.ts b/packages/preload/src/preload/polyfills.ts similarity index 100% rename from plugins/src/preload/polyfills.ts rename to packages/preload/src/preload/polyfills.ts diff --git a/plugins/src/preload/rcp/hooks.ts b/packages/preload/src/preload/rcp/hooks.ts similarity index 100% rename from plugins/src/preload/rcp/hooks.ts rename to packages/preload/src/preload/rcp/hooks.ts diff --git a/plugins/src/preload/rcp/index.ts b/packages/preload/src/preload/rcp/index.ts similarity index 100% rename from plugins/src/preload/rcp/index.ts rename to packages/preload/src/preload/rcp/index.ts diff --git a/plugins/src/preload/rcp/socket.ts b/packages/preload/src/preload/rcp/socket.ts similarity index 100% rename from plugins/src/preload/rcp/socket.ts rename to packages/preload/src/preload/rcp/socket.ts diff --git a/plugins/src/preload/super-potato.ts b/packages/preload/src/preload/super-potato.ts similarity index 100% rename from plugins/src/preload/super-potato.ts rename to packages/preload/src/preload/super-potato.ts diff --git a/plugins/src/types.d.ts b/packages/preload/src/types.d.ts similarity index 100% rename from plugins/src/types.d.ts rename to packages/preload/src/types.d.ts diff --git a/plugins/src/views/App.tsx b/packages/preload/src/views/App.tsx similarity index 100% rename from plugins/src/views/App.tsx rename to packages/preload/src/views/App.tsx diff --git a/plugins/src/views/assets/pengu.jpg b/packages/preload/src/views/assets/pengu.jpg similarity index 100% rename from plugins/src/views/assets/pengu.jpg rename to packages/preload/src/views/assets/pengu.jpg diff --git a/plugins/src/views/assets/pengu2.jpg b/packages/preload/src/views/assets/pengu2.jpg similarity index 100% rename from plugins/src/views/assets/pengu2.jpg rename to packages/preload/src/views/assets/pengu2.jpg diff --git a/plugins/src/views/components/CommandBar/Animator.tsx b/packages/preload/src/views/components/CommandBar/Animator.tsx similarity index 100% rename from plugins/src/views/components/CommandBar/Animator.tsx rename to packages/preload/src/views/components/CommandBar/Animator.tsx diff --git a/plugins/src/views/components/CommandBar/SearchBar.tsx b/packages/preload/src/views/components/CommandBar/SearchBar.tsx similarity index 100% rename from plugins/src/views/components/CommandBar/SearchBar.tsx rename to packages/preload/src/views/components/CommandBar/SearchBar.tsx diff --git a/plugins/src/views/components/CommandBar/SearchItem.tsx b/packages/preload/src/views/components/CommandBar/SearchItem.tsx similarity index 100% rename from plugins/src/views/components/CommandBar/SearchItem.tsx rename to packages/preload/src/views/components/CommandBar/SearchItem.tsx diff --git a/plugins/src/views/components/CommandBar/SearchResults.tsx b/packages/preload/src/views/components/CommandBar/SearchResults.tsx similarity index 100% rename from plugins/src/views/components/CommandBar/SearchResults.tsx rename to packages/preload/src/views/components/CommandBar/SearchResults.tsx diff --git a/plugins/src/views/components/CommandBar/data.ts b/packages/preload/src/views/components/CommandBar/data.ts similarity index 100% rename from plugins/src/views/components/CommandBar/data.ts rename to packages/preload/src/views/components/CommandBar/data.ts diff --git a/plugins/src/views/components/CommandBar/index.tsx b/packages/preload/src/views/components/CommandBar/index.tsx similarity index 100% rename from plugins/src/views/components/CommandBar/index.tsx rename to packages/preload/src/views/components/CommandBar/index.tsx diff --git a/plugins/src/views/components/CommandBar/root.ts b/packages/preload/src/views/components/CommandBar/root.ts similarity index 100% rename from plugins/src/views/components/CommandBar/root.ts rename to packages/preload/src/views/components/CommandBar/root.ts diff --git a/plugins/src/views/components/CommandBar/utils.ts b/packages/preload/src/views/components/CommandBar/utils.ts similarity index 100% rename from plugins/src/views/components/CommandBar/utils.ts rename to packages/preload/src/views/components/CommandBar/utils.ts diff --git a/plugins/src/views/components/Toaster.tsx b/packages/preload/src/views/components/Toaster.tsx similarity index 100% rename from plugins/src/views/components/Toaster.tsx rename to packages/preload/src/views/components/Toaster.tsx diff --git a/plugins/src/views/components/Welcome.tsx b/packages/preload/src/views/components/Welcome.tsx similarity index 100% rename from plugins/src/views/components/Welcome.tsx rename to packages/preload/src/views/components/Welcome.tsx diff --git a/plugins/src/views/env.d.ts b/packages/preload/src/views/env.d.ts similarity index 100% rename from plugins/src/views/env.d.ts rename to packages/preload/src/views/env.d.ts diff --git a/plugins/src/views/index.tsx b/packages/preload/src/views/index.tsx similarity index 100% rename from plugins/src/views/index.tsx rename to packages/preload/src/views/index.tsx diff --git a/plugins/src/views/lib/i18n.ts b/packages/preload/src/views/lib/i18n.ts similarity index 100% rename from plugins/src/views/lib/i18n.ts rename to packages/preload/src/views/lib/i18n.ts diff --git a/plugins/src/views/lib/updater.ts b/packages/preload/src/views/lib/updater.ts similarity index 100% rename from plugins/src/views/lib/updater.ts rename to packages/preload/src/views/lib/updater.ts diff --git a/plugins/src/views/style.css b/packages/preload/src/views/style.css similarity index 100% rename from plugins/src/views/style.css rename to packages/preload/src/views/style.css diff --git a/plugins/src/views/trans.json b/packages/preload/src/views/trans.json similarity index 100% rename from plugins/src/views/trans.json rename to packages/preload/src/views/trans.json diff --git a/plugins/tailwind.config.ts b/packages/preload/tailwind.config.ts similarity index 100% rename from plugins/tailwind.config.ts rename to packages/preload/tailwind.config.ts diff --git a/plugins/tsconfig.json b/packages/preload/tsconfig.json similarity index 100% rename from plugins/tsconfig.json rename to packages/preload/tsconfig.json diff --git a/plugins/twind.config.ts b/packages/preload/twind.config.ts similarity index 100% rename from plugins/twind.config.ts rename to packages/preload/twind.config.ts diff --git a/plugins/vite.config.ts b/packages/preload/vite.config.ts similarity index 100% rename from plugins/vite.config.ts rename to packages/preload/vite.config.ts diff --git a/packages/types/.gitkeep b/packages/types/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/pengu.sln b/pengu.sln deleted file mode 100644 index 0741c334..00000000 --- a/pengu.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.8.34322.80 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core", "core\core.vcxproj", "{0C5BB758-5A09-4D96-BBBC-5F03D3512B58}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0C5BB758-5A09-4D96-BBBC-5F03D3512B58}.Debug|x64.ActiveCfg = Debug|x64 - {0C5BB758-5A09-4D96-BBBC-5F03D3512B58}.Debug|x64.Build.0 = Debug|x64 - {0C5BB758-5A09-4D96-BBBC-5F03D3512B58}.Release|x64.ActiveCfg = Release|x64 - {0C5BB758-5A09-4D96-BBBC-5F03D3512B58}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {99AC0486-E5EC-495B-93FA-E8F0D758D065} - EndGlobalSection -EndGlobal diff --git a/pengu.slnx b/pengu.slnx new file mode 100644 index 00000000..8eac3a51 --- /dev/null +++ b/pengu.slnx @@ -0,0 +1,6 @@ + + + + + + From cfedf7ba33344d0f683ae42687a76261a5aef8dd Mon Sep 17 00:00:00 2001 From: Nguyen Duy Date: Wed, 6 May 2026 17:50:33 +0700 Subject: [PATCH 02/13] docs: add design doc for the pengu architecture --- docs/design.md | 580 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 580 insertions(+) create mode 100644 docs/design.md diff --git a/docs/design.md b/docs/design.md new file mode 100644 index 00000000..df3e7f9d --- /dev/null +++ b/docs/design.md @@ -0,0 +1,580 @@ +# Pengu Loader — Design + +This document describes the architecture of Pengu Loader as it stands on the `main` branch (v1.2.0, unreleased). It supersedes the earlier `HOW_IT_WORKS.md` (last updated for v0.6.0) and is written for contributors to Pengu itself; it also covers enough of the runtime surface to be useful to plugin authors. + +> **Version map.** Production today still ships **v1.1.6**, a Windows-only WPF/.NET Framework loader that uses IFEO injection and disables plugins by renaming `index.js` to `index.js_`. **v1.2.0** is the unreleased cross-platform rewrite documented here. The Tauri-based loader UI in [`packages/hub/`](../packages/hub/) is itself transitional — the planned final form is a from-scratch .NET 10 host (WebView2 on Windows, WKWebView on macOS) living in [`app/`](../app/), at which point `packages/hub/` will be reduced to the SolidJS frontend it serves. + +--- + +## 1. Overview + +Pengu Loader is a plugin loader for the **League of Legends Client UX** (LCUX), a CEF (Chromium Embedded Framework) based desktop app. It loads JavaScript plugins from disk into the running LCUX renderer, exposes a small native API surface to those plugins, and adds quality-of-life hooks (DevTools, transparency, hotkeys, super-potato mode, etc.). + +The project is split into three components: + +| Component | Path | Language | Role | +| --- | --- | --- | --- | +| **Core** | [`core/`](../core/) | C++20 | Native module loaded into LCUX's CEF processes (browser + renderer). Owns every CEF, libcef, and OS-level hook. | +| **Preload** | [`packages/preload/`](../packages/preload/) | TypeScript (Vite/IIFE) | A single bundled script embedded into the core. Runs inside the renderer's V8 context to bootstrap the plugin runtime, expose JS APIs, and load user plugins. | +| **Loader** | [`packages/hub/`](../packages/hub/) (Tauri, transitional) → [`app/`](../app/) (.NET 10, planned) | Rust + SolidJS today | A standalone GUI app that installs/uninstalls the core, manages plugins, and watches for LCUX launches on macOS. **Not loaded into LCUX itself.** | + +The core is the only component that actually runs inside LCUX. The preload is *embedded* into the core at build time and executed as JavaScript in the main renderer's V8 context. The loader is just an installer/manager. + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Riot Client Services (RCS) │ +│ spawns LCUX with --app-port= --app-token= │ +└────────────────┬────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ LeagueClientUx.exe (CEF browser process) │ +│ ╔═══════════════════════════════════════════════════════╗ │ +│ ║ core.{dll,dylib} loaded via IFEO / symlink / dylib ║ │ +│ ║ • hooks libcef exports (cef_initialize, ...) ║ │ +│ ║ • registers https://plugins/ and https://riotclient ║ │ +│ ║ • injects itself into renderer children ║ │ +│ ║ • window manipulation + transparency ║ │ +│ ║ • DevTools, hotkeys, IPC ║ │ +│ ╚═══════════════════════════════════════════════════════╝ │ +└────────────────┬────────────────────────────────────────────┘ + │ spawns + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ LeagueClientUxRender.exe --type=renderer │ +│ ╔═══════════════════════════════════════════════════════╗ │ +│ ║ same core module (injected via CreateProcessW hook) ║ │ +│ ║ • hooks cef_execute_process → OnContextCreated ║ │ +│ ║ • exposes window.Pengu, window.__native, window.os ║ │ +│ ║ • runs embedded preload.js ║ │ +│ ║ → loads each plugin via ║ │ +│ ║ import('https://plugins/') ║ │ +│ ╚═══════════════════════════════════════════════════════╝ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 2. Core module + +### 2.1 Process targeting + +The core is a single shared library (`core.dll` on Windows, `core.dylib` on macOS) that handles both CEF roles by inspecting the host executable name on load. + +In [`dllmain.cc`](../core/src/dllmain.cc): + +- If the host exe name contains `LeagueClientUx.exe` (Windows) or is `LeagueClientUx` (macOS), `HookBrowserProcess()` runs. +- If the host is `LeagueClientUxRender.exe` (Windows) or `LeagueClientUx Helper (Renderer)` (macOS) **and** the command line includes `--type=renderer`, `HookRendererProcess()` runs. +- Anything else (sandbox helpers, GPU helpers, crashpad) is left alone. + +On Windows the browser process additionally hooks `CreateProcessW` to detect renderer children being spawned. When a `LeagueClientUxRender.exe ... --type=renderer` is created, the hook: + +1. Adds `CREATE_SUSPENDED` to the creation flags. +2. After the call returns, uses `VirtualAllocEx` + `WriteProcessMemory` + `CreateRemoteThread` with `LoadLibraryW` to inject the core DLL into the child. +3. Resumes the thread. + +This is needed because IFEO only fires on the parent process — children don't inherit it. The renderer DLL has to be injected explicitly. + +On macOS the dylib injection happens once into `libEGL.dylib` inside the CEF framework (see [§2.3](#23-activation-paths) below); CEF then loads the same dylib into both browser and renderer processes naturally, so no `CreateProcessW`-equivalent is needed. + +### 2.2 CEF binding style — no libcef_wrapper + +Pengu uses CEF's **C API** (`cef_*_t` structs of function pointers) directly rather than the `libcef_wrapper` C++ wrapper. The reasons are documented in [`pengu.h:32-75`](../core/src/pengu.h#L32-L75): + +- Performance: avoids the wrapper's virtual-call layer. +- Hooking: function pointers in C structs are easier to swap than vtable methods. +- Distribution: no need to ship `libcef_wrapper.lib` and match its compiler/runtime. + +The `cef_bind_method` macro and `CefRefCount` template in [`pengu.h`](../core/src/pengu.h) let C++ classes plug member functions straight into C-API function-pointer slots, with a `static_assert` to catch ABI drift when CEF headers change. + +### 2.3 Activation paths + +How does `core.dll/dylib` get loaded into LCUX in the first place? Three modes, exposed in the loader UI as `Universal`, `Targeted`, `OnDemand`: + +#### Windows — IFEO (Universal, default) + +[`packages/hub/src-tauri/src/windows/mod_ifeo.rs`](../packages/hub/src-tauri/src/windows/mod_ifeo.rs) writes a registry value: + +``` +HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\ + LeagueClientUx.exe\ + Debugger = rundll32 "C:\path\to\core.dll", #6000 +``` + +When Windows launches `LeagueClientUx.exe`, the IFEO `Debugger` value redirects through `rundll32`, which loads `core.dll` and calls its ordinal-6000 export. That export ([`_BootstrapEntry` in `dllmain.cc`](../core/src/dllmain.cc#L120-L159)): + +1. Re-launches the original `LeagueClientUx.exe` with `CREATE_SUSPENDED | DEBUG_ONLY_THIS_PROCESS`. +2. Strips the debugger flag via `NtRemoveProcessDebug` (so the process doesn't actually run under a debugger — `DEBUG_ONLY_THIS_PROCESS` is just used to prevent IFEO from re-firing). +3. Injects `core.dll` into the new process via `CreateRemoteThread → LoadLibraryW`. +4. Resumes the suspended thread. + +This is loosely the same idea as the original Mecha injector (per the v0.6.0 doc) but uses `rundll32` rather than Detours and immediately removes the debugger to avoid CEF performance penalties. + +Vanguard does **not** scan or block this path: LCUX and `LeagueClientUxRender.exe` are explicitly scope-excluded from Vanguard's anti-cheat boundary, and `core.dll` itself lives outside the LoL install directory. + +#### Windows — symlink + dllproxy (Targeted, fallback) + +Some environments don't allow IFEO (locked-down domain policies, AV interference, etc.). The fallback in [`mod_symlink.rs`](../packages/hub/src-tauri/src/windows/mod_symlink.rs) creates a symlink in the LoL install folder pointing at `core.dll`: + +``` +\version.dll ───► C:\path\to\core.dll +``` + +`version.dll` is the chosen target because LCUX statically links against it (so it loads on startup), and it's not a name Vanguard cares about. The legacy alternatives `d3d9.dll` and `dwrite.dll` still work but are less preferred. (The v0.6.0 doc described d3d9 proxying; that approach is functionally subsumed by the more flexible `version.dll` shape.) + +Because `LoadLibrary("version.dll")` will resolve to our DLL instead of `C:\Windows\System32\version.dll`, [`dllproxy.cc`](../core/src/dllproxy.cc) re-exports every symbol the real `version.dll` provides (`GetFileVersionInfo*`, `VerQueryValue*`, etc.) and forwards them to the system DLL on first call. `d3d9.dll` and `dwrite.dll` exports are also forwarded for compatibility. + +Symlink mode requires admin or Windows Developer Mode for the initial install, but uninstall does not. + +#### macOS — insert_dylib (OnDemand, only model) + +There is no IFEO on macOS, and no equivalent of in-process DLL hijacking that survives Riot Client launching LCUX. The chosen approach: when the user opens LoL, the loader (running in the tray) injects the dylib by patching a CEF framework binary in place. + +[`packages/hub/src-tauri/src/macos/socket.rs`](../packages/hub/src-tauri/src/macos/socket.rs) runs a daemon thread that subscribes to Riot Client Services' WAMP WebSocket on `/product-session/v1/sessions/...`. When a session for `productId == "league_of_legends"` is created, the daemon calls [`core::install_module`](../packages/hub/src-tauri/src/macos/core.rs): + +1. Backs up `League of Legends.app/Contents/Frameworks/Chromium Embedded Framework.framework/Libraries/libEGL.dylib` to `libEGL.dylib.bak`. +2. Uses `insert_dylib` (either the bundled `core/insert_dylib.c` Rust port, or an external binary if present in `bin/`) to add a `LC_LOAD_DYLIB` load command pointing at `core.dylib`. +3. CEF loads `libEGL.dylib` into both the LCUX process and its renderer Helper, dragging `core.dylib` along with it. + +When LCUX closes (`Delete` event on the same WAMP topic), the daemon restores the backup. This is the model the loader UI calls **OnDemand**. + +OnDemand is a defined enum value on Windows too but is not wired up — IFEO is strictly more reliable there. + +### 2.4 CEF version pinning + +[`libcef.cc`](../core/src/libcef.cc) refuses to attach if the runtime libcef major version doesn't match the headers Pengu was compiled against: + +```cpp +if (get_version == nullptr || get_version(0) != CEF_VERSION_MAJOR) +{ + if (is_browser) + dialog::alert("Pengu does not support your Client version.", "Pengu Loader"); + return false; +} +``` + +Pengu currently targets **CEF 108** (commit `bf18f34`, headers under [`core/cef/`](../core/cef/)). Riot bumps LCUX's CEF major version roughly every two years, and each bump requires a Pengu release with new headers and possibly updated patterns (see next section). + +### 2.5 Transparency hook (`CefContext::GetBackgroundColor`) + +CEF, unlike Electron's patched Chromium, hardcodes white as the compositor root paint colour when alpha is zero. Setting `cef_browser_settings_t.background_color = 0` at create time isn't honoured end-to-end — the renderer still paints opaque white behind transparent UI, defeating any `WS_EX_LAYERED` / `NSVisualEffectView` work the loader does on the host window. + +The fix in [`libcef.cc:11-25`](../core/src/libcef.cc#L11-L25) is a memory pattern scan inside libcef for the unexported `CefContext::GetBackgroundColor` function: + +```cpp +#if OS_WIN +const char *pattern = "41 83 F8 01 74 0B 41 83 F8 02 75 0A 45 31 C0"; +#elif OS_MAC +const char *pattern = "55 48 89 E5 83 FA 01 74 ?? 83 FA 02 75 ??"; +#endif +``` + +Once located, the function is hooked to unconditionally return `0` (transparent). With the compositor returning transparent, the host window's vibrancy/Mica/acrylic effect actually shows through. See PenguLoader/PenguLoader#25 for the full investigation history. + +The patterns are stable for the lifetime of a CEF major version (~2 years). Updating Pengu for a new CEF major requires re-deriving them by hand against the new `libcef.dll`/`libcef.dylib`. + +### 2.6 Cache path + +[`browser.cc`](../core/src/browser/browser.cc) overrides `cef_settings_t.cache_path` and `root_cache_path` in the `cef_initialize` hook, and `cef_request_context_settings_t.cache_path` in the `CreateContext` hook, pointing them at: + +- Windows: `%LOCALAPPDATA%\Riot Games\League of Legends\Cache` +- macOS: `/Users/Shared/Riot Games/League Client/Cache` + +**Why.** Up to and including 2024 / CEF 108, Riot didn't set any cache path on LCUX, so the client effectively ran in incognito mode — every session re-downloaded fonts, images, partner-iframe assets, etc. Pengu's override gives LCUX a real on-disk cache, making iframes and external resources noticeably faster. + +Riot eventually started providing their own cache path (under `/Saved/webcache`). The v1.2.0 `main` branch always overrides; the `refactor-2` branch contains a deferral fix (only set if Riot left the field empty), which will be ported back. Vanguard does not interact with cache files. + +The `root_cache_path` set is required by CEF 108+, which warns/errors if it's missing (commit `d6aef96`). + +--- + +## 3. Browser-process hooks + +The browser process is the CEF process that owns windows, IO, and the CEF browser host objects. Pengu hooks four libcef exports here. + +### 3.1 `cef_initialize` — settings and command line + +[`browser.cc:204-235`](../core/src/browser/browser.cc#L204-L235): + +- Wraps `app->on_before_command_line_processing` so we can shape the cmdline (next subsection). +- Sets `cache_path` and `root_cache_path` (see [§2.6](#26-cache-path)). + +### 3.2 Command-line shaping — `OnBeforeCommandLineProcessing` + +Riot bakes a number of switches into LCUX's command line. Pengu intercepts the callback to: + +1. **Capture RCS credentials** for the `https://riotclient/` proxy if `use_riotclient` is enabled. The `--riotclient-app-port` and `--riotclient-auth-token` switches are stripped by CEF *after* this callback returns, so this is the only safe point to read them ([`browser.cc:138-142`](../core/src/browser/browser.cc#L138-L142)). The mechanism is pragmatic and acknowledged-fragile. +2. **Strip `--no-proxy-server`** if `use_proxy` is set, by rebuilding the entire command line via `init_from_string`. +3. **Append `--remote-debugging-port=`** if `debug_port` is set in config (undocumented; see [§3.5](#35-devtools-built-in-and-remote)). +4. **Append `--disable-web-security`** if `isecure_mode` is set. +5. **Optimisation switches** (`optimized_client`, default on): `disable-background-timer-throttling`, `disable-backgrounding-occluded-windows`, `disable-renderer-backgrounding`, `disable-metrics`, `disable-component-update`, `disable-domain-reliability`, `disable-translate`, `disable-gpu-watchdog`, `disable-renderer-accessibility`, `no-sandbox`. +6. **Super-potato switches** if enabled: `disable-smooth-scrolling`, `wm-window-animations-disabled`, `animation-duration-scale=0`. + +### 3.3 `cef_browser_host_create_browser` — main browser handshake + +LCUX creates many CEF browsers in its lifetime: the main UX shell, the auth popup, embedded eSports streams, partner iframes hosted as separate browsers, etc. Plugins should only load into the main shell. + +The detection in [`browser.cc:102-128`](../core/src/browser/browser.cc#L102-L128) checks the URL on `CreateBrowser`: + +```cpp +if (url_.startw("https://riot:") && url_.endw("/bootstrap.html")) +{ + extra_info->set_null(extra_info, &u"is_main"_s); + HookMainBrowserClient(client); +} +``` + +The `https://riot:` prefix matches the `riot:@127.0.0.1:` userinfo that RCS bakes into LCUX's URLs. The `bootstrap.html` filename is the very first page LCUX loads, which then redirects to `index.html` (the actual UI shell). This pattern has been stable across Client patches. + +Stamping `is_main` into the browser's `extra_info` dictionary is how the *renderer* side knows which browser is the main one — `extra_info` is passed through to `OnBrowserCreated` in the renderer process via CEF's IPC. See [§4](#4-renderer-process-hooks). + +For the main browser, [`HookMainBrowserClient`](../core/src/browser/browser.cc#L37-L100) wraps the client to: + +- Hook `get_keyboard_handler` for hotkeys ([§3.6](#36-hotkeys)). +- Hook `get_life_span_handler` to wrap `on_after_created` and call `browser::setup_window` ([§3.7](#37-window-stack-and-transparency)). +- Hook `on_process_message_received` for the renderer-to-browser IPC channel ([§3.8](#38-renderer-to-browser-ipc)). + +### 3.4 `cef_request_context_create_context` — custom schemes + +When a request context is created, Pengu registers two scheme handler factories on it: + +- **`https://plugins/`** — serves files out of the user's `plugins/` directory as ES modules. This is the heart of the plugin model and is documented in detail in [§6](#6-plugin-scheme-handler). +- **`https://riotclient/`** — opt-in proxy to the Riot Client's REST/WS API. See [§3.9](#39-riotclient-proxy). + +Why the same `https` scheme rather than custom `pengu://`: the LCUX `index.html` historically had a CSP that only permitted `https://` origins (later relaxed to allow Riot partner iframes). Re-using `https` with a synthetic hostname avoids needing a CSP exemption and integrates cleanly with the rest of LCUX's web stack. + +### 3.5 DevTools (built-in and remote) + +DevTools is **off by default** in v1.2.0 (`use_devtools=false`). v1.1.6 had it on, v1.2.0 added a permission-gating model where users opt in. + +**Built-in DevTools** is opened from JavaScript via `OpenDevTools()` (or hotkeys F12 / Ctrl+Shift+I). The renderer sends an `@open-devtools` IPC process message; the browser-side handler in [`devtools.cc`](../core/src/browser/devtools.cc) calls `host->show_dev_tools(host, &wi, new DevToolsClient(browser_id), &settings, nullptr)`. + +The `DevToolsClient` argument is the key trick. League sets a non-1.0 zoom on the main browser host (e.g. 0.8× or 1.6× depending on user UI scale settings); if `show_dev_tools` is called with `nullptr` for the client, the DevTools window inherits that zoom and becomes unusable. Passing a fresh client decouples them. The custom client also installs a `DevToolsKeyboardHandler` for Ctrl±/Ctrl0 zoom (and Cmd-C/V/X fixes on macOS), and uses `window::get_scaling(window) - 1.0` as the initial zoom so DevTools defaults to 1.0× per-DPI. + +`devtools_map_` dedupes by parent browser ID — a second `OpenDevTools()` call brings the existing window to the foreground rather than opening a duplicate. + +**Remote DevTools** is enabled via the undocumented `debug_port` config option, which appends `--remote-debugging-port=` to LCUX's command line. The user can then attach Chrome at `http://localhost:/json`. It is undocumented because exposing remote control of the LCUX renderer over loopback is a real attack surface; advanced users can opt in by editing the config file directly. + +### 3.6 Hotkeys + +[`keyboard.cc`](../core/src/browser/keyboard.cc), gated on `use_hotkeys` (default on), main browser only: + +| Key (Win) | Key (Mac) | Effect | +| --- | --- | --- | +| F12 / Ctrl+Shift+I | F12 / Cmd+Alt+I | Open DevTools (also gated on `use_devtools`) | +| Ctrl+Shift+R | Cmd+Alt+R | `browser->reload_ignore_cache` | +| Ctrl+Shift+Enter | Cmd+Alt+Enter | Confirm dialog → `fetch('/riotclient/kill-and-restart-ux')` for full restart | + +All hotkeys are bypassed when focus is on an editable field, so they don't fight chat/search inputs. + +### 3.7 Window stack and transparency + +LCUX's window hierarchy on Windows ([`window.cc:53-66`](../core/src/browser/window.cc#L53-L66)): + +``` +RCLIENT <- Riot Client top-level window + CefBrowserWindow <- default container by CreateBrowser() + Chrome_WidgetWin_0 <- the actual Chromium widget + Chrome_RenderWidgetHostHWND + Intermediate D3D Window +``` + +Out of the box, `CefBrowserWindow` has its own opaque background, which would defeat any layered-window transparency on `RCLIENT`. [`browser::setup_window`](../core/src/browser/window.cc#L68-L113) hides `CefBrowserWindow` and re-parents `Chrome_WidgetWin_0` directly to `RCLIENT` so the compositor's transparent output paints straight onto the host window. + +Combined with the [`GetBackgroundColor` hook](#25-transparency-hook-cefcontextgetbackgroundcolor) and the `apply_vibrancy` / `set_theme` calls (driven from JS via `Effect.apply`), this gives Electron-style Mica/acrylic/blur-behind on Windows and `NSVisualEffectView` material on macOS. + +On macOS, no re-parenting is needed; `browser::window` is just the CEF host's `NSView*` and `NSVisualEffectView` does the heavy lifting natively. + +#### Silent mode + +If `silent_mode` is enabled, [`window.cc:96-110`](../core/src/browser/window.cc#L96-L110) hooks `ShowWindow`, `SetWindowPos`, and the window's `WndProc` to suppress LCU's own foreground/topmost calls. This is for users who don't want LCU flashing or popping itself topmost on matchmaking-found / postgame events. As a side-effect, the post-game lobby will not auto-show the client window. + +### 3.8 Renderer-to-browser IPC + +The browser-side `on_process_message_received` handler ([`browser.cc:56-99`](../core/src/browser/browser.cc#L56-L99)) understands four message names from the renderer: + +| Message | Effect | +| --- | --- | +| `@open-devtools` | Calls `browser::open_devtools` (gated on `use_devtools`) | +| `@reload-client` | `browser->reload_ignore_cache` | +| `@set-window-vibrancy` | Calls `window::apply_vibrancy` or `clear_vibrancy` on the host window | +| `@set-window-theme` | Calls `window::set_theme(dark)` | + +The renderer side sends these via `frame->send_process_message(PID_BROWSER, msg)` from the V8 helper functions in [`v8_helper.cc`](../core/src/renderer/v8_helper.cc). + +### 3.9 `https://riotclient/` proxy + +[`riotclient.cc`](../core/src/browser/riotclient.cc) registers an opt-in scheme handler that proxies `https://riotclient/` to `https://127.0.0.1:/` with `Authorization: Basic base64("riot:")` injected and `Access-Control-Allow-Origin: *` to let LCUX's web context call it. + +LCUX already proxies many Riot endpoints under its own `/riotclient/...` path internally, so most plugins never need this. The custom scheme exists for the rare cases where a plugin needs an RCS endpoint LCUX doesn't re-expose (multi-product session info, account-switching, etc.). + +It is **off by default** (`use_riotclient=false`), historically default-on but tightened in v1.2.0 alongside the rest of the permission model — handing every loaded plugin a Basic-auth-injected channel to RCS is risky if any plugin is malicious. The credential extraction described in [§3.2](#32-command-line-shaping--onbeforecommandlineprocessing) is fragile and is a known weak point. + +--- + +## 4. Renderer-process hooks + +The renderer process runs V8 and Blink. Pengu hooks the renderer-side `cef_execute_process` to attach to its `cef_render_process_handler_t`, then wires up two callbacks. + +### 4.1 `OnBrowserCreated` — adopt the `is_main` flag + +[`renderer.cc:259-268`](../core/src/renderer/renderer.cc#L259-L268): + +```cpp +is_main_ = extra_info && extra_info->has_key(extra_info, &u"is_main"_s); +``` + +The `extra_info` dictionary is the same one stamped by the browser side in [§3.3](#33-cef_browser_host_create_browser--main-browser-handshake). When the renderer process is servicing the main shell's browser, `is_main_` becomes `true`. + +> **Known issue.** `is_main_` is a per-process global reassigned on every `OnBrowserCreated`. If CEF ever reuses one renderer process for both a main and a non-main browser, the flag flickers. The narrowing URL check in `OnContextCreated` (next section) saves us in practice, but this is belt-and-suspenders, not a guarantee. + +### 4.2 `OnContextCreated` — main-frame plugin injection + +For each new V8 context, [`renderer.cc:215-242`](../core/src/renderer/renderer.cc#L215-L242): + +1. Checks `is_main_ && url.startw("https://riot:") && url.endw("/index.html")`. Sub-browsers, popups, and the bootstrap step itself are skipped. +2. Calls three setup functions: + - `ExposeOsObject` — `window.os = { name, version, build }` (per-platform values from [`platform.h`](../core/src/platform.h)). + - `ExposeNativeFunctions` — `window.__native = { OpenDevTools, OpenPluginsFolder, ReloadClient, SetWindowTheme, SetWindowVibrancy, LoadDataStore, SaveDataStore }`. The preload reads these and then `delete window.__native` so plugins can't use them directly. + - `LoadPlugins` — populates `window.Pengu = { version, plugins, disabledPlugins, superPotato, isMac }` where `plugins` is an array of plugin entry paths discovered on disk. +3. Runs the embedded preload script via `frame->execute_java_script`. + +### 4.3 Plugin discovery + +[`get_plugin_entries`](../core/src/renderer/renderer.cc#L15-L78) walks the configured plugins directory and recognises three layouts: + +``` +plugins/ + plugin-name.js — top-level JS file + plugin-name/ + index.js — plugin folder + @author/ + plugin-name/ + index.js — author-namespaced plugin +``` + +Names starting with `_` or `.` are skipped. Subfolders inside an `@author` folder are also subject to the underscore/dot skip. The list of relative paths is passed to JS via `Pengu.plugins`. + +### 4.4 Native V8 handler + +`__native` is exposed via a single shared `cef_v8handler_t` ([`renderer.cc:80-119`](../core/src/renderer/renderer.cc#L80-L119)) that dispatches by function name into a `std::unordered_map` populated from two entry tables: + +- [`v8_DataStoreEntries`](../core/src/renderer/v8_datastore.cc) — `LoadDataStore`, `SaveDataStore`. +- [`v8_HelperEntries`](../core/src/renderer/v8_helper.cc) — `OpenDevTools`, `OpenPluginsFolder`, `ReloadClient`, `SetWindowVibrancy`, `SetWindowTheme`. + +The `OpenPluginsFolder` helper runs in-process (calls `shell::open_folder`); the others marshal to the browser process via process messages. + +--- + +## 5. Preload bundle + +[`packages/preload/`](../packages/preload/) is a TypeScript codebase compiled by Vite into a single IIFE that runs at the top of every main-frame V8 context. Its entry is [`src/index.ts`](../packages/preload/src/index.ts), which imports two trees: + +- `./preload/` — the runtime bootstrap (DataStore, Effect, RCP/socket, plugin loader). +- `./views/` — the in-client overlay UI (Solid components for plugin toggles, etc.). + +### 5.1 Build pipeline + +The Vite config in [`packages/preload/vite.config.ts`](../packages/preload/vite.config.ts) has two modes: + +**Dev (`vite dev`):** Vite serves the bundle on `https://localhost:3001`. esbuild emits `dist/preload.js` (IIFE) with a footer that imports `@vite/client` and `src/views/index.tsx` from the dev server, so HMR works inside LCUX. The C++ side, when `_DEBUG`, reads `dist/preload.js` from disk at runtime ([`renderer.cc:200`](../core/src/renderer/renderer.cc#L200)) and `execute_java_script`s it. + +**Build (`vite build`):** Vite emits `dist/preload.js`, then a custom `pengu-build` plugin emits `dist/preload.g.h` containing `_preload_script[]` as a C `unsigned char` array. The renderer `#include`s that header in non-debug builds and runs the embedded bytes ([`renderer.cc:209-211`](../core/src/renderer/renderer.cc#L209-L211)). + +> **Known issue.** The recent `7635b4c` refactor moved `plugins/` → `packages/preload/`, but the C++ paths in `renderer.cc` still reference `../plugins/dist/preload.js` and `../../plugins/dist/preload.g.h`, which no longer resolve. Release builds will fail until either the C++ paths are updated or the build emits to `core/plugins/dist/`. To be fixed. + +### 5.2 Plugin loader + +[`preload/loader.ts`](../packages/preload/src/preload/loader.ts) is the entry point that turns `Pengu.plugins` into actually loaded modules: + +1. **Filter disabled plugins.** `Pengu.disabledPlugins` arrives as a comma-separated string of FNV-1a 32-bit hashes (lowercased, forward-slash-normalised paths). The loader parses it into a `Set`, hashes each entry, and filters out matches. Any entry matching `/^@default\//i` is also dropped — `@default/` was the v0.6 built-in plugin namespace from the old "League Loader" branding and is vestigial. +2. **Dynamic-import each entry.** `await import('https://plugins/' + entry)`. This goes through the [`https://plugins/` scheme handler](#6-plugin-scheme-handler). +3. **Init.** If the module exports an `init` function, await it with `{ rcp, socket, meta }`. `meta.name` is the plugin's folder name (omitted for top-level `name.js` plugins). +4. **Load.** If the module exports `load` or `default` as a function, register it as a `window` `'load'` listener. + +The whole batch is wrapped in `Promise.all`, then `rcp.preInit('rcp-fe-common-libs', () => waitable)` blocks the first RCP plugin until plugin loading finishes, ensuring plugins see a consistent initial state. + +> **Known issue.** The hash-based disable mechanism is essentially untested in v1.2.0. v1.1.6 disables plugins by renaming `index.js` → `index.js_` on disk; the Tauri loader has partial backward-compat for that ([`packages/hub/src/lib/plugins.ts`](../packages/hub/src/lib/plugins.ts) `isIndex` checks `path += '_'`), but the end-to-end flow has not been validated. + +### 5.3 Public surface exposed to plugins + +After the preload runs, plugins see: + +- **`window.Pengu`** — `{ version, plugins, superPotato, isMac }` (frozen). `disabledPlugins` is consumed by the loader and removed. +- **`window.os`** — `{ name, version, build }`. +- **`window.DataStore`** — `{ has, get, set, remove }` for shared key-value storage. +- **`window.Effect`** — `{ apply, clear, setTheme }` for window vibrancy/theme. Translates between Windows backdrop types (`transparent`, `blurbehind`, `acrylic`, `unified`, `mica` with sub-modes `auto/none/mica/acrylic/tabbed`) and macOS `NSVisualEffectMaterial` values. +- **`window.rcp`** — `{ preInit, postInit, whenReady, get }`. Wraps Riot's plugin announce/registration mechanism so plugins can intercept other plugins' init. +- **`window.rcp` is a `RCP` class instance**, but the global `rcp` and `socket` are also passed as `init` arguments to plugins. +- **`window.openDevTools`**, **`openPluginsFolder`**, **`reloadClient`**, **`restartClient`**, **`getScriptPath`** — convenience helpers. + +`window.__native` is deleted from the global scope by [`api/native.ts`](../packages/preload/src/preload/api/native.ts) immediately after capture, so plugins must go through the typed wrappers. + +### 5.4 RCP hooks + +[`preload/rcp/hooks.ts`](../packages/preload/src/preload/rcp/hooks.ts) wraps `document.dispatchEvent` to intercept Riot's `riotPlugin.announce:` events. Each announce carries a `registrationHandler` that gets wrapped so before/after callbacks registered via `rcp.preInit(name, fn)` and `rcp.postInit(name, fn)` fire at the right lifecycle points (before the registrar runs, after the registered API is fulfilled). `whenReady(name | names[])` returns a promise resolving to the plugin's API once it's fulfilled. + +This is the canonical way for plugins to extend or wrap LCUX's own "rcp-fe-*" frontend plugins. + +### 5.5 WAMP socket helper + +[`preload/rcp/socket.ts`](../packages/preload/src/preload/rcp/socket.ts) opens a WAMP WebSocket to LCUX's internal LCDS endpoint (URL provided by `rcp-fe-common-libs.context.socket._endpoint`) and exposes `socket.observe(api, listener)` / `disconnect(api, listener)`. The api string is normalised into LCDS's `OnJsonApiEvent_` topic format (or `OnJsonApiEvent` when api is `'all'`). + +This is the standard way for plugins to subscribe to live LCU events (queue state, gameflow, summoner, etc.). + +### 5.6 Late-listener polyfills + +[`preload/load-hooks.ts`](../packages/preload/src/preload/load-hooks.ts) wraps `window.addEventListener` and `document.addEventListener` so that plugins registering for `load` / `DOMContentLoaded` *after* those events have already fired still get their listener invoked (via `setTimeout(listener, 1)`). Without this, plugins that bundle slowly enough to register past the `load` event would silently fail. + +### 5.7 Super-potato mode + +[`preload/super-potato.ts`](../packages/preload/src/preload/super-potato.ts), enabled when `Pengu.superPotato` is true: + +- Injects a global stylesheet that disables CSS transitions everywhere except a curated set of essential animations (spinners, vignette celebrations). +- Wraps `document.createElement` to inject the same stylesheet into any new shadow root. +- Calls Riot's `lol-settings/v1/local/lol-user-experience` endpoint to enable the in-game potato mode. + +Combined with the cmdline switches added by `optimized_client` and the super-potato cmdline switches in [§3.2](#32-command-line-shaping--onbeforecommandlineprocessing), this shaves significant CPU on low-end machines. + +--- + +## 6. Plugin scheme handler + +The `https://plugins/` scheme handler ([`assets.cc`](../core/src/browser/assets.cc)) is the heart of the plugin model. It serves files out of the configured `plugins/` directory as ES modules, with on-the-fly module shims that mimic Vite's import semantics. + +### 6.1 Path resolution + +For a request to `https://plugins/?`: + +1. URL-decode the path. +2. Resolve to `/`. +3. If the path ends in `/`, append `index.js`. +4. If the leaf has no extension, try `.js`, then `/index.js`. +5. If none of those exist, return 404. + +### 6.2 Module shims + +If the request `resource_type` is `RT_SCRIPT` (i.e. an `import` or `