//=========================================== // THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template //=========================================== import type { StackClientApp } from "../lib/hexclave-app"; import { captureError } from "@hexclave/shared/dist/utils/errors"; import { runAsynchronously } from "@hexclave/shared/dist/utils/promises"; import { isLikelyDevelopmentEnvironment } from "../in-page-ui/dev-environment"; import { canMountIntoDom } from "../in-page-ui/dom"; import type { createDevTool as CreateDevToolFn } from "./dev-tool-core"; // Hexclave rebrand: UI-only local pref — straight rename (one-time reset is harmless) const OVERRIDE_KEY = '__hexclave-dev-tool-override'; function getOverride(): boolean | null { try { const val = localStorage.getItem(OVERRIDE_KEY); if (val === 'true') return true; if (val === 'false') return false; } catch {} return null; } let activeDevToolOption: true | "auto" | undefined = undefined; let activeProjectIsDevelopmentEnvironment = false; function shouldShow(): boolean { const override = getOverride(); if (override !== null) return override; if (!canMountIntoDom()) return false; // If devTool was explicitly set to true, always show if (activeDevToolOption === true) return true; // Development environment projects should expose the tool even when the // consuming app itself was built or hosted as production. if (activeProjectIsDevelopmentEnvironment) return true; // "auto" behavior (the default): every in-page UI that is only appropriate while developing shares one heuristic, so // the dev tool and the error cards can never disagree about whether this looks like a development environment. return isLikelyDevelopmentEnvironment(); } let activeCleanup: (() => void) | null = null; let activeApp: StackClientApp | null = null; let mountGeneration = 0; let createDevToolPromise: Promise | null = null; function loadCreateDevTool(): Promise { if (!createDevToolPromise) { createDevToolPromise = import("./dev-tool-core").then(m => m.createDevTool).catch((err) => { createDevToolPromise = null; throw err; }); } return createDevToolPromise; } function tryMount() { if (activeCleanup) { activeCleanup(); activeCleanup = null; } if (!shouldShow() || !activeApp || !canMountIntoDom()) return; const generation = ++mountGeneration; const app = activeApp; runAsynchronously(async () => { const createDevTool = await loadCreateDevTool(); if (generation !== mountGeneration) return; if (!shouldShow() || activeApp !== app || !canMountIntoDom()) return; activeCleanup = createDevTool(app); }, { noErrorLogging: true, onError: (error) => { captureError("dev-tool-mount", error); }, }); } /** * Mounts the Hexclave dev tool on the page. * * Visibility is determined by the `devTool` option: * - `true`: always show * - `false`: never show (caller gates this — mountDevTool won't be called) * - `"auto"` / `undefined`: show based on NODE_ENV or localhost/file: heuristics * * Console commands (also work in production): * HexclaveDevTool.enable() — force-show the dev tool * HexclaveDevTool.disable() — force-hide the dev tool * HexclaveDevTool.reset() — revert to auto behavior */ export function mountDevTool(app: StackClientApp, devToolOption?: boolean | "auto"): () => void { activeApp = app; activeDevToolOption = devToolOption === false ? undefined : devToolOption ?? undefined; activeProjectIsDevelopmentEnvironment = false; tryMount(); // The project endpoint is also the source of config warnings, so this uses // the backend's development-environment flag instead of inferring it from // the SDK's build or the current page's origin. queueMicrotask(() => { runAsynchronously(async () => { const project = await app.getProject(); if (activeApp !== app) return; activeProjectIsDevelopmentEnvironment = project.isDevelopmentEnvironment; if (activeProjectIsDevelopmentEnvironment && activeCleanup === null) { tryMount(); } }, { noErrorLogging: true, onError: (error) => { captureError("dev-tool-project-detection", error); }, }); }); // Capture the cleanup created by THIS specific mount call so that React // StrictMode's double-invoke doesn't let the first effect's cleanup tear // down the second mount (which would cause the tool to disappear silently). const myCleanup = activeCleanup; return () => { activeApp = null; activeProjectIsDevelopmentEnvironment = false; if (activeCleanup === myCleanup && myCleanup != null) { activeCleanup = null; myCleanup(); } }; } // Expose console commands: HexclaveDevTool.enable() / .disable() / .reset() if (typeof window !== 'undefined') { // Hexclave rebrand: expose under both the legacy and new global names. (window as any).HexclaveDevTool = (window as any).HexclaveDevTool = { enable() { try { localStorage.setItem(OVERRIDE_KEY, 'true'); } catch {} tryMount(); console.log('[Stack DevTool] Enabled. Refresh if the panel does not appear.'); }, disable() { try { localStorage.setItem(OVERRIDE_KEY, 'false'); } catch {} if (activeCleanup) { activeCleanup(); activeCleanup = null; } console.log('[Stack DevTool] Disabled.'); }, reset() { try { localStorage.removeItem(OVERRIDE_KEY); } catch {} if (shouldShow()) { tryMount(); } else if (activeCleanup) { activeCleanup(); activeCleanup = null; } console.log('[Stack DevTool] Reset to default (auto-detect based on project, NODE_ENV, or localhost/file: origin).'); }, }; }