Merge remote-tracking branch 'upstream/dev' into dev
This commit is contained in:
commit
31715ad513
7 changed files with 912 additions and 223 deletions
54
package.json
54
package.json
|
|
@ -15,44 +15,46 @@
|
|||
"devDependencies": {
|
||||
"@saithodev/semantic-release-backmerge": "^4.0.1",
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/exec": "^7.1.0",
|
||||
"@semantic-release/exec": "^6.0.3",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"semantic-release": "^25.0.2",
|
||||
"@swc/core": "^1.15.7",
|
||||
"semantic-release": "^24.2.7",
|
||||
"@swc/core": "^1.13.1",
|
||||
"@swc/helpers": "^0.5.17",
|
||||
"@types/node": "^25.0.3",
|
||||
"@types/node": "^20.19.9",
|
||||
"@types/yargs-parser": "^21.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.50.0",
|
||||
"@typescript-eslint/parser": "^8.50.0",
|
||||
"@typescript-eslint/typescript-estree": "^8.50.0",
|
||||
"chalk": "^5.6.2",
|
||||
"esbuild": "^0.27.2",
|
||||
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
||||
"@typescript-eslint/parser": "^7.18.0",
|
||||
"@typescript-eslint/typescript-estree": "^7.18.0",
|
||||
"chalk": "^5.4.1",
|
||||
"esbuild": "^0.20.2",
|
||||
"esbuild-plugin-globals": "^0.2.0",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-plugin-import-alias": "^1.2.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"eslint-plugin-unused-imports": "^4.3.0",
|
||||
"typescript": "^5.9.3",
|
||||
"yargs-parser": "^22.0.0"
|
||||
"eslint-plugin-unused-imports": "^3.2.0",
|
||||
"typescript": "^5.8.3",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@gullerya/object-observer": "^6.1.4",
|
||||
"@tanstack/react-query": "^5.90.12",
|
||||
"@types/chroma-js": "~3.1.2",
|
||||
"@types/lodash": "~4.17.21",
|
||||
"@types/react": "19.2.7",
|
||||
"es-toolkit": "^1.43.0",
|
||||
"@tanstack/react-query": "^5.83.0",
|
||||
"@types/chroma-js": "~2.4.5",
|
||||
"@types/lodash": "~4.17.20",
|
||||
"@types/react": "19.1.8",
|
||||
"es-toolkit": "^1.39.7",
|
||||
"fuzzysort": "^3.1.0",
|
||||
"intl-messageformat": "^11.0.4",
|
||||
"moment": "2.30.1",
|
||||
"react-native": "0.83.1",
|
||||
"react-native-reanimated": "^4.2.1",
|
||||
"react-native-webview": "^13.16.0",
|
||||
"spitroast": "^2.1.6",
|
||||
"type-fest": "^5.3.1"
|
||||
"intl-messageformat": "^10.7.16",
|
||||
"moment": "2.22.2",
|
||||
"react-devtools": "^7.0.1",
|
||||
"react-native": "0.78.0",
|
||||
"react-native-reanimated": "^3.18.0",
|
||||
"spitroast": "^1.4.4",
|
||||
"type-fest": "^4.41.0"
|
||||
},
|
||||
"trustedDependencies": [
|
||||
"@swc/core"
|
||||
"@swc/core",
|
||||
"electron",
|
||||
"esbuild"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,8 @@ import { CheckState, useFileExists } from "@core/ui/hooks/useFS";
|
|||
import AssetBrowser from "@core/ui/settings/pages/Developer/AssetBrowser";
|
||||
import { useProxy } from "@core/vendetta/storage";
|
||||
import { findAssetId } from "@lib/api/assets";
|
||||
import { connectToDebugger } from "@lib/api/debug";
|
||||
import {
|
||||
getReactDevToolsProp,
|
||||
getReactDevToolsVersion,
|
||||
isLoaderConfigSupported,
|
||||
isReactDevToolsPreloaded,
|
||||
isVendettaLoader,
|
||||
} from "@lib/api/native/loader";
|
||||
import { connectToDebugger, disconnectFromDebugger, isConnectedToDebugger } from "@lib/api/debug";
|
||||
import { getReactDevToolsProp, getReactDevToolsVersion, isLoaderConfigSupported, isReactDevToolsPreloaded, isVendettaLoader } from "@lib/api/native/loader";
|
||||
import { loaderConfig, settings } from "@lib/api/settings";
|
||||
import { lazyDestructure } from "@lib/utils/lazy";
|
||||
import { NavigationNative } from "@metro/common";
|
||||
|
|
@ -31,6 +25,7 @@ import { createStyles, TextStyleSheet } from "@ui/styles";
|
|||
import { NativeModules } from "react-native";
|
||||
import { ScrollView, StyleSheet } from "react-native";
|
||||
import { showToast } from "@ui/toasts";
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
const { hideActionSheet } = lazyDestructure(() =>
|
||||
findByProps("openLazy", "hideActionSheet"),
|
||||
|
|
@ -45,8 +40,7 @@ const { AlertModal, AlertActionButton } = lazyDestructure(() =>
|
|||
findByProps("AlertModal", "AlertActions"),
|
||||
);
|
||||
|
||||
// todo: This link isnt minified, but it probably should be
|
||||
const RDT_EMBED_LINK = "https://codeberg.org/raincord/Devtools/raw/branch/main/reactDevtools.js";
|
||||
const RDT_EMBED_LINK = "https://codeberg.org/raincord/raindevtools/raw/branch/dev/dist/index.bundle";
|
||||
|
||||
const useStyles = createStyles({
|
||||
leadingText: {
|
||||
|
|
@ -64,6 +58,7 @@ const useStyles = createStyles({
|
|||
|
||||
export default function Developer() {
|
||||
const [rdtFileExists, fs] = useFileExists("preloads/reactDevtools.js");
|
||||
const [isDebuggerConnected, setIsDebuggerConnected] = useState(isConnectedToDebugger());
|
||||
|
||||
const styles = useStyles();
|
||||
const navigation = NavigationNative.useNavigation();
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
import { getThemeFromLoader, selectTheme, themes } from "@lib/addons/themes";
|
||||
import { findAssetId } from "@lib/api/assets";
|
||||
import { getLoaderName, getLoaderVersion, isThemeSupported, getReactDevToolsProp } from "@lib/api/native/loader";
|
||||
import { getLoaderName, getLoaderVersion, isThemeSupported, getReactDevToolsProp, isReactDevToolsPreloaded } from "@lib/api/native/loader";
|
||||
import { BundleUpdaterManager, NativeClientInfoModule, NativeDeviceModule } from "@lib/api/native/modules";
|
||||
import { after } from "@lib/api/patcher";
|
||||
import { settings } from "@lib/api/settings";
|
||||
import { logger } from "@lib/utils/logger";
|
||||
import { showToast } from "@ui/toasts";
|
||||
import { version } from "bunny-build-info";
|
||||
import { Platform, type PlatformConstants } from "react-native";
|
||||
import { StyleSheet } from "react-native";
|
||||
import { Platform, type PlatformConstants, StyleSheet } from "react-native";
|
||||
|
||||
export interface RNConstants extends PlatformConstants {
|
||||
// Android
|
||||
|
|
@ -44,38 +43,291 @@ export async function toggleSafeMode() {
|
|||
setTimeout(BundleUpdaterManager.reload, 400);
|
||||
}
|
||||
|
||||
let socket: WebSocket;
|
||||
// --- Debugger State & Logic ---
|
||||
let socket: WebSocket | undefined;
|
||||
let originalConsoleLog: any;
|
||||
let originalConsoleError: any;
|
||||
let originalConsoleWarn: any;
|
||||
let originalLoggerLog: any;
|
||||
let originalLoggerError: any;
|
||||
let originalLoggerWarn: any;
|
||||
|
||||
const VERSION = 1;
|
||||
|
||||
enum MessageType {
|
||||
Hello = 'hello',
|
||||
Hi = 'hi',
|
||||
Log = 'log',
|
||||
Run = 'run',
|
||||
}
|
||||
|
||||
enum LogLevel {
|
||||
Debug = 'debug',
|
||||
Default = 'default',
|
||||
Warn = 'warn',
|
||||
Error = 'error',
|
||||
}
|
||||
|
||||
interface LogMessage {
|
||||
type: MessageType.Log;
|
||||
data: {
|
||||
level: LogLevel;
|
||||
message: any[];
|
||||
};
|
||||
}
|
||||
|
||||
interface HelloMessage {
|
||||
type: MessageType.Hello;
|
||||
data: {
|
||||
version: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface RunMessage {
|
||||
type: MessageType.Run;
|
||||
data: {
|
||||
code: string;
|
||||
};
|
||||
}
|
||||
|
||||
function serializeMessage(msg: any): string {
|
||||
return JSON.stringify(msg);
|
||||
}
|
||||
|
||||
function sendLog(level: LogLevel, ...args: any[]) {
|
||||
if (socket?.readyState === WebSocket.OPEN) {
|
||||
const message: LogMessage = {
|
||||
type: MessageType.Log,
|
||||
data: {
|
||||
level,
|
||||
message: args
|
||||
}
|
||||
};
|
||||
socket.send(serializeMessage(message));
|
||||
}
|
||||
}
|
||||
|
||||
function patchConsoleAndLogger() {
|
||||
originalConsoleLog = console.log;
|
||||
console.log = function(...args: any[]) {
|
||||
originalConsoleLog.apply(console, args);
|
||||
sendLog(LogLevel.Default, ...args);
|
||||
};
|
||||
|
||||
originalConsoleError = console.error;
|
||||
console.error = function(...args: any[]) {
|
||||
originalConsoleError.apply(console, args);
|
||||
sendLog(LogLevel.Error, ...args);
|
||||
};
|
||||
|
||||
originalConsoleWarn = console.warn;
|
||||
console.warn = function(...args: any[]) {
|
||||
originalConsoleWarn.apply(console, args);
|
||||
sendLog(LogLevel.Warn, ...args);
|
||||
};
|
||||
|
||||
if (logger) {
|
||||
originalLoggerLog = logger.log;
|
||||
logger.log = function(...args: any[]) {
|
||||
originalLoggerLog.apply(logger, args);
|
||||
sendLog(LogLevel.Default, ...args);
|
||||
};
|
||||
|
||||
originalLoggerError = logger.error;
|
||||
logger.error = function(...args: any[]) {
|
||||
originalLoggerError.apply(logger, args);
|
||||
sendLog(LogLevel.Error, ...args);
|
||||
};
|
||||
|
||||
originalLoggerWarn = logger.warn;
|
||||
logger.warn = function(...args: any[]) {
|
||||
originalLoggerWarn.apply(logger, args);
|
||||
sendLog(LogLevel.Warn, ...args);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function unpatchConsoleAndLogger() {
|
||||
if (originalConsoleLog) {
|
||||
console.log = originalConsoleLog;
|
||||
originalConsoleLog = undefined;
|
||||
}
|
||||
if (originalConsoleError) {
|
||||
console.error = originalConsoleError;
|
||||
originalConsoleError = undefined;
|
||||
}
|
||||
if (originalConsoleWarn) {
|
||||
console.warn = originalConsoleWarn;
|
||||
originalConsoleWarn = undefined;
|
||||
}
|
||||
|
||||
if (logger) {
|
||||
if (originalLoggerLog) {
|
||||
logger.log = originalLoggerLog;
|
||||
originalLoggerLog = undefined;
|
||||
}
|
||||
if (originalLoggerError) {
|
||||
logger.error = originalLoggerError;
|
||||
originalLoggerError = undefined;
|
||||
}
|
||||
if (originalLoggerWarn) {
|
||||
logger.warn = originalLoggerWarn;
|
||||
originalLoggerWarn = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function connectToDebugger(url: string) {
|
||||
if (socket !== undefined && socket.readyState !== WebSocket.CLOSED) socket.close();
|
||||
if (socket !== undefined && socket.readyState !== WebSocket.CLOSED) {
|
||||
unpatchConsoleAndLogger();
|
||||
socket.close();
|
||||
}
|
||||
|
||||
if (!url) {
|
||||
showToast("Invalid debugger URL!", findAssetId("Small"));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
socket = new WebSocket(`ws://${url}`);
|
||||
|
||||
socket.addEventListener("open", () => showToast("Connected to debugger.", findAssetId("Check")));
|
||||
socket.addEventListener("open", () => {
|
||||
showToast("Connected to debugger.", findAssetId("Check"));
|
||||
|
||||
const hello: HelloMessage = {
|
||||
type: MessageType.Hello,
|
||||
data: {
|
||||
version: VERSION
|
||||
}
|
||||
};
|
||||
socket?.send(serializeMessage(hello));
|
||||
|
||||
patchConsoleAndLogger();
|
||||
});
|
||||
|
||||
socket.addEventListener("message", (message: any) => {
|
||||
try {
|
||||
(0, eval)(message.data);
|
||||
const data = JSON.parse(message.data);
|
||||
|
||||
if (data.type === MessageType.Run && data.data?.code) {
|
||||
try {
|
||||
(0, eval)(data.data.code);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
console.error("Error executing remote code:", e);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
try {
|
||||
(0, eval)(message.data);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.addEventListener("close", () => {
|
||||
showToast("Disconnected from debugger.", findAssetId("Small"));
|
||||
unpatchConsoleAndLogger();
|
||||
});
|
||||
|
||||
socket.addEventListener("error", (err: any) => {
|
||||
console.log(`Debugger error: ${err.message}`);
|
||||
showToast("An error occurred with the debugger connection!", findAssetId("Small"));
|
||||
unpatchConsoleAndLogger();
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error("Failed to connect to debugger:", e);
|
||||
showToast("Failed to connect to debugger!", findAssetId("Small"));
|
||||
}
|
||||
}
|
||||
|
||||
export function disconnectFromDebugger() {
|
||||
if (socket) {
|
||||
unpatchConsoleAndLogger();
|
||||
socket.close();
|
||||
socket = undefined;
|
||||
showToast("Disconnected from debugger.", findAssetId("Check"));
|
||||
}
|
||||
}
|
||||
|
||||
export function isConnectedToDebugger(): boolean {
|
||||
return socket?.readyState === WebSocket.OPEN;
|
||||
}
|
||||
|
||||
// --- React DevTools (RDT) Logic ---
|
||||
const rdtPort = 8097;
|
||||
export let rdtClient: WebSocket | null = null;
|
||||
export let rdtConnected = false;
|
||||
const changeHooks = new Set<(value: boolean) => void>();
|
||||
|
||||
function bump() {
|
||||
for (const x of changeHooks) x(rdtConnected);
|
||||
}
|
||||
|
||||
function cleanupRdt() {
|
||||
rdtClient = null;
|
||||
rdtConnected = false;
|
||||
bump();
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function connectRdt(url: string, quiet?: boolean) {
|
||||
if (!isReactDevToolsPreloaded() || rdtClient) return;
|
||||
|
||||
const base = url.split(":").slice(0, -1).join(":");
|
||||
const ws = (rdtClient = new WebSocket(`ws://${base}:${rdtPort}`));
|
||||
|
||||
ws.addEventListener('open', () => {
|
||||
if (!quiet) showToast("Connected to React DevTools", findAssetId("CheckmarkSmallIcon"));
|
||||
rdtConnected = true;
|
||||
bump();
|
||||
});
|
||||
|
||||
ws.addEventListener('close', () => {
|
||||
cleanupRdt();
|
||||
});
|
||||
|
||||
ws.addEventListener('error', (e: any) => {
|
||||
cleanupRdt();
|
||||
const err = e?.message ?? e?.stack ?? String(e);
|
||||
logger.error('React DevTools error:', err);
|
||||
if (!quiet) showToast(err, findAssetId("CircleXIcon-primary"));
|
||||
});
|
||||
|
||||
const devTools = window[getReactDevToolsProp() || "__vendetta_rdc"];
|
||||
if (devTools?.connectToDevTools) {
|
||||
devTools.connectToDevTools({
|
||||
websocket: ws,
|
||||
resolveRNStyle: StyleSheet.flatten,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function disconnectRdt() {
|
||||
rdtClient?.close();
|
||||
}
|
||||
|
||||
export function useIsRdtConnected() {
|
||||
const [connected, update] = React.useState(rdtConnected);
|
||||
|
||||
React.useEffect(() => {
|
||||
changeHooks.add(update);
|
||||
return () => void changeHooks.delete(update);
|
||||
}, []);
|
||||
|
||||
return connected;
|
||||
}
|
||||
|
||||
// --- Core Utils ---
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function patchLogHook() {
|
||||
const unpatch = after("nativeLoggingHook", globalThis, args => {
|
||||
if (socket?.readyState === WebSocket.OPEN) socket.send(JSON.stringify({ message: args[0], level: args[1] }));
|
||||
if (socket?.readyState === WebSocket.OPEN) {
|
||||
sendLog(args[1] === 'error' ? LogLevel.Error : args[1] === 'warn' ? LogLevel.Warn : LogLevel.Default, args[0]);
|
||||
}
|
||||
logger.log(args[0]);
|
||||
});
|
||||
|
||||
|
|
@ -89,19 +341,13 @@ export function patchLogHook() {
|
|||
export const versionHash = version;
|
||||
|
||||
export function getDebugInfo() {
|
||||
// Hermes
|
||||
const hermesProps = window.HermesInternal.getRuntimeProperties();
|
||||
const hermesVer = hermesProps["OSS Release Version"];
|
||||
const padding = "for RN ";
|
||||
|
||||
// RN
|
||||
const PlatformConstants = Platform.constants as RNConstants;
|
||||
const rnVer = PlatformConstants.reactNativeVersion;
|
||||
|
||||
return {
|
||||
/**
|
||||
* @deprecated use `bunny` field
|
||||
* */
|
||||
vendetta: {
|
||||
version: versionHash.split("-")[0],
|
||||
loader: getLoaderName(),
|
||||
|
|
@ -126,47 +372,17 @@ export function getDebugInfo() {
|
|||
buildType: hermesProps.Build,
|
||||
bytecodeVersion: hermesProps["Bytecode Version"],
|
||||
},
|
||||
...Platform.select(
|
||||
{
|
||||
android: {
|
||||
os: {
|
||||
name: "Android",
|
||||
version: PlatformConstants.Release,
|
||||
sdk: PlatformConstants.Version
|
||||
},
|
||||
},
|
||||
ios: {
|
||||
os: {
|
||||
name: PlatformConstants.systemName,
|
||||
version: PlatformConstants.osVersion
|
||||
},
|
||||
}
|
||||
}
|
||||
)!,
|
||||
...Platform.select(
|
||||
{
|
||||
android: {
|
||||
device: {
|
||||
manufacturer: PlatformConstants.Manufacturer,
|
||||
brand: PlatformConstants.Brand,
|
||||
model: PlatformConstants.Model,
|
||||
codename: NativeDeviceModule.device
|
||||
}
|
||||
},
|
||||
ios: {
|
||||
device: {
|
||||
manufacturer: NativeDeviceModule.deviceManufacturer,
|
||||
brand: NativeDeviceModule.deviceBrand,
|
||||
model: NativeDeviceModule.deviceModel,
|
||||
codename: NativeDeviceModule.device
|
||||
}
|
||||
}
|
||||
}
|
||||
)!
|
||||
...Platform.select({
|
||||
android: { os: { name: "Android", version: PlatformConstants.Release, sdk: PlatformConstants.Version } },
|
||||
ios: { os: { name: PlatformConstants.systemName, version: PlatformConstants.osVersion } }
|
||||
})!,
|
||||
...Platform.select({
|
||||
android: { device: { manufacturer: PlatformConstants.Manufacturer, brand: PlatformConstants.Brand, model: PlatformConstants.Model, codename: NativeDeviceModule.device } },
|
||||
ios: { device: { manufacturer: NativeDeviceModule.deviceManufacturer, brand: NativeDeviceModule.deviceBrand, model: NativeDeviceModule.deviceModel, codename: NativeDeviceModule.device } }
|
||||
})!
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
|
@ -180,22 +396,11 @@ export function initDebugger() {
|
|||
}
|
||||
if (settings.autoDevTools) {
|
||||
try {
|
||||
(async () => {
|
||||
const devTools = window[getReactDevToolsProp() || "__vendetta_rdc"];
|
||||
|
||||
if (!devTools?.connectToDevTools) {
|
||||
showToast("Invalid devTools URL!", findAssetId("Small"));
|
||||
return;
|
||||
if (settings.devToolsUrl) {
|
||||
connectRdt(settings.devToolsUrl, true);
|
||||
}
|
||||
|
||||
await devTools.connectToDevTools({
|
||||
host: settings.devToolsUrl.split(":")?.[0],
|
||||
resolveRNStyle: StyleSheet.flatten,
|
||||
});
|
||||
})();
|
||||
} catch (e) {
|
||||
logger.error("Failed to connect to ReactDevTools during startup:", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -175,10 +175,7 @@ export function getThemeFilePath() {
|
|||
|
||||
export function isReactDevToolsPreloaded() {
|
||||
if (isPyonLoader()) {
|
||||
return Boolean(window.__reactDevTools);
|
||||
}
|
||||
if (isRa1nLoader()) {
|
||||
return false;
|
||||
return Boolean(window.__REACT_DEVTOOLS__);
|
||||
}
|
||||
if (isVendettaLoader()) {
|
||||
return vendettaLoaderIdentity!!.features.devtools != null;
|
||||
|
|
@ -191,7 +188,7 @@ export function getReactDevToolsProp(): string | null {
|
|||
if (!isReactDevToolsPreloaded()) return null;
|
||||
|
||||
if (isPyonLoader()) {
|
||||
window.__pyoncord_rdt = window.__reactDevTools.exports;
|
||||
window.__pyoncord_rdt = window.__REACT_DEVTOOLS__.exports;
|
||||
return "__pyoncord_rdt";
|
||||
}
|
||||
|
||||
|
|
@ -206,7 +203,7 @@ export function getReactDevToolsVersion() {
|
|||
if (!isReactDevToolsPreloaded()) return null;
|
||||
|
||||
if (isPyonLoader()) {
|
||||
return window.__reactDevTools.version || null;
|
||||
return window.__REACT_DEVTOOLS__.version || null;
|
||||
}
|
||||
if (isVendettaLoader()) {
|
||||
return vendettaLoaderIdentity!!.features.devtools!!.version;
|
||||
|
|
|
|||
67
src/lib/api/reactdevtools.ts
Normal file
67
src/lib/api/reactdevtools.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import { logger } from "@lib/utils/logger";
|
||||
import { getReactDevToolsProp, isReactDevToolsPreloaded } from "./native/loader";
|
||||
import { showToast } from "@lib/ui/toasts";
|
||||
import { findAssetId } from "./assets";
|
||||
|
||||
const rdtPort = 8097;
|
||||
|
||||
export let rdtClient: WebSocket | null = null;
|
||||
export let rdtConnected = false;
|
||||
|
||||
const changeHooks = new Set<(value: boolean) => void>();
|
||||
|
||||
function bump() {
|
||||
for (const x of changeHooks) x(rdtConnected);
|
||||
}
|
||||
function cleanup() {
|
||||
rdtClient = null;
|
||||
rdtConnected = false;
|
||||
bump();
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function connectRdt(url: string, quiet?: boolean) {
|
||||
if (!isReactDevToolsPreloaded() || rdtClient) return;
|
||||
|
||||
const base = url.split(":").slice(0, -1);
|
||||
const ws = (rdtClient = new WebSocket(`ws://${base}:${rdtPort}`));
|
||||
|
||||
ws.addEventListener('open', () => {
|
||||
if (!quiet) showToast("Connected to React DevTools", findAssetId("CheckmarkSmallIcon"));
|
||||
|
||||
rdtConnected = true;
|
||||
bump();
|
||||
});
|
||||
|
||||
ws.addEventListener('close', () => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
ws.addEventListener('error', (e: any) => {
|
||||
cleanup();
|
||||
|
||||
const err = e?.message ?? e?.stack ?? String(e);
|
||||
logger.error('React DevTools error:', err);
|
||||
|
||||
if (!quiet) showToast(err, findAssetId("CircleXIcon-primary"));
|
||||
});
|
||||
|
||||
window[getReactDevToolsProp() || "__vendetta_rdc"].connectToDevTools({
|
||||
websocket: ws,
|
||||
});
|
||||
}
|
||||
|
||||
export function disconnectRdt() {
|
||||
rdtClient?.close();
|
||||
}
|
||||
|
||||
export function useIsRdtConnected() {
|
||||
const [connected, update] = React.useState(rdtConnected);
|
||||
|
||||
React.useEffect(() => {
|
||||
changeHooks.add(update);
|
||||
return () => void changeHooks.delete(update);
|
||||
});
|
||||
|
||||
return connected;
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { findByNameLazy } from "@metro/wrappers";
|
||||
|
||||
type LoggerFunction = (...messages: any[]) => void;
|
||||
|
||||
export interface Logger {
|
||||
log: LoggerFunction;
|
||||
info: LoggerFunction;
|
||||
|
|
@ -13,3 +14,185 @@ export interface Logger {
|
|||
|
||||
export const LoggerClass = findByNameLazy("Logger");
|
||||
export const logger: Logger = new LoggerClass("ShiggyCord");
|
||||
|
||||
let socket: WebSocket | undefined;
|
||||
let originalConsoleLog: any;
|
||||
let originalConsoleError: any;
|
||||
let originalConsoleWarn: any;
|
||||
let originalLoggerLog: any;
|
||||
let originalLoggerError: any;
|
||||
let originalLoggerWarn: any;
|
||||
|
||||
interface LogMessage {
|
||||
type: 'log';
|
||||
data: {
|
||||
level: 'debug' | 'default' | 'warn' | 'error';
|
||||
message: any[];
|
||||
};
|
||||
}
|
||||
|
||||
interface HelloMessage {
|
||||
type: 'hello';
|
||||
data: {
|
||||
version: number;
|
||||
};
|
||||
}
|
||||
|
||||
const VERSION = 1;
|
||||
|
||||
function serializeMessage(msg: any): string {
|
||||
return JSON.stringify(msg);
|
||||
}
|
||||
|
||||
function sendLog(level: 'debug' | 'default' | 'warn' | 'error', ...args: any[]) {
|
||||
if (socket?.readyState === WebSocket.OPEN) {
|
||||
const message: LogMessage = {
|
||||
type: 'log',
|
||||
data: {
|
||||
level,
|
||||
message: args
|
||||
}
|
||||
};
|
||||
socket.send(serializeMessage(message));
|
||||
}
|
||||
}
|
||||
|
||||
function patchConsoleAndLogger() {
|
||||
originalConsoleLog = console.log;
|
||||
console.log = function(...args: any[]) {
|
||||
originalConsoleLog.apply(console, args);
|
||||
sendLog('default', ...args);
|
||||
};
|
||||
|
||||
originalConsoleError = console.error;
|
||||
console.error = function(...args: any[]) {
|
||||
originalConsoleError.apply(console, args);
|
||||
sendLog('error', ...args);
|
||||
};
|
||||
|
||||
originalConsoleWarn = console.warn;
|
||||
console.warn = function(...args: any[]) {
|
||||
originalConsoleWarn.apply(console, args);
|
||||
sendLog('warn', ...args);
|
||||
};
|
||||
|
||||
if (logger) {
|
||||
originalLoggerLog = logger.log;
|
||||
logger.log = function(...args: any[]) {
|
||||
originalLoggerLog.apply(logger, args);
|
||||
sendLog('default', ...args);
|
||||
};
|
||||
|
||||
originalLoggerError = logger.error;
|
||||
logger.error = function(...args: any[]) {
|
||||
originalLoggerError.apply(logger, args);
|
||||
sendLog('error', ...args);
|
||||
};
|
||||
|
||||
originalLoggerWarn = logger.warn;
|
||||
logger.warn = function(...args: any[]) {
|
||||
originalLoggerWarn.apply(logger, args);
|
||||
sendLog('warn', ...args);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function unpatchConsoleAndLogger() {
|
||||
if (originalConsoleLog) {
|
||||
console.log = originalConsoleLog;
|
||||
originalConsoleLog = undefined;
|
||||
}
|
||||
if (originalConsoleError) {
|
||||
console.error = originalConsoleError;
|
||||
originalConsoleError = undefined;
|
||||
}
|
||||
if (originalConsoleWarn) {
|
||||
console.warn = originalConsoleWarn;
|
||||
originalConsoleWarn = undefined;
|
||||
}
|
||||
|
||||
if (logger) {
|
||||
if (originalLoggerLog) {
|
||||
logger.log = originalLoggerLog;
|
||||
originalLoggerLog = undefined;
|
||||
}
|
||||
if (originalLoggerError) {
|
||||
logger.error = originalLoggerError;
|
||||
originalLoggerError = undefined;
|
||||
}
|
||||
if (originalLoggerWarn) {
|
||||
logger.warn = originalLoggerWarn;
|
||||
originalLoggerWarn = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function connectToDebugger(url: string) {
|
||||
if (socket !== undefined && socket.readyState !== WebSocket.CLOSED) {
|
||||
unpatchConsoleAndLogger();
|
||||
socket.close();
|
||||
}
|
||||
|
||||
if (!url) {
|
||||
console.error("Invalid debugger URL!");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
socket = new WebSocket(`ws://${url}`);
|
||||
|
||||
socket.addEventListener("open", () => {
|
||||
console.log("Connected to debugger.");
|
||||
|
||||
const hello: HelloMessage = {
|
||||
type: 'hello',
|
||||
data: {
|
||||
version: VERSION
|
||||
}
|
||||
};
|
||||
socket?.send(serializeMessage(hello));
|
||||
|
||||
patchConsoleAndLogger();
|
||||
});
|
||||
|
||||
socket.addEventListener("message", (message: any) => {
|
||||
try {
|
||||
const data = JSON.parse(message.data);
|
||||
|
||||
if (data.type === 'run' && data.data?.code) {
|
||||
try {
|
||||
(0, eval)(data.data.code);
|
||||
} catch (e) {
|
||||
console.error("Error executing remote code:", e);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Error processing message:", e);
|
||||
}
|
||||
});
|
||||
|
||||
socket.addEventListener("close", () => {
|
||||
console.log("Disconnected from debugger.");
|
||||
unpatchConsoleAndLogger();
|
||||
});
|
||||
|
||||
socket.addEventListener("error", (err: any) => {
|
||||
console.error(`Debugger error: ${err.message}`);
|
||||
unpatchConsoleAndLogger();
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("Failed to connect to debugger:", e);
|
||||
}
|
||||
}
|
||||
|
||||
export function disconnectFromDebugger() {
|
||||
if (socket) {
|
||||
unpatchConsoleAndLogger();
|
||||
socket.close();
|
||||
socket = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function isConnectedToDebugger(): boolean {
|
||||
return socket?.readyState === WebSocket.OPEN;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue