From 0bb08ea5e12aaa57aca66dce150b84df8dd8c27f Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 13 Aug 2025 14:08:22 +0200 Subject: [PATCH 01/10] feat: add broadcast channel into event bus --- packages/devtools/src/components/tabs.tsx | 49 +++++++++++++--------- packages/devtools/src/styles/use-styles.ts | 13 ++++-- packages/event-bus/src/client/client.ts | 18 +++++--- 3 files changed, 53 insertions(+), 27 deletions(-) diff --git a/packages/devtools/src/components/tabs.tsx b/packages/devtools/src/components/tabs.tsx index 185681c41..79526d925 100644 --- a/packages/devtools/src/components/tabs.tsx +++ b/packages/devtools/src/components/tabs.tsx @@ -25,26 +25,37 @@ export const Tabs = (props: TabsProps) => { )} - + + + + ) } diff --git a/packages/devtools/src/styles/use-styles.ts b/packages/devtools/src/styles/use-styles.ts index d67cf4f0d..db3019e61 100644 --- a/packages/devtools/src/styles/use-styles.ts +++ b/packages/devtools/src/styles/use-styles.ts @@ -181,7 +181,7 @@ const stylesFactory = () => { border: none; transition: all 0.2s ease-in-out; border-left: 2px solid transparent; - &:hover:not(.close):not(.active) { + &:hover:not(.close):not(.active):not(.detach) { background-color: ${colors.gray[700]}; color: ${colors.gray[100]}; border-left: 2px solid ${colors.purple[500]}; @@ -191,8 +191,15 @@ const stylesFactory = () => { color: ${colors.gray[100]}; border-left: 2px solid ${colors.purple[500]}; } - &.close { - margin-top: auto; + &.detach { + &:hover { + background-color: ${colors.gray[700]}; + } + &:hover { + color: ${colors.green[500]}; + } + } + &.close { &:hover { background-color: ${colors.gray[700]}; } diff --git a/packages/event-bus/src/client/client.ts b/packages/event-bus/src/client/client.ts index 393522ce3..cc3227df5 100644 --- a/packages/event-bus/src/client/client.ts +++ b/packages/event-bus/src/client/client.ts @@ -30,7 +30,7 @@ export class ClientEventBus { #eventTarget: EventTarget #debug: boolean #connectToServerBus: boolean - + #broadcastChannel: BroadcastChannel | null #dispatcher = (e: Event) => { const event = (e as CustomEvent).detail this.emitToServer(event) @@ -48,16 +48,19 @@ export class ClientEventBus { connectToServerBus = false, }: ClientEventBusConfig = {}) { this.#debug = debug + this.#broadcastChannel = new BroadcastChannel("tanstack-devtools") this.#eventSource = null this.#port = port this.#socket = null this.#connectToServerBus = connectToServerBus this.#eventTarget = this.getGlobalTarget() - + this.#broadcastChannel.onmessage = e => { + this.emitToClients(e.data, true) + } this.debugLog('Initializing client event bus') } - private emitToClients(event: TanStackDevtoolsEvent) { + private emitToClients(event: TanStackDevtoolsEvent, fromBroadcastChannel = false) { this.debugLog('Emitting event from client bus', event) const specificEvent = new CustomEvent(event.type, { detail: event }) this.debugLog('Emitting event to specific client listeners', event) @@ -65,6 +68,11 @@ export class ClientEventBus { const globalEvent = new CustomEvent('tanstack-devtools-global', { detail: event, }) + // We only emit the events if they didn't come from the broadcast channel + // otherwise it would infinitely send events between + if (!fromBroadcastChannel) { + this.#broadcastChannel?.postMessage(event) + } this.debugLog('Emitting event to global client listeners', event) this.#eventTarget.dispatchEvent(globalEvent) } @@ -83,7 +91,7 @@ export class ClientEventBus { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: json, - }).catch(() => {}) + }).catch(() => { }) } } start() { @@ -176,6 +184,6 @@ export class ClientEventBus { try { const event = JSON.parse(data) as TanStackDevtoolsEvent this.emitToClients(event) - } catch {} + } catch { } } } From 2332b61c4bd2de574abe2869ad782488a875c3cf Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Thu, 14 Aug 2025 07:56:55 +0200 Subject: [PATCH 02/10] feat: dettached mode --- .../devtools/src/components/content-panel.tsx | 8 +-- .../devtools/src/components/main-panel.tsx | 8 +-- packages/devtools/src/components/tabs.tsx | 27 +++++++--- .../devtools/src/context/devtools-context.tsx | 40 +++++++++++++- .../devtools/src/context/devtools-store.ts | 4 +- .../src/context/use-devtools-context.ts | 28 +++++++++- packages/devtools/src/devtools.tsx | 49 +++++++++++++---- .../detached/use-check-if-still-detached.ts | 53 +++++++++++++++++++ .../src/hooks/detached/use-remove-body.ts | 18 +++++++ .../detached/use-reset-detachment-check.ts | 10 ++++ .../detached/use-sync-state-when-detached.ts | 41 ++++++++++++++ .../devtools/src/hooks/use-event-listener.ts | 39 ++++++++++++++ packages/devtools/src/styles/use-styles.ts | 15 ++++-- packages/devtools/src/utils/detached.ts | 11 ++++ packages/devtools/src/utils/storage.ts | 16 ++++++ 15 files changed, 339 insertions(+), 28 deletions(-) create mode 100644 packages/devtools/src/hooks/detached/use-check-if-still-detached.ts create mode 100644 packages/devtools/src/hooks/detached/use-remove-body.ts create mode 100644 packages/devtools/src/hooks/detached/use-reset-detachment-check.ts create mode 100644 packages/devtools/src/hooks/detached/use-sync-state-when-detached.ts create mode 100644 packages/devtools/src/hooks/use-event-listener.ts create mode 100644 packages/devtools/src/utils/detached.ts diff --git a/packages/devtools/src/components/content-panel.tsx b/packages/devtools/src/components/content-panel.tsx index 24a007b7d..c641c942a 100644 --- a/packages/devtools/src/components/content-panel.tsx +++ b/packages/devtools/src/components/content-panel.tsx @@ -1,4 +1,5 @@ -import { useDevtoolsSettings } from '../context/use-devtools-context' +import { Show } from 'solid-js' +import { useDetachedWindowControls, useDevtoolsSettings } from '../context/use-devtools-context' import { useStyles } from '../styles/use-styles' import type { JSX } from 'solid-js/jsx-runtime' @@ -9,14 +10,15 @@ export const ContentPanel = (props: { }) => { const styles = useStyles() const { settings } = useDevtoolsSettings() + const { isDetached } = useDetachedWindowControls() return (
- {props.handleDragStart ? ( +
- ) : null} +
{props.children}
) diff --git a/packages/devtools/src/components/main-panel.tsx b/packages/devtools/src/components/main-panel.tsx index 38c1bcbc5..c39060e84 100644 --- a/packages/devtools/src/components/main-panel.tsx +++ b/packages/devtools/src/components/main-panel.tsx @@ -1,9 +1,10 @@ import clsx from 'clsx' -import { useDevtoolsSettings, useHeight } from '../context/use-devtools-context' +import { useDetachedWindowControls, useDevtoolsSettings, useHeight } from '../context/use-devtools-context' import { useStyles } from '../styles/use-styles' import { TANSTACK_DEVTOOLS } from '../utils/storage' import type { Accessor, JSX } from 'solid-js' + export const MainPanel = (props: { isOpen: Accessor children: JSX.Element @@ -12,14 +13,15 @@ export const MainPanel = (props: { const styles = useStyles() const { height } = useHeight() const { settings } = useDevtoolsSettings() + const { isDetached } = useDetachedWindowControls() return (
void @@ -11,7 +12,21 @@ interface TabsProps { export const Tabs = (props: TabsProps) => { const styles = useStyles() const { state, setState } = useDevtoolsState() + const { setDetachedWindowOwner, detachedWindowOwner, detachedWindow } = useDetachedWindowControls() + const handleDetachment = () => { + const rdtWindow = window.open( + window.location.href, + "", + `popup,width=${window.innerWidth},height=${state().height},top=${window.screen.height},left=${window.screenLeft}}` + ) + if (rdtWindow) { + setDetachedWindowOwner(true) + setStorageItem(TANSTACK_DEVTOOLS_IS_DETACHED, "true") + setSessionItem(TANSTACK_DEVTOOLS_DETACHED_OWNER, "true") + rdtWindow.TDT_MOUNTED = true + } + } return (
@@ -25,16 +40,16 @@ export const Tabs = (props: TabsProps) => { )} -
- + } -
+
}
) } diff --git a/packages/devtools/src/context/devtools-context.tsx b/packages/devtools/src/context/devtools-context.tsx index cb808457d..fdec51f58 100644 --- a/packages/devtools/src/context/devtools-context.tsx +++ b/packages/devtools/src/context/devtools-context.tsx @@ -2,11 +2,16 @@ import { createContext } from 'solid-js' import { createStore } from 'solid-js/store' import { tryParseJson } from '../utils/sanitize' import { + TANSTACK_DEVTOOLS_CHECK_DETACHED, + TANSTACK_DEVTOOLS_DETACHED, TANSTACK_DEVTOOLS_SETTINGS, TANSTACK_DEVTOOLS_STATE, getStorageItem, + setSessionItem, setStorageItem, } from '../utils/storage' +import { checkIsDetached, checkIsDetachedOwner, checkIsDetachedWindow } from '../utils/detached' +import { useRemoveBody } from '../hooks/detached/use-remove-body' import { initialState } from './devtools-store' import type { DevtoolsStore } from './devtools-store' import type { JSX, Setter } from 'solid-js' @@ -91,13 +96,40 @@ const generatePluginId = (plugin: TanStackDevtoolsPlugin, index: number) => { return index.toString() } -const getExistingStateFromStorage = ( +const setIsDetachedIfRequired = () => { + const isDetachedWindow = checkIsDetachedWindow() + if (!isDetachedWindow && window.TDT_MOUNTED) { + setSessionItem(TANSTACK_DEVTOOLS_DETACHED, "true") + } +} + +const resetIsDetachedCheck = () => { + setStorageItem(TANSTACK_DEVTOOLS_CHECK_DETACHED, "false") +} + +const detachedModeSetup = () => { + resetIsDetachedCheck() + setIsDetachedIfRequired() + const isDetachedWindow = checkIsDetachedWindow() + const isDetached = checkIsDetached() + const isDetachedOwner = checkIsDetachedOwner() + + if (isDetachedWindow && !isDetached) { + window.close() + } + + return { + detachedWindow: window.TDT_MOUNTED ?? isDetachedWindow, + detachedWindowOwner: isDetachedOwner, + } +} +export const getExistingStateFromStorage = ( config?: TanStackDevtoolsConfig, plugins?: Array, ) => { const existingState = getStorageItem(TANSTACK_DEVTOOLS_STATE) const settings = getSettings() - + const { detachedWindow, detachedWindowOwner } = detachedModeSetup() const state: DevtoolsStore = { ...initialState, plugins: @@ -112,6 +144,8 @@ const getExistingStateFromStorage = ( ...initialState.state, ...(existingState ? JSON.parse(existingState) : {}), }, + detachedWindow, + detachedWindowOwner, settings: { ...initialState.settings, ...config, @@ -128,6 +162,8 @@ export const DevtoolsProvider = (props: ContextProps) => { getExistingStateFromStorage(props.config, props.plugins), ) + useRemoveBody(store) + const value = { store, setStore: ( diff --git a/packages/devtools/src/context/devtools-store.ts b/packages/devtools/src/context/devtools-store.ts index 26e05e158..15732c15e 100644 --- a/packages/devtools/src/context/devtools-store.ts +++ b/packages/devtools/src/context/devtools-store.ts @@ -55,8 +55,10 @@ export type DevtoolsStore = { activeTab: TabName height: number activePlugin?: string | undefined - persistOpen: boolean + persistOpen: boolean, } + detachedWindowOwner?: boolean + detachedWindow?: boolean plugins?: Array } diff --git a/packages/devtools/src/context/use-devtools-context.ts b/packages/devtools/src/context/use-devtools-context.ts index cdf256f63..dfd5cd779 100644 --- a/packages/devtools/src/context/use-devtools-context.ts +++ b/packages/devtools/src/context/use-devtools-context.ts @@ -7,7 +7,7 @@ import type { DevtoolsStore } from './devtools-store.js' * Returns an object containing the current state and setState function of the ShellContext. * Throws an error if used outside of a ShellContextProvider. */ -const useDevtoolsContext = () => { +export const useDevtoolsContext = () => { const context = useContext(DevtoolsContext) if (context === undefined) { throw new Error( @@ -92,3 +92,29 @@ export const useHeight = () => { return { height, setHeight } } + +declare global { + interface Window { + TDT_MOUNTED: boolean | undefined + } +} + +export const useDetachedWindowControls = () => { + const { store, setStore } = useDevtoolsContext() + const detachedWindowOwner = createMemo(() => store.detachedWindowOwner) + const detachedWindow = createMemo(() => store.detachedWindow) + const mounted = createMemo(() => Boolean(window.TDT_MOUNTED)) + const setDetachedWindowOwner = (isDetachedWindowOwner: boolean) => { + setStore((prev) => ({ + ...prev, + detachedWindowOwner: isDetachedWindowOwner, + })) + } + + return { + detachedWindow: detachedWindow() || mounted(), + detachedWindowOwner, + setDetachedWindowOwner, + isDetached: Boolean(detachedWindow() || detachedWindowOwner()), + } +} diff --git a/packages/devtools/src/devtools.tsx b/packages/devtools/src/devtools.tsx index 458cb161f..0eb7c6aae 100644 --- a/packages/devtools/src/devtools.tsx +++ b/packages/devtools/src/devtools.tsx @@ -1,26 +1,33 @@ -import { Show, createEffect, createSignal } from 'solid-js' +import { Show, createEffect, createSignal, onCleanup } from 'solid-js' import { createShortcut } from '@solid-primitives/keyboard' import { + useDetachedWindowControls, useDevtoolsSettings, useHeight, usePersistOpen, } from './context/use-devtools-context' import { useDisableTabbing } from './hooks/use-disable-tabbing' -import { TANSTACK_DEVTOOLS } from './utils/storage' +import { TANSTACK_DEVTOOLS, TANSTACK_DEVTOOLS_DETACHED_OWNER, TANSTACK_DEVTOOLS_IS_DETACHED, setSessionItem, setStorageItem, } from './utils/storage' import { Trigger } from './components/trigger' import { MainPanel } from './components/main-panel' import { ContentPanel } from './components/content-panel' import { Tabs } from './components/tabs' import { TabContent } from './components/tab-content' +import { useResetDetachmentCheck } from './hooks/detached/use-reset-detachment-check' +import { useSyncStateWhenDetached } from './hooks/detached/use-sync-state-when-detached' +import { useWindowListener } from './hooks/use-event-listener' +import { useCheckIfStillDetached } from './hooks/detached/use-check-if-still-detached' export default function DevTools() { + const { detachedWindowOwner, isDetached, setDetachedWindowOwner } = useDetachedWindowControls() const { settings } = useDevtoolsSettings() const { setHeight } = useHeight() const { persistOpen, setPersistOpen } = usePersistOpen() const [rootEl, setRootEl] = createSignal() const [isOpen, setIsOpen] = createSignal( - settings().defaultOpen || persistOpen(), + isDetached || settings().defaultOpen || persistOpen(), ) + let panelRef: HTMLDivElement | undefined = undefined const [isResizing, setIsResizing] = createSignal(false) const toggleOpen = () => { @@ -28,7 +35,10 @@ export default function DevTools() { setIsOpen(!open) setPersistOpen(!open) } - createEffect(() => {}) + + useSyncStateWhenDetached() + useResetDetachmentCheck() + useCheckIfStillDetached() // Used to resize the panel const handleDragStart = ( panelElement: HTMLDivElement | undefined, @@ -116,10 +126,14 @@ export default function DevTools() { return }) createEffect(() => { - window.addEventListener('keydown', (e) => { + const event = (e: KeyboardEvent) => { if (e.key === 'Escape' && isOpen()) { toggleOpen() } + } + window.addEventListener('keydown', event) + onCleanup(() => { + window.removeEventListener('keydown', event) }) }) useDisableTabbing(isOpen) @@ -136,14 +150,31 @@ export default function DevTools() { }) }) - createEffect(() => {}) + createEffect(() => { + if (isDetached) { + useWindowListener("resize", () => { + setHeight(window.innerHeight) + }) + } + }) + return ( -
+
+ + false} + setIsOpen={() => { + setDetachedWindowOwner(false) + setStorageItem(TANSTACK_DEVTOOLS_IS_DETACHED, "false") + setSessionItem(TANSTACK_DEVTOOLS_DETACHED_OWNER, "false") + }} + /> + diff --git a/packages/devtools/src/hooks/detached/use-check-if-still-detached.ts b/packages/devtools/src/hooks/detached/use-check-if-still-detached.ts new file mode 100644 index 000000000..d44e0bf71 --- /dev/null +++ b/packages/devtools/src/hooks/detached/use-check-if-still-detached.ts @@ -0,0 +1,53 @@ + +import { createEffect, onCleanup, } from "solid-js" +import { + TANSTACK_DEVTOOLS_CHECK_DETACHED, + TANSTACK_DEVTOOLS_DETACHED, + TANSTACK_DEVTOOLS_DETACHED_OWNER, + TANSTACK_DEVTOOLS_IS_DETACHED, + getBooleanFromStorage, + setStorageItem, +} from "../../utils/storage" +import { getExistingStateFromStorage } from "../../context/devtools-context.jsx" +import { useDevtoolsContext } from "../../context/use-devtools-context" + +export const useCheckIfStillDetached = () => { + const context = useDevtoolsContext() + + const checkDetachment = (e: StorageEvent) => { + // We only care about the should_check key + if (e.key !== TANSTACK_DEVTOOLS_CHECK_DETACHED) { + return + } + const isDetached = getBooleanFromStorage(TANSTACK_DEVTOOLS_IS_DETACHED); + if (!isDetached) { + return + } + const shouldCheckDetached = getBooleanFromStorage(TANSTACK_DEVTOOLS_CHECK_DETACHED) + + // If the detached window is unloaded we want to check if it is still there + if (shouldCheckDetached) { + setTimeout(() => { + // On reload the detached window will set the flag back to false so we can check if it is still detached + const isNotDetachedAnymore = getBooleanFromStorage(TANSTACK_DEVTOOLS_CHECK_DETACHED) + // The window hasn't set it back to true so it is not detached anymore and we clean all the detached state + if (isNotDetachedAnymore) { + setStorageItem(TANSTACK_DEVTOOLS_IS_DETACHED, "false") + setStorageItem(TANSTACK_DEVTOOLS_CHECK_DETACHED, "false") + sessionStorage.removeItem(TANSTACK_DEVTOOLS_DETACHED_OWNER) + sessionStorage.removeItem(TANSTACK_DEVTOOLS_DETACHED) + const state = getExistingStateFromStorage() + context.setStore(prev => ({ + ...prev, + ...state, + plugins: prev.plugins + })) + } + }, 200) + } + } + createEffect(() => { + window.addEventListener("storage", checkDetachment) + onCleanup(() => window.removeEventListener("storage", checkDetachment)) + },) +} diff --git a/packages/devtools/src/hooks/detached/use-remove-body.ts b/packages/devtools/src/hooks/detached/use-remove-body.ts new file mode 100644 index 000000000..abfb762f2 --- /dev/null +++ b/packages/devtools/src/hooks/detached/use-remove-body.ts @@ -0,0 +1,18 @@ + +import { createEffect } from "solid-js" +import { useStyles } from "../../styles/use-styles" +import type { DevtoolsStore } from "../../context/devtools-store" + +export const useRemoveBody = (state: DevtoolsStore) => { + const styles = useStyles() + createEffect(() => { + if (!state.detachedWindow) { + return + } + + const coverEl = document.createElement("div") + coverEl.classList.add(styles().cover) + document.body.appendChild(coverEl) + + }) +} diff --git a/packages/devtools/src/hooks/detached/use-reset-detachment-check.ts b/packages/devtools/src/hooks/detached/use-reset-detachment-check.ts new file mode 100644 index 000000000..bc6caf464 --- /dev/null +++ b/packages/devtools/src/hooks/detached/use-reset-detachment-check.ts @@ -0,0 +1,10 @@ + +import { useDetachedWindowControls } from "../../context/use-devtools-context" +import { TANSTACK_DEVTOOLS_CHECK_DETACHED, setStorageItem, } from "../../utils/storage" +import { useWindowListener } from "../use-event-listener" +// called on windows unmount +export const useResetDetachmentCheck = () => { + const { isDetached } = useDetachedWindowControls() + + useWindowListener("unload", () => setStorageItem(TANSTACK_DEVTOOLS_CHECK_DETACHED, "true"), isDetached) +} diff --git a/packages/devtools/src/hooks/detached/use-sync-state-when-detached.ts b/packages/devtools/src/hooks/detached/use-sync-state-when-detached.ts new file mode 100644 index 000000000..c4e09321d --- /dev/null +++ b/packages/devtools/src/hooks/detached/use-sync-state-when-detached.ts @@ -0,0 +1,41 @@ +import { getExistingStateFromStorage } from "../../context/devtools-context" +import { useDevtoolsContext, useDevtoolsSettings, useDevtoolsState } from "../../context/use-devtools-context" +import { TANSTACK_DEVTOOLS_SETTINGS, TANSTACK_DEVTOOLS_STATE } from "../../utils/storage" +import { useWindowListener } from "../use-event-listener" + +const refreshRequiredKeys = [TANSTACK_DEVTOOLS_SETTINGS, TANSTACK_DEVTOOLS_STATE] + +// Sync state with local storage when in detached mode +export const useSyncStateWhenDetached = () => { + const { store } = useDevtoolsContext() + const { state, setState } = useDevtoolsState() + const { setSettings, settings } = useDevtoolsSettings() + useWindowListener("storage", (e) => { + // Not in detached mode + if (!store.detachedWindow && !store.detachedWindowOwner) { + return + } + // Not caused by the dev tools + if (e.key && !refreshRequiredKeys.includes(e.key)) { + return + } + // Check if the settings have not changed and early return + if (e.key === TANSTACK_DEVTOOLS_SETTINGS) { + const oldSettings = JSON.stringify(settings()) + if (oldSettings === e.newValue) { + return + } + } + // Check if the state has not changed and early return + if (e.key === TANSTACK_DEVTOOLS_STATE) { + const oldState = JSON.stringify(state()) + if (oldState === e.newValue) { + return + } + } + // store new state + const newState = getExistingStateFromStorage() + setState(newState.state) + setSettings(newState.settings) + }) +} diff --git a/packages/devtools/src/hooks/use-event-listener.ts b/packages/devtools/src/hooks/use-event-listener.ts new file mode 100644 index 000000000..3cda4d7ad --- /dev/null +++ b/packages/devtools/src/hooks/use-event-listener.ts @@ -0,0 +1,39 @@ +import { createEffect, onCleanup, } from "solid-js"; + +type Events = HTMLElementEventMap & WindowEventMap & DocumentEventMap & MediaQueryListEventMap; + +type ListenerElements = Document | HTMLElement | MediaQueryList | Window; + + +export const useWindowListener = ( + type: TEvent, + handler: (event: WindowEventMap[TEvent]) => void, + options?: boolean | AddEventListenerOptions +) => useEventListener(typeof window !== "undefined" ? window : undefined, type, handler, options); + + +const useEventListener = < + TEvent extends Events[keyof Events], + TType extends keyof Pick +>( + element: ListenerElements | undefined, + type: TType, + handler: (event: Events[TType]) => void, + options?: AddEventListenerOptions | boolean +) => { + let savedHandler = handler; + + createEffect(() => { + savedHandler = handler; + }); + + createEffect(() => { + if (!element) return; + const listener: EventListenerOrEventListenerObject = event => savedHandler(event as never); + + element.addEventListener(type, listener, options); + onCleanup(() => { + element.removeEventListener(type, listener, options); + }); + }); +}; diff --git a/packages/devtools/src/styles/use-styles.ts b/packages/devtools/src/styles/use-styles.ts index db3019e61..45cf676c1 100644 --- a/packages/devtools/src/styles/use-styles.ts +++ b/packages/devtools/src/styles/use-styles.ts @@ -12,6 +12,7 @@ const stylesFactory = () => { return { devtoolsPanelContainer: ( panelLocation: TanStackDevtoolsConfig['panelLocation'], + isDetached: boolean ) => css` direction: ltr; position: fixed; @@ -20,9 +21,8 @@ const stylesFactory = () => { ${panelLocation}: 0; right: 0; z-index: 99999; - width: 100%; - - max-height: 90%; + width: 100%; + ${isDetached ? "" : "max-height: 90%;"} border-top: 1px solid ${colors.gray[700]}; transform-origin: top; `, @@ -320,6 +320,15 @@ const stylesFactory = () => { grid-template-columns: 1fr; } `, + cover: css` + position: fixed; + width: 100vw; + height: 100vh; + z-index: 9997; + background-color: ${colors.darkGray[700]}; + top: 0; + left:0; + ` } } diff --git a/packages/devtools/src/utils/detached.ts b/packages/devtools/src/utils/detached.ts new file mode 100644 index 000000000..5977a951c --- /dev/null +++ b/packages/devtools/src/utils/detached.ts @@ -0,0 +1,11 @@ +import { + TANSTACK_DEVTOOLS_DETACHED, + TANSTACK_DEVTOOLS_DETACHED_OWNER, + TANSTACK_DEVTOOLS_IS_DETACHED, + getBooleanFromSession, + getBooleanFromStorage, +} from "./storage.js" + +export const checkIsDetachedWindow = () => getBooleanFromSession(TANSTACK_DEVTOOLS_DETACHED) +export const checkIsDetached = () => getBooleanFromStorage(TANSTACK_DEVTOOLS_IS_DETACHED) +export const checkIsDetachedOwner = () => getBooleanFromSession(TANSTACK_DEVTOOLS_DETACHED_OWNER) diff --git a/packages/devtools/src/utils/storage.ts b/packages/devtools/src/utils/storage.ts index aa46abfa0..85e940cd8 100644 --- a/packages/devtools/src/utils/storage.ts +++ b/packages/devtools/src/utils/storage.ts @@ -7,6 +7,22 @@ export const setStorageItem = (key: string, value: string) => { } } +const getSessionItem = (key: string) => sessionStorage.getItem(key) +export const getBooleanFromStorage = (key: string) => getStorageItem(key) === "true" +export const getBooleanFromSession = (key: string) => getSessionItem(key) === "true" +export const setSessionItem = (key: string, value: string) => { + try { + sessionStorage.setItem(key, value) + } catch (e) { + return + } +} + export const TANSTACK_DEVTOOLS = 'tanstack_devtools' export const TANSTACK_DEVTOOLS_STATE = 'tanstack_devtools_state' export const TANSTACK_DEVTOOLS_SETTINGS = 'tanstack_devtools_settings' + +export const TANSTACK_DEVTOOLS_DETACHED = "tanstack_devtools_detached" +export const TANSTACK_DEVTOOLS_DETACHED_OWNER = "tanstack_devtools_detached_owner" +export const TANSTACK_DEVTOOLS_IS_DETACHED = "tanstack_devtools_is_detached" +export const TANSTACK_DEVTOOLS_CHECK_DETACHED = "tanstack_devtools_check_detached" \ No newline at end of file From bc5d125a525f784ad1f940e245ab6dd449d7d9c0 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 14 Aug 2025 06:01:11 +0000 Subject: [PATCH 03/10] ci: apply automated fixes --- .../devtools/src/components/content-panel.tsx | 5 +- .../devtools/src/components/main-panel.tsx | 9 +- packages/devtools/src/components/tabs.tsx | 104 ++++++++++++------ .../devtools/src/context/devtools-context.tsx | 10 +- .../devtools/src/context/devtools-store.ts | 2 +- packages/devtools/src/devtools.tsx | 20 +++- .../detached/use-check-if-still-detached.ts | 33 +++--- .../src/hooks/detached/use-remove-body.ts | 10 +- .../detached/use-reset-detachment-check.ts | 16 ++- .../detached/use-sync-state-when-detached.ts | 22 +++- .../devtools/src/hooks/use-event-listener.ts | 49 +++++---- packages/devtools/src/styles/use-styles.ts | 14 +-- packages/devtools/src/utils/detached.ts | 11 +- packages/devtools/src/utils/storage.ts | 16 ++- packages/event-bus/src/client/client.ts | 13 ++- 15 files changed, 211 insertions(+), 123 deletions(-) diff --git a/packages/devtools/src/components/content-panel.tsx b/packages/devtools/src/components/content-panel.tsx index c641c942a..e40144184 100644 --- a/packages/devtools/src/components/content-panel.tsx +++ b/packages/devtools/src/components/content-panel.tsx @@ -1,5 +1,8 @@ import { Show } from 'solid-js' -import { useDetachedWindowControls, useDevtoolsSettings } from '../context/use-devtools-context' +import { + useDetachedWindowControls, + useDevtoolsSettings, +} from '../context/use-devtools-context' import { useStyles } from '../styles/use-styles' import type { JSX } from 'solid-js/jsx-runtime' diff --git a/packages/devtools/src/components/main-panel.tsx b/packages/devtools/src/components/main-panel.tsx index c39060e84..bd705722f 100644 --- a/packages/devtools/src/components/main-panel.tsx +++ b/packages/devtools/src/components/main-panel.tsx @@ -1,10 +1,13 @@ import clsx from 'clsx' -import { useDetachedWindowControls, useDevtoolsSettings, useHeight } from '../context/use-devtools-context' +import { + useDetachedWindowControls, + useDevtoolsSettings, + useHeight, +} from '../context/use-devtools-context' import { useStyles } from '../styles/use-styles' import { TANSTACK_DEVTOOLS } from '../utils/storage' import type { Accessor, JSX } from 'solid-js' - export const MainPanel = (props: { isOpen: Accessor children: JSX.Element @@ -18,7 +21,7 @@ export const MainPanel = (props: {
void @@ -12,18 +20,19 @@ interface TabsProps { export const Tabs = (props: TabsProps) => { const styles = useStyles() const { state, setState } = useDevtoolsState() - const { setDetachedWindowOwner, detachedWindowOwner, detachedWindow } = useDetachedWindowControls() + const { setDetachedWindowOwner, detachedWindowOwner, detachedWindow } = + useDetachedWindowControls() const handleDetachment = () => { const rdtWindow = window.open( window.location.href, - "", - `popup,width=${window.innerWidth},height=${state().height},top=${window.screen.height},left=${window.screenLeft}}` + '', + `popup,width=${window.innerWidth},height=${state().height},top=${window.screen.height},left=${window.screenLeft}}`, ) if (rdtWindow) { setDetachedWindowOwner(true) - setStorageItem(TANSTACK_DEVTOOLS_IS_DETACHED, "true") - setSessionItem(TANSTACK_DEVTOOLS_DETACHED_OWNER, "true") + setStorageItem(TANSTACK_DEVTOOLS_IS_DETACHED, 'true') + setSessionItem(TANSTACK_DEVTOOLS_DETACHED_OWNER, 'true') rdtWindow.TDT_MOUNTED = true } } @@ -40,37 +49,60 @@ export const Tabs = (props: TabsProps) => { )} - {!detachedWindow &&
- {!detachedWindowOwner() && } - + )} + -
} + + + + + +
+ )}
) } diff --git a/packages/devtools/src/context/devtools-context.tsx b/packages/devtools/src/context/devtools-context.tsx index fdec51f58..28748fd84 100644 --- a/packages/devtools/src/context/devtools-context.tsx +++ b/packages/devtools/src/context/devtools-context.tsx @@ -10,7 +10,11 @@ import { setSessionItem, setStorageItem, } from '../utils/storage' -import { checkIsDetached, checkIsDetachedOwner, checkIsDetachedWindow } from '../utils/detached' +import { + checkIsDetached, + checkIsDetachedOwner, + checkIsDetachedWindow, +} from '../utils/detached' import { useRemoveBody } from '../hooks/detached/use-remove-body' import { initialState } from './devtools-store' import type { DevtoolsStore } from './devtools-store' @@ -99,12 +103,12 @@ const generatePluginId = (plugin: TanStackDevtoolsPlugin, index: number) => { const setIsDetachedIfRequired = () => { const isDetachedWindow = checkIsDetachedWindow() if (!isDetachedWindow && window.TDT_MOUNTED) { - setSessionItem(TANSTACK_DEVTOOLS_DETACHED, "true") + setSessionItem(TANSTACK_DEVTOOLS_DETACHED, 'true') } } const resetIsDetachedCheck = () => { - setStorageItem(TANSTACK_DEVTOOLS_CHECK_DETACHED, "false") + setStorageItem(TANSTACK_DEVTOOLS_CHECK_DETACHED, 'false') } const detachedModeSetup = () => { diff --git a/packages/devtools/src/context/devtools-store.ts b/packages/devtools/src/context/devtools-store.ts index 15732c15e..ee2a5bcdc 100644 --- a/packages/devtools/src/context/devtools-store.ts +++ b/packages/devtools/src/context/devtools-store.ts @@ -55,7 +55,7 @@ export type DevtoolsStore = { activeTab: TabName height: number activePlugin?: string | undefined - persistOpen: boolean, + persistOpen: boolean } detachedWindowOwner?: boolean detachedWindow?: boolean diff --git a/packages/devtools/src/devtools.tsx b/packages/devtools/src/devtools.tsx index 0eb7c6aae..06cc85ae6 100644 --- a/packages/devtools/src/devtools.tsx +++ b/packages/devtools/src/devtools.tsx @@ -7,7 +7,13 @@ import { usePersistOpen, } from './context/use-devtools-context' import { useDisableTabbing } from './hooks/use-disable-tabbing' -import { TANSTACK_DEVTOOLS, TANSTACK_DEVTOOLS_DETACHED_OWNER, TANSTACK_DEVTOOLS_IS_DETACHED, setSessionItem, setStorageItem, } from './utils/storage' +import { + TANSTACK_DEVTOOLS, + TANSTACK_DEVTOOLS_DETACHED_OWNER, + TANSTACK_DEVTOOLS_IS_DETACHED, + setSessionItem, + setStorageItem, +} from './utils/storage' import { Trigger } from './components/trigger' import { MainPanel } from './components/main-panel' import { ContentPanel } from './components/content-panel' @@ -19,7 +25,8 @@ import { useWindowListener } from './hooks/use-event-listener' import { useCheckIfStillDetached } from './hooks/detached/use-check-if-still-detached' export default function DevTools() { - const { detachedWindowOwner, isDetached, setDetachedWindowOwner } = useDetachedWindowControls() + const { detachedWindowOwner, isDetached, setDetachedWindowOwner } = + useDetachedWindowControls() const { settings } = useDevtoolsSettings() const { setHeight } = useHeight() const { persistOpen, setPersistOpen } = usePersistOpen() @@ -152,7 +159,7 @@ export default function DevTools() { createEffect(() => { if (isDetached) { - useWindowListener("resize", () => { + useWindowListener('resize', () => { setHeight(window.innerHeight) }) } @@ -165,14 +172,15 @@ export default function DevTools() { isOpen={() => false} setIsOpen={() => { setDetachedWindowOwner(false) - setStorageItem(TANSTACK_DEVTOOLS_IS_DETACHED, "false") - setSessionItem(TANSTACK_DEVTOOLS_DETACHED_OWNER, "false") + setStorageItem(TANSTACK_DEVTOOLS_IS_DETACHED, 'false') + setSessionItem(TANSTACK_DEVTOOLS_DETACHED_OWNER, 'false') }} /> { const context = useDevtoolsContext() @@ -19,35 +18,39 @@ export const useCheckIfStillDetached = () => { if (e.key !== TANSTACK_DEVTOOLS_CHECK_DETACHED) { return } - const isDetached = getBooleanFromStorage(TANSTACK_DEVTOOLS_IS_DETACHED); + const isDetached = getBooleanFromStorage(TANSTACK_DEVTOOLS_IS_DETACHED) if (!isDetached) { return } - const shouldCheckDetached = getBooleanFromStorage(TANSTACK_DEVTOOLS_CHECK_DETACHED) + const shouldCheckDetached = getBooleanFromStorage( + TANSTACK_DEVTOOLS_CHECK_DETACHED, + ) // If the detached window is unloaded we want to check if it is still there if (shouldCheckDetached) { setTimeout(() => { // On reload the detached window will set the flag back to false so we can check if it is still detached - const isNotDetachedAnymore = getBooleanFromStorage(TANSTACK_DEVTOOLS_CHECK_DETACHED) + const isNotDetachedAnymore = getBooleanFromStorage( + TANSTACK_DEVTOOLS_CHECK_DETACHED, + ) // The window hasn't set it back to true so it is not detached anymore and we clean all the detached state if (isNotDetachedAnymore) { - setStorageItem(TANSTACK_DEVTOOLS_IS_DETACHED, "false") - setStorageItem(TANSTACK_DEVTOOLS_CHECK_DETACHED, "false") + setStorageItem(TANSTACK_DEVTOOLS_IS_DETACHED, 'false') + setStorageItem(TANSTACK_DEVTOOLS_CHECK_DETACHED, 'false') sessionStorage.removeItem(TANSTACK_DEVTOOLS_DETACHED_OWNER) sessionStorage.removeItem(TANSTACK_DEVTOOLS_DETACHED) const state = getExistingStateFromStorage() - context.setStore(prev => ({ + context.setStore((prev) => ({ ...prev, ...state, - plugins: prev.plugins + plugins: prev.plugins, })) } }, 200) } } createEffect(() => { - window.addEventListener("storage", checkDetachment) - onCleanup(() => window.removeEventListener("storage", checkDetachment)) - },) + window.addEventListener('storage', checkDetachment) + onCleanup(() => window.removeEventListener('storage', checkDetachment)) + }) } diff --git a/packages/devtools/src/hooks/detached/use-remove-body.ts b/packages/devtools/src/hooks/detached/use-remove-body.ts index abfb762f2..f75acadd0 100644 --- a/packages/devtools/src/hooks/detached/use-remove-body.ts +++ b/packages/devtools/src/hooks/detached/use-remove-body.ts @@ -1,7 +1,6 @@ - -import { createEffect } from "solid-js" -import { useStyles } from "../../styles/use-styles" -import type { DevtoolsStore } from "../../context/devtools-store" +import { createEffect } from 'solid-js' +import { useStyles } from '../../styles/use-styles' +import type { DevtoolsStore } from '../../context/devtools-store' export const useRemoveBody = (state: DevtoolsStore) => { const styles = useStyles() @@ -10,9 +9,8 @@ export const useRemoveBody = (state: DevtoolsStore) => { return } - const coverEl = document.createElement("div") + const coverEl = document.createElement('div') coverEl.classList.add(styles().cover) document.body.appendChild(coverEl) - }) } diff --git a/packages/devtools/src/hooks/detached/use-reset-detachment-check.ts b/packages/devtools/src/hooks/detached/use-reset-detachment-check.ts index bc6caf464..87169b04b 100644 --- a/packages/devtools/src/hooks/detached/use-reset-detachment-check.ts +++ b/packages/devtools/src/hooks/detached/use-reset-detachment-check.ts @@ -1,10 +1,16 @@ - -import { useDetachedWindowControls } from "../../context/use-devtools-context" -import { TANSTACK_DEVTOOLS_CHECK_DETACHED, setStorageItem, } from "../../utils/storage" -import { useWindowListener } from "../use-event-listener" +import { useDetachedWindowControls } from '../../context/use-devtools-context' +import { + TANSTACK_DEVTOOLS_CHECK_DETACHED, + setStorageItem, +} from '../../utils/storage' +import { useWindowListener } from '../use-event-listener' // called on windows unmount export const useResetDetachmentCheck = () => { const { isDetached } = useDetachedWindowControls() - useWindowListener("unload", () => setStorageItem(TANSTACK_DEVTOOLS_CHECK_DETACHED, "true"), isDetached) + useWindowListener( + 'unload', + () => setStorageItem(TANSTACK_DEVTOOLS_CHECK_DETACHED, 'true'), + isDetached, + ) } diff --git a/packages/devtools/src/hooks/detached/use-sync-state-when-detached.ts b/packages/devtools/src/hooks/detached/use-sync-state-when-detached.ts index c4e09321d..26596175c 100644 --- a/packages/devtools/src/hooks/detached/use-sync-state-when-detached.ts +++ b/packages/devtools/src/hooks/detached/use-sync-state-when-detached.ts @@ -1,16 +1,26 @@ -import { getExistingStateFromStorage } from "../../context/devtools-context" -import { useDevtoolsContext, useDevtoolsSettings, useDevtoolsState } from "../../context/use-devtools-context" -import { TANSTACK_DEVTOOLS_SETTINGS, TANSTACK_DEVTOOLS_STATE } from "../../utils/storage" -import { useWindowListener } from "../use-event-listener" +import { getExistingStateFromStorage } from '../../context/devtools-context' +import { + useDevtoolsContext, + useDevtoolsSettings, + useDevtoolsState, +} from '../../context/use-devtools-context' +import { + TANSTACK_DEVTOOLS_SETTINGS, + TANSTACK_DEVTOOLS_STATE, +} from '../../utils/storage' +import { useWindowListener } from '../use-event-listener' -const refreshRequiredKeys = [TANSTACK_DEVTOOLS_SETTINGS, TANSTACK_DEVTOOLS_STATE] +const refreshRequiredKeys = [ + TANSTACK_DEVTOOLS_SETTINGS, + TANSTACK_DEVTOOLS_STATE, +] // Sync state with local storage when in detached mode export const useSyncStateWhenDetached = () => { const { store } = useDevtoolsContext() const { state, setState } = useDevtoolsState() const { setSettings, settings } = useDevtoolsSettings() - useWindowListener("storage", (e) => { + useWindowListener('storage', (e) => { // Not in detached mode if (!store.detachedWindow && !store.detachedWindowOwner) { return diff --git a/packages/devtools/src/hooks/use-event-listener.ts b/packages/devtools/src/hooks/use-event-listener.ts index 3cda4d7ad..05bfe7857 100644 --- a/packages/devtools/src/hooks/use-event-listener.ts +++ b/packages/devtools/src/hooks/use-event-listener.ts @@ -1,39 +1,50 @@ -import { createEffect, onCleanup, } from "solid-js"; +import { createEffect, onCleanup } from 'solid-js' -type Events = HTMLElementEventMap & WindowEventMap & DocumentEventMap & MediaQueryListEventMap; - -type ListenerElements = Document | HTMLElement | MediaQueryList | Window; +type Events = HTMLElementEventMap & + WindowEventMap & + DocumentEventMap & + MediaQueryListEventMap +type ListenerElements = Document | HTMLElement | MediaQueryList | Window export const useWindowListener = ( type: TEvent, handler: (event: WindowEventMap[TEvent]) => void, - options?: boolean | AddEventListenerOptions -) => useEventListener(typeof window !== "undefined" ? window : undefined, type, handler, options); - + options?: boolean | AddEventListenerOptions, +) => + useEventListener( + typeof window !== 'undefined' ? window : undefined, + type, + handler, + options, + ) const useEventListener = < TEvent extends Events[keyof Events], - TType extends keyof Pick + TType extends keyof Pick< + Events, + { [K in keyof Events]: Events[K] extends TEvent ? K : never }[keyof Events] + >, >( element: ListenerElements | undefined, type: TType, handler: (event: Events[TType]) => void, - options?: AddEventListenerOptions | boolean + options?: AddEventListenerOptions | boolean, ) => { - let savedHandler = handler; + let savedHandler = handler createEffect(() => { - savedHandler = handler; - }); + savedHandler = handler + }) createEffect(() => { - if (!element) return; - const listener: EventListenerOrEventListenerObject = event => savedHandler(event as never); + if (!element) return + const listener: EventListenerOrEventListenerObject = (event) => + savedHandler(event as never) - element.addEventListener(type, listener, options); + element.addEventListener(type, listener, options) onCleanup(() => { - element.removeEventListener(type, listener, options); - }); - }); -}; + element.removeEventListener(type, listener, options) + }) + }) +} diff --git a/packages/devtools/src/styles/use-styles.ts b/packages/devtools/src/styles/use-styles.ts index 45cf676c1..86bf4f279 100644 --- a/packages/devtools/src/styles/use-styles.ts +++ b/packages/devtools/src/styles/use-styles.ts @@ -12,7 +12,7 @@ const stylesFactory = () => { return { devtoolsPanelContainer: ( panelLocation: TanStackDevtoolsConfig['panelLocation'], - isDetached: boolean + isDetached: boolean, ) => css` direction: ltr; position: fixed; @@ -21,8 +21,8 @@ const stylesFactory = () => { ${panelLocation}: 0; right: 0; z-index: 99999; - width: 100%; - ${isDetached ? "" : "max-height: 90%;"} + width: 100%; + ${isDetached ? '' : 'max-height: 90%;'} border-top: 1px solid ${colors.gray[700]}; transform-origin: top; `, @@ -192,14 +192,14 @@ const stylesFactory = () => { border-left: 2px solid ${colors.purple[500]}; } &.detach { - &:hover { + &:hover { background-color: ${colors.gray[700]}; } &:hover { color: ${colors.green[500]}; } } - &.close { + &.close { &:hover { background-color: ${colors.gray[700]}; } @@ -327,8 +327,8 @@ const stylesFactory = () => { z-index: 9997; background-color: ${colors.darkGray[700]}; top: 0; - left:0; - ` + left: 0; + `, } } diff --git a/packages/devtools/src/utils/detached.ts b/packages/devtools/src/utils/detached.ts index 5977a951c..ed17878ae 100644 --- a/packages/devtools/src/utils/detached.ts +++ b/packages/devtools/src/utils/detached.ts @@ -4,8 +4,11 @@ import { TANSTACK_DEVTOOLS_IS_DETACHED, getBooleanFromSession, getBooleanFromStorage, -} from "./storage.js" +} from './storage.js' -export const checkIsDetachedWindow = () => getBooleanFromSession(TANSTACK_DEVTOOLS_DETACHED) -export const checkIsDetached = () => getBooleanFromStorage(TANSTACK_DEVTOOLS_IS_DETACHED) -export const checkIsDetachedOwner = () => getBooleanFromSession(TANSTACK_DEVTOOLS_DETACHED_OWNER) +export const checkIsDetachedWindow = () => + getBooleanFromSession(TANSTACK_DEVTOOLS_DETACHED) +export const checkIsDetached = () => + getBooleanFromStorage(TANSTACK_DEVTOOLS_IS_DETACHED) +export const checkIsDetachedOwner = () => + getBooleanFromSession(TANSTACK_DEVTOOLS_DETACHED_OWNER) diff --git a/packages/devtools/src/utils/storage.ts b/packages/devtools/src/utils/storage.ts index 85e940cd8..04898cfd2 100644 --- a/packages/devtools/src/utils/storage.ts +++ b/packages/devtools/src/utils/storage.ts @@ -8,8 +8,10 @@ export const setStorageItem = (key: string, value: string) => { } const getSessionItem = (key: string) => sessionStorage.getItem(key) -export const getBooleanFromStorage = (key: string) => getStorageItem(key) === "true" -export const getBooleanFromSession = (key: string) => getSessionItem(key) === "true" +export const getBooleanFromStorage = (key: string) => + getStorageItem(key) === 'true' +export const getBooleanFromSession = (key: string) => + getSessionItem(key) === 'true' export const setSessionItem = (key: string, value: string) => { try { sessionStorage.setItem(key, value) @@ -22,7 +24,9 @@ export const TANSTACK_DEVTOOLS = 'tanstack_devtools' export const TANSTACK_DEVTOOLS_STATE = 'tanstack_devtools_state' export const TANSTACK_DEVTOOLS_SETTINGS = 'tanstack_devtools_settings' -export const TANSTACK_DEVTOOLS_DETACHED = "tanstack_devtools_detached" -export const TANSTACK_DEVTOOLS_DETACHED_OWNER = "tanstack_devtools_detached_owner" -export const TANSTACK_DEVTOOLS_IS_DETACHED = "tanstack_devtools_is_detached" -export const TANSTACK_DEVTOOLS_CHECK_DETACHED = "tanstack_devtools_check_detached" \ No newline at end of file +export const TANSTACK_DEVTOOLS_DETACHED = 'tanstack_devtools_detached' +export const TANSTACK_DEVTOOLS_DETACHED_OWNER = + 'tanstack_devtools_detached_owner' +export const TANSTACK_DEVTOOLS_IS_DETACHED = 'tanstack_devtools_is_detached' +export const TANSTACK_DEVTOOLS_CHECK_DETACHED = + 'tanstack_devtools_check_detached' diff --git a/packages/event-bus/src/client/client.ts b/packages/event-bus/src/client/client.ts index cc3227df5..4d5123b09 100644 --- a/packages/event-bus/src/client/client.ts +++ b/packages/event-bus/src/client/client.ts @@ -48,19 +48,22 @@ export class ClientEventBus { connectToServerBus = false, }: ClientEventBusConfig = {}) { this.#debug = debug - this.#broadcastChannel = new BroadcastChannel("tanstack-devtools") + this.#broadcastChannel = new BroadcastChannel('tanstack-devtools') this.#eventSource = null this.#port = port this.#socket = null this.#connectToServerBus = connectToServerBus this.#eventTarget = this.getGlobalTarget() - this.#broadcastChannel.onmessage = e => { + this.#broadcastChannel.onmessage = (e) => { this.emitToClients(e.data, true) } this.debugLog('Initializing client event bus') } - private emitToClients(event: TanStackDevtoolsEvent, fromBroadcastChannel = false) { + private emitToClients( + event: TanStackDevtoolsEvent, + fromBroadcastChannel = false, + ) { this.debugLog('Emitting event from client bus', event) const specificEvent = new CustomEvent(event.type, { detail: event }) this.debugLog('Emitting event to specific client listeners', event) @@ -91,7 +94,7 @@ export class ClientEventBus { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: json, - }).catch(() => { }) + }).catch(() => {}) } } start() { @@ -184,6 +187,6 @@ export class ClientEventBus { try { const event = JSON.parse(data) as TanStackDevtoolsEvent this.emitToClients(event) - } catch { } + } catch {} } } From 042158f1f5af5f9f207bbd6f7bf459a6c44e7e2c Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Thu, 14 Aug 2025 08:18:29 +0200 Subject: [PATCH 04/10] fix: tests --- packages/event-bus-client/tests/index.test.ts | 7 ++++--- packages/event-bus/tests/index.test.ts | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/event-bus-client/tests/index.test.ts b/packages/event-bus-client/tests/index.test.ts index 048ffa4a6..a17b41a8c 100644 --- a/packages/event-bus-client/tests/index.test.ts +++ b/packages/event-bus-client/tests/index.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it, vi } from 'vitest' import { ClientEventBus } from '@tanstack/devtools-event-bus/client' import { EventClient } from '../src' +vi.spyOn(BroadcastChannel.prototype, 'postMessage').mockImplementation(() => { }) // start the client bus for testing const bus = new ClientEventBus() bus.start() @@ -55,7 +56,7 @@ describe('EventClient', () => { const targetEmitSpy = vi.spyOn(target, 'dispatchEvent') const targetListenSpy = vi.spyOn(target, 'addEventListener') const targetRemoveSpy = vi.spyOn(target, 'removeEventListener') - const cleanup = client.on('test:event', () => {}) + const cleanup = client.on('test:event', () => { }) cleanup() client.emit('test:event', { foo: 'bar' }) expect(targetEmitSpy).toHaveBeenCalledWith(expect.any(Event)) @@ -79,7 +80,7 @@ describe('EventClient', () => { const targetEmitSpy = vi.spyOn(target, 'dispatchEvent') const targetListenSpy = vi.spyOn(target, 'addEventListener') const targetRemoveSpy = vi.spyOn(target, 'removeEventListener') - const cleanup = client.on('test:event', () => {}) + const cleanup = client.on('test:event', () => { }) cleanup() client.emit('test:event', { foo: 'bar' }) expect(targetEmitSpy).toHaveBeenCalledWith(expect.any(Event)) @@ -102,7 +103,7 @@ describe('EventClient', () => { }) const eventBusSpy = vi.spyOn(clientBusEmitTarget, 'addEventListener') - client.on('event', () => {}) + client.on('event', () => { }) expect(eventBusSpy).toHaveBeenCalledWith( 'test:event', expect.any(Function), diff --git a/packages/event-bus/tests/index.test.ts b/packages/event-bus/tests/index.test.ts index 305c4ead9..cfaff05bf 100644 --- a/packages/event-bus/tests/index.test.ts +++ b/packages/event-bus/tests/index.test.ts @@ -1,13 +1,14 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import { ClientEventBus } from '../src/client' +vi.spyOn(BroadcastChannel.prototype, 'postMessage').mockImplementation(() => { }) describe('ClientEventBus', () => { describe('debug', () => { afterEach(() => { vi.restoreAllMocks() }) it('should log events to the console when debug set to true', () => { - const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) + const logSpy = vi.spyOn(console, 'log').mockImplementation(() => { }) const clientBus = new ClientEventBus({ debug: true }) clientBus.start() @@ -20,7 +21,7 @@ describe('ClientEventBus', () => { }) it('should not log events to the console when debug set to false', () => { - const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) + const logSpy = vi.spyOn(console, 'log').mockImplementation(() => { }) const clientBus = new ClientEventBus({ debug: false }) clientBus.start() From 1820f08caf6aae09b1a2d8255a2d9ead10a13f97 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 14 Aug 2025 06:19:05 +0000 Subject: [PATCH 05/10] ci: apply automated fixes --- packages/event-bus-client/tests/index.test.ts | 8 ++++---- packages/event-bus/tests/index.test.ts | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/event-bus-client/tests/index.test.ts b/packages/event-bus-client/tests/index.test.ts index a17b41a8c..333704156 100644 --- a/packages/event-bus-client/tests/index.test.ts +++ b/packages/event-bus-client/tests/index.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it, vi } from 'vitest' import { ClientEventBus } from '@tanstack/devtools-event-bus/client' import { EventClient } from '../src' -vi.spyOn(BroadcastChannel.prototype, 'postMessage').mockImplementation(() => { }) +vi.spyOn(BroadcastChannel.prototype, 'postMessage').mockImplementation(() => {}) // start the client bus for testing const bus = new ClientEventBus() bus.start() @@ -56,7 +56,7 @@ describe('EventClient', () => { const targetEmitSpy = vi.spyOn(target, 'dispatchEvent') const targetListenSpy = vi.spyOn(target, 'addEventListener') const targetRemoveSpy = vi.spyOn(target, 'removeEventListener') - const cleanup = client.on('test:event', () => { }) + const cleanup = client.on('test:event', () => {}) cleanup() client.emit('test:event', { foo: 'bar' }) expect(targetEmitSpy).toHaveBeenCalledWith(expect.any(Event)) @@ -80,7 +80,7 @@ describe('EventClient', () => { const targetEmitSpy = vi.spyOn(target, 'dispatchEvent') const targetListenSpy = vi.spyOn(target, 'addEventListener') const targetRemoveSpy = vi.spyOn(target, 'removeEventListener') - const cleanup = client.on('test:event', () => { }) + const cleanup = client.on('test:event', () => {}) cleanup() client.emit('test:event', { foo: 'bar' }) expect(targetEmitSpy).toHaveBeenCalledWith(expect.any(Event)) @@ -103,7 +103,7 @@ describe('EventClient', () => { }) const eventBusSpy = vi.spyOn(clientBusEmitTarget, 'addEventListener') - client.on('event', () => { }) + client.on('event', () => {}) expect(eventBusSpy).toHaveBeenCalledWith( 'test:event', expect.any(Function), diff --git a/packages/event-bus/tests/index.test.ts b/packages/event-bus/tests/index.test.ts index cfaff05bf..e249f1aad 100644 --- a/packages/event-bus/tests/index.test.ts +++ b/packages/event-bus/tests/index.test.ts @@ -1,14 +1,14 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import { ClientEventBus } from '../src/client' -vi.spyOn(BroadcastChannel.prototype, 'postMessage').mockImplementation(() => { }) +vi.spyOn(BroadcastChannel.prototype, 'postMessage').mockImplementation(() => {}) describe('ClientEventBus', () => { describe('debug', () => { afterEach(() => { vi.restoreAllMocks() }) it('should log events to the console when debug set to true', () => { - const logSpy = vi.spyOn(console, 'log').mockImplementation(() => { }) + const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) const clientBus = new ClientEventBus({ debug: true }) clientBus.start() @@ -21,7 +21,7 @@ describe('ClientEventBus', () => { }) it('should not log events to the console when debug set to false', () => { - const logSpy = vi.spyOn(console, 'log').mockImplementation(() => { }) + const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) const clientBus = new ClientEventBus({ debug: false }) clientBus.start() From fc8592287ccfd15876d8f6b24666e9479cecb15e Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Thu, 14 Aug 2025 08:24:13 +0200 Subject: [PATCH 06/10] fix: test --- packages/event-bus-client/tests/index.test.ts | 13 +++++++++---- packages/event-bus/tests/index.test.ts | 11 ++++++++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/event-bus-client/tests/index.test.ts b/packages/event-bus-client/tests/index.test.ts index 333704156..aad84ce0a 100644 --- a/packages/event-bus-client/tests/index.test.ts +++ b/packages/event-bus-client/tests/index.test.ts @@ -2,7 +2,12 @@ import { describe, expect, it, vi } from 'vitest' import { ClientEventBus } from '@tanstack/devtools-event-bus/client' import { EventClient } from '../src' -vi.spyOn(BroadcastChannel.prototype, 'postMessage').mockImplementation(() => {}) +vi.stubGlobal('BroadcastChannel', class { + postMessage = vi.fn() + addEventListener = vi.fn() + removeEventListener = vi.fn() + close = vi.fn() +}) // start the client bus for testing const bus = new ClientEventBus() bus.start() @@ -56,7 +61,7 @@ describe('EventClient', () => { const targetEmitSpy = vi.spyOn(target, 'dispatchEvent') const targetListenSpy = vi.spyOn(target, 'addEventListener') const targetRemoveSpy = vi.spyOn(target, 'removeEventListener') - const cleanup = client.on('test:event', () => {}) + const cleanup = client.on('test:event', () => { }) cleanup() client.emit('test:event', { foo: 'bar' }) expect(targetEmitSpy).toHaveBeenCalledWith(expect.any(Event)) @@ -80,7 +85,7 @@ describe('EventClient', () => { const targetEmitSpy = vi.spyOn(target, 'dispatchEvent') const targetListenSpy = vi.spyOn(target, 'addEventListener') const targetRemoveSpy = vi.spyOn(target, 'removeEventListener') - const cleanup = client.on('test:event', () => {}) + const cleanup = client.on('test:event', () => { }) cleanup() client.emit('test:event', { foo: 'bar' }) expect(targetEmitSpy).toHaveBeenCalledWith(expect.any(Event)) @@ -103,7 +108,7 @@ describe('EventClient', () => { }) const eventBusSpy = vi.spyOn(clientBusEmitTarget, 'addEventListener') - client.on('event', () => {}) + client.on('event', () => { }) expect(eventBusSpy).toHaveBeenCalledWith( 'test:event', expect.any(Function), diff --git a/packages/event-bus/tests/index.test.ts b/packages/event-bus/tests/index.test.ts index e249f1aad..1b4951b29 100644 --- a/packages/event-bus/tests/index.test.ts +++ b/packages/event-bus/tests/index.test.ts @@ -1,14 +1,19 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import { ClientEventBus } from '../src/client' -vi.spyOn(BroadcastChannel.prototype, 'postMessage').mockImplementation(() => {}) +vi.stubGlobal('BroadcastChannel', class { + postMessage = vi.fn() + addEventListener = vi.fn() + removeEventListener = vi.fn() + close = vi.fn() +}) describe('ClientEventBus', () => { describe('debug', () => { afterEach(() => { vi.restoreAllMocks() }) it('should log events to the console when debug set to true', () => { - const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) + const logSpy = vi.spyOn(console, 'log').mockImplementation(() => { }) const clientBus = new ClientEventBus({ debug: true }) clientBus.start() @@ -21,7 +26,7 @@ describe('ClientEventBus', () => { }) it('should not log events to the console when debug set to false', () => { - const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) + const logSpy = vi.spyOn(console, 'log').mockImplementation(() => { }) const clientBus = new ClientEventBus({ debug: false }) clientBus.start() From 313bad92e2236b6f9ad55f71488860ea78e12793 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 14 Aug 2025 06:24:48 +0000 Subject: [PATCH 07/10] ci: apply automated fixes --- packages/event-bus-client/tests/index.test.ts | 21 +++++++++++-------- packages/event-bus/tests/index.test.ts | 19 ++++++++++------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/packages/event-bus-client/tests/index.test.ts b/packages/event-bus-client/tests/index.test.ts index aad84ce0a..0701b9b47 100644 --- a/packages/event-bus-client/tests/index.test.ts +++ b/packages/event-bus-client/tests/index.test.ts @@ -2,12 +2,15 @@ import { describe, expect, it, vi } from 'vitest' import { ClientEventBus } from '@tanstack/devtools-event-bus/client' import { EventClient } from '../src' -vi.stubGlobal('BroadcastChannel', class { - postMessage = vi.fn() - addEventListener = vi.fn() - removeEventListener = vi.fn() - close = vi.fn() -}) +vi.stubGlobal( + 'BroadcastChannel', + class { + postMessage = vi.fn() + addEventListener = vi.fn() + removeEventListener = vi.fn() + close = vi.fn() + }, +) // start the client bus for testing const bus = new ClientEventBus() bus.start() @@ -61,7 +64,7 @@ describe('EventClient', () => { const targetEmitSpy = vi.spyOn(target, 'dispatchEvent') const targetListenSpy = vi.spyOn(target, 'addEventListener') const targetRemoveSpy = vi.spyOn(target, 'removeEventListener') - const cleanup = client.on('test:event', () => { }) + const cleanup = client.on('test:event', () => {}) cleanup() client.emit('test:event', { foo: 'bar' }) expect(targetEmitSpy).toHaveBeenCalledWith(expect.any(Event)) @@ -85,7 +88,7 @@ describe('EventClient', () => { const targetEmitSpy = vi.spyOn(target, 'dispatchEvent') const targetListenSpy = vi.spyOn(target, 'addEventListener') const targetRemoveSpy = vi.spyOn(target, 'removeEventListener') - const cleanup = client.on('test:event', () => { }) + const cleanup = client.on('test:event', () => {}) cleanup() client.emit('test:event', { foo: 'bar' }) expect(targetEmitSpy).toHaveBeenCalledWith(expect.any(Event)) @@ -108,7 +111,7 @@ describe('EventClient', () => { }) const eventBusSpy = vi.spyOn(clientBusEmitTarget, 'addEventListener') - client.on('event', () => { }) + client.on('event', () => {}) expect(eventBusSpy).toHaveBeenCalledWith( 'test:event', expect.any(Function), diff --git a/packages/event-bus/tests/index.test.ts b/packages/event-bus/tests/index.test.ts index 1b4951b29..6d214c55c 100644 --- a/packages/event-bus/tests/index.test.ts +++ b/packages/event-bus/tests/index.test.ts @@ -1,19 +1,22 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import { ClientEventBus } from '../src/client' -vi.stubGlobal('BroadcastChannel', class { - postMessage = vi.fn() - addEventListener = vi.fn() - removeEventListener = vi.fn() - close = vi.fn() -}) +vi.stubGlobal( + 'BroadcastChannel', + class { + postMessage = vi.fn() + addEventListener = vi.fn() + removeEventListener = vi.fn() + close = vi.fn() + }, +) describe('ClientEventBus', () => { describe('debug', () => { afterEach(() => { vi.restoreAllMocks() }) it('should log events to the console when debug set to true', () => { - const logSpy = vi.spyOn(console, 'log').mockImplementation(() => { }) + const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) const clientBus = new ClientEventBus({ debug: true }) clientBus.start() @@ -26,7 +29,7 @@ describe('ClientEventBus', () => { }) it('should not log events to the console when debug set to false', () => { - const logSpy = vi.spyOn(console, 'log').mockImplementation(() => { }) + const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) const clientBus = new ClientEventBus({ debug: false }) clientBus.start() From d2d18dad812268ae36398bd572db6985bb969450 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Thu, 14 Aug 2025 09:46:46 +0200 Subject: [PATCH 08/10] chore: close panel in all cases --- examples/react/basic/src/index.tsx | 151 ++++++++++++++++-- examples/react/basic/src/setup.tsx | 32 ++-- .../detached/use-check-if-still-detached.ts | 9 ++ 3 files changed, 164 insertions(+), 28 deletions(-) diff --git a/examples/react/basic/src/index.tsx b/examples/react/basic/src/index.tsx index 14679c246..0c617654e 100644 --- a/examples/react/basic/src/index.tsx +++ b/examples/react/basic/src/index.tsx @@ -1,24 +1,153 @@ import { createRoot } from 'react-dom/client' +import { QueryClient, QueryClientProvider, useQuery, useQueryClient } from '@tanstack/react-query' +import { useState } from 'react' import Devtools from './setup' -import { queryPlugin } from './plugin' -setTimeout(() => { - queryPlugin.emit('test', { - title: 'Test Event', - description: - 'This is a test event from the TanStack Query Devtools plugin.', - }) -}, 1000) -queryPlugin.on('test', (event) => { - console.log('Received test event:', event) +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24 hours + }, + }, }) +type Post = { + id: number + title: string + body: string +} + +function Posts({ + setPostId, +}: { + setPostId: React.Dispatch> +}) { + const queryClient = useQueryClient() + const { status, data, error, isFetching } = usePosts() + + return ( +
+

Posts

+
+ {status === 'pending' ? ( + 'Loading...' + ) : status === 'error' ? ( + Error: {error.message} + ) : ( + <> + +
{isFetching ? 'Background Updating...' : ' '}
+ + )} +
+
+ ) +} + +const getPostById = async (id: number): Promise => { + const response = await fetch( + `https://jsonplaceholder.typicode.com/posts/${id}`, + ) + return await response.json() +} + +function usePost(postId: number) { + return useQuery({ + queryKey: ['post', postId], + queryFn: () => getPostById(postId), + enabled: !!postId, + }) +} + +function Post({ + postId, + setPostId, +}: { + postId: number + setPostId: React.Dispatch> +}) { + const { status, data, error, isFetching } = usePost(postId) + + return ( +
+ + {!postId || status === 'pending' ? ( + 'Loading...' + ) : status === 'error' ? ( + Error: {error.message} + ) : ( + <> +

{data.title}

+
+

{data.body}

+
+
{isFetching ? 'Background Updating...' : ' '}
+ + )} +
+ ) +} +function usePosts() { + return useQuery({ + queryKey: ['posts'], + queryFn: async (): Promise> => { + const response = await fetch('https://jsonplaceholder.typicode.com/posts') + return await response.json() + }, + }) +} function App() { + const [postId, setPostId] = useState(-1) + return (
+ +

+ As you visit the posts below, you will notice them in a loading state + the first time you load them. However, after you return to this list and + click on any posts you have already visited again, you will see them + load instantly and background refresh right before your eyes!{' '} + + (You may need to throttle your network speed to simulate longer + loading sequences) + +

+ {postId > -1 ? ( + + ) : ( + + )} + +

TanStack Devtools React Basic Example

-
) } diff --git a/examples/react/basic/src/setup.tsx b/examples/react/basic/src/setup.tsx index cb6d3c06e..74222c68b 100644 --- a/examples/react/basic/src/setup.tsx +++ b/examples/react/basic/src/setup.tsx @@ -1,4 +1,4 @@ -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' + import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools' import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools' import { @@ -57,26 +57,24 @@ const routeTree = rootRoute.addChildren([indexRoute, aboutRoute]) const router = createRouter({ routeTree }) -const queryClient = new QueryClient() export default function DevtoolsExample() { return ( <> - - , - }, - { - name: 'Tanstack Router', - render: , - }, - ]} - /> - - + , + }, + { + name: 'Tanstack Router', + render: , + }, + ]} + /> + + ) } diff --git a/packages/devtools/src/hooks/detached/use-check-if-still-detached.ts b/packages/devtools/src/hooks/detached/use-check-if-still-detached.ts index 025144c7e..e4ffa7288 100644 --- a/packages/devtools/src/hooks/detached/use-check-if-still-detached.ts +++ b/packages/devtools/src/hooks/detached/use-check-if-still-detached.ts @@ -4,6 +4,7 @@ import { TANSTACK_DEVTOOLS_DETACHED, TANSTACK_DEVTOOLS_DETACHED_OWNER, TANSTACK_DEVTOOLS_IS_DETACHED, + getBooleanFromSession, getBooleanFromStorage, setStorageItem, } from '../../utils/storage' @@ -14,11 +15,18 @@ export const useCheckIfStillDetached = () => { const context = useDevtoolsContext() const checkDetachment = (e: StorageEvent) => { + + const isWindowOwner = getBooleanFromSession(TANSTACK_DEVTOOLS_DETACHED_OWNER) + // close the window if the main panel closed it via trigger + if (e.key === TANSTACK_DEVTOOLS_IS_DETACHED && e.newValue === "false" && !isWindowOwner) { + window.close() + } // We only care about the should_check key if (e.key !== TANSTACK_DEVTOOLS_CHECK_DETACHED) { return } const isDetached = getBooleanFromStorage(TANSTACK_DEVTOOLS_IS_DETACHED) + if (!isDetached) { return } @@ -33,6 +41,7 @@ export const useCheckIfStillDetached = () => { const isNotDetachedAnymore = getBooleanFromStorage( TANSTACK_DEVTOOLS_CHECK_DETACHED, ) + // The window hasn't set it back to true so it is not detached anymore and we clean all the detached state if (isNotDetachedAnymore) { setStorageItem(TANSTACK_DEVTOOLS_IS_DETACHED, 'false') From d2dc2499e9928d5a2a2267d341b4b6e9ad9a9e25 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 14 Aug 2025 07:47:28 +0000 Subject: [PATCH 09/10] ci: apply automated fixes --- examples/react/basic/src/index.tsx | 24 ++++++++++--------- examples/react/basic/src/setup.tsx | 3 --- .../detached/use-check-if-still-detached.ts | 11 ++++++--- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/examples/react/basic/src/index.tsx b/examples/react/basic/src/index.tsx index 0c617654e..a51cdbfe8 100644 --- a/examples/react/basic/src/index.tsx +++ b/examples/react/basic/src/index.tsx @@ -1,9 +1,13 @@ import { createRoot } from 'react-dom/client' -import { QueryClient, QueryClientProvider, useQuery, useQueryClient } from '@tanstack/react-query' +import { + QueryClient, + QueryClientProvider, + useQuery, + useQueryClient, +} from '@tanstack/react-query' import { useState } from 'react' import Devtools from './setup' - const queryClient = new QueryClient({ defaultOptions: { queries: { @@ -47,9 +51,9 @@ function Posts({ // ones that are cached queryClient.getQueryData(['post', post.id]) ? { - fontWeight: 'bold', - color: 'green', - } + fontWeight: 'bold', + color: 'green', + } : {} } > @@ -127,14 +131,12 @@ function App() { return (
- +

As you visit the posts below, you will notice them in a loading state - the first time you load them. However, after you return to this list and - click on any posts you have already visited again, you will see them - load instantly and background refresh right before your eyes!{' '} + the first time you load them. However, after you return to this list + and click on any posts you have already visited again, you will see + them load instantly and background refresh right before your eyes!{' '} (You may need to throttle your network speed to simulate longer loading sequences) diff --git a/examples/react/basic/src/setup.tsx b/examples/react/basic/src/setup.tsx index 74222c68b..3cdd7efb6 100644 --- a/examples/react/basic/src/setup.tsx +++ b/examples/react/basic/src/setup.tsx @@ -1,4 +1,3 @@ - import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools' import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools' import { @@ -57,7 +56,6 @@ const routeTree = rootRoute.addChildren([indexRoute, aboutRoute]) const router = createRouter({ routeTree }) - export default function DevtoolsExample() { return ( <> @@ -74,7 +72,6 @@ export default function DevtoolsExample() { ]} /> - ) } diff --git a/packages/devtools/src/hooks/detached/use-check-if-still-detached.ts b/packages/devtools/src/hooks/detached/use-check-if-still-detached.ts index e4ffa7288..e17201adf 100644 --- a/packages/devtools/src/hooks/detached/use-check-if-still-detached.ts +++ b/packages/devtools/src/hooks/detached/use-check-if-still-detached.ts @@ -15,10 +15,15 @@ export const useCheckIfStillDetached = () => { const context = useDevtoolsContext() const checkDetachment = (e: StorageEvent) => { - - const isWindowOwner = getBooleanFromSession(TANSTACK_DEVTOOLS_DETACHED_OWNER) + const isWindowOwner = getBooleanFromSession( + TANSTACK_DEVTOOLS_DETACHED_OWNER, + ) // close the window if the main panel closed it via trigger - if (e.key === TANSTACK_DEVTOOLS_IS_DETACHED && e.newValue === "false" && !isWindowOwner) { + if ( + e.key === TANSTACK_DEVTOOLS_IS_DETACHED && + e.newValue === 'false' && + !isWindowOwner + ) { window.close() } // We only care about the should_check key From 424b33406f80505129c1028e6599a3768e89e468 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Thu, 14 Aug 2025 10:03:16 +0200 Subject: [PATCH 10/10] chore: rename window object --- packages/devtools/src/components/tabs.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/devtools/src/components/tabs.tsx b/packages/devtools/src/components/tabs.tsx index 1cbf2eb52..16349c2c6 100644 --- a/packages/devtools/src/components/tabs.tsx +++ b/packages/devtools/src/components/tabs.tsx @@ -23,17 +23,17 @@ export const Tabs = (props: TabsProps) => { const { setDetachedWindowOwner, detachedWindowOwner, detachedWindow } = useDetachedWindowControls() const handleDetachment = () => { - const rdtWindow = window.open( + const detachedWindow = window.open( window.location.href, '', `popup,width=${window.innerWidth},height=${state().height},top=${window.screen.height},left=${window.screenLeft}}`, ) - if (rdtWindow) { + if (detachedWindow) { setDetachedWindowOwner(true) setStorageItem(TANSTACK_DEVTOOLS_IS_DETACHED, 'true') setSessionItem(TANSTACK_DEVTOOLS_DETACHED_OWNER, 'true') - rdtWindow.TDT_MOUNTED = true + detachedWindow.TDT_MOUNTED = true } } return (