diff --git a/src/core/ui/settings/pages/Developer/index.tsx b/src/core/ui/settings/pages/Developer/index.tsx
index 217e0aa..4e9e23c 100644
--- a/src/core/ui/settings/pages/Developer/index.tsx
+++ b/src/core/ui/settings/pages/Developer/index.tsx
@@ -4,337 +4,244 @@ 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 { 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";
-import {
- Button,
- LegacyFormText,
- Stack,
- TableRow,
- TableRowGroup,
- TableSwitchRow,
- TextInput,
-} from "@metro/common/components";
+import { Button, LegacyFormText, Stack, TableRow, TableRowGroup, TableSwitchRow, TextInput } from "@metro/common/components";
import { findByProps } from "@metro/wrappers";
import { semanticColors } from "@ui/color";
import { ErrorBoundary } from "@ui/components";
-import ErrorBoundaryScreen from "@core/ui/reporter/components/ErrorBoundaryScreen";
import { createStyles, TextStyleSheet } from "@ui/styles";
import { NativeModules } from "react-native";
import { ScrollView, StyleSheet } from "react-native";
import { showToast } from "@ui/toasts";
-const { hideActionSheet } = lazyDestructure(() =>
- findByProps("openLazy", "hideActionSheet"),
-);
-const { showSimpleActionSheet } = lazyDestructure(() =>
- findByProps("showSimpleActionSheet"),
-);
-const { openAlert } = lazyDestructure(() =>
- findByProps("openAlert", "dismissAlert"),
-);
-const { AlertModal, AlertActionButton } = lazyDestructure(() =>
- findByProps("AlertModal", "AlertActions"),
-);
+const { hideActionSheet } = lazyDestructure(() => findByProps("openLazy", "hideActionSheet"));
+const { showSimpleActionSheet } = lazyDestructure(() => findByProps("showSimpleActionSheet"));
+const { openAlert } = lazyDestructure(() => findByProps("openAlert", "dismissAlert"));
+const { AlertModal, AlertActionButton } = lazyDestructure(() => findByProps("AlertModal", "AlertActions"));
const RDT_EMBED_LINK = "https://cdn.bwlok.dev/rdt/devTools.js";
const useStyles = createStyles({
- leadingText: {
- ...TextStyleSheet["heading-md/semibold"],
- color: semanticColors.TEXT_MUTED,
- marginRight: -4,
- },
- inputGroup: {
- marginTop: 4,
- borderTopLeftRadius: 16,
- borderTopRightRadius: 16,
- overflow: "hidden",
- },
+ leadingText: {
+ ...TextStyleSheet["heading-md/semibold"],
+ color: semanticColors.TEXT_MUTED,
+ marginRight: -4
+ },
});
export default function Developer() {
- const [rdtFileExists, fs] = useFileExists("preloads/reactDevtools.js");
+ const [rdtFileExists, fs] = useFileExists("preloads/reactDevtools.js");
- const styles = useStyles();
- const navigation = NavigationNative.useNavigation();
+ const styles = useStyles();
+ const navigation = NavigationNative.useNavigation();
- useProxy(settings);
- useProxy(loaderConfig);
+ useProxy(settings);
+ useProxy(loaderConfig);
- return (
-
-
-
-
- (
-
- ws://
-
- )}
- defaultValue={settings.debuggerUrl}
- onChange={(v: string) => (settings.debuggerUrl = v)}
- />
-
- }
- onPress={() => connectToDebugger(settings.debuggerUrl)}
- />
-
-
+ return (
+
+
+
- {isReactDevToolsPreloaded() && (
-
- (
-
- ws://
-
- )}
- defaultValue={settings.devToolsUrl}
- onChange={(v: string) => (settings.devToolsUrl = v)}
- />
-
-
- }
- onPress={async () => {
- if (!settings.devToolsUrl?.trim()) {
- showToast("Invalid devTools URL!", findAssetId("Small"));
- return;
- }
-
- try {
- const devTools =
- window[getReactDevToolsProp() || "__vendetta_rdc"];
-
- if (!devTools?.connectToDevTools) {
- showToast(
- "React DevTools not available",
- findAssetId("Small"),
- );
- return;
- }
-
- await devTools.connectToDevTools({
- host: settings.devToolsUrl.split(":")?.[0],
- resolveRNStyle: StyleSheet.flatten,
- });
- } catch (error) {
- showToast(
- "Failed to connect to React DevTools",
- findAssetId("Small"),
- );
- }
- }}
- />
-
-
- {isLoaderConfigSupported() && isVendettaLoader() && (
-
- }
- value={loaderConfig.loadReactDevTools}
- onValueChange={(v: boolean) => {
- loaderConfig.loadReactDevTools = v;
- }}
- />
- )}
-
- )}
-
- {isLoaderConfigSupported() && (
-
- }
- value={loaderConfig.customLoadUrl.enabled}
- onValueChange={(v: boolean) => {
- loaderConfig.customLoadUrl.enabled = v;
- }}
- />
- {loaderConfig.customLoadUrl.enabled && (
-
- (loaderConfig.customLoadUrl.url = v)
- }
- placeholder="http://localhost:4040/shiggycord.js"
- label={Strings.SHIGGYCORD_URL}
- />
- }
- />
- )}
-
- )}
-
-
- }
- trailing={TableRow.Arrow}
- onPress={() =>
- navigation.push("SHIGGYCORD_CUSTOM_PAGE", {
- title: Strings.ASSET_BROWSER,
- render: AssetBrowser,
- })
- }
- />
- }
- trailing={
-
-
-
- }
- onPress={() => {
- openAlert(
- "shiggycord-clear-bundle-reload-confirmation",
-
-
- NativeModules.BundleUpdaterManager.reload()
- }
- />
-
+ }
+ value={settings.autoDebugger}
+ onValueChange={(v: boolean) => {
+ settings.autoDebugger = v;
+ }}
/>
- }
- />,
- );
- }}
- />
-
-
-
-
- );
-}
+ }
+ onPress={() => connectToDebugger(settings.debuggerUrl)}
+ />
+
+
+ {isReactDevToolsPreloaded() && <>
+
+
+ ws://}
+ defaultValue={settings.devToolsUrl}
+ onChange={(v: string) => settings.devToolsUrl = v}
+ />
+
+ }
+ value={settings.autoDevTools}
+ onValueChange={(v: boolean) => {
+ settings.autoDevTools = v;
+ }}
+ />
+
+ }
+ onPress={async () => {
+ if (!settings.devToolsUrl?.trim()) {
+ showToast("Invalid devTools URL!", findAssetId("Small"));
+ return;
+ }
+
+ try {
+ const devTools = window[getReactDevToolsProp() || "__vendetta_rdc"];
+
+ if (!devTools?.connectToDevTools) {
+ showToast("Invalid devTools URL!", findAssetId("Small"));
+ return;
+ }
+
+ await devTools.connectToDevTools({
+ host: settings.devToolsUrl.split(":")?.[0],
+ resolveRNStyle: StyleSheet.flatten,
+ });
+ } catch (error) {
+ showToast("Invalid devTools URL!", findAssetId("Small"));
+ }
+ }}
+
+ />
+
+ >}
+ {isLoaderConfigSupported() && <>
+
+ }
+ value={loaderConfig.customLoadUrl.enabled}
+ onValueChange={(v: boolean) => {
+ loaderConfig.customLoadUrl.enabled = v;
+ }}
+ />
+ {loaderConfig.customLoadUrl.enabled && loaderConfig.customLoadUrl.url = v}
+ placeholder="http://localhost:4040/kettu.js"
+ label={Strings.PUPU_URL}
+ />} />}
+ {isReactDevToolsPreloaded() && isVendettaLoader() && }
+ value={loaderConfig.loadReactDevTools}
+ onValueChange={(v: boolean) => {
+ loaderConfig.loadReactDevTools = v;
+ }}
+ />}
+
+ >}
+
+ }
+ onPress={() => {
+ openAlert("pupu-clear-bundle-reload-confirmation",
+ NativeModules.BundleUpdaterManager.reload()} />
+
+
+ }
+ />);
+ }}
+ />
+ }
+ trailing={TableRow.Arrow}
+ onPress={() => navigation.push("PUPU_CUSTOM_PAGE", {
+ title: Strings.ASSET_BROWSER,
+ render: AssetBrowser,
+ })}
+ />
+ }
+ onPress={() => showSimpleActionSheet({
+ key: "ErrorBoundaryTools",
+ header: {
+ title: "Which ErrorBoundary do you want to trip?",
+ icon: ,
+ onClose: () => hideActionSheet(),
+ },
+ options: [
+ // @ts-expect-error
+ // Of course, to trigger an error, we need to do something incorrectly. The below will do!
+ { label: Strings.PUPU, onPress: () => navigation.push("PUPU_CUSTOM_PAGE", { render: () => }) },
+ { label: "Discord", isDestructive: true, onPress: () => navigation.push("PUPU_CUSTOM_PAGE", { noErrorBoundary: true }) },
+ ],
+ })}
+ />
+ }
+ trailing={
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/lib/api/debug.ts b/src/lib/api/debug.ts
index 8d301c2..870739b 100644
--- a/src/lib/api/debug.ts
+++ b/src/lib/api/debug.ts
@@ -1,6 +1,6 @@
import { getThemeFromLoader, selectTheme, themes } from "@lib/addons/themes";
import { findAssetId } from "@lib/api/assets";
-import { getLoaderName, getLoaderVersion, isThemeSupported } from "@lib/api/native/loader";
+import { getLoaderName, getLoaderVersion, isThemeSupported, getReactDevToolsProp } 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";
@@ -8,6 +8,7 @@ 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";
export interface RNConstants extends PlatformConstants {
// Android
@@ -164,3 +165,37 @@ export function getDebugInfo() {
)!
};
}
+
+
+/**
+ * @internal
+ */
+export function initDebugger() {
+ if (settings.autoDebugger) {
+ try {
+ connectToDebugger(settings.debuggerUrl);
+ } catch (e) {
+ logger.error("Failed to connect to Debugger during startup:", e);
+ }
+ }
+ if (settings.autoDevTools) {
+ try {
+ (async () => {
+ const devTools = window[getReactDevToolsProp() || "__vendetta_rdc"];
+
+ if (!devTools?.connectToDevTools) {
+ showToast("Invalid devTools URL!", findAssetId("Small"));
+ return;
+ }
+
+ await devTools.connectToDevTools({
+ host: settings.devToolsUrl.split(":")?.[0],
+ resolveRNStyle: StyleSheet.flatten,
+ });
+ })();
+ } catch (e) {
+ logger.error("Failed to connect to ReactDevTools during startup:", e);
+ }
+ }
+
+}
diff --git a/src/lib/api/settings.ts b/src/lib/api/settings.ts
index 0679937..6d8ffb7 100644
--- a/src/lib/api/settings.ts
+++ b/src/lib/api/settings.ts
@@ -1,111 +1,35 @@
-import {
- createFileBackend,
- createMMKVBackend,
- createStorage,
- wrapSync,
-} from "@core/vendetta/storage";
+import { createFileBackend, createMMKVBackend, createStorage, wrapSync } from "@core/vendetta/storage";
import { getLoaderConfigPath } from "@lib/api/native/loader";
export interface Settings {
- debuggerUrl: string;
- developerSettings: boolean;
- enableDiscordDeveloperSettings: boolean;
- safeMode?: {
- enabled: boolean;
- currentThemeId?: string;
- };
- enableEvalCommand?: boolean;
+ debuggerUrl: string;
+ devToolsUrl: string;
+ autoDebugger: boolean;
+ autoDevTools: boolean;
+ developerSettings: boolean;
+ enableDiscordDeveloperSettings: boolean;
+ safeMode?: {
+ enabled: boolean;
+ currentThemeId?: string;
+ };
+ enableEvalCommand?: boolean;
}
export interface LoaderConfig {
- customLoadUrl: {
- enabled: boolean;
- url: string;
- };
- loadReactDevTools: boolean;
+ customLoadUrl: {
+ enabled: boolean;
+ url: string;
+ };
+ loadReactDevTools: boolean;
}
-/**
- * New: Updater settings persisted in MMKV.
- * Fields:
- * - autoUpdateEnabled: whether updates are applied automatically
- * - checkOnStart: whether to check for updates on app start
- * - updateChannel: optional update channel (stable/beta/alpha)
- * - lastChecked: ISO timestamp of last update check
- *
- * Plugin-related additions:
- * - fetchPluginsOnStart: when true, plugins (external repositories) are fetched at startup;
- * when false, plugin repository fetching is deferred and controlled from the Updater UI.
- * - repoAutoFetchOverrides: per-repository overrides for auto-fetch behavior
- * (keyed by repository URL, boolean value overrides fetchPluginsOnStart).
- */
-export interface UpdaterSettings {
- // Whether updates (bundle-level auto-updates) should be applied automatically
- autoUpdateEnabled: boolean;
- // Whether to perform an update check on app start
- checkOnStart: boolean;
- // Optional channel preference for updates
- updateChannel?: "stable" | "beta" | "alpha";
- // ISO timestamp of the last time the updater checked (plugins + bundle)
- lastChecked?: string | null;
+export const settings = wrapSync(createStorage(createMMKVBackend("VENDETTA_SETTINGS")));
- // -------------------------
- // Plugin-related settings
- // -------------------------
- // Whether external plugin repositories should be fetched at startup
- fetchPluginsOnStart?: boolean;
- // Per-repo override for auto-fetch behavior (repoUrl => boolean)
- repoAutoFetchOverrides?: Record;
-
- // -------------------------
- // Bundle update settings
- // -------------------------
- // How often (in hours) to check the project's releases for bundle updates.
- // Default should be treated as 3 if unset.
- bundleCheckIntervalHours?: number;
- // Releases URL (used for checking the bundle). Defaults to:
- // https://github.com/kmmiio99o/ShiggyCord/releases/
- bundleReleasesUrl?: string;
- // Whether to ignore prerelease releases when checking bundle updates.
- // Defaults to true.
- bundleIgnorePrereleases?: boolean;
- // ISO timestamp of the last bundle check
- lastBundleChecked?: string | null;
- // Last known bundle version discovered by the updater (if any)
- lastBundleVersion?: string | null;
-
- // Latest discovered bundle tag (extracted from custom URL content or GitHub).
- // This represents the most recent tag/identifier the updater detected.
- bundleLatestTag?: string | null;
- // The URL used to determine the latest bundle (download URL or release page).
- bundleLatestUrl?: string | null;
- // Whether a newer bundle than the installed one is available.
- bundleAvailable?: boolean;
-}
-
-export const settings = wrapSync(
- createStorage(createMMKVBackend("VENDETTA_SETTINGS")),
-);
-
-export const loaderConfig = wrapSync(
- createStorage(
+export const loaderConfig = wrapSync(createStorage(
createFileBackend(getLoaderConfigPath(), {
- customLoadUrl: {
- enabled: false,
- url: "http://localhost:4040/shiggycord.js",
- },
- loadReactDevTools: false,
- }),
- ),
-);
-
-/**
- * Updater settings storage.
- * Stored under MMKV key: VENDETTA_UPDATER_SETTINGS
- * Defaults should be handled by UI code when reading values if necessary.
- */
-export const updaterSettings = wrapSync(
- createStorage(
- createMMKVBackend("VENDETTA_UPDATER_SETTINGS"),
- ),
-);
+ customLoadUrl: {
+ enabled: false,
+ url: "http://localhost:4040/kettu.js"
+ }
+ })
+));