diff --git a/scripts/build.mjs b/scripts/build.mjs index f243e5c..989aade 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -50,7 +50,7 @@ const config = { window: "globalThis", __DEV__: JSON.stringify(releaseBranch !== "main"), }, - inject: ["./shims/asyncIteratorSymbol.js", "./shims/promiseAllSettled.js", "./shims/weakref.js"], + inject: ["./shims/asyncIteratorSymbol.js", "./shims/promiseAllSettled.js"], legalComments: "none", alias: { "!ShiggyCord-deps-shim!": "./shims/depsModule.ts", diff --git a/shims/weakref.js b/shims/weakref.js deleted file mode 100644 index 98cabf0..0000000 --- a/shims/weakref.js +++ /dev/null @@ -1,128 +0,0 @@ -// @ts-nocheck -/** - * WeakRef / FinalizationRegistry shim - * - * Purpose: - * - Provide a safe fallback when the runtime doesn't implement WeakRef or FinalizationRegistry. - * - This fallback avoids a ReferenceError and keeps code paths functional, but it does NOT - * emulate true weak-reference GC behavior. Use it only as a compatibility fallback. - * - */ - -let _shimWarned = false; - -function _warnOnce(msg) { - if (!_shimWarned) { - // keep this quiet by default; print only when fallback is actually used - try { - // Prefer console.warn when available - if (typeof console !== "undefined" && typeof console.warn === "function") { - console.warn(`[weakref-shim] ${msg}`); - } - } catch (_) {} - _shimWarned = true; - } -} - -/** - * WeakRef fallback - * - Keeps a strong reference to the target (so it will not be GC'd). - * - Implements the minimal `deref()` API. - */ -class WeakRefFallback { - constructor(value) { - // Keep a strong reference: this is *not* weak. - this.__value = value; - _warnOnce( - "WeakRef is not available in this runtime. Using a strong-reference fallback — memory usage/semantics may differ." - ); - } - - deref() { - return this.__value; - } -} - -/** - * FinalizationRegistry fallback - * - No-op registry: register/unregister do nothing. - * - Implement `cleanupSome` as a best-effort sync call when a callback is provided, - * but it cannot be tied to GC. - */ -class FinalizationRegistryFallback { - constructor(cleanupCallback) { - // store callback to allow optional manual invocation via cleanupSome - this.__cleanupCallback = typeof cleanupCallback === "function" ? cleanupCallback : null; - _warnOnce( - "FinalizationRegistry is not available in this runtime. Using a no-op fallback — finalizers will not run based on GC." - ); - } - - /** - * register(target, heldValue, unregisterToken?) - * - No-op in fallback. We keep no strong references here on purpose (can't emulate GC). - */ - register(_target /*, _heldValue, _unregisterToken */) { - // intentionally no-op - } - - /** - * unregister(unregisterToken) - * - No-op in fallback. - */ - unregister(_unregisterToken) { - // intentionally no-op - } - - /** - * cleanupSome(callback?) - * - If provided a callback or a stored cleanup callback exists, call it immediately. - * - This is NOT equivalent to real FinalizationRegistry cleanup; it's only a manual helper. - */ - cleanupSome(callback) { - const cb = typeof callback === "function" ? callback : this.__cleanupCallback; - if (typeof cb === "function") { - try { - cb(); - } catch (e) { - try { - if (typeof console !== "undefined" && typeof console.error === "function") { - console.error("[weakref-shim] error running cleanupSome callback:", e); - } - } catch (_) {} - } - } - } -} - -// If native implementations exist, prefer them; otherwise install fallbacks. -// We assign to globalThis so code using `WeakRef`/`FinalizationRegistry` globals doesn't crash. -const NativeWeakRef = - typeof globalThis !== "undefined" && typeof globalThis.WeakRef === "function" ? globalThis.WeakRef : undefined; - -const NativeFinalizationRegistry = - typeof globalThis !== "undefined" && typeof globalThis.FinalizationRegistry === "function" - ? globalThis.FinalizationRegistry - : undefined; - -const WeakRefImpl = NativeWeakRef ?? WeakRefFallback; -const FinalizationRegistryImpl = NativeFinalizationRegistry ?? FinalizationRegistryFallback; - -try { - if (typeof globalThis !== "undefined") { - if (typeof globalThis.WeakRef === "undefined") { - // Install fallback - globalThis.WeakRef = WeakRefImpl; - } - if (typeof globalThis.FinalizationRegistry === "undefined") { - globalThis.FinalizationRegistry = FinalizationRegistryImpl; - } - } -} catch (e) { - // Some embedder environments may forbid writing globals; ignore in that case. - _warnOnce("Unable to assign WeakRef/FinalizationRegistry to globalThis: " + (e && e.message)); -} - -// Export the implementations so the inject mechanism (or explicit imports) can reference them. -// Export names are sorted to satisfy lint rules that require sorted exports. -export { FinalizationRegistryImpl as FinalizationRegistry, WeakRefImpl as WeakRef }; diff --git a/src/core/ui/settings/pages/Plugins/CorePlugins.tsx b/src/core/ui/settings/pages/Plugins/CorePlugins.tsx new file mode 100644 index 0000000..c08e4c2 --- /dev/null +++ b/src/core/ui/settings/pages/Plugins/CorePlugins.tsx @@ -0,0 +1,45 @@ +import { React } from "@metro/common"; +import AddonPage from "@core/ui/components/AddonPage"; +import PluginCard from "@core/ui/settings/pages/Plugins/components/PluginCard"; +import { corePluginInstances, registeredPlugins } from "@lib/addons/plugins"; +import { UnifiedPluginModel } from "@core/ui/settings/pages/Plugins/models"; +import unifyBunnyPlugin from "@core/ui/settings/pages/Plugins/models/bunny"; +import { Strings } from "@core/i18n"; + +/** + * Core plugins settings page + * + * This page shows only the built-in core plugins. Core plugins are always + * installed (can't be uninstalled) and are registered in `corePluginInstances`. + * + * We reuse the existing `AddonPage` + `PluginCard` components and feed them + * the normalized `UnifiedPluginModel` objects produced by `unifyBunnyPlugin`. + */ + +export default function CorePlugins() { + return ( + + CardComponent={PluginCard} + title={Strings.PLUGINS} + searchKeywords={[ + "name", + "description", + (p) => + p.authors + ?.map((a: any) => (typeof a === "string" ? a : a.name)) + .join() || "", + ]} + // Provide simple sort options so the Search input renders round and a sort menu appears + sortOptions={{ + "Name (A-Z)": (a, b) => a.name.localeCompare(b.name), + "Name (Z-A)": (a, b) => b.name.localeCompare(a.name), + }} + // Only core plugins: map corePluginInstances -> registeredPlugins -> unify + items={[...corePluginInstances.keys()] + .map((id) => registeredPlugins.get(id)) + .filter((m): m is any => m !== undefined) + .map(unifyBunnyPlugin)} + ListHeaderComponent={() => null} + /> + ); +} diff --git a/src/core/ui/settings/pages/Plugins/index.tsx b/src/core/ui/settings/pages/Plugins/index.tsx index 97e3e68..2c2514b 100644 --- a/src/core/ui/settings/pages/Plugins/index.tsx +++ b/src/core/ui/settings/pages/Plugins/index.tsx @@ -702,7 +702,7 @@ export default function Plugins() { // Use the insertion order of pluginSettings so newly-installed plugins // (which add entries to pluginSettings) appear first. const installedBnIds = Object.keys(pluginSettings || {}) - .filter((id) => registeredPlugins.has(id)) + .filter((id) => registeredPlugins.has(id) && !isCorePlugin(id)) .slice() .reverse(); @@ -710,12 +710,12 @@ export default function Plugins() { .map((id) => registeredPlugins.get(id)!) .map(unifyBunnyPlugin); - // Merge lists: show Vendetta-managed plugins first, + // Merge lists: show Vendetta-managed plugins first (recent first), // then Bunny-installed externals (recent first). const allPlugins = [...vdPlugins, ...bnPlugins]; - // Include core plugins in the list - return allPlugins; + // Filter out core plugins from the list + return allPlugins.filter((plugin) => !isCorePlugin(plugin.id)); }} ListHeaderComponent={() => null} installAction={{