diff --git a/bun.lock b/bun.lock index 24d75f7..c8be63b 100644 --- a/bun.lock +++ b/bun.lock @@ -1,12 +1,10 @@ { "lockfileVersion": 1, - "configVersion": 0, "workspaces": { "": { - "name": "ShiggyCord", + "name": "Kettu", "dependencies": { "@gullerya/object-observer": "^6.1.4", - "@shopify/react-native-skia": "^2.4.2", "@tanstack/react-query": "^5.83.0", "@types/chroma-js": "~2.4.5", "@types/lodash": "~4.17.20", @@ -16,7 +14,7 @@ "intl-messageformat": "^10.7.16", "moment": "2.22.2", "react-native": "0.78.0", - "react-native-reanimated": "^4.1.3", + "react-native-reanimated": "^3.18.0", "spitroast": "^1.4.4", "type-fest": "^4.41.0", }, @@ -474,8 +472,6 @@ "@semantic-release/release-notes-generator": ["@semantic-release/release-notes-generator@14.0.3", "", { "dependencies": { "conventional-changelog-angular": "^8.0.0", "conventional-changelog-writer": "^8.0.0", "conventional-commits-filter": "^5.0.0", "conventional-commits-parser": "^6.0.0", "debug": "^4.0.0", "get-stream": "^7.0.0", "import-from-esm": "^2.0.0", "into-stream": "^7.0.0", "lodash-es": "^4.17.21", "read-package-up": "^11.0.0" }, "peerDependencies": { "semantic-release": ">=20.1.0" } }, "sha512-XxAZRPWGwO5JwJtS83bRdoIhCiYIx8Vhr+u231pQAsdFIAbm19rSVJLdnBN+Avvk7CKvNQE/nJ4y7uqKH6WTiw=="], - "@shopify/react-native-skia": ["@shopify/react-native-skia@2.4.2", "", { "dependencies": { "canvaskit-wasm": "0.40.0", "react-reconciler": "0.31.0" }, "peerDependencies": { "react": ">=19.0", "react-native": ">=0.78", "react-native-reanimated": ">=3.19.1" }, "optionalPeers": ["react-native", "react-native-reanimated"], "bin": { "setup-skia-web": "scripts/setup-canvaskit.js" } }, "sha512-W+boV6E/hvRPcA8L7QRKnLs+05/iMEDbhVRLIx0UpW7NOQ8EbWCnu16mCU9BYJ/4s/y3NyYNk+wfP45Gf0LARQ=="], - "@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], "@sindresorhus/is": ["@sindresorhus/is@4.6.0", "", {}, "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="], @@ -570,8 +566,6 @@ "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], - "@webgpu/types": ["@webgpu/types@0.1.21", "", {}, "sha512-pUrWq3V5PiSGFLeLxoGqReTZmiiXwY3jRkIG5sLLKjyqNxrwm/04b4nw7LSmGWJcKk59XOM/YRTUwOzo4MMlow=="], - "JSONStream": ["JSONStream@1.3.5", "", { "dependencies": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" }, "bin": { "JSONStream": "./bin.js" } }, "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ=="], "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], @@ -686,8 +680,6 @@ "caniuse-lite": ["caniuse-lite@1.0.30001727", "", {}, "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q=="], - "canvaskit-wasm": ["canvaskit-wasm@0.40.0", "", { "dependencies": { "@webgpu/types": "0.1.21" } }, "sha512-Od2o+ZmoEw9PBdN/yCGvzfu0WVqlufBPEWNG452wY7E9aT8RBE+ChpZF526doOlg7zumO4iCS+RAeht4P0Gbpw=="], - "chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], "char-regex": ["char-regex@1.0.2", "", {}, "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw=="], @@ -1410,13 +1402,9 @@ "react-native": ["react-native@0.78.0", "", { "dependencies": { "@jest/create-cache-key-function": "^29.6.3", "@react-native/assets-registry": "0.78.0", "@react-native/codegen": "0.78.0", "@react-native/community-cli-plugin": "0.78.0", "@react-native/gradle-plugin": "0.78.0", "@react-native/js-polyfills": "0.78.0", "@react-native/normalize-colors": "0.78.0", "@react-native/virtualized-lists": "0.78.0", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", "babel-jest": "^29.7.0", "babel-plugin-syntax-hermes-parser": "0.25.1", "base64-js": "^1.5.1", "chalk": "^4.0.0", "commander": "^12.0.0", "event-target-shim": "^5.0.1", "flow-enums-runtime": "^0.0.6", "glob": "^7.1.1", "invariant": "^2.2.4", "jest-environment-node": "^29.6.3", "memoize-one": "^5.0.0", "metro-runtime": "^0.81.0", "metro-source-map": "^0.81.0", "nullthrows": "^1.1.1", "pretty-format": "^29.7.0", "promise": "^8.3.0", "react-devtools-core": "^6.0.1", "react-refresh": "^0.14.0", "regenerator-runtime": "^0.13.2", "scheduler": "0.25.0", "semver": "^7.1.3", "stacktrace-parser": "^0.1.10", "whatwg-fetch": "^3.0.0", "ws": "^6.2.3", "yargs": "^17.6.2" }, "peerDependencies": { "@types/react": "^19.0.0", "react": "^19.0.0" }, "optionalPeers": ["@types/react"], "bin": { "react-native": "cli.js" } }, "sha512-3PO4tNvCN6BdAKcoY70v1sLfxYCmDR4KS1VTY+kIBKy5Qznp27QNxL7zBQjvS6Jp91gc8N82QbysQrfBlwg9gQ=="], - "react-native-is-edge-to-edge": ["react-native-is-edge-to-edge@1.2.1", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q=="], + "react-native-is-edge-to-edge": ["react-native-is-edge-to-edge@1.1.7", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-EH6i7E8epJGIcu7KpfXYXiV2JFIYITtq+rVS8uEb+92naMRBdxhTuS8Wn2Q7j9sqyO0B+Xbaaf9VdipIAmGW4w=="], - "react-native-reanimated": ["react-native-reanimated@4.1.3", "", { "dependencies": { "react-native-is-edge-to-edge": "^1.2.1", "semver": "7.7.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0", "react": "*", "react-native": "*", "react-native-worklets": ">=0.5.0" } }, "sha512-GP8wsi1u3nqvC1fMab/m8gfFwFyldawElCcUSBJQgfrXeLmsPPUOpDw44lbLeCpcwUuLa05WTVePdTEwCLTUZg=="], - - "react-native-worklets": ["react-native-worklets@0.6.1", "", { "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", "@babel/plugin-transform-class-properties": "^7.0.0-0", "@babel/plugin-transform-classes": "^7.0.0-0", "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", "@babel/plugin-transform-optional-chaining": "^7.0.0-0", "@babel/plugin-transform-shorthand-properties": "^7.0.0-0", "@babel/plugin-transform-template-literals": "^7.0.0-0", "@babel/plugin-transform-unicode-regex": "^7.0.0-0", "@babel/preset-typescript": "^7.16.7", "convert-source-map": "^2.0.0", "semver": "7.7.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0", "react": "*", "react-native": "*" } }, "sha512-URca8l7c7Uog7gv4mcg9KILdJlnbvwdS5yfXQYf5TDkD2W1VY1sduEKrD+sA3lUPXH/TG1vmXAvNxCNwPMYgGg=="], - - "react-reconciler": ["react-reconciler@0.31.0", "", { "dependencies": { "scheduler": "^0.25.0" }, "peerDependencies": { "react": "^19.0.0" } }, "sha512-7Ob7Z+URmesIsIVRjnLoDGwBEG/tVitidU0nMsqX/eeJaLY89RISO/10ERe0MqmzuKUUB1rmY+h1itMbUHg9BQ=="], + "react-native-reanimated": ["react-native-reanimated@3.19.0", "", { "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", "@babel/plugin-transform-class-properties": "^7.0.0-0", "@babel/plugin-transform-classes": "^7.0.0-0", "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", "@babel/plugin-transform-optional-chaining": "^7.0.0-0", "@babel/plugin-transform-shorthand-properties": "^7.0.0-0", "@babel/plugin-transform-template-literals": "^7.0.0-0", "@babel/plugin-transform-unicode-regex": "^7.0.0-0", "@babel/preset-typescript": "^7.16.7", "convert-source-map": "^2.0.0", "invariant": "^2.2.4", "react-native-is-edge-to-edge": "1.1.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0", "react": "*", "react-native": "*" } }, "sha512-FNfqLuPuVHsW9KcsZtnJqIPlMQvuySnSFJXgSt9fVDPqptbSUkiAF6MthUwd4Mxt05hCRcbV+T65CENgVS5iCg=="], "react-refresh": ["react-refresh@0.14.2", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="], diff --git a/package.json b/package.json index c261670..78d95d7 100644 --- a/package.json +++ b/package.json @@ -1,58 +1,57 @@ { - "name": "ShiggyCord", - "private": true, - "author": { - "name": "kmmiio99o", - "url": "https://github.com/kmmiio99o" - }, - "scripts": { - "build": "node scripts/build.mjs", - "serve": "node scripts/serve.mjs", - "serve:adb": "bun run serve --adb", - "lint": "eslint ./src --ext .js,.jsx,.ts,.tsx" - }, - "license": "BSD-3-Clause", - "devDependencies": { - "@saithodev/semantic-release-backmerge": "^4.0.1", - "@semantic-release/changelog": "^6.0.3", - "@semantic-release/exec": "^6.0.3", - "@semantic-release/git": "^10.0.1", - "semantic-release": "^24.2.7", - "@swc/core": "^1.13.1", - "@swc/helpers": "^0.5.17", - "@types/node": "^20.19.9", - "@types/yargs-parser": "^21.0.3", - "@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": "^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": "^3.2.0", - "typescript": "^5.8.3", - "yargs-parser": "^21.1.1" - }, - "dependencies": { - "@gullerya/object-observer": "^6.1.4", - "@shopify/react-native-skia": "^2.4.2", - "@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": "^10.7.16", - "moment": "2.22.2", - "react-native": "0.78.0", - "react-native-reanimated": "^4.1.3", - "spitroast": "^1.4.4", - "type-fest": "^4.41.0" - }, - "trustedDependencies": [ - "@swc/core" - ] -} + "name": "Kettu", + "private": true, + "author": { + "name": "cocobo1", + "url": "https://github.com/C0C0B01" + }, + "scripts": { + "build": "node scripts/build.mjs", + "serve": "node scripts/serve.mjs", + "serve:adb": "bun run serve --adb", + "lint": "eslint ./src --ext .js,.jsx,.ts,.tsx" + }, + "license": "BSD-3-Clause", + "devDependencies": { + "@saithodev/semantic-release-backmerge": "^4.0.1", + "@semantic-release/changelog": "^6.0.3", + "@semantic-release/exec": "^6.0.3", + "@semantic-release/git": "^10.0.1", + "semantic-release": "^24.2.7", + "@swc/core": "^1.13.1", + "@swc/helpers": "^0.5.17", + "@types/node": "^20.19.9", + "@types/yargs-parser": "^21.0.3", + "@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": "^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": "^3.2.0", + "typescript": "^5.8.3", + "yargs-parser": "^21.1.1" + }, + "dependencies": { + "@gullerya/object-observer": "^6.1.4", + "@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": "^10.7.16", + "moment": "2.22.2", + "react-native": "0.78.0", + "react-native-reanimated": "^3.18.0", + "spitroast": "^1.4.4", + "type-fest": "^4.41.0" + }, + "trustedDependencies": [ + "@swc/core" + ] +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 9bab7dc..89aaccf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,9 @@ import initFixes from "@core/fixes"; import { initFetchI18nStrings } from "@core/i18n"; import initSettings from "@core/ui/settings"; import { initVendettaObject } from "@core/vendetta/api"; +import { VdPluginManager } from "@core/vendetta/plugins"; import { updateFonts } from "@lib/addons/fonts"; +import { initPlugins, updatePlugins } from "@lib/addons/plugins"; import { initThemes } from "@lib/addons/themes"; import { patchCommands } from "@lib/api/commands"; import { patchLogHook } from "@lib/api/debug"; @@ -11,114 +13,36 @@ import { injectFluxInterceptor } from "@lib/api/flux"; import { patchJsx } from "@lib/api/react/jsx"; import { logger } from "@lib/utils/logger"; import { patchSettings } from "@ui/settings"; -import { InteractionManager } from "react-native"; -import { updaterSettings } from "@lib/api/settings"; -import { getDebugInfo } from "@lib/api/debug"; - -// Debug toggle helper (temporary runtime fallback). The helper is dynamically -// imported when needed (to avoid bundling it permanently) and removed after use. import * as lib from "./lib"; -/** - * Start sequence split into critical (UI) and deferred (network/plugin) work. - * The goal is to get the UI ready quickly and run heavy tasks after interactions. - */ export default async () => { - // Critical initializers that are required for the app's UI and basic features. - const criticalInits = [ - initThemes(), - injectFluxInterceptor(), - patchSettings(), - patchLogHook(), - patchCommands(), - patchJsx(), - patchErrorBoundary(), - initVendettaObject(), - initFetchI18nStrings(), - initSettings(), - initFixes(), - ]; + // Load everything in parallel - // Run critical inits and collect unpatchers/cleanup handlers. - await Promise.all(criticalInits) - .then((u) => u.forEach((f) => f && lib.unload.push(f))) - .catch((e) => { - // Log but don't abort — critical inits failing should be visible in logs. - console.warn("Critical initialization error:", e); - }); - - // Expose the library object early so UI and other code can access window.bunny. - window.bunny = lib; - - logger.log( - "ShiggyCord: UI-critical initialization complete — deferring plugin & network work", - ); - - // Deferred work: run after interactions to avoid blocking initial paint and navigation. - const runDeferred = async () => { - const { VdPluginManager } = await import("@core/vendetta/plugins"); - const { initPlugins, updatePlugins } = await import("@lib/addons/plugins"); - - // Initialize Vendetta plugins (may start many plugins) — do not block UI. - VdPluginManager.initPlugins() - .then((u) => lib.unload.push(u)) - .catch((e) => logger.log("Vendetta init failed:", e)); - - // Start ShiggyCord (Bunny) plugins now without forcing repository updates. - // Plugin repository fetching is deferred so the app can finish launching first. - // Stagger plugin startup to reduce CPU/memory spikes: use smaller batches and a small interval. - // This keeps the UI responsive while plugins initialize in the background. - initPlugins({ staggerInterval: 500, batchSize: 2 }); - - // Attempt a lightweight recovery toggle if some core plugins failed to start. - try { - // Dynamically import the helper (if present) but suppress verbose errors. - const mod = await import("@core/debug/toggleCorePlugins").catch( - () => null, - ); - if (mod?.default) { - // Run helper with minimal noise; ignore failures. - mod.default({ offDuration: 1500 }).catch(() => {}); - } - - // Try to remove the helper source file (best-effort, ignore failures). - await import("@lib/api/native/fs") - .then((fs) => fs.removeFile("src/core/debug/toggleCorePlugins.ts")) - .catch(() => {}); - } catch { - // suppressed - } - - // Update fonts in background - updateFonts().catch((e) => logger.log("updateFonts failed:", e)); - - // Schedule plugin repository update after a delay (5 minutes) so update work - // does not impact initial launch performance. - setTimeout( - () => { - updatePlugins().catch((e) => - logger.log("updatePlugins failed (deferred 5min):", e), - ); - }, - 5 * 60 * 1000, + await Promise.all([ + initThemes(), + injectFluxInterceptor(), + patchSettings(), + patchLogHook(), + patchCommands(), + patchJsx(), + initVendettaObject(), + initFetchI18nStrings(), + initSettings(), + initFixes(), + patchErrorBoundary(), + updatePlugins(), + updateFonts(), + initPlugins(), + VdPluginManager.initPlugins(), + ]).then( + // Push them all to unloader + u => u.forEach(f => f && lib.unload.push(f)) ); - // Note: we intentionally moved the call to `updatePlugins()` above so core - // plugins are registered prior to calling `initPlugins()`. - }; + // Assign window object + window.bunny = lib; - // Preferred: wait until interactions finish (animations / navigation). - try { - InteractionManager.runAfterInteractions(() => { - // small delay to ensure native lifecycle settled - setTimeout(runDeferred, 50); - }); - } catch (e) { - // Fallback if InteractionManager isn't available for any reason. - setTimeout(runDeferred, 200); - } - - // Final ready log for basic UI availability. - logger.log("ShiggyCord is ready."); -}; + // We good :) + logger.log("Kettu is ready!"); +}; \ No newline at end of file diff --git a/src/lib/addons/themes/colors/index.ts b/src/lib/addons/themes/colors/index.ts index 8085e87..1da31dc 100644 --- a/src/lib/addons/themes/colors/index.ts +++ b/src/lib/addons/themes/colors/index.ts @@ -1,5 +1,3 @@ -import fixStatusBar from "./statusbar"; -import fixKeyboard from "./keyboard"; import patchChatBackground from "./patches/background"; import patchDefinitionAndResolver from "./patches/resolver"; import patchStorage from "./patches/storage"; @@ -8,16 +6,13 @@ import { updateBunnyColor } from "./updater"; /** @internal */ export default function initColors(manifest: ColorManifest | null) { - const patches = [ - patchStorage(), - patchDefinitionAndResolver(), - patchChatBackground(), - ]; + if (manifest) updateBunnyColor(manifest, { update: false }); - fixStatusBar(); - fixKeyboard(); + const patches = [ + patchStorage(), + patchDefinitionAndResolver(), + patchChatBackground() + ]; - if (manifest) updateBunnyColor(manifest, { update: false }); - - return () => patches.forEach((p) => p()); -} + return () => patches.forEach(p => p()); +} \ No newline at end of file diff --git a/src/lib/addons/themes/colors/patches/resolver.ts b/src/lib/addons/themes/colors/patches/resolver.ts index c21b365..c6411f2 100644 --- a/src/lib/addons/themes/colors/patches/resolver.ts +++ b/src/lib/addons/themes/colors/patches/resolver.ts @@ -24,18 +24,6 @@ const SEMANTIC_FALLBACK_MAP: Record = { const origRawColor = { ...tokenReference.RawColor }; -function getRawFallback(key: string): string | undefined { - const isDark = isThemeModule.isThemeDark?.(_colorRef.current?.reference); - - if (!isDark) return undefined; - - const DARK_MODE_RAW_FALLBACK: Record = { - "PRIMARY_230": origRawColor.PRIMARY_700 || "#000000" - }; - - return DARK_MODE_RAW_FALLBACK[key]; -} - export default function patchDefinitionAndResolver() { const callback = ([theme]: any[]) => theme === _colorRef.key ? [_colorRef.current!.reference] : void 0; @@ -47,9 +35,6 @@ export default function patchDefinitionAndResolver() { const ret = _colorRef.current?.raw[key]; if (ret) return ret; - const fallback = getRawFallback(key); - if (fallback) return fallback; - return origRawColor[key]; } }); @@ -83,11 +68,6 @@ export default function patchDefinitionAndResolver() { return colorDef.opacity === 1 ? rawValue : chroma(rawValue).alpha(colorDef.opacity).hex(); } - const fallbackValue = getRawFallback(colorDef.raw); - if (fallbackValue) { - return colorDef.opacity === 1 ? fallbackValue : chroma(fallbackValue).alpha(colorDef.opacity).hex(); - } - // Fallback to default return orig(...args); }), @@ -109,4 +89,4 @@ function extractInfo(themeName: string, colorObj: any): [name: string, colorDef: const colorDef = tokenReference.SemanticColor[propName]; return [propName, colorDef[themeName]]; -} +} \ No newline at end of file diff --git a/src/lib/addons/themes/colors/updater.ts b/src/lib/addons/themes/colors/updater.ts index c7b265b..8c3d85b 100644 --- a/src/lib/addons/themes/colors/updater.ts +++ b/src/lib/addons/themes/colors/updater.ts @@ -9,6 +9,7 @@ const origRawColor = { ...tokenRef.RawColor }; const AppearanceManager = findByPropsLazy("updateTheme"); const ThemeStore = findByStoreNameLazy("ThemeStore"); const FormDivider = findByPropsLazy("DIVIDER_COLORS"); +import { colorsPref } from "./preferences"; let _inc = 1; @@ -30,30 +31,42 @@ export const _colorRef: InternalColorRef = { export function updateBunnyColor(colorManifest: ColorManifest | null, { update = true } ) { if (settings.safeMode?.enabled) return; + const resolveType = (type = "dark") => (colorsPref.type ?? type) === "dark" ? "darker" : "light"; const internalDef = colorManifest ? parseColorManifest(colorManifest) : null; - const ref = Object.assign(_colorRef, { - current: internalDef, - key: `bn-theme-${++_inc}`, - lastSetDiscordTheme: !ThemeStore.theme.startsWith("bn-theme-") - ? ThemeStore.theme - : _colorRef.lastSetDiscordTheme - }); - if (internalDef != null) { - tokenRef.Theme[ref.key.toUpperCase()] = ref.key; - FormDivider.DIVIDER_COLORS[ref.key] = FormDivider.DIVIDER_COLORS[ref.current!.reference]; + if (resolveType() == "light" || update) { + var ref = Object.assign(_colorRef, { + current: internalDef, + key: `bn-theme-${++_inc}`, + lastSetDiscordTheme: !ThemeStore.theme.startsWith("bn-theme-") || !ThemeStore.theme.startsWith("darker") + ? ThemeStore.theme + : _colorRef.lastSetDiscordTheme + }); + } else { + var ref = Object.assign(_colorRef, { + current: internalDef, + key: `dark`, + lastSetDiscordTheme: !ThemeStore.theme.startsWith("bn-theme-") || !ThemeStore.theme.startsWith("darker") + ? ThemeStore.theme + : _colorRef.lastSetDiscordTheme + }); + } - Object.keys(tokenRef.Shadow).forEach(k => tokenRef.Shadow[k][ref.key] = tokenRef.Shadow[k][ref.current!.reference]); - Object.keys(tokenRef.SemanticColor).forEach(k => { - tokenRef.SemanticColor[k][ref.key] = { - ...tokenRef.SemanticColor[k][ref.current!.reference] - }; - }); - } + if (internalDef != null) { + tokenRef.Theme[ref.key.toUpperCase()] = ref.key; + FormDivider.DIVIDER_COLORS[ref.key] = FormDivider.DIVIDER_COLORS[ref.current!.reference]; + + Object.keys(tokenRef.Shadow).forEach(k => tokenRef.Shadow[k][ref.key] = tokenRef.Shadow[k][ref.current!.reference]); + Object.keys(tokenRef.SemanticColor).forEach(k => { + tokenRef.SemanticColor[k][ref.key] = { + ...tokenRef.SemanticColor[k][ref.current!.reference] + }; + }); + } if (update) { AppearanceManager.setShouldSyncAppearanceSettings(false); - AppearanceManager.updateTheme(internalDef != null ? ref.key : ref.lastSetDiscordTheme); + AppearanceManager.updateTheme(internalDef != null ? ref.key : "darker"); } } diff --git a/src/lib/addons/themes/index.ts b/src/lib/addons/themes/index.ts index 65d6522..4dcf455 100644 --- a/src/lib/addons/themes/index.ts +++ b/src/lib/addons/themes/index.ts @@ -1,21 +1,10 @@ -import { - awaitStorage, - createFileBackend, - createMMKVBackend, - createStorage, - wrapSync, -} from "@core/vendetta/storage"; +import { awaitStorage, createFileBackend, createMMKVBackend, createStorage, wrapSync } from "@core/vendetta/storage"; import { writeFile } from "@lib/api/native/fs"; -import { - getStoredTheme, - getThemeFilePath, - isPyonLoader, - isThemeSupported, -} from "@lib/api/native/loader"; +import { getStoredTheme, getThemeFilePath, isPyonLoader, isThemeSupported } from "@lib/api/native/loader"; import { awaitStorage as newAwaitStorage } from "@lib/api/storage"; import { safeFetch } from "@lib/utils"; import { Platform } from "react-native"; - +import { settings } from "@lib/api/settings"; import initColors from "./colors"; import { applyAndroidAlphaKeys, normalizeToHex } from "./colors/parser"; import { colorsPref } from "./colors/preferences"; @@ -23,157 +12,157 @@ import { VendettaThemeManifest } from "./colors/types"; import { updateBunnyColor } from "./colors/updater"; export interface VdThemeInfo { - id: string; - selected: boolean; - data: VendettaThemeManifest; + id: string; + selected: boolean; + data: VendettaThemeManifest; } -export const themes = wrapSync( - createStorage>( - createMMKVBackend("VENDETTA_THEMES"), - ), -); +export const themes = wrapSync(createStorage>(createMMKVBackend("VENDETTA_THEMES"))); /** * @internal */ export async function writeThemeToNative(theme: VdThemeInfo | {}) { - if (typeof theme !== "object") throw new Error("Theme must be an object"); + if (typeof theme !== "object") throw new Error("Theme must be an object"); - // Save the current theme as current-theme.json. When supported by loader, - // this json will be written to appropriate path and be used to theme the native side. - await createFileBackend(getThemeFilePath() || "theme.json").set(theme); + // Save the current theme as current-theme.json. When supported by loader, + // this json will be written to appropriate path and be used to theme the native side. + await createFileBackend(getThemeFilePath() || "theme.json").set(theme); } // Process data for some compatiblity with native side function processData(data: VendettaThemeManifest) { - if (data.semanticColors) { - const { semanticColors } = data; + if (data.semanticColors) { + const { semanticColors } = data; - for (const key in semanticColors) { - for (const index in semanticColors[key]) { - semanticColors[key][index] &&= - normalizeToHex(semanticColors[key][index] as string) || false; - } - } - } - - if (data.rawColors) { - const { rawColors } = data; - - for (const key in rawColors) { - const normalized = normalizeToHex(rawColors[key]); - if (normalized) data.rawColors[key] = normalized; + for (const key in semanticColors) { + for (const index in semanticColors[key]) { + semanticColors[key][index] &&= normalizeToHex(semanticColors[key][index] as string) || false; + } + } } - if (Platform.OS === "android") applyAndroidAlphaKeys(rawColors); - } + if (data.rawColors) { + const { rawColors } = data; - // this field is required by the spec but vd seems to ignore this - // so are most vd themes - data.spec ??= 2; + for (const key in rawColors) { + const normalized = normalizeToHex(rawColors[key]); + if (normalized) data.rawColors[key] = normalized; + } - return data; + if (Platform.OS === "android") applyAndroidAlphaKeys(rawColors); + } + + if (data.spec === undefined) { + if (!('theme_color_map' in data)) { + data.spec = 2; + } + //If not then Enmity theme and no spec + } + + return data; } function validateTheme(themeJSON: any): boolean { - if (typeof themeJSON !== "object" || themeJSON === null) return false; - if (themeJSON.spec !== 2 && themeJSON.spec !== 3) return false; - if (themeJSON.spec === 3 && !themeJSON.main) return false; + if (typeof themeJSON !== "object" || themeJSON === null) return false; - return true; + if (themeJSON.spec === 3 && !themeJSON.main) return false; + + if (themeJSON.spec === 2) return true; + + if (themeJSON.theme_color_map) return true; + + return themeJSON.spec === 2 || themeJSON.spec === 3; } export async function fetchTheme(url: string, selected = false) { - let themeJSON: any; + let themeJSON: any; - try { - themeJSON = await (await safeFetch(url, { cache: "no-store" })).json(); - } catch { - throw new Error(`Failed to fetch theme at ${url}`); - } + try { + themeJSON = await (await safeFetch(url, { cache: "no-store" })).json(); + } catch { + throw new Error(`Failed to fetch theme at ${url}`); + } - // Validate theme - if (!validateTheme(themeJSON)) throw new Error(`Invalid theme at ${url}`); + // Validate theme + if (!validateTheme(themeJSON)) throw new Error(`Invalid theme at ${url}`); - themes[url] = { - id: url, - selected: selected, - data: processData(themeJSON), - }; + themes[url] = { + id: url, + selected: selected, + data: processData(themeJSON), + }; - if (selected) { - writeThemeToNative(themes[url]); - updateBunnyColor(themes[url].data, { update: true }); - } + if (selected) { + writeThemeToNative(themes[url]); + updateBunnyColor(themes[url].data, { update: true }); + } } export async function installTheme(url: string) { - if (typeof url !== "string" || url in themes) - throw new Error("Theme already installed"); - await fetchTheme(url); + if (typeof url !== "string" || url in themes) throw new Error("Theme already installed"); + await fetchTheme(url); } export function selectTheme(theme: VdThemeInfo | null, write = true) { - if (theme) theme.selected = true; - Object.keys(themes).forEach( - (k) => (themes[k].selected = themes[k].id === theme?.id), - ); + if (theme) theme.selected = true; + Object.keys(themes).forEach( + k => themes[k].selected = themes[k].id === theme?.id + ); - if (theme == null && write) { - updateBunnyColor(null, { update: false, startup: true }); - return writeThemeToNative({}); - } else if (theme) { - updateBunnyColor(theme.data, { update: true, startup: false }); - return writeThemeToNative(theme); - } + if (theme == null && write) { + updateBunnyColor(null, { update: true }); + return writeThemeToNative({}); + } else if (theme) { + updateBunnyColor(theme.data, { update: true }); + return writeThemeToNative(theme); + } } export async function removeTheme(id: string) { - const theme = themes[id]; - if (theme.selected) await selectTheme(null); - delete themes[id]; + const theme = themes[id]; + if (theme.selected) await selectTheme(null); + delete themes[id]; - return theme.selected; + return theme.selected; } export async function updateThemes() { - await awaitStorage(themes); - const currentTheme = getThemeFromLoader(); - await Promise.allSettled( - Object.keys(themes).map((id) => fetchTheme(id, currentTheme?.id === id)), - ); + await awaitStorage(themes); + const currentTheme = getThemeFromLoader(); + await Promise.allSettled(Object.keys(themes).map(id => fetchTheme(id, currentTheme?.id === id))); } export function getCurrentTheme() { - return Object.values(themes).find((t) => t.selected) ?? null; + return Object.values(themes).find(t => t.selected) ?? null; } /** * @internal */ export function getThemeFromLoader(): VdThemeInfo | null { - return getStoredTheme(); + return getStoredTheme(); } /** * @internal */ export async function initThemes() { - if (!isThemeSupported()) return; + if (!isThemeSupported()) return; + if (settings.safeMode?.enabled) return; - try { - if (isPyonLoader()) { - writeFile("../vendetta_theme.json", "null"); + try { + if (isPyonLoader()) { + writeFile("../vendetta_theme.json", "null"); + } + + await newAwaitStorage(colorsPref); + + const currentTheme = getThemeFromLoader(); + initColors(currentTheme?.data ?? null); + + updateThemes().catch(e => console.error("Failed to update themes", e)); + } catch (e) { + console.error("Failed to initialize themes", e); } - - await newAwaitStorage(colorsPref); - - const currentTheme = getThemeFromLoader(); - initColors(currentTheme?.data ?? null); - - updateThemes().catch((e) => console.error("Failed to update themes", e)); - } catch (e) { - console.error("Failed to initialize themes", e); - } }