diff --git a/src/def.d.ts b/src/def.d.ts
deleted file mode 100644
index 68702bc..0000000
--- a/src/def.d.ts
+++ /dev/null
@@ -1,522 +0,0 @@
-import * as _spitroast from "spitroast";
-import _React from "react";
-import _RN from "react-native";
-import _Clipboard from "@react-native-clipboard/clipboard";
-import _moment from "moment";
-import _chroma from "chroma-js";
-import _lodash from "lodash";
-
-type MetroModules = { [id: number]: any };
-
-// Component types
-interface SummaryProps {
- label: string;
- icon?: string;
- noPadding?: boolean;
- noAnimation?: boolean;
- children: JSX.Element | JSX.Element[];
-}
-
-interface ErrorBoundaryProps {
- children: JSX.Element | JSX.Element[];
-}
-
-interface CodeblockProps {
- selectable?: boolean;
- style?: _RN.TextStyle;
- children?: string;
-}
-
-interface SearchProps {
- onChangeText?: (v: string) => void;
- placeholder?: string;
- style?: _RN.TextStyle;
-}
-
-// Helper types for API functions
-type PropIntellisense
= Record
& Record;
-type PropsFinder = (...props: T[]) => PropIntellisense;
-type PropsFinderAll = (...props: T[]) => PropIntellisense[];
-
-type LoggerFunction = (...messages: any[]) => void;
-interface Logger {
- log: LoggerFunction;
- info: LoggerFunction;
- warn: LoggerFunction;
- error: LoggerFunction;
- time: LoggerFunction;
- trace: LoggerFunction;
- verbose: LoggerFunction;
-}
-
-type SearchTree = Record;
-type SearchFilter = (tree: SearchTree) => boolean;
-interface FindInTreeOptions {
- walkable?: string[];
- ignore?: string[];
- maxDepth?: number;
-}
-
-interface Asset {
- name: string;
- id: number;
-}
-
-export enum ButtonColors {
- BRAND = "brand",
- RED = "red",
- GREEN = "green",
- PRIMARY = "primary",
- TRANSPARENT = "transparent",
- GREY = "grey",
- LIGHTGREY = "lightgrey",
- WHITE = "white",
- LINK = "link"
-}
-
-interface ConfirmationAlertOptions {
- title?: string;
- content: string | JSX.Element | (string | JSX.Element)[];
- confirmText?: string;
- confirmColor?: ButtonColors;
- onConfirm: () => void;
- secondaryConfirmText?: string;
- onConfirmSecondary?: () => void;
- cancelText?: string;
- onCancel?: () => void;
- isDismissable?: boolean;
-}
-
-interface InputAlertProps {
- title?: string;
- confirmText?: string;
- confirmColor?: ButtonColors;
- onConfirm: (input: string) => (void | Promise);
- cancelText?: string;
- placeholder?: string;
- initialValue?: string;
- secureTextEntry?: boolean;
-}
-
-interface Author {
- name: string;
- id?: string;
-}
-
-// See https://github.com/vendetta-mod/polymanifest
-interface PluginManifest {
- name: string;
- description: string;
- authors: Author[];
- main: string;
- hash: string;
- // Vendor-specific field, contains our own data
- vendetta?: {
- icon?: string;
- };
-}
-
-interface Plugin {
- id: string;
- manifest: PluginManifest;
- enabled: boolean;
- update: boolean;
- js: string;
-}
-
-interface ThemeData {
- name: string;
- description?: string;
- authors?: Author[];
- spec: number;
- semanticColors?: Record;
- rawColors?: Record;
- background?: {
- url: string;
- blur?: number;
- /**
- * The alpha value of the background.
- * `CHAT_BACKGROUND` of semanticColors alpha value will be ignored when this is specified
- */
- alpha?: number;
- }
-}
-
-interface Theme {
- id: string;
- selected: boolean;
- data: ThemeData;
-}
-
-interface Settings {
- debuggerUrl: string;
- developerSettings: boolean;
- safeMode?: {
- enabled: boolean;
- currentThemeId?: string;
- };
-}
-
-interface ApplicationCommand {
- description: string;
- name: string;
- options: ApplicationCommandOption[];
- execute: (args: any[], ctx: CommandContext) => CommandResult | void | Promise | Promise;
- id?: string;
- applicationId: string;
- displayName: string;
- displayDescription: string;
- inputType: ApplicationCommandInputType;
- type: ApplicationCommandType;
-}
-
-export enum ApplicationCommandInputType {
- BUILT_IN,
- BUILT_IN_TEXT,
- BUILT_IN_INTEGRATION,
- BOT,
- PLACEHOLDER,
-}
-
-interface ApplicationCommandOption {
- name: string;
- description: string;
- required?: boolean;
- type: ApplicationCommandOptionType;
- displayName: string;
- displayDescription: string;
-}
-
-export enum ApplicationCommandOptionType {
- SUB_COMMAND = 1,
- SUB_COMMAND_GROUP,
- STRING,
- INTEGER,
- BOOLEAN,
- USER,
- CHANNEL,
- ROLE,
- MENTIONABLE,
- NUMBER,
- ATTACHMENT,
-}
-
-export enum ApplicationCommandType {
- CHAT = 1,
- USER,
- MESSAGE,
-}
-
-interface CommandContext {
- channel: any;
- guild: any;
-}
-
-interface CommandResult {
- content: string;
- tts?: boolean;
-}
-
-interface RNConstants extends _RN.PlatformConstants {
- // Android
- Version: number;
- Release: string;
- Serial: string;
- Fingerprint: string;
- Model: string;
- Brand: string;
- Manufacturer: string;
- ServerHost?: string;
-
- // iOS
- forceTouchAvailable: boolean;
- interfaceIdiom: string;
- osVersion: string;
- systemName: string;
-}
-
-/**
- * A key-value storage based upon `SharedPreferences` on Android.
- *
- * These types are based on Android though everything should be the same between
- * platforms.
- */
-interface MMKVManager {
- /**
- * Get the value for the given `key`, or null
- * @param key The key to fetch
- */
- getItem: (key: string) => Promise;
- /**
- * Deletes the value for the given `key`
- * @param key The key to delete
- */
- removeItem: (key: string) => void;
- /**
- * Sets the value of `key` to `value`
- */
- setItem: (key: string, value: string) => void;
- /**
- * Goes through every item in storage and returns it, excluding the
- * keys specified in `exclude`.
- * @param exclude A list of items to exclude from result
- */
- refresh: (exclude: string[]) => Promise>;
- /**
- * You will be murdered if you use this function.
- * Clears ALL of Discord's settings.
- */
- clear: () => void;
-}
-
-interface FileManager {
- /**
- * @param path **Full** path to file
- */
- fileExists: (path: string) => Promise;
- /**
- * Allowed URI schemes on Android: `file://`, `content://` ([See here](https://developer.android.com/reference/android/content/ContentResolver#accepts-the-following-uri-schemes:_3))
- */
- getSize: (uri: string) => Promise;
- /**
- * @param path **Full** path to file
- * @param encoding Set to `base64` in order to encode response
- */
- readFile(path: string, encoding: "base64" | "utf8"): Promise;
- saveFileToGallery?(uri: string, fileName: string, fileType: "PNG" | "JPEG"): Promise;
- /**
- * Beware! This function has differing functionality on iOS and Android.
- * @param storageDir Either `cache` or `documents`.
- * @param path Path in `storageDir`, parents are recursively created.
- * @param data The data to write to the file
- * @param encoding Set to `base64` if `data` is base64 encoded.
- * @returns Promise that resolves to path of the file once it got written
- */
- writeFile(storageDir: "cache" | "documents", path: string, data: string, encoding: "base64" | "utf8"): Promise;
- removeFile(storageDir: "cache" | "documents", path: string): Promise;
- getConstants: () => {
- /**
- * The path the `documents` storage dir (see {@link writeFile}) represents.
- */
- DocumentsDirPath: string;
- CacheDirPath: string;
- };
- /**
- * Will apparently cease to exist some time in the future so please use {@link getConstants} instead.
- * @deprecated
- */
- DocumentsDirPath: string;
-}
-
-type EmitterEvent = "SET" | "GET" | "DEL";
-
-interface EmitterListenerData {
- path: string[];
- value?: any;
-}
-
-type EmitterListener = (
- event: EmitterEvent,
- data: EmitterListenerData | any
-) => any;
-
-type EmitterListeners = Record>
-
-interface Emitter {
- listeners: EmitterListeners;
- on: (event: EmitterEvent, listener: EmitterListener) => void;
- off: (event: EmitterEvent, listener: EmitterListener) => void;
- once: (event: EmitterEvent, listener: EmitterListener) => void;
- emit: (event: EmitterEvent, data: EmitterListenerData) => void;
-}
-
-interface StorageBackend {
- get: () => unknown | Promise;
- set: (data: unknown) => void | Promise;
-}
-
-interface LoaderConfig {
- customLoadUrl: {
- enabled: boolean;
- url: string;
- };
- loadReactDevTools: boolean;
-}
-
-interface LoaderIdentity {
- name: string;
- features: {
- loaderConfig?: boolean;
- devtools?: {
- prop: string;
- version: string;
- },
- themes?: {
- prop: string;
- }
- }
-}
-
-interface DiscordStyleSheet {
- [index: string]: any,
- createStyles: >(sheet: T | (() => T)) => () => T;
- createThemedStyleSheet: typeof import("react-native").StyleSheet.create;
-}
-
-interface VendettaObject {
- patcher: {
- after: typeof _spitroast.after;
- before: typeof _spitroast.before;
- instead: typeof _spitroast.instead;
- };
- metro: {
- find: (filter: (m: any) => boolean) => any;
- findAll: (filter: (m: any) => boolean) => any[];
- findByProps: PropsFinder;
- findByPropsAll: PropsFinderAll;
- findByName: (name: string, defaultExp?: boolean) => any;
- findByNameAll: (name: string, defaultExp?: boolean) => any[];
- findByDisplayName: (displayName: string, defaultExp?: boolean) => any;
- findByDisplayNameAll: (displayName: string, defaultExp?: boolean) => any[];
- findByTypeName: (typeName: string, defaultExp?: boolean) => any;
- findByTypeNameAll: (typeName: string, defaultExp?: boolean) => any[];
- findByStoreName: (name: string) => any;
- common: {
- constants: PropIntellisense<"Fonts" | "Permissions">;
- channels: PropIntellisense<"getVoiceChannelId">;
- i18n: PropIntellisense<"Messages">;
- url: PropIntellisense<"openURL">;
- toasts: PropIntellisense<"open" | "close">;
- stylesheet: DiscordStyleSheet;
- clipboard: typeof _Clipboard;
- assets: PropIntellisense<"registerAsset">;
- invites: PropIntellisense<"acceptInviteAndTransitionToInviteChannel">;
- commands: PropIntellisense<"getBuiltInCommands">;
- navigation: PropIntellisense<"pushLazy">;
- navigationStack: PropIntellisense<"createStackNavigator">;
- NavigationNative: PropIntellisense<"NavigationContainer">;
- // You may ask: "Why not just install Flux's types?"
- // Answer: Discord have a (presumably proprietary) fork. It's wildly different.
- Flux: PropIntellisense<"connectStores">;
- FluxDispatcher: PropIntellisense<"_currentDispatchActionType">;
- React: typeof _React;
- ReactNative: typeof _RN;
- moment: typeof _moment;
- chroma: typeof _chroma;
- lodash: typeof _lodash;
- util: PropIntellisense<"inspect" | "isNullOrUndefined">;
- };
- };
- constants: {
- DISCORD_SERVER: string;
- DISCORD_SERVER_ID: string;
- PLUGINS_CHANNEL_ID: string;
- THEMES_CHANNEL_ID: string;
- GITHUB: string;
- PROXY_PREFIX: string;
- HTTP_REGEX: RegExp;
- HTTP_REGEX_MULTI: RegExp;
- };
- utils: {
- findInReactTree: (tree: SearchTree, filter: SearchFilter) => any;
- findInTree: (tree: SearchTree, filter: SearchFilter, options: FindInTreeOptions) => any;
- safeFetch: (input: RequestInfo | URL, options?: RequestInit, timeout?: number) => Promise;
- unfreeze: (obj: object) => object;
- without: (object: O, ...keys: K) => Omit;
- };
- debug: {
- connectToDebugger: (url: string) => void;
- // TODO: Type output?
- getDebugInfo: () => void;
- }
- ui: {
- components: {
- // Discord
- Forms: PropIntellisense<"Form" | "FormSection">;
- General: PropIntellisense<"Button" | "Text" | "View">;
- Alert: _React.ComponentType;
- Button: _React.ComponentType & { Looks: any, Colors: ButtonColors, Sizes: any };
- HelpMessage: _React.ComponentType;
- SafeAreaView: typeof _RN.SafeAreaView;
- // Vendetta
- Summary: _React.ComponentType;
- ErrorBoundary: _React.ComponentType;
- Codeblock: _React.ComponentType;
- Search: _React.ComponentType;
- }
- toasts: {
- showToast: (content: string, asset?: number) => void;
- };
- alerts: {
- showConfirmationAlert: (options: ConfirmationAlertOptions) => void;
- showCustomAlert: (component: _React.ComponentType, props: any) => void;
- showInputAlert: (options: InputAlertProps) => void;
- };
- assets: {
- all: Record;
- find: (filter: (a: any) => void) => Asset | null | undefined;
- getAssetByName: (name: string) => Asset;
- getAssetByID: (id: number) => Asset;
- getAssetIDByName: (name: string) => number;
- };
- // TODO: Make a vain attempt to type these
- semanticColors: Record;
- rawColors: Record;
- };
- plugins: {
- plugins: Record;
- fetchPlugin: (id: string) => Promise;
- installPlugin: (id: string, enabled?: boolean) => Promise;
- startPlugin: (id: string) => Promise;
- stopPlugin: (id: string, disable?: boolean) => void;
- removePlugin: (id: string) => void;
- getSettings: (id: string) => JSX.Element;
- };
- themes: {
- themes: Record;
- fetchTheme: (id: string, selected?: boolean) => Promise;
- installTheme: (id: string) => Promise;
- selectTheme: (id: string) => Promise;
- removeTheme: (id: string) => Promise;
- getCurrentTheme: () => Theme | null;
- updateThemes: () => Promise;
- };
- commands: {
- registerCommand: (command: ApplicationCommand) => () => void;
- };
- storage: {
- createProxy: (target: T) => { proxy: T, emitter: Emitter };
- useProxy: (storage: T) => T;
- createStorage: (backend: StorageBackend) => Promise>;
- wrapSync: >(store: T) => Awaited;
- awaitSyncWrapper: (store: any) => Promise;
- createMMKVBackend: (store: string) => StorageBackend;
- createFileBackend: (file: string) => StorageBackend;
- };
- settings: Settings;
- loader: {
- identity?: LoaderIdentity;
- config: LoaderConfig;
- };
- logger: Logger;
- version: string;
- unload: () => void;
-}
-
-interface VendettaPluginObject {
- id: string;
- manifest: PluginManifest;
- storage: Record;
-}
-
-declare global {
- type React = typeof _React;
- const __vendettaVersion: string;
-
- interface Window {
- [key: PropertyKey]: any;
- modules: MetroModules;
- vendetta: VendettaObject;
- React: typeof _React;
- }
-}
diff --git a/src/global.d.ts b/src/global.d.ts
new file mode 100644
index 0000000..5640bca
--- /dev/null
+++ b/src/global.d.ts
@@ -0,0 +1,15 @@
+import { MetroModules } from "./lib/metro/filters";
+
+declare global {
+ type React = typeof import("react");
+ const __vendettaVersion: string;
+
+ interface Window {
+ [key: string]: any;
+ modules: MetroModules;
+ vendetta: any;
+ React: typeof import("react");
+ }
+}
+
+export {};
diff --git a/src/lib/commands.ts b/src/lib/commands.ts
index 4717d0b..9fa7be0 100644
--- a/src/lib/commands.ts
+++ b/src/lib/commands.ts
@@ -1,9 +1,68 @@
-import { ApplicationCommand, ApplicationCommandType } from "@types";
import { commands as commandsModule } from "@metro/common";
import { after } from "@lib/patcher";
let commands: ApplicationCommand[] = [];
+export interface ApplicationCommand {
+ description: string;
+ name: string;
+ options: ApplicationCommandOption[];
+ execute: (args: any[], ctx: CommandContext) => CommandResult | void | Promise | Promise;
+ id?: string;
+ applicationId: string;
+ displayName: string;
+ displayDescription: string;
+ inputType: ApplicationCommandInputType;
+ type: ApplicationCommandType;
+}
+
+export enum ApplicationCommandInputType {
+ BUILT_IN,
+ BUILT_IN_TEXT,
+ BUILT_IN_INTEGRATION,
+ BOT,
+ PLACEHOLDER,
+}
+
+export interface ApplicationCommandOption {
+ name: string;
+ description: string;
+ required?: boolean;
+ type: ApplicationCommandOptionType;
+ displayName: string;
+ displayDescription: string;
+}
+
+export enum ApplicationCommandOptionType {
+ SUB_COMMAND = 1,
+ SUB_COMMAND_GROUP,
+ STRING,
+ INTEGER,
+ BOOLEAN,
+ USER,
+ CHANNEL,
+ ROLE,
+ MENTIONABLE,
+ NUMBER,
+ ATTACHMENT,
+}
+
+export enum ApplicationCommandType {
+ CHAT = 1,
+ USER,
+ MESSAGE,
+}
+
+export interface CommandContext {
+ channel: any;
+ guild: any;
+}
+
+export interface CommandResult {
+ content: string;
+ tts?: boolean;
+}
+
export function patchCommands() {
const unpatch = after("getBuiltInCommands", commandsModule, ([type], res: ApplicationCommand[]) => {
if (type === ApplicationCommandType.CHAT) return res.concat(commands);
diff --git a/src/lib/debug.ts b/src/lib/debug.ts
index 7d63b89..8dc35e2 100644
--- a/src/lib/debug.ts
+++ b/src/lib/debug.ts
@@ -1,4 +1,3 @@
-import { RNConstants } from "@types";
import { ReactNative as RN } from "@metro/common";
import { after } from "@lib/patcher";
import { getThemeFromLoader, selectTheme } from "@lib/themes";
@@ -8,8 +7,27 @@ import { showToast } from "@ui/toasts";
import settings from "@lib/settings";
import logger from "@lib/logger";
import { getLoaderName, isThemeSupported } from "./loader";
+import type { PlatformConstants } from "react-native";
export let socket: WebSocket;
+export interface RNConstants extends PlatformConstants {
+ // Android
+ Version: number;
+ Release: string;
+ Serial: string;
+ Fingerprint: string;
+ Model: string;
+ Brand: string;
+ Manufacturer: string;
+ ServerHost?: string;
+
+ // iOS
+ forceTouchAvailable: boolean;
+ interfaceIdiom: string;
+ osVersion: string;
+ systemName: string;
+}
+
export async function toggleSafeMode() {
settings.safeMode = { ...settings.safeMode, enabled: !settings.safeMode?.enabled }
if (isThemeSupported()) {
diff --git a/src/lib/emitter.ts b/src/lib/emitter.ts
index 1bfbc08..4b64555 100644
--- a/src/lib/emitter.ts
+++ b/src/lib/emitter.ts
@@ -1,11 +1,32 @@
-import { Emitter, EmitterEvent, EmitterListener, EmitterListenerData, EmitterListeners } from "@types";
-
export enum Events {
GET = "GET",
SET = "SET",
DEL = "DEL",
};
+export type EmitterEvent = "SET" | "GET" | "DEL";
+
+export interface EmitterListenerData {
+ path: string[];
+ value?: any;
+}
+
+export type EmitterListener = (
+ event: EmitterEvent,
+ data: EmitterListenerData | any
+) => any;
+
+export type EmitterListeners = Record>
+
+export interface Emitter {
+ listeners: EmitterListeners;
+ on: (event: EmitterEvent, listener: EmitterListener) => void;
+ off: (event: EmitterEvent, listener: EmitterListener) => void;
+ once: (event: EmitterEvent, listener: EmitterListener) => void;
+ emit: (event: EmitterEvent, data: EmitterListenerData) => void;
+}
+
+
export default function createEmitter(): Emitter {
return {
listeners: Object.values(Events).reduce((acc, val: string) => ((acc[val] = new Set()), acc), {}) as EmitterListeners,
diff --git a/src/lib/loader.ts b/src/lib/loader.ts
index a96867a..3e454f2 100644
--- a/src/lib/loader.ts
+++ b/src/lib/loader.ts
@@ -1,10 +1,24 @@
-import type { LoaderConfig, LoaderIdentity, Theme } from "@types";
+import { Theme } from "./themes";
// @ts-ignore
const pyonLoaderIdentity = globalThis.__PYON_LOADER__;
// @ts-ignore
const vendettaLoaderIdentity = globalThis.__vendetta_loader;
+export interface VendettaLoaderIdentity {
+ name: string;
+ features: {
+ loaderConfig?: boolean;
+ devtools?: {
+ prop: string;
+ version: string;
+ },
+ themes?: {
+ prop: string;
+ }
+ }
+}
+
export function isVendettaLoader() {
return vendettaLoaderIdentity != null;
}
@@ -48,10 +62,10 @@ function polyfillVendettaLoaderIdentity() {
configurable: true
});
- return loader as LoaderIdentity;
+ return loader as VendettaLoaderIdentity;
}
-export function getVendettaLoaderIdentity(): LoaderIdentity | null {
+export function getVendettaLoaderIdentity(): VendettaLoaderIdentity | null {
// @ts-ignore
if (globalThis.__vendetta_loader) return globalThis.__vendetta_loader;
return polyfillVendettaLoaderIdentity();
diff --git a/src/lib/logger.ts b/src/lib/logger.ts
index 1e107df..2b5fbce 100644
--- a/src/lib/logger.ts
+++ b/src/lib/logger.ts
@@ -1,6 +1,16 @@
-import { Logger } from "@types";
import { findByProps } from "@metro/filters";
+export type LoggerFunction = (...messages: any[]) => void;
+export interface Logger {
+ log: LoggerFunction;
+ info: LoggerFunction;
+ warn: LoggerFunction;
+ error: LoggerFunction;
+ time: LoggerFunction;
+ trace: LoggerFunction;
+ verbose: LoggerFunction;
+}
+
export const logModule = findByProps("setLogFn").default;
const logger: Logger = new logModule("Vendetta");
diff --git a/src/lib/metro/common.ts b/src/lib/metro/common.ts
index 0eeef10..1f86f77 100644
--- a/src/lib/metro/common.ts
+++ b/src/lib/metro/common.ts
@@ -1,8 +1,13 @@
import { find, findByProps, findByStoreName } from "@metro/filters";
-import { DiscordStyleSheet } from "@types";
import { ReactNative as RN } from "@lib/preinit";
import type { StyleSheet } from "react-native";
+export interface DiscordStyleSheet {
+ [index: string]: any,
+ createStyles: >(sheet: T | (() => T)) => () => T;
+ createThemedStyleSheet: typeof import("react-native").StyleSheet.create;
+}
+
const ThemeStore = findByStoreName("ThemeStore");
const colorModule = findByProps("colors", "unsafe_rawColors");
const colorResolver = colorModule.internal ?? colorModule.meta;
diff --git a/src/lib/metro/filters.ts b/src/lib/metro/filters.ts
index 97a9bd6..98d6305 100644
--- a/src/lib/metro/filters.ts
+++ b/src/lib/metro/filters.ts
@@ -1,6 +1,10 @@
-import { MetroModules, PropsFinder, PropsFinderAll } from "@types";
import { instead } from "@lib/patcher";
+export type MetroModules = { [id: number]: any };
+export type PropIntellisense = Record
& Record;
+export type PropsFinder = (...props: T[]) => PropIntellisense;
+export type PropsFinderAll = (...props: T[]) => PropIntellisense[];
+
// Metro require
declare const __r: (moduleId: number) => any;
diff --git a/src/lib/native.ts b/src/lib/native.ts
index 622033b..b365b0b 100644
--- a/src/lib/native.ts
+++ b/src/lib/native.ts
@@ -1,9 +1,81 @@
-import { MMKVManager as _MMKVManager, FileManager as _FileManager } from "@types";
const nmp = window.nativeModuleProxy;
-export const MMKVManager = nmp.MMKVManager as _MMKVManager;
+/**
+ * A key-value storage based upon `SharedPreferences` on Android.
+ *
+ * These types are based on Android though everything should be the same between
+ * platforms.
+ */
+export interface MMKVManager {
+ /**
+ * Get the value for the given `key`, or null
+ * @param key The key to fetch
+ */
+ getItem: (key: string) => Promise;
+ /**
+ * Deletes the value for the given `key`
+ * @param key The key to delete
+ */
+ removeItem: (key: string) => void;
+ /**
+ * Sets the value of `key` to `value`
+ */
+ setItem: (key: string, value: string) => void;
+ /**
+ * Goes through every item in storage and returns it, excluding the
+ * keys specified in `exclude`.
+ * @param exclude A list of items to exclude from result
+ */
+ refresh: (exclude: string[]) => Promise>;
+ /**
+ * You will be murdered if you use this function.
+ * Clears ALL of Discord's settings.
+ */
+ clear: () => void;
+}
+
+export interface FileManager {
+ /**
+ * @param path **Full** path to file
+ */
+ fileExists: (path: string) => Promise;
+ /**
+ * Allowed URI schemes on Android: `file://`, `content://` ([See here](https://developer.android.com/reference/android/content/ContentResolver#accepts-the-following-uri-schemes:_3))
+ */
+ getSize: (uri: string) => Promise;
+ /**
+ * @param path **Full** path to file
+ * @param encoding Set to `base64` in order to encode response
+ */
+ readFile(path: string, encoding: "base64" | "utf8"): Promise;
+ saveFileToGallery?(uri: string, fileName: string, fileType: "PNG" | "JPEG"): Promise;
+ /**
+ * Beware! This function has differing functionality on iOS and Android.
+ * @param storageDir Either `cache` or `documents`.
+ * @param path Path in `storageDir`, parents are recursively created.
+ * @param data The data to write to the file
+ * @param encoding Set to `base64` if `data` is base64 encoded.
+ * @returns Promise that resolves to path of the file once it got written
+ */
+ writeFile(storageDir: "cache" | "documents", path: string, data: string, encoding: "base64" | "utf8"): Promise;
+ removeFile(storageDir: "cache" | "documents", path: string): Promise;
+ getConstants: () => {
+ /**
+ * The path the `documents` storage dir (see {@link writeFile}) represents.
+ */
+ DocumentsDirPath: string;
+ CacheDirPath: string;
+ };
+ /**
+ * Will apparently cease to exist some time in the future so please use {@link getConstants} instead.
+ * @deprecated
+ */
+ DocumentsDirPath: string;
+}
+
+export const MMKVManager = nmp.MMKVManager as MMKVManager;
//! 173.10 renamed this to RTNFileManager.
-export const FileManager = (nmp.DCDFileManager ?? nmp.RTNFileManager) as _FileManager;
+export const FileManager = (nmp.DCDFileManager ?? nmp.RTNFileManager) as FileManager;
//! 173.13 renamed this to RTNClientInfoManager.
export const ClientInfoManager = nmp.InfoDictionaryManager ?? nmp.RTNClientInfoManager;
//! 173.14 renamed this to RTNDeviceManager.
diff --git a/src/lib/plugins.ts b/src/lib/plugins.ts
index 9d0b346..f207da8 100644
--- a/src/lib/plugins.ts
+++ b/src/lib/plugins.ts
@@ -1,9 +1,9 @@
-import { PluginManifest, Plugin } from "@types";
import { safeFetch } from "@lib/utils";
import { awaitSyncWrapper, createMMKVBackend, createStorage, purgeStorage, wrapSync } from "@lib/storage";
import { allSettled } from "@lib/polyfills";
import logger, { logModule } from "@lib/logger";
import settings from "@lib/settings";
+import { Author } from "./types";
type EvaledPlugin = {
onLoad?(): void;
@@ -11,6 +11,27 @@ type EvaledPlugin = {
settings: JSX.Element;
};
+// See https://github.com/vendetta-mod/polymanifest
+export interface PluginManifest {
+ name: string;
+ description: string;
+ authors: Author[];
+ main: string;
+ hash: string;
+ // Vendor-specific field, contains our own data
+ vendetta?: {
+ icon?: string;
+ };
+}
+
+export interface Plugin {
+ id: string;
+ manifest: PluginManifest;
+ enabled: boolean;
+ update: boolean;
+ js: string;
+}
+
export const plugins = wrapSync(createStorage>(createMMKVBackend("VENDETTA_PLUGINS")));
const loadedPlugins: Record = {};
diff --git a/src/lib/settings.ts b/src/lib/settings.ts
index af9c5d1..0ec3ad7 100644
--- a/src/lib/settings.ts
+++ b/src/lib/settings.ts
@@ -1,6 +1,22 @@
-import { LoaderConfig, Settings } from "@types";
import { createFileBackend, createMMKVBackend, createStorage, wrapSync } from "@lib/storage";
import { getLoaderConfigPath } from "./loader";
+export interface Settings {
+ debuggerUrl: string;
+ developerSettings: boolean;
+ safeMode?: {
+ enabled: boolean;
+ currentThemeId?: string;
+ };
+}
+
+export interface LoaderConfig {
+ customLoadUrl: {
+ enabled: boolean;
+ url: string;
+ };
+ loadReactDevTools: boolean;
+}
+
export default wrapSync(createStorage(createMMKVBackend("VENDETTA_SETTINGS")));
export const loaderConfig = wrapSync(createStorage(createFileBackend(getLoaderConfigPath())));
diff --git a/src/lib/storage/backends.ts b/src/lib/storage/backends.ts
index 6296181..c3bcbb7 100644
--- a/src/lib/storage/backends.ts
+++ b/src/lib/storage/backends.ts
@@ -1,7 +1,11 @@
-import { StorageBackend } from "@types";
import { MMKVManager, FileManager } from "@lib/native";
import { ReactNative as RN } from "@metro/common";
+export interface StorageBackend {
+ get: () => unknown | Promise;
+ set: (data: unknown) => void | Promise;
+}
+
const ILLEGAL_CHARS_REGEX = /[<>:"\/\\|?*]/g;
const filePathFixer = (file: string): string => RN.Platform.select({
diff --git a/src/lib/storage/index.ts b/src/lib/storage/index.ts
index fb72e35..88ada11 100644
--- a/src/lib/storage/index.ts
+++ b/src/lib/storage/index.ts
@@ -1,5 +1,5 @@
-import { Emitter, StorageBackend } from "@types";
-import createEmitter from "@lib/emitter";
+import createEmitter, { Emitter } from "@lib/emitter";
+import { StorageBackend } from "@lib/storage/backends";
const emitterSymbol = Symbol.for("vendetta.storage.emitter");
const syncAwaitSymbol = Symbol.for("vendetta.storage.accessor");
diff --git a/src/lib/themes.ts b/src/lib/themes.ts
index 13fabc4..250d56b 100644
--- a/src/lib/themes.ts
+++ b/src/lib/themes.ts
@@ -1,4 +1,3 @@
-import { Theme, ThemeData } from "@types";
import { ReactNative as RN, chroma } from "@metro/common";
import { findInReactTree, safeFetch } from "@lib/utils";
import { find, findByName, findByProps, findByStoreName } from "@metro/filters";
@@ -7,6 +6,31 @@ import { createFileBackend, createMMKVBackend, createStorage, wrapSync, awaitSyn
import logger from "@lib/logger";
import { ThemeManager } from "./native";
import { getStoredTheme, getThemeFilePath } from "./loader";
+import { Author } from "./types";
+
+export interface ThemeData {
+ name: string;
+ description?: string;
+ authors?: Author[];
+ spec: number;
+ semanticColors?: Record;
+ rawColors?: Record;
+ background?: {
+ url: string;
+ blur?: number;
+ /**
+ * The alpha value of the background.
+ * `CHAT_BACKGROUND` of semanticColors alpha value will be ignored when this is specified
+ */
+ alpha?: number;
+ }
+}
+
+export interface Theme {
+ id: string;
+ selected: boolean;
+ data: ThemeData;
+}
//! As of 173.10, early-finding this does not work.
// Somehow, this is late enough, though?
diff --git a/src/lib/types.ts b/src/lib/types.ts
new file mode 100644
index 0000000..830070f
--- /dev/null
+++ b/src/lib/types.ts
@@ -0,0 +1,16 @@
+export interface Author {
+ name: string;
+ id?: string;
+}
+
+export enum ButtonColors {
+ BRAND = "brand",
+ RED = "red",
+ GREEN = "green",
+ PRIMARY = "primary",
+ TRANSPARENT = "transparent",
+ GREY = "grey",
+ LIGHTGREY = "lightgrey",
+ WHITE = "white",
+ LINK = "link"
+}
\ No newline at end of file
diff --git a/src/lib/utils/findInReactTree.ts b/src/lib/utils/findInReactTree.ts
index 002f3e3..9d4ea53 100644
--- a/src/lib/utils/findInReactTree.ts
+++ b/src/lib/utils/findInReactTree.ts
@@ -1,5 +1,5 @@
-import { SearchFilter } from "@types";
import { findInTree } from "@lib/utils";
+import { SearchFilter } from "./findInTree";
export default (tree: { [key: string]: any }, filter: SearchFilter): any => findInTree(tree, filter, {
walkable: ["props", "children", "child", "sibling"],
diff --git a/src/lib/utils/findInTree.ts b/src/lib/utils/findInTree.ts
index f8a19cd..3c3c9c4 100644
--- a/src/lib/utils/findInTree.ts
+++ b/src/lib/utils/findInTree.ts
@@ -1,7 +1,13 @@
// This has been completely reimplemented at this point, but the disclaimer at the end of disclaimers still counts.
// https://github.com/Cordwood/Cordwood/blob/91c0b971bbf05e112927df75415df99fa105e1e7/src/lib/utils/findInTree.ts
-import { FindInTreeOptions, SearchTree, SearchFilter } from "@types";
+export type SearchTree = Record;
+export type SearchFilter = (tree: SearchTree) => boolean;
+export interface FindInTreeOptions {
+ walkable?: string[];
+ ignore?: string[];
+ maxDepth?: number;
+}
function treeSearch(tree: SearchTree, filter: SearchFilter, opts: Required, depth: number): any {
if (depth > opts.maxDepth) return;
diff --git a/src/lib/vendettaObject.ts b/src/lib/vendettaObject.ts
index c6531f6..0508ee5 100644
--- a/src/lib/vendettaObject.ts
+++ b/src/lib/vendettaObject.ts
@@ -1,4 +1,3 @@
-import { VendettaObject } from "@types";
import patcher from "@lib/patcher";
import { getVendettaLoaderIdentity } from "@lib/loader";
import settings, { loaderConfig } from "@lib/settings";
@@ -16,23 +15,23 @@ import * as assets from "@ui/assets";
import * as color from "@ui/color";
import * as utils from "@lib/utils";
-export const createVendettaObject = (unloads: any[]): VendettaObject => ({
+export const createVendettaObject = (unloads: any[]): any => ({
patcher: {
before: patcher.before,
after: patcher.after,
instead: patcher.instead
},
metro: {
- find: (filter) => metro.find(filter),
- findAll: (filter) => metro.findAll(filter),
- findByProps: (filter) => metro.findByProps(filter),
- findByPropsAll: (filter) => metro.findByPropsAll(filter),
- findByName: (name, defaultExp?) => metro.findByName(name, defaultExp),
- findByNameAll: (name, defaultExp?) => metro.findByNameAll(name, defaultExp),
- findByDisplayName: (displayName, defaultExp?) => metro.findByDisplayName(displayName, defaultExp),
- findByDisplayNameAll: (displayName, defaultExp?) => metro.findByDisplayNameAll(displayName, defaultExp),
- findByTypeName: (typeName, defaultExp?) => metro.findByTypeName(typeName, defaultExp),
- findByTypeNameAll: (typeName, defaultExp?) => metro.findByTypeNameAll(typeName, defaultExp),
+ find: (filter: (m: any) => boolean) => metro.find(filter),
+ findAll: (filter: (m: any) => boolean) => metro.findAll(filter),
+ findByProps: (filter: any) => metro.findByProps(filter),
+ findByPropsAll: (filter: any) => metro.findByPropsAll(filter),
+ findByName: (name: string, defaultExp?: boolean | undefined) => metro.findByName(name, defaultExp),
+ findByNameAll: (name: string, defaultExp?: boolean | undefined) => metro.findByNameAll(name, defaultExp),
+ findByDisplayName: (displayName: string, defaultExp?: boolean | undefined) => metro.findByDisplayName(displayName, defaultExp),
+ findByDisplayNameAll: (displayName: string, defaultExp?: boolean | undefined) => metro.findByDisplayNameAll(displayName, defaultExp),
+ findByTypeName: (typeName: string, defaultExp?: boolean | undefined) => metro.findByTypeName(typeName, defaultExp),
+ findByTypeNameAll: (typeName: string, defaultExp?: boolean | undefined) => metro.findByTypeNameAll(typeName, defaultExp),
findByStoreName: (name: string) => metro.findByStoreName(name),
common: {
constants: common.constants,
@@ -69,11 +68,11 @@ export const createVendettaObject = (unloads: any[]): VendettaObject => ({
THEMES_CHANNEL_ID: "1091880434939482202",
},
utils: {
- findInReactTree: (tree, filter) => utils.findInReactTree(tree, filter),
- findInTree: (tree, filter, options) => utils.findInTree(tree, filter, options),
- safeFetch: (input, options?, timeout?) => utils.safeFetch(input, options, timeout),
+ findInReactTree: (tree: { [key: string]: any; }, filter: any) => utils.findInReactTree(tree, filter),
+ findInTree: (tree: any, filter: any, options: any) => utils.findInTree(tree, filter, options),
+ safeFetch: (input: RequestInfo | URL, options?: RequestInit | undefined, timeout?: number | undefined) => utils.safeFetch(input, options, timeout),
unfreeze: (obj: object) => utils.unfreeze(obj),
- without: (object, ...keys) => utils.without(object, ...keys)
+ without: (object: any, ...keys: any) => utils.without(object, ...keys)
},
debug: {
connectToDebugger: (url: string) => debug.connectToDebugger(url),
@@ -96,9 +95,9 @@ export const createVendettaObject = (unloads: any[]): VendettaObject => ({
showToast: (content: string, asset?: number) => toasts.showToast(content, asset)
},
alerts: {
- showConfirmationAlert: (options) => alerts.showConfirmationAlert(options),
- showCustomAlert: (component, props: any) => alerts.showCustomAlert(component, props),
- showInputAlert: (options) => alerts.showInputAlert(options)
+ showConfirmationAlert: (options: any) => alerts.showConfirmationAlert(options),
+ showCustomAlert: (component: React.ComponentType, props: any) => alerts.showCustomAlert(component, props),
+ showInputAlert: (options: any) => alerts.showInputAlert(options)
},
assets: {
all: assets.all,
@@ -132,7 +131,7 @@ export const createVendettaObject = (unloads: any[]): VendettaObject => ({
registerCommand: commands.registerCommand
},
storage: {
- createProxy: (target) => storage.createProxy(target),
+ createProxy: (target: any) => storage.createProxy(target),
useProxy: (_storage: T) => storage.useProxy(_storage),
createStorage: (backend: any) => storage.createStorage(backend),
wrapSync: (store: any) => storage.wrapSync(store),
@@ -146,18 +145,17 @@ export const createVendettaObject = (unloads: any[]): VendettaObject => ({
config: loaderConfig,
},
logger: {
- log: (...message) => console.log(...message),
- info: (...message) => console.info(...message),
- warn: (...message) => console.warn(...message),
- error: (...message) => console.error(...message),
- time: (...message) => console.time(...message),
- trace: (...message) => console.trace(...message),
- verbose: (...message) => console.log(...message)
+ log: (...message: any) => console.log(...message),
+ info: (...message: any) => console.info(...message),
+ warn: (...message: any) => console.warn(...message),
+ error: (...message: any) => console.error(...message),
+ time: (...message: any) => console.time(...message),
+ trace: (...message: any) => console.trace(...message),
+ verbose: (...message: any) => console.log(...message)
},
version: debug.versionHash,
unload: () => {
unloads.filter(i => typeof i === "function").forEach(p => p());
- // @ts-expect-error explode
delete window.vendetta;
},
});
diff --git a/src/ui/alerts.ts b/src/ui/alerts.ts
index b35a3fe..03cac07 100644
--- a/src/ui/alerts.ts
+++ b/src/ui/alerts.ts
@@ -1,6 +1,6 @@
-import { ConfirmationAlertOptions, InputAlertProps } from "@types";
import { findByProps } from "@metro/filters";
-import InputAlert from "@ui/components/InputAlert";
+import InputAlert, { InputAlertProps } from "@ui/components/InputAlert";
+import { ButtonColors } from "@/lib/types";
const Alerts = findByProps("openLazy", "close");
@@ -9,6 +9,19 @@ interface InternalConfirmationAlertOptions extends Omit void;
+ secondaryConfirmText?: string;
+ onConfirmSecondary?: () => void;
+ cancelText?: string;
+ onCancel?: () => void;
+ isDismissable?: boolean;
+}
+
export function showConfirmationAlert(options: ConfirmationAlertOptions) {
const internalOptions = options as InternalConfirmationAlertOptions;
diff --git a/src/ui/assets.ts b/src/ui/assets.ts
index 842abe9..11ff78b 100644
--- a/src/ui/assets.ts
+++ b/src/ui/assets.ts
@@ -1,9 +1,13 @@
-import { Asset } from "@types";
import { assets } from "@metro/common";
import { after } from "@lib/patcher";
export const all: Record = {};
+export interface Asset {
+ name: string;
+ id: number;
+}
+
export function patchAssets() {
const unpatch = after("registerAsset", assets, (args: Asset[], id: number) => {
const asset = args[0];
diff --git a/src/ui/components/Codeblock.tsx b/src/ui/components/Codeblock.tsx
index d3e9540..3c51001 100644
--- a/src/ui/components/Codeblock.tsx
+++ b/src/ui/components/Codeblock.tsx
@@ -1,7 +1,12 @@
-import { CodeblockProps } from "@types";
import { ReactNative as RN, stylesheet, constants } from "@metro/common";
import { semanticColors } from "@ui/color";
+export interface CodeblockProps {
+ selectable?: boolean;
+ style?: import("react-native").TextStyle;
+ children?: string;
+}
+
const styles = stylesheet.createThemedStyleSheet({
codeBlock: {
fontFamily: constants.Fonts.CODE_SEMIBOLD,
diff --git a/src/ui/components/ErrorBoundary.tsx b/src/ui/components/ErrorBoundary.tsx
index 12b068d..4929501 100644
--- a/src/ui/components/ErrorBoundary.tsx
+++ b/src/ui/components/ErrorBoundary.tsx
@@ -1,4 +1,3 @@
-import { ErrorBoundaryProps } from "@types";
import { React, ReactNative as RN, stylesheet } from "@metro/common";
import { Forms, Button, Codeblock } from "@ui/components";
@@ -7,6 +6,10 @@ interface ErrorBoundaryState {
errText?: string;
}
+export interface ErrorBoundaryProps {
+ children: JSX.Element | JSX.Element[];
+}
+
const styles = stylesheet.createThemedStyleSheet({
view: {
flex: 1,
diff --git a/src/ui/components/InputAlert.tsx b/src/ui/components/InputAlert.tsx
index 75efc7d..59d5e2c 100644
--- a/src/ui/components/InputAlert.tsx
+++ b/src/ui/components/InputAlert.tsx
@@ -1,10 +1,21 @@
-import { InputAlertProps } from "@types";
+import { ButtonColors } from "@/lib/types";
import { findByProps } from "@metro/filters";
import { Forms, Alert } from "@ui/components";
const { FormInput } = Forms;
const Alerts = findByProps("openLazy", "close");
+export interface InputAlertProps {
+ title?: string;
+ confirmText?: string;
+ confirmColor?: ButtonColors;
+ onConfirm: (input: string) => (void | Promise);
+ cancelText?: string;
+ placeholder?: string;
+ initialValue?: string;
+ secureTextEntry?: boolean;
+}
+
export default function InputAlert({ title, confirmText, confirmColor, onConfirm, cancelText, placeholder, initialValue = "", secureTextEntry }: InputAlertProps) {
const [value, setValue] = React.useState(initialValue);
const [error, setError] = React.useState("");
diff --git a/src/ui/components/Search.tsx b/src/ui/components/Search.tsx
index cd25346..d0517e4 100644
--- a/src/ui/components/Search.tsx
+++ b/src/ui/components/Search.tsx
@@ -1,41 +1,21 @@
-import { SearchProps } from "@types";
import { ReactNative as RN } from "@metro/common";
import { findByProps } from "@metro/filters";
import ErrorBoundary from "@ui/components/ErrorBoundary";
import { getAssetIDByName } from "@ui/assets";
-const { FormIcon } = findByProps("FormIcon");
const { TextInput } = findByProps("TableRow");
-const props = {
- "placeholder": "Search",
- "returnKeyType": "search",
- "size": "md",
- "onFocus": "ƒ () {}",
- "value": "",
- "grow": true,
- "autoCorrect": false,
- "autoCapitalize": "none",
- "accessibilityRole": "search",
- "leadingIcon": "ƒ () {}",
- "isClearable": true,
- "onChangeText": "ƒ () {}",
- "editable": true,
- "onBlur": "ƒ () {}",
- "style": [
- "{padding: 12, paddingTop: 12}",
- "{color: \"#e4e5e8\", fontFamily: \"ggsans-Medium, Noto…}",
- "{paddingEnd: undefined, paddingStart: 36.3636360168…}",
- "{padding: 12, paddingTop: 12}",
- "{color: \"#e4e5e8\", fontFamily: \"ggsans-Medium, Noto…}",
- "{paddingEnd: undefined, paddingStart: 36.3636360168…}"
- ],
- "placeholderTextColor": "#828391"
-};
+export interface SearchProps {
+ onChangeText?: (v: string) => void;
+ placeholder?: string;
+ style?: import("react-native").TextStyle;
+}
+
function SearchIcon() {
return
}
+
export default ({ onChangeText, placeholder, style }: SearchProps) => {
const [query, setQuery] = React.useState("");
diff --git a/src/ui/components/Summary.tsx b/src/ui/components/Summary.tsx
index 68c610c..2cb8152 100644
--- a/src/ui/components/Summary.tsx
+++ b/src/ui/components/Summary.tsx
@@ -1,10 +1,16 @@
-import { SummaryProps } from "@types";
import { ReactNative as RN } from "@metro/common";
import { getAssetIDByName } from "@ui/assets";
import { findByProps } from "@/lib/metro/filters";
const { FormRow } = findByProps("FormRow");
const { TableRow } = findByProps("TableRow");
+export interface SummaryProps {
+ label: string;
+ icon?: string;
+ noPadding?: boolean;
+ noAnimation?: boolean;
+ children: JSX.Element | JSX.Element[];
+}
export default function Summary({ label, icon, noPadding = false, noAnimation = false, children }: SummaryProps) {
const [hidden, setHidden] = React.useState(true);
diff --git a/src/ui/safeMode.tsx b/src/ui/safeMode.tsx
index 4142be3..21e34a9 100644
--- a/src/ui/safeMode.tsx
+++ b/src/ui/safeMode.tsx
@@ -1,4 +1,3 @@
-import { ButtonColors } from "@types";
import { ReactNative as RN, stylesheet } from "@metro/common";
import { findByName, findByProps, findByStoreName } from "@metro/filters";
import { after } from "@lib/patcher";
@@ -7,6 +6,7 @@ import { DeviceManager } from "@lib/native";
import { semanticColors } from "@ui/color";
import { Button, Codeblock, ErrorBoundary as _ErrorBoundary, SafeAreaView } from "@ui/components";
import settings from "@lib/settings";
+import { ButtonColors } from "@/lib/types";
const ErrorBoundary = findByName("ErrorBoundary");
diff --git a/src/ui/settings/components/AssetDisplay.tsx b/src/ui/settings/components/AssetDisplay.tsx
index 3b85e82..3a6c4bf 100644
--- a/src/ui/settings/components/AssetDisplay.tsx
+++ b/src/ui/settings/components/AssetDisplay.tsx
@@ -1,7 +1,7 @@
-import { Asset } from "@types";
import { ReactNative as RN, clipboard } from "@metro/common";
import { showToast } from "@ui/toasts";
import { Forms } from "@ui/components";
+import { Asset } from "@/ui/assets";
interface AssetDisplayProps { asset: Asset }
diff --git a/src/ui/settings/components/PluginCard.tsx b/src/ui/settings/components/PluginCard.tsx
index be43cce..bb302e2 100644
--- a/src/ui/settings/components/PluginCard.tsx
+++ b/src/ui/settings/components/PluginCard.tsx
@@ -1,12 +1,12 @@
-import { ButtonColors, Plugin } from "@types";
import { NavigationNative, clipboard } from "@metro/common";
-import { removePlugin, startPlugin, stopPlugin, getSettings, fetchPlugin } from "@lib/plugins";
+import { removePlugin, startPlugin, stopPlugin, getSettings, fetchPlugin, Plugin } from "@lib/plugins";
import { MMKVManager } from "@lib/native";
import { getAssetIDByName } from "@ui/assets";
import { showToast } from "@ui/toasts";
import { showConfirmationAlert } from "@ui/alerts";
import Card, { CardWrapper } from "@ui/settings/components/Card";
import { useProxy } from "@/lib/storage";
+import { ButtonColors } from "@/lib/types";
async function stopThenStart(plugin: Plugin, callback: Function) {
if (plugin.enabled) stopPlugin(plugin.id, false);
diff --git a/src/ui/settings/components/ThemeCard.tsx b/src/ui/settings/components/ThemeCard.tsx
index 015fd20..acc0b96 100644
--- a/src/ui/settings/components/ThemeCard.tsx
+++ b/src/ui/settings/components/ThemeCard.tsx
@@ -1,6 +1,5 @@
-import { ButtonColors, Theme } from "@types";
import { clipboard } from "@metro/common";
-import { applyTheme, fetchTheme, removeTheme, selectTheme, themes } from "@lib/themes";
+import { Theme, applyTheme, fetchTheme, removeTheme, selectTheme, themes } from "@lib/themes";
import { useProxy } from "@lib/storage";
import { BundleUpdaterManager } from "@lib/native";
import { getAssetIDByName } from "@ui/assets";
@@ -8,6 +7,7 @@ import { showConfirmationAlert } from "@ui/alerts";
import { showToast } from "@ui/toasts";
import settings from "@lib/settings";
import Card, { CardWrapper } from "@ui/settings/components/Card";
+import { ButtonColors } from "@/lib/types";
async function selectAndApply(value: boolean, id: string) {
try {
diff --git a/src/ui/settings/pages/Plugins.tsx b/src/ui/settings/pages/Plugins.tsx
index c5ac722..e3c5e22 100644
--- a/src/ui/settings/pages/Plugins.tsx
+++ b/src/ui/settings/pages/Plugins.tsx
@@ -1,6 +1,5 @@
-import { Plugin } from "@types";
import { useProxy } from "@lib/storage";
-import { installPlugin, plugins } from "@lib/plugins";
+import { Plugin, installPlugin, plugins } from "@lib/plugins";
import settings from "@lib/settings";
import AddonPage from "@ui/settings/components/AddonPage";
import PluginCard from "@ui/settings/components/PluginCard";
diff --git a/src/ui/settings/pages/Themes.tsx b/src/ui/settings/pages/Themes.tsx
index e68ba61..bc1c9c6 100644
--- a/src/ui/settings/pages/Themes.tsx
+++ b/src/ui/settings/pages/Themes.tsx
@@ -1,10 +1,10 @@
-import { Theme, ButtonColors } from "@types";
import { useProxy } from "@lib/storage";
-import { installTheme, themes } from "@lib/themes";
+import { Theme, installTheme, themes } from "@lib/themes";
import { Button } from "@ui/components";
import settings from "@lib/settings";
import AddonPage from "@ui/settings/components/AddonPage";
import ThemeCard from "@ui/settings/components/ThemeCard";
+import { ButtonColors } from "@/lib/types";
export default function Themes() {
useProxy(settings);
diff --git a/tsconfig.json b/tsconfig.json
index 9410367..d4a6b86 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -13,8 +13,7 @@
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"paths": {
- "@/*": ["src/*"],
- "@types": ["src/def.d.ts"],
+ "@*": ["src*"],
"@lib/*": ["src/lib/*"],
"@metro/*": ["src/lib/metro/*"],
"@ui/*": ["src/ui/*"]