Merge remote-tracking branch 'upstream/dev' into dev
This commit is contained in:
commit
9ca96e1c26
7 changed files with 224 additions and 336 deletions
20
bun.lock
20
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=="],
|
||||
|
||||
|
|
|
|||
113
package.json
113
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"
|
||||
]
|
||||
}
|
||||
130
src/index.ts
130
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!");
|
||||
};
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -24,18 +24,6 @@ const SEMANTIC_FALLBACK_MAP: Record<string, string> = {
|
|||
|
||||
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<string, string> = {
|
||||
"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]];
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Record<string, VdThemeInfo>>(
|
||||
createMMKVBackend("VENDETTA_THEMES"),
|
||||
),
|
||||
);
|
||||
export const themes = wrapSync(createStorage<Record<string, VdThemeInfo>>(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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue