From 5ad56932fc63f2fb3d232a76ac9a5ddb79aa05d6 Mon Sep 17 00:00:00 2001 From: everestmcarthur <280397636+everestmcarthur@users.noreply.github.com> Date: Sun, 2 Aug 2026 11:05:26 -0400 Subject: [PATCH] ServerDrawer: add a fiber capture point guaranteed to actually mount --- .../src/components/ServerDrawerSheet.tsx | 6 ++ plugins/server-drawer/src/lib/fiberCapture.ts | 51 ++++++++++++++ .../src/patches/hideGuildsBar.tsx | 68 ++----------------- 3 files changed, 62 insertions(+), 63 deletions(-) create mode 100644 plugins/server-drawer/src/lib/fiberCapture.ts diff --git a/plugins/server-drawer/src/components/ServerDrawerSheet.tsx b/plugins/server-drawer/src/components/ServerDrawerSheet.tsx index 9cad7b2..522dead 100644 --- a/plugins/server-drawer/src/components/ServerDrawerSheet.tsx +++ b/plugins/server-drawer/src/components/ServerDrawerSheet.tsx @@ -3,6 +3,7 @@ import { View, Text, Pressable, Animated, Dimensions, StyleSheet, BackHandler } import { lazy } from "../lib/lazy"; import { rawFind, rawFindByFunctionProps } from "../lib/rawFind"; import { getFlux, getHaptic, getColorModule } from "../lib/commonModules"; +import { captureFiberRef } from "../lib/fiberCapture"; import { GuildNode } from "../utils/theme"; import GuildItem from "./GuildItem"; import FolderItem from "./FolderItem"; @@ -122,6 +123,11 @@ export default function ServerDrawerSheet({ gestureContext }: { gestureContext: return ( + {/* Invisible - see lib/fiberCapture.ts. This is the reliable capture point: it's + guaranteed to mount whenever the drawer itself is visibly rendering, independent of + whether hideGuildsBar.tsx's own capture point (which depends on that separate + intercept actually firing) works or not. */} + k.startsWith("__reactFiber$") || k.startsWith("__reactInternalInstance$") + ); + if (fiberKey) fiber = instance[fiberKey]; + } + if (!fiber) return; + + if (fiber.return === undefined && fiber.child === undefined) { + const nested = fiber.stateNode ?? fiber.fiber ?? fiber._debugOwner; + if (nested && (nested.return !== undefined || nested.child !== undefined)) { + fiber = nested; + } else { + return; + } + } + + let root = fiber; + let guard = 0; + while (root.return && guard < 5000) { + root = root.return; + guard++; + } + + (window as any).__serverDrawerFiberRoot = root; + (window as any).__serverDrawerFiberSelf = fiber; + } catch { + // Best-effort diagnostic capture - a failure here shouldn't affect the caller's own render. + } +} diff --git a/plugins/server-drawer/src/patches/hideGuildsBar.tsx b/plugins/server-drawer/src/patches/hideGuildsBar.tsx index 61b0cbe..d8ff721 100644 --- a/plugins/server-drawer/src/patches/hideGuildsBar.tsx +++ b/plugins/server-drawer/src/patches/hideGuildsBar.tsx @@ -1,72 +1,14 @@ import React from "react"; import { View } from "react-native"; import { registerTypeDetector, registerIntercept } from "../lib/createElementIntercept"; +import { captureFiberRef } from "../lib/fiberCapture"; const TAG = "[ServerDrawer]"; -// Key Inspector's own fiber capture only fires from a View rendered inside ITS OWN Settings -// screen - and Settings very likely renders as a separate native modal/root from MainTabs' guild -// tab content, so a fiber captured there can never reach anything under the Guilds tab (Quest -// Dock included) via .return, no matter what screen was visited beforehand. Confirmed live: two -// independent searches for Quest Dock components both came up empty even while a real quest was -// visibly rendering on the Guilds screen, and a fiber-tree walk from a Settings-screen capture -// couldn't find QuestDockWithQuestContext at all. -// -// GuildsBar's hidden slot is a reliable alternative capture point: it's confirmed to actually -// mount on the Guilds screen (that's this whole file's job), and it's a sibling of the Quest Dock -// in the same tree (both live under MainTabsNavigatorPanel), so walking up from here and back down -// reaches Quest Dock correctly. Stored under the same window.__keyInspectorFiberRoot/ -// __keyInspectorFiberSelf globals Key Inspector's Eval scripts already expect, so no existing -// script needs to change - this just gives them a capture point that's actually reachable. -function captureFiberRef(instance: any) { - if (!instance) return; - try { - let fiber: any = null; - - if (instance._internalFiberInstanceHandleDEV) { - fiber = instance._internalFiberInstanceHandleDEV; - } else if (instance._internalInstanceHandle) { - fiber = instance._internalInstanceHandle; - } else if (instance.__internalInstanceHandle) { - fiber = instance.__internalInstanceHandle; - } else { - const fiberKey = Object.keys(instance).find( - (k) => k.startsWith("__reactFiber$") || k.startsWith("__reactInternalInstance$") - ); - if (fiberKey) fiber = instance[fiberKey]; - } - if (!fiber) return; - - if (fiber.return === undefined && fiber.child === undefined) { - const nested = fiber.stateNode ?? fiber.fiber ?? fiber._debugOwner; - if (nested && (nested.return !== undefined || nested.child !== undefined)) { - fiber = nested; - } else { - return; - } - } - - let root = fiber; - let guard = 0; - while (root.return && guard < 5000) { - root = root.return; - guard++; - } - - // Dedicated globals, not window.__keyInspectorFiberRoot/__keyInspectorFiberSelf: Key - // Inspector's own Settings screen has its own capture View using those same names, and its - // ref re-fires every time that screen mounts - including just scrolling to the Eval console - // to paste the next script. Sharing the names meant navigating back to Settings silently - // overwrote this capture with one rooted in Settings' own (likely separate-modal) tree, - // which is exactly the failure this capture point exists to avoid. Confirmed live: a text - // dump meant to read GuildsBar's slot came back with Key Inspector's own UI strings instead. - (window as any).__serverDrawerFiberRoot = root; - (window as any).__serverDrawerFiberSelf = fiber; - } catch { - // Best-effort diagnostic capture - a failure here shouldn't affect GuildsBar staying hidden. - } -} - +// Whether this capture point actually mounts depends entirely on whether the intercept below +// fires (unverified independently of it - see lib/fiberCapture.ts for the primary, confirmed +// capture point inside ServerDrawerSheet.tsx itself, which is guaranteed to mount whenever the +// drawer is visibly showing). Kept here too as a second chance, harmless either way. function Nothing() { return React.createElement(View, { ref: captureFiberRef,