this repo has no description

Merge branch 'develop' into notnite/moonbase-dep-prompt

Changed files
+1044 -161
.github
nix
packages
browser
core-extensions
injector
src
node-preload
src
types
web-preload
src
+2
.github/workflows/nightly.yml
···
- name: Build moonlight
env:
NODE_ENV: production
+
MOONLIGHT_BRANCH: nightly
+
MOONLIGHT_VERSION: ${{ github.sha }}
run: pnpm run build
- name: Write ref/commit to file
+2
.github/workflows/release.yml
···
- name: Build moonlight
env:
NODE_ENV: production
+
MOONLIGHT_BRANCH: stable
+
MOONLIGHT_VERSION: ${{ github.ref_name }}
run: pnpm run build
- name: Create archive
run: |
+11 -3
build.mjs
···
const browser = process.argv.includes("--browser");
const mv2 = process.argv.includes("--mv2");
+
const buildBranch = process.env.MOONLIGHT_BRANCH ?? "dev";
+
const buildVersion = process.env.MOONLIGHT_VERSION ?? "dev";
+
const external = [
"electron",
"fs",
···
const define = {
MOONLIGHT_ENV: `"${name}"`,
-
MOONLIGHT_PROD: prod.toString()
+
MOONLIGHT_PROD: prod.toString(),
+
MOONLIGHT_BRANCH: `"${buildBranch}"`,
+
MOONLIGHT_VERSION: `"${buildVersion}"`
};
for (const iterName of [
···
entryPoints: [entry],
outfile,
-
format: "cjs",
+
format: "iife",
+
globalName: "module.exports",
+
platform: ["web-preload", "browser"].includes(name) ? "browser" : "node",
treeShaking: true,
···
entryPoints,
outdir,
-
format: "cjs",
+
format: "iife",
+
globalName: "module.exports",
platform: "node",
treeShaking: true,
+3 -86
flake.nix
···
};
outputs = { self, nixpkgs, flake-utils, pnpm2nix }:
-
let
-
mkMoonlight = { pkgs, mkPnpmPackage }:
-
mkPnpmPackage rec {
-
workspace = ./.;
-
src = ./.;
-
components = [
-
"packages/core"
-
"packages/core-extensions"
-
"packages/injector"
-
"packages/node-preload"
-
"packages/types"
-
"packages/web-preload"
-
];
-
distDirs = [ "dist" ];
-
-
copyNodeModules = true;
-
buildPhase = "pnpm run build";
-
installPhase = "cp -r dist $out";
-
-
meta = with pkgs.lib; {
-
description = "Yet another Discord mod";
-
homepage = "https://moonlight-mod.github.io/";
-
license = licenses.lgpl3;
-
maintainers = with maintainers; [ notnite ];
-
};
-
};
-
-
nameTable = {
-
discord = "Discord";
-
discord-ptb = "DiscordPTB";
-
discord-canary = "DiscordCanary";
-
discord-development = "DiscordDevelopment";
-
};
-
-
darwinNameTable = {
-
discord = "Discord";
-
discord-ptb = "Discord PTB";
-
discord-canary = "Discord Canary";
-
discord-development = "Discord Development";
-
};
-
-
mkOverride = prev: moonlight: name:
-
let discord = prev.${name};
-
in discord.overrideAttrs (old: {
-
installPhase = let
-
folderName = nameTable.${name};
-
darwinFolderName = darwinNameTable.${name};
-
-
injected = ''
-
require("${moonlight}/injector").inject(
-
require("path").join(__dirname, "../_app.asar")
-
);
-
'';
-
-
packageJson = ''
-
{"name":"discord","main":"./injector.js","private":true}
-
'';
-
-
in old.installPhase + "\n" + ''
-
resources="$out/opt/${folderName}/resources"
-
if [ ! -d "$resources" ]; then
-
resources="$out/Applications/${darwinFolderName}.app/Contents/Resources"
-
fi
-
-
mv "$resources/app.asar" "$resources/_app.asar"
-
mkdir -p "$resources/app"
-
-
cat > "$resources/app/injector.js" <<EOF
-
${injected}
-
EOF
-
-
echo '${packageJson}' > "$resources/app/package.json"
-
'';
-
});
-
-
overlay = final: prev: rec {
-
moonlight-mod = mkMoonlight {
-
pkgs = final;
-
mkPnpmPackage = pnpm2nix.packages.${final.system}.mkPnpmPackage;
-
};
-
discord = mkOverride prev moonlight-mod "discord";
-
discord-ptb = mkOverride prev moonlight-mod "discord-ptb";
-
discord-canary = mkOverride prev moonlight-mod "discord-canary";
-
discord-development =
-
mkOverride prev moonlight-mod "discord-development";
-
};
+
let overlay = import ./nix/overlay.nix { inherit pnpm2nix; };
in flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
···
overlays = [ overlay ];
};
in {
+
# Don't use these unless you're testing things
packages.default = pkgs.moonlight-mod;
packages.moonlight-mod = pkgs.moonlight-mod;
···
packages.discord-development = pkgs.discord-development;
}) // {
overlays.default = overlay;
+
homeModules.default = ./nix/home-manager.nix;
};
}
+28
nix/default.nix
···
+
{ pkgs, mkPnpmPackage }:
+
+
mkPnpmPackage rec {
+
workspace = ./..;
+
src = ./..;
+
+
# Work around a bug with how it expects dist
+
components = [
+
"packages/core"
+
"packages/core-extensions"
+
"packages/injector"
+
"packages/node-preload"
+
"packages/types"
+
"packages/web-preload"
+
];
+
distDirs = [ "dist" ];
+
+
copyNodeModules = true;
+
buildPhase = "pnpm run build";
+
installPhase = "cp -r dist $out";
+
+
meta = with pkgs.lib; {
+
description = "Yet another Discord mod";
+
homepage = "https://moonlight-mod.github.io/";
+
license = licenses.lgpl3;
+
maintainers = with maintainers; [ notnite ];
+
};
+
}
+56
nix/home-manager.nix
···
+
{ config, lib, pkgs, ... }:
+
+
let cfg = config.programs.moonlight-mod;
+
in {
+
options.programs.moonlight-mod = {
+
enable = lib.mkEnableOption "Yet another Discord mod";
+
+
configs = let
+
# TODO: type this
+
type = lib.types.nullOr (lib.types.attrs);
+
default = null;
+
in {
+
stable = lib.mkOption {
+
inherit type default;
+
description = "Configuration for Discord Stable";
+
};
+
+
ptb = lib.mkOption {
+
inherit type default;
+
description = "Configuration for Discord PTB";
+
};
+
+
canary = lib.mkOption {
+
inherit type default;
+
description = "Configuration for Discord Canary";
+
};
+
+
development = lib.mkOption {
+
inherit type default;
+
description = "Configuration for Discord Development";
+
};
+
};
+
};
+
+
config = lib.mkIf cfg.enable {
+
xdg.configFile."moonlight-mod/stable.json" =
+
lib.mkIf (cfg.configs.stable != null) {
+
text = builtins.toJSON cfg.configs.stable;
+
};
+
+
xdg.configFile."moonlight-mod/ptb.json" =
+
lib.mkIf (cfg.configs.ptb != null) {
+
text = builtins.toJSON cfg.configs.ptb;
+
};
+
+
xdg.configFile."moonlight-mod/canary.json" =
+
lib.mkIf (cfg.configs.canary != null) {
+
text = builtins.toJSON cfg.configs.canary;
+
};
+
+
xdg.configFile."moonlight-mod/development.json" =
+
lib.mkIf (cfg.configs.development != null) {
+
text = builtins.toJSON cfg.configs.development;
+
};
+
};
+
}
+60
nix/overlay.nix
···
+
{ pnpm2nix }:
+
+
let
+
nameTable = {
+
discord = "Discord";
+
discord-ptb = "DiscordPTB";
+
discord-canary = "DiscordCanary";
+
discord-development = "DiscordDevelopment";
+
};
+
+
darwinNameTable = {
+
discord = "Discord";
+
discord-ptb = "Discord PTB";
+
discord-canary = "Discord Canary";
+
discord-development = "Discord Development";
+
};
+
+
mkOverride = prev: moonlight: name:
+
let discord = prev.${name};
+
in discord.overrideAttrs (old: {
+
installPhase = let
+
folderName = nameTable.${name};
+
darwinFolderName = darwinNameTable.${name};
+
+
injected = ''
+
require("${moonlight}/injector").inject(
+
require("path").join(__dirname, "../_app.asar")
+
);
+
'';
+
+
packageJson = ''
+
{"name":"discord","main":"./injector.js","private":true}
+
'';
+
+
in old.installPhase + "\n" + ''
+
resources="$out/opt/${folderName}/resources"
+
if [ ! -d "$resources" ]; then
+
resources="$out/Applications/${darwinFolderName}.app/Contents/Resources"
+
fi
+
+
mv "$resources/app.asar" "$resources/_app.asar"
+
mkdir -p "$resources/app"
+
+
cat > "$resources/app/injector.js" <<EOF
+
${injected}
+
EOF
+
+
echo '${packageJson}' > "$resources/app/package.json"
+
'';
+
});
+
in final: prev: rec {
+
moonlight-mod = final.callPackage ./default.nix {
+
pkgs = final;
+
mkPnpmPackage = pnpm2nix.packages.${final.system}.mkPnpmPackage;
+
};
+
discord = mkOverride prev moonlight-mod "discord";
+
discord-ptb = mkOverride prev moonlight-mod "discord-ptb";
+
discord-canary = mkOverride prev moonlight-mod "discord-canary";
+
discord-development = mkOverride prev moonlight-mod "discord-development";
+
}
+1
packages/browser/manifest.json
···
],
"host_permissions": [
"https://moonlight-mod.github.io/*",
+
"https://api.github.com/*",
"https://*.discord.com/*"
],
"content_scripts": [
+2
packages/browser/manifestv2.json
···
"scripting",
"webNavigation",
"https://*.discord.com/assets/*.js",
+
"https://moonlight-mod.github.io/*",
+
"https://api.github.com/*",
"https://*.discord.com/*"
],
"background": {
+11 -1
packages/browser/src/index.ts
···
import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
import { getExtensions } from "@moonlight-mod/core/extension";
import { loadExtensions } from "@moonlight-mod/core/extension/loader";
-
import { MoonlightBrowserFS, MoonlightNode } from "@moonlight-mod/types";
+
import {
+
MoonlightBranch,
+
MoonlightBrowserFS,
+
MoonlightNode
+
} from "@moonlight-mod/types";
import { IndexedDB } from "@zenfs/dom";
import { configure } from "@zenfs/core";
import * as fs from "@zenfs/core/promises";
···
processedExtensions,
nativesCache: {},
+
version: MOONLIGHT_VERSION,
+
branch: MOONLIGHT_BRANCH as MoonlightBranch,
+
getConfig,
getConfigOption: <T>(ext: string, name: string) => {
const config = getConfig(ext);
···
return new Logger(id);
},
+
getMoonlightDir() {
+
return "/";
+
},
getExtensionDir: (ext: string) => {
return `/extensions/${ext}`;
},
+2 -2
packages/core-extensions/package.json
···
"name": "@moonlight-mod/core-extensions",
"private": true,
"dependencies": {
-
"@moonlight-mod/types": "workspace:*",
-
"@moonlight-mod/core": "workspace:*"
+
"@moonlight-mod/core": "workspace:*",
+
"@moonlight-mod/types": "workspace:*"
}
}
+3
packages/core-extensions/src/moonbase/consts.ts
···
+
export const githubRepo = "moonlight-mod/moonlight";
+
export const nightlyRefUrl = "https://moonlight-mod.github.io/moonlight/ref";
+
export const userAgent = `moonlight/${moonlightNode.version} (https://github.com/moonlight-mod/moonlight)`;
+17 -3
packages/core-extensions/src/moonbase/index.tsx
···
-
import { ExtensionWebExports } from "@moonlight-mod/types";
+
import { ExtensionWebpackModule } from "@moonlight-mod/types";
-
export const webpackModules: ExtensionWebExports["webpackModules"] = {
+
export const webpackModules: Record<string, ExtensionWebpackModule> = {
stores: {
dependencies: [
{ id: "discord/packages/flux" },
···
{ ext: "moonbase", id: "ui" }
],
entrypoint: true
+
},
+
+
updates: {
+
dependencies: [
+
{ id: "react" },
+
{ ext: "moonbase", id: "stores" },
+
{ ext: "notices", id: "notices" },
+
{
+
ext: "spacepack",
+
id: "spacepack"
+
}
+
],
+
entrypoint: true
}
};
export const styles = [
".moonbase-settings > :first-child { margin-top: 0px; }",
-
"textarea.moonbase-resizeable { resize: vertical }"
+
"textarea.moonbase-resizeable { resize: vertical }",
+
".moonbase-updates-notice { background-color: #222034; color: #FFFBA6; }"
];
+1 -1
packages/core-extensions/src/moonbase/manifest.json
···
"tagline": "The official settings UI for moonlight",
"authors": ["Cynosphere", "NotNite"]
},
-
"dependencies": ["spacepack", "settings", "common"],
+
"dependencies": ["spacepack", "settings", "common", "notices"],
"settings": {
"sections": {
"displayName": "Split into sections",
+43 -2
packages/core-extensions/src/moonbase/node.ts
···
import path from "path";
import extractAsar from "@moonlight-mod/core/asar";
import { repoUrlFile } from "@moonlight-mod/types/constants";
+
import { githubRepo, userAgent, nightlyRefUrl } from "./consts";
+
import { MoonlightBranch } from "types/src";
const logger = moonlightNode.getLogger("moonbase");
+
async function checkForMoonlightUpdate() {
+
try {
+
if (moonlightNode.branch === MoonlightBranch.STABLE) {
+
const req = await fetch(
+
`https://api.github.com/repos/${githubRepo}/releases/latest`,
+
{
+
headers: {
+
"User-Agent": userAgent
+
}
+
}
+
);
+
const json: { name: string } = await req.json();
+
return json.name !== moonlightNode.version ? json.name : null;
+
} else if (moonlightNode.branch === MoonlightBranch.NIGHTLY) {
+
const req = await fetch(nightlyRefUrl, {
+
headers: {
+
"User-Agent": userAgent
+
}
+
});
+
const ref = (await req.text()).split("\n")[0];
+
return ref !== moonlightNode.version ? ref : null;
+
}
+
+
return null;
+
} catch (e) {
+
logger.error("Error checking for moonlight update", e);
+
return null;
+
}
+
}
+
async function fetchRepositories(repos: string[]) {
const ret: Record<string, RepositoryManifest[]> = {};
for (const repo of repos) {
try {
-
const req = await fetch(repo);
+
const req = await fetch(repo, {
+
headers: {
+
"User-Agent": userAgent
+
}
+
});
const json = await req.json();
ret[repo] = json;
} catch (e) {
···
url: string,
repo: string
) {
-
const req = await fetch(url);
+
const req = await fetch(url, {
+
headers: {
+
"User-Agent": userAgent
+
}
+
});
const dir = moonlightNode.getExtensionDir(manifest.id);
// remake it in case of updates
···
}
const exports: MoonbaseNatives = {
+
checkForMoonlightUpdate,
fetchRepositories,
installExtension,
deleteExtension,
+1
packages/core-extensions/src/moonbase/types.ts
···
import { DetectedExtension, ExtensionManifest } from "types/src";
export type MoonbaseNatives = {
+
checkForMoonlightUpdate(): Promise<string | null>;
fetchRepositories(
repos: string[]
): Promise<Record<string, RepositoryManifest[]>>;
+95 -31
packages/core-extensions/src/moonbase/webpackModules/stores.ts
···
-
import { Config, constants, ExtensionLoadSource } from "@moonlight-mod/types";
+
import {
+
Config,
+
ExtensionLoadSource,
+
MoonlightBranch
+
} from "@moonlight-mod/types";
import {
ExtensionState,
MoonbaseExtension,
···
import { Store } from "@moonlight-mod/wp/discord/packages/flux";
import Dispatcher from "@moonlight-mod/wp/discord/Dispatcher";
import extractAsar from "@moonlight-mod/core/asar";
-
import { repoUrlFile } from "@moonlight-mod/types/constants";
+
import { mainRepo, repoUrlFile } from "@moonlight-mod/types/constants";
+
import { githubRepo, userAgent, nightlyRefUrl } from "../consts";
const logger = moonlight.getLogger("moonbase");
···
if (window._moonlightBrowserFS != null) {
const browserFS = window._moonlightBrowserFS!;
natives = {
+
checkForMoonlightUpdate: async () => {
+
try {
+
if (moonlight.branch === MoonlightBranch.STABLE) {
+
const req = await fetch(
+
`https://api.github.com/repos/${githubRepo}/releases/latest`,
+
{
+
headers: {
+
"User-Agent": userAgent
+
}
+
}
+
);
+
const json: { name: string } = await req.json();
+
return json.name !== moonlight.version ? json.name : null;
+
} else if (moonlight.branch === MoonlightBranch.NIGHTLY) {
+
const req = await fetch(nightlyRefUrl, {
+
headers: {
+
"User-Agent": userAgent
+
}
+
});
+
const ref = (await req.text()).split("\n")[0];
+
return ref !== moonlight.version ? ref : null;
+
}
+
+
return null;
+
} catch (e) {
+
logger.error("Error checking for moonlight update", e);
+
return null;
+
}
+
},
+
fetchRepositories: async (repos) => {
const ret: Record<string, RepositoryManifest[]> = {};
···
submitting: boolean;
installing: boolean;
+
newVersion: string | null;
+
shouldShowNotice: boolean;
+
extensions: { [id: number]: MoonbaseExtension };
updates: { [id: number]: { version: string; download: string } };
···
this.modified = false;
this.submitting = false;
this.installing = false;
+
+
this.newVersion = null;
+
this.shouldShowNotice = false;
this.extensions = {};
this.updates = {};
···
};
}
-
natives!.fetchRepositories(this.config.repositories).then((ret) => {
-
for (const [repo, exts] of Object.entries(ret)) {
-
try {
-
for (const ext of exts) {
-
const level = ext.apiLevel ?? 1;
-
if (level !== window.moonlight.apiLevel) continue;
+
natives!
+
.fetchRepositories(this.config.repositories)
+
.then((ret) => {
+
for (const [repo, exts] of Object.entries(ret)) {
+
try {
+
for (const ext of exts) {
+
const level = ext.apiLevel ?? 1;
+
if (level !== window.moonlight.apiLevel) continue;
-
const uniqueId = this.extensionIndex++;
-
const extensionData = {
-
id: ext.id,
-
uniqueId,
-
manifest: ext,
-
source: { type: ExtensionLoadSource.Normal, url: repo },
-
state: ExtensionState.NotDownloaded
-
};
+
const uniqueId = this.extensionIndex++;
+
const extensionData = {
+
id: ext.id,
+
uniqueId,
+
manifest: ext,
+
source: { type: ExtensionLoadSource.Normal, url: repo },
+
state: ExtensionState.NotDownloaded
+
};
+
+
if (this.alreadyExists(extensionData)) {
+
// Make sure the download URL is properly updated
+
for (const [id, e] of Object.entries(this.extensions)) {
+
if (e.id === ext.id && e.source.url === repo) {
+
this.extensions[parseInt(id)].manifest = {
+
...e.manifest,
+
download: ext.download
+
};
+
break;
+
}
+
}
+
+
if (this.hasUpdate(extensionData)) {
+
this.updates[uniqueId] = {
+
version: ext.version!,
+
download: ext.download
+
};
+
}
-
if (this.alreadyExists(extensionData)) {
-
if (this.hasUpdate(extensionData)) {
-
this.updates[uniqueId] = {
-
version: ext.version!,
-
download: ext.download
-
};
+
continue;
}
-
continue;
+
this.extensions[uniqueId] = extensionData;
}
-
-
this.extensions[uniqueId] = extensionData;
+
} catch (e) {
+
logger.error(`Error processing repository ${repo}`, e);
}
-
} catch (e) {
-
logger.error(`Error processing repository ${repo}`, e);
}
-
}
-
this.emitChange();
-
});
+
this.emitChange();
+
})
+
.then(natives!.checkForMoonlightUpdate)
+
.then((version) => {
+
this.newVersion = version;
+
this.emitChange();
+
})
+
.then(() => {
+
this.shouldShowNotice =
+
this.newVersion != null || Object.keys(this.updates).length > 0;
+
this.emitChange();
+
});
}
private alreadyExists(ext: MoonbaseExtension) {
···
private getRank(ext: MoonbaseExtension) {
if (ext.source.type === ExtensionLoadSource.Developer) return 3;
if (ext.source.type === ExtensionLoadSource.Core) return 2;
-
if (ext.source.url === constants.mainRepo) return 1;
+
if (ext.source.url === mainRepo) return 1;
return 0;
}
+81
packages/core-extensions/src/moonbase/webpackModules/updates.ts
···
+
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
+
import Notices from "@moonlight-mod/wp/notices_notices";
+
import { MoonlightBranch } from "types/src";
+
+
// FIXME: not indexed as importable
+
const Constants = spacepack.require("discord/Constants");
+
const UserSettingsSections = spacepack.findObjectFromKey(
+
Constants,
+
"APPEARANCE_THEME_PICKER"
+
);
+
+
function plural(str: string, num: number) {
+
return `${str}${num > 1 ? "s" : ""}`;
+
}
+
+
function listener() {
+
if (MoonbaseSettingsStore.shouldShowNotice) {
+
// @ts-expect-error epic type fail
+
MoonbaseSettingsStore.removeChangeListener(listener);
+
+
const version = MoonbaseSettingsStore.newVersion;
+
const extensionUpdateCount = Object.keys(
+
MoonbaseSettingsStore.updates
+
).length;
+
const hasExtensionUpdates = extensionUpdateCount > 0;
+
+
let message;
+
+
if (version != null) {
+
message =
+
moonlightNode.branch === MoonlightBranch.NIGHTLY
+
? `A new version of moonlight is available`
+
: `moonlight ${version} is available`;
+
}
+
+
if (hasExtensionUpdates) {
+
let concat = false;
+
if (message == null) {
+
message = "";
+
} else {
+
concat = true;
+
message += ", and ";
+
}
+
message += `${extensionUpdateCount} ${concat ? "" : "moonlight "}${plural(
+
"extension",
+
extensionUpdateCount
+
)} can be updated`;
+
}
+
+
if (message != null) message += ".";
+
+
Notices.addNotice({
+
element: message,
+
color: "moonbase-updates-notice",
+
buttons: hasExtensionUpdates
+
? [
+
{
+
name: "Open Moonbase",
+
onClick: () => {
+
const { open } = spacepack.findByExports(
+
"setSection",
+
"clearSubsection"
+
)[0].exports.Z;
+
+
// settings is lazy loaded thus lazily patched
+
// FIXME: figure out a way to detect if settings has been opened
+
// alreadyjust so the transition isnt as jarring
+
open(UserSettingsSections.ACCOUNT);
+
setTimeout(() => open("moonbase", 0), 0);
+
return true;
+
}
+
}
+
]
+
: []
+
});
+
}
+
}
+
+
// @ts-expect-error epic type fail
+
MoonbaseSettingsStore.addChangeListener(listener);
+6 -2
packages/core-extensions/src/nativeFixes/host.ts
···
(moonlightHost.getConfigOption<boolean>("nativeFixes", "vaapi") ?? true) &&
!noAccel
) {
-
enabledFeatures.push("VaapiVideoEncoder", "VaapiVideoDecoder");
if (process.platform === "linux")
-
enabledFeatures.push("VaapiVideoDecodeLinuxGL");
+
// These will eventually be renamed https://source.chromium.org/chromium/chromium/src/+/5482210941a94d70406b8da962426e4faca7fce4
+
enabledFeatures.push(
+
"VaapiVideoEncoder",
+
"VaapiVideoDecoder",
+
"VaapiVideoDecodeLinuxGL"
+
);
}
app.commandLine.appendSwitch(
+5 -10
packages/core-extensions/src/nativeFixes/manifest.json
···
"meta": {
"name": "Native Fixes",
"tagline": "Various configurable fixes for Discord and Electron",
-
"authors": [
-
"Cynosphere",
-
"adryd"
-
],
-
"tags": [
-
"fixes"
-
]
+
"authors": ["Cynosphere", "adryd"],
+
"tags": ["fixes"]
},
"settings": {
"devtoolsThemeFix": {
···
},
"linuxSpeechDispatcher": {
"displayName": "Enable speech-dispatcher for TTS on Linux",
-
"description": "Has no effect on other operating systems",
+
"description": "Fixes text-to-speech. Has no effect on other operating systems",
"type": "boolean",
"default": true
},
"vaapi": {
-
"displayName": "Enable VAAPI features",
-
"description": "Provides additional hardware acceleration",
+
"displayName": "Enable VAAPI features on Linux",
+
"description": "Provides hardware accelerated video encode and decode. Has no effect on other operating systems",
"type": "boolean",
"default": true
}
+2 -1
packages/core-extensions/src/noHideToken/manifest.json
···
"apiLevel": 2,
"meta": {
"name": "No Hide Token",
-
"tagline": "Disables removal of token from localStorage when opening dev tools",
+
"tagline": "Prevents you from being logged-out on hard-crash",
+
"description": "Prevents you from being logged-out on hard-crash by disabling removal of token from localStorage when opening dev tools",
"authors": ["adryd"],
"tags": ["dangerZone", "development"]
}
+46
packages/core-extensions/src/notices/index.ts
···
+
import type { ExtensionWebpackModule, Patch } from "@moonlight-mod/types";
+
+
export const patches: Patch[] = [
+
{
+
find: ".GUILD_RAID_NOTIFICATION:",
+
replace: {
+
match:
+
/(?<=return(\(0,.\.jsx\))\(.+?\);)case .{1,2}\..{1,3}\.GUILD_RAID_NOTIFICATION:/,
+
replacement: (orig, createElement) =>
+
`case "__moonlight_notice":return${createElement}(require("notices_component").default,{});${orig}`
+
}
+
},
+
{
+
find: '"NoticeStore"',
+
replace: [
+
{
+
match: /\[.{1,2}\..{1,3}\.CONNECT_SPOTIFY\]:{/,
+
replacement: (orig: string) =>
+
`__moonlight_notice:{predicate:()=>require("notices_notices").default.shouldShowNotice()},${orig}`
+
},
+
{
+
match: /=\[(.{1,2}\..{1,3}\.QUARANTINED,)/g,
+
replacement: (_, orig) => `=["__moonlight_notice",${orig}`
+
}
+
]
+
}
+
];
+
+
export const webpackModules: Record<string, ExtensionWebpackModule> = {
+
notices: {
+
dependencies: [
+
{ id: "discord/packages/flux" },
+
{ id: "discord/Dispatcher" }
+
]
+
},
+
+
component: {
+
dependencies: [
+
{ id: "react" },
+
{ id: "discord/Dispatcher" },
+
{ id: "discord/components/common/index" },
+
{ id: "discord/packages/flux" },
+
{ ext: "notices", id: "notices" }
+
]
+
}
+
};
+10
packages/core-extensions/src/notices/manifest.json
···
+
{
+
"id": "notices",
+
"apiLevel": 2,
+
"meta": {
+
"name": "Notices",
+
"tagline": "An API for adding notices at the top of the page",
+
"authors": ["Cynosphere", "NotNite"],
+
"tags": ["library"]
+
}
+
}
+56
packages/core-extensions/src/notices/webpackModules/component.tsx
···
+
import React from "@moonlight-mod/wp/react";
+
import Dispatcher from "@moonlight-mod/wp/discord/Dispatcher";
+
import * as Components from "@moonlight-mod/wp/discord/components/common/index";
+
import { useStateFromStoresObject } from "@moonlight-mod/wp/discord/packages/flux";
+
import NoticesStore from "@moonlight-mod/wp/notices_notices";
+
import type { Notice } from "@moonlight-mod/types/coreExtensions/notices";
+
+
// FIXME: types
+
const { Notice, NoticeCloseButton, PrimaryCTANoticeButton } = Components;
+
+
function popAndDismiss(notice: Notice) {
+
NoticesStore.popNotice();
+
if (notice?.onDismiss) {
+
notice.onDismiss();
+
}
+
if (!NoticesStore.shouldShowNotice()) {
+
Dispatcher.dispatch({
+
type: "NOTICE_DISMISS"
+
});
+
}
+
}
+
+
export default function UpdateNotice() {
+
const { notice } = useStateFromStoresObject([NoticesStore], () => ({
+
notice: NoticesStore.getCurrentNotice()
+
}));
+
+
if (notice == null) return <></>;
+
+
return (
+
<Notice color={notice.color}>
+
{notice.element}
+
+
{(notice.showClose ?? true) && (
+
<NoticeCloseButton
+
onClick={() => popAndDismiss(notice)}
+
noticeType="__moonlight_notice"
+
/>
+
)}
+
+
{(notice.buttons ?? []).map((button) => (
+
<PrimaryCTANoticeButton
+
key={button.name}
+
onClick={() => {
+
if (button.onClick()) {
+
popAndDismiss(notice);
+
}
+
}}
+
noticeType="__moonlight_notice"
+
>
+
{button.name}
+
</PrimaryCTANoticeButton>
+
))}
+
</Notice>
+
);
+
}
+58
packages/core-extensions/src/notices/webpackModules/notices.ts
···
+
import { Store } from "@moonlight-mod/wp/discord/packages/flux";
+
import Dispatcher from "@moonlight-mod/wp/discord/Dispatcher";
+
import type {
+
Notice,
+
Notices
+
} from "@moonlight-mod/types/coreExtensions/notices";
+
+
// very lazy way of doing this, FIXME
+
let open = false;
+
+
class NoticesStore extends Store<any> {
+
private notices: Notice[] = [];
+
+
constructor() {
+
super(Dispatcher);
+
}
+
+
addNotice(notice: Notice) {
+
this.notices.push(notice);
+
if (open && this.notices.length !== 0) {
+
Dispatcher.dispatch({
+
type: "NOTICE_SHOW",
+
notice: { type: "__moonlight_notice" }
+
});
+
}
+
this.emitChange();
+
}
+
+
popNotice() {
+
this.notices.shift();
+
this.emitChange();
+
}
+
+
getCurrentNotice() {
+
return this.notices.length > 0 ? this.notices[0] : null;
+
}
+
+
shouldShowNotice() {
+
return this.notices.length > 0;
+
}
+
}
+
+
const store: Notices = new NoticesStore();
+
+
function showNotice() {
+
open = true;
+
if (store.shouldShowNotice()) {
+
Dispatcher.dispatch({
+
type: "NOTICE_SHOW",
+
notice: { type: "__moonlight_notice" }
+
});
+
}
+
}
+
+
Dispatcher.subscribe("CONNECTION_OPEN", showNotice);
+
Dispatcher.subscribe("CONNECTION_OPEN_SUPPLEMENTAL", showNotice);
+
+
export default store;
+2
packages/core-extensions/src/rocketship/host.ts
···
+
import "./host/permissions";
+
import "./host/venmic";
+96
packages/core-extensions/src/rocketship/host/permissions.ts
···
+
import type { BrowserWindow } from "electron";
+
+
type PermissionRequestHandler = (
+
webContents: Electron.WebContents,
+
permission: string,
+
callback: (permissionGranted: boolean) => void,
+
details: Electron.PermissionRequestHandlerHandlerDetails
+
) => void;
+
+
type PermissionCheckHandler = (
+
webContents: Electron.WebContents | null,
+
permission: string,
+
requestingOrigin: string,
+
details: Electron.PermissionCheckHandlerHandlerDetails
+
) => boolean;
+
+
moonlightHost.events.on(
+
"window-created",
+
(window: BrowserWindow, isMainWindow: boolean) => {
+
if (!isMainWindow) return;
+
const windowSession = window.webContents.session;
+
+
// setPermissionRequestHandler
+
windowSession.setPermissionRequestHandler(
+
(webcontents, permission, callback, details) => {
+
let cbResult = false;
+
function fakeCallback(result: boolean) {
+
cbResult = result;
+
}
+
+
if (caughtPermissionRequestHandler) {
+
caughtPermissionRequestHandler(
+
webcontents,
+
permission,
+
fakeCallback,
+
details
+
);
+
}
+
+
if (permission === "media" || permission === "display-capture") {
+
cbResult = true;
+
}
+
+
callback(cbResult);
+
}
+
);
+
+
let caughtPermissionRequestHandler: PermissionRequestHandler | undefined;
+
+
windowSession.setPermissionRequestHandler =
+
function catchSetPermissionRequestHandler(
+
handler: (
+
webcontents: Electron.WebContents,
+
permission: string,
+
callback: (permissionGranted: boolean) => void
+
) => void
+
) {
+
caughtPermissionRequestHandler = handler;
+
};
+
+
// setPermissionCheckHandler
+
windowSession.setPermissionCheckHandler(
+
(webcontents, permission, requestingOrigin, details) => {
+
return false;
+
}
+
);
+
+
let caughtPermissionCheckHandler: PermissionCheckHandler | undefined;
+
+
windowSession.setPermissionCheckHandler(
+
(webcontents, permission, requestingOrigin, details) => {
+
let result = false;
+
+
if (caughtPermissionCheckHandler) {
+
result = caughtPermissionCheckHandler(
+
webcontents,
+
permission,
+
requestingOrigin,
+
details
+
);
+
}
+
+
if (permission === "media" || permission === "display-capture") {
+
result = true;
+
}
+
+
return result;
+
}
+
);
+
+
windowSession.setPermissionCheckHandler =
+
function catchSetPermissionCheckHandler(handler: PermissionCheckHandler) {
+
caughtPermissionCheckHandler = handler;
+
};
+
}
+
);
+35
packages/core-extensions/src/rocketship/host/types.ts
···
+
// https://github.com/Vencord/venmic/blob/d737ef33eaae7a73d03ec02673e008cf0243434d/lib/module.d.ts
+
type DefaultProps = "node.name" | "application.name";
+
+
type LiteralUnion<LiteralType, BaseType extends string> =
+
| LiteralType
+
| (BaseType & Record<never, never>);
+
+
type Optional<Type, Key extends keyof Type> = Partial<Pick<Type, Key>> &
+
Omit<Type, Key>;
+
+
export type Node<T extends string = never> = Record<
+
LiteralUnion<T, string>,
+
string
+
>;
+
+
export interface LinkData {
+
include: Node[];
+
exclude: Node[];
+
+
ignore_devices?: boolean;
+
+
only_speakers?: boolean;
+
only_default_speakers?: boolean;
+
+
workaround?: Node[];
+
}
+
+
export interface PatchBay {
+
unlink(): void;
+
+
list<T extends string = DefaultProps>(props?: T[]): Node<T>[];
+
link(
+
data: Optional<LinkData, "exclude"> | Optional<LinkData, "include">
+
): boolean;
+
}
+77
packages/core-extensions/src/rocketship/host/venmic.ts
···
+
import type { BrowserWindow } from "electron";
+
import { app, desktopCapturer } from "electron";
+
import path from "node:path";
+
import { type PatchBay } from "./types";
+
+
const logger = moonlightHost.getLogger("rocketship");
+
+
function getPatchbay() {
+
try {
+
const venmic = require(
+
path.join(path.dirname(moonlightHost.asarPath), "..", "venmic.node")
+
) as { PatchBay: new () => PatchBay };
+
const patchbay = new venmic.PatchBay();
+
return patchbay;
+
} catch (error) {
+
logger.error("Failed to load venmic.node:", error);
+
return null;
+
}
+
}
+
+
const patchbay = getPatchbay();
+
+
// TODO: figure out how to map source to window with venmic
+
function linkVenmic() {
+
if (patchbay == null) return false;
+
+
try {
+
const pid =
+
app
+
.getAppMetrics()
+
.find((proc) => proc.name === "Audio Service")
+
?.pid?.toString() ?? "";
+
+
logger.info("Audio Service PID:", pid);
+
+
patchbay.unlink();
+
return patchbay.link({
+
exclude: [
+
{ "application.process.id": pid },
+
{ "media.class": "Stream/Input/Audio" }
+
],
+
ignore_devices: true,
+
only_speakers: true,
+
only_default_speakers: true
+
});
+
} catch (error) {
+
logger.error("Failed to link venmic:", error);
+
return false;
+
}
+
}
+
+
moonlightHost.events.on(
+
"window-created",
+
(window: BrowserWindow, isMainWindow: boolean) => {
+
if (!isMainWindow) return;
+
const windowSession = window.webContents.session;
+
+
// @ts-expect-error these types ancient
+
windowSession.setDisplayMediaRequestHandler(
+
(request: any, callback: any) => {
+
const linked = linkVenmic();
+
desktopCapturer
+
.getSources({ types: ["screen", "window"] })
+
.then((sources) => {
+
//logger.debug("desktopCapturer.getSources", sources);
+
logger.debug("Linked to venmic:", linked);
+
+
callback({
+
video: sources[0],
+
audio: "loopback"
+
});
+
});
+
},
+
{ useSystemPicker: true }
+
);
+
}
+
);
+130
packages/core-extensions/src/rocketship/index.ts
···
+
import { Patch } from "@moonlight-mod/types";
+
+
const logger = moonlight.getLogger("rocketship");
+
const getDisplayMediaOrig = navigator.mediaDevices.getDisplayMedia;
+
+
async function getVenmicStream() {
+
try {
+
const devices = await navigator.mediaDevices.enumerateDevices();
+
logger.debug("Devices:", devices);
+
+
// This isn't vencord :(
+
const id = devices.find((device) => device.label === "vencord-screen-share")
+
?.deviceId;
+
if (!id) return null;
+
logger.debug("Got venmic device ID:", id);
+
+
const stream = await navigator.mediaDevices.getUserMedia({
+
audio: {
+
deviceId: {
+
exact: id
+
},
+
autoGainControl: false,
+
echoCancellation: false,
+
noiseSuppression: false
+
}
+
});
+
+
return stream.getAudioTracks();
+
} catch (error) {
+
logger.warn("Failed to get venmic stream:", error);
+
return null;
+
}
+
}
+
+
navigator.mediaDevices.getDisplayMedia = async function getDisplayMediaRedirect(
+
options
+
) {
+
const orig = await getDisplayMediaOrig.call(this, options);
+
+
const venmic = await getVenmicStream();
+
logger.debug("venmic", venmic);
+
if (venmic != null) {
+
// venmic will be proxying all audio, so we need to remove the original
+
// tracks to not cause overlap
+
for (const track of orig.getAudioTracks()) {
+
orig.removeTrack(track);
+
}
+
+
for (const track of venmic) {
+
orig.addTrack(track);
+
}
+
}
+
+
return orig;
+
};
+
+
export const patches: Patch[] = [
+
// "Ensure discord_voice is happy"
+
{
+
find: "RustAudioDeviceModule",
+
replace: [
+
{
+
match: /static supported\(\)\{.+?\}/,
+
replacement: "static supported(){return true}"
+
},
+
{
+
match: "supported(){return!0}",
+
replacement: "supported(){return true}"
+
}
+
]
+
},
+
// Remove Native media engine from list of choices
+
{
+
find: '.CAMERA_BACKGROUND_LIVE="cameraBackgroundLive"',
+
replace: {
+
match: /.\..{1,2}\.NATIVE,/,
+
replacement: ""
+
}
+
},
+
// Stub out browser checks to allow us to use WebRTC voice on Embedded
+
{
+
find: "Using Unified Plan (",
+
replace: {
+
match: /return .\..{1,2}\?\((.)\.info/,
+
replacement: (_, logger) => `return true?(${logger}.info`
+
}
+
},
+
{
+
find: '"UnifiedConnection("',
+
replace: {
+
match: /this\.videoSupported=.\..{1,2};/,
+
replacement: "this.videoSupported=true;"
+
}
+
},
+
{
+
find: "OculusBrowser",
+
replace: [
+
{
+
match: /"Firefox"===(.)\(\)\.name/g,
+
replacement: (orig, info) => `true||${orig}`
+
}
+
]
+
},
+
{
+
find: ".getMediaEngine().getDesktopSource",
+
replace: {
+
match: /.\.isPlatformEmbedded/,
+
replacement: "false"
+
}
+
},
+
{
+
// Matching MediaEngineStore
+
find: '"displayName","MediaEngineStore")',
+
replace: [
+
// Prevent loading of krisp native module by stubbing out desktop checks
+
{
+
match:
+
/\(\(0,.\.isWindows\)\(\)\|\|\(0,.\.isLinux\)\(\)\|\|.+?&&!__OVERLAY__/,
+
replacement: (orig, macosPlatformCheck) => `false&&!__OVERLAY__`
+
},
+
// Enable loading of web krisp equivelant by replacing isWeb with true
+
{
+
match:
+
/\(0,.\.isWeb\)\(\)&&(.{1,2}\.supports\(.{1,2}\..{1,2}.NOISE_CANCELLATION)/,
+
replacement: (orig, supportsNoiseCancellation) =>
+
`true&&${supportsNoiseCancellation}`
+
}
+
]
+
}
+
];
+10
packages/core-extensions/src/rocketship/manifest.json
···
+
{
+
"id": "rocketship",
+
"apiLevel": 2,
+
"meta": {
+
"name": "Rocketship",
+
"tagline": "Adds new features when using rocketship",
+
"description": "**This extension only works on Linux when using rocketship: https://github.com/moonlight-mod/rocketship**. Adds new features to the Discord Linux client with rocketship, like better screensharing.",
+
"authors": ["NotNite", "Cynosphere", "adryd"]
+
}
+
}
+3 -3
packages/core-extensions/src/settings/index.ts
···
{
find: '"useGenerateUserSettingsSections"',
replace: {
-
match: /(?<=\.push\(.+?\)}\)\)}\),)./,
-
replacement: (sections: string) =>
-
`require("settings_settings").Settings._mutateSections(${sections})`
+
match: /(?<=\.push\(.+?\)}\)\)}\),)(.+?)}/,
+
replacement: (_, sections: string) =>
+
`require("settings_settings").Settings._mutateSections(${sections})}`
}
},
{
+10 -6
packages/injector/src/index.ts
···
app
} from "electron";
import Module from "node:module";
-
import { constants } from "@moonlight-mod/types";
+
import { constants, MoonlightBranch } from "@moonlight-mod/types";
import { readConfig } from "@moonlight-mod/core/config";
import { getExtensions } from "@moonlight-mod/core/extension";
import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
···
constructor(opts: BrowserWindowConstructorOptions) {
oldPreloadPath = opts.webPreferences!.preload;
-
// Only overwrite preload if its the actual main client window
-
if (opts.webPreferences!.preload!.indexOf("discord_desktop_core") > -1) {
+
const isMainWindow =
+
opts.webPreferences!.preload!.indexOf("discord_desktop_core") > -1;
+
+
if (isMainWindow)
opts.webPreferences!.preload = require.resolve("./node-preload.js");
-
}
// Event for modifying window options
-
moonlightHost.events.emit("window-options", opts);
+
moonlightHost.events.emit("window-options", opts, isMainWindow);
super(opts);
// Event for when a window is created
-
moonlightHost.events.emit("window-created", this);
+
moonlightHost.events.emit("window-created", this, isMainWindow);
this.webContents.session.webRequest.onHeadersReceived((details, cb) => {
if (details.responseHeaders != null) {
···
extensions: [],
dependencyGraph: new Map()
},
+
+
version: MOONLIGHT_VERSION,
+
branch: MOONLIGHT_BRANCH as MoonlightBranch,
getConfig,
getConfigOption: <T>(ext: string, name: string) => {
+12 -2
packages/node-preload/src/index.ts
···
import path from "path";
import { readConfig, writeConfig } from "@moonlight-mod/core/config";
-
import { constants } from "@moonlight-mod/types";
+
import { constants, MoonlightBranch } from "@moonlight-mod/types";
import { getExtensions } from "@moonlight-mod/core/extension";
-
import { getExtensionsPath } from "@moonlight-mod/core/util/data";
+
import {
+
getExtensionsPath,
+
getMoonlightDir
+
} from "@moonlight-mod/core/util/data";
import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
import {
loadExtensions,
···
extensions,
processedExtensions,
nativesCache: {},
+
+
version: MOONLIGHT_VERSION,
+
branch: MOONLIGHT_BRANCH as MoonlightBranch,
+
getConfig,
getConfigOption: <T>(ext: string, name: string) => {
const config = getConfig(ext);
···
return new Logger(id);
},
+
getMoonlightDir() {
+
return getMoonlightDir();
+
},
getExtensionDir: (ext: string) => {
const extPath = getExtensionsPath();
return path.join(extPath, ext);
+1
packages/types/src/coreExtensions.ts
···
export * as Settings from "./coreExtensions/settings";
export * as Markdown from "./coreExtensions/markdown";
export * as ContextMenu from "./coreExtensions/contextMenu";
+
export * as Notices from "./coreExtensions/notices";
+21
packages/types/src/coreExtensions/notices.ts
···
+
import type { Store } from "@moonlight-mod/mappings/discord/packages/flux";
+
+
export type NoticeButton = {
+
name: string;
+
onClick: () => boolean; // return true to dismiss the notice after the button is clicked
+
};
+
+
export type Notice = {
+
element: React.ReactNode;
+
color?: string;
+
showClose?: boolean;
+
buttons?: NoticeButton[];
+
onDismiss?: () => void;
+
};
+
+
export type Notices = Store<any> & {
+
addNotice: (notice: Notice) => void;
+
popNotice: () => void;
+
getCurrentNotice: () => Notice | null;
+
shouldShowNotice: () => boolean;
+
};
+3
packages/types/src/discord/require.ts
···
import { Markdown } from "../coreExtensions/markdown";
import { Settings } from "../coreExtensions/settings";
import { Spacepack } from "../coreExtensions/spacepack";
+
import { Notices } from "../coreExtensions/notices";
declare function WebpackRequire(id: string): any;
···
declare function WebpackRequire(id: "contextMenu_contextMenu"): ContextMenu;
declare function WebpackRequire(id: "markdown_markdown"): Markdown;
+
+
declare function WebpackRequire(id: "notices_notices"): Notices;
declare function WebpackRequire(id: "settings_settings"): {
Settings: Settings;
+7 -5
packages/types/src/discord/webpack.ts
···
import WebpackRequire from "./require";
+
import { WebpackRequire as MappingsWebpackRequire } from "@moonlight-mod/mappings";
-
export type WebpackRequireType = typeof WebpackRequire & {
-
c: Record<string, WebpackModule>;
-
m: Record<string, WebpackModuleFunc>;
-
e: (module: number | string) => Promise<void>;
-
};
+
export type WebpackRequireType = typeof MappingsWebpackRequire &
+
typeof WebpackRequire & {
+
c: Record<string, WebpackModule>;
+
m: Record<string, WebpackModuleFunc>;
+
e: (module: number | string) => Promise<void>;
+
};
export type WebpackModule = {
id: string | number;
+16
packages/types/src/globals.ts
···
extensions: DetectedExtension[];
processedExtensions: ProcessedExtensions;
+
version: string;
+
branch: MoonlightBranch;
+
getConfig: (ext: string) => ConfigExtension["config"];
getConfigOption: <T>(ext: string, name: string) => T | undefined;
getLogger: (id: string) => Logger;
···
processedExtensions: ProcessedExtensions;
nativesCache: Record<string, any>;
+
version: string;
+
branch: MoonlightBranch;
+
getConfig: (ext: string) => ConfigExtension["config"];
getConfigOption: <T>(ext: string, name: string) => T | undefined;
getNatives: (ext: string) => any | undefined;
getLogger: (id: string) => Logger;
+
getMoonlightDir: () => string;
getExtensionDir: (ext: string) => string;
writeConfig: (config: Config) => void;
};
···
registerWebpackModule: (module: IdentifiedWebpackModule) => void;
};
+
version: string;
+
branch: MoonlightBranch;
+
getConfig: (ext: string) => ConfigExtension["config"];
getConfigOption: <T>(ext: string, name: string) => T | undefined;
getNatives: (ext: string) => any | undefined;
···
NodePreload = "node-preload",
WebPreload = "web-preload"
}
+
+
export enum MoonlightBranch {
+
STABLE = "stable",
+
NIGHTLY = "nightly",
+
DEV = "dev"
+
}
+6
packages/types/src/import.d.ts
···
export = Markdown;
}
+
declare module "@moonlight-mod/wp/notices_notices" {
+
import { CoreExtensions } from "@moonlight-mod/types";
+
const Notices: CoreExtensions.Notices.Notices;
+
export = Notices;
+
}
+
declare module "@moonlight-mod/wp/settings_settings" {
import { CoreExtensions } from "@moonlight-mod/types";
export const Settings: CoreExtensions.Settings.Settings;
+2
packages/types/src/index.ts
···
const MOONLIGHT_NODE_PRELOAD: boolean;
const MOONLIGHT_WEB_PRELOAD: boolean;
const MOONLIGHT_BROWSER: boolean;
+
const MOONLIGHT_BRANCH: string;
+
const MOONLIGHT_VERSION: string;
var moonlightHost: MoonlightHost;
var moonlightNode: MoonlightNode;
+10 -3
packages/web-preload/src/index.ts
···
registerPatch,
registerWebpackModule
} from "@moonlight-mod/core/patch";
-
import { constants } from "@moonlight-mod/types";
+
import { constants, MoonlightBranch } from "@moonlight-mod/types";
import { installStyles } from "@moonlight-mod/core/styles";
import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
import LunAST from "@moonlight-mod/lunast";
···
registerWebpackModule
},
+
version: MOONLIGHT_VERSION,
+
branch: MOONLIGHT_BRANCH as MoonlightBranch,
+
getConfig: moonlightNode.getConfig.bind(moonlightNode),
getConfigOption: moonlightNode.getConfigOption.bind(moonlightNode),
getNatives: moonlightNode.getNatives.bind(moonlightNode),
···
logger.error("Error setting up web-preload", e);
}
-
window.addEventListener("DOMContentLoaded", () => {
+
if (MOONLIGHT_ENV === "web-preload") {
+
window.addEventListener("DOMContentLoaded", () => {
+
installStyles();
+
});
+
} else {
installStyles();
-
});
+
}
}
if (MOONLIGHT_ENV === "web-preload") {
+1
pnpm-lock.yaml
···
eslint@8.55.0:
resolution: {integrity: sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options.
hasBin: true
espree@9.6.1: