From 422acf3888564c54bfba4db7990fa6d5fdb3b857 Mon Sep 17 00:00:00 2001 From: axuj Date: Wed, 15 Oct 2025 17:38:57 +0800 Subject: [PATCH 1/6] feat(devtools): add source inspector with element highlighting Extract the source opening logic into a new SourceInspector component that highlights elements on Shift+Ctrl hover, improving the devtools user experience for inspecting sources. --- .../src/components/source-inspector.tsx | 113 ++++++++++++++++++ packages/devtools/src/devtools.tsx | 31 +---- 2 files changed, 116 insertions(+), 28 deletions(-) create mode 100644 packages/devtools/src/components/source-inspector.tsx diff --git a/packages/devtools/src/components/source-inspector.tsx b/packages/devtools/src/components/source-inspector.tsx new file mode 100644 index 00000000..2f0515b6 --- /dev/null +++ b/packages/devtools/src/components/source-inspector.tsx @@ -0,0 +1,113 @@ +import { createEffect, createSignal, onCleanup } from 'solid-js' + +export const SourceInspector = () => { + const [isHighlighting, setIsHighlighting] = createSignal(false) + const [currentElement, setCurrentElement] = createSignal( + null, + ) + const [currentElementBounding, setCurrentElementBounding] = createSignal({ + width: 0, + height: 0, + left: 0, + top: 0, + }) + + createEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + const isShiftHeld = e.shiftKey + const isCtrlHeld = e.ctrlKey || e.metaKey + if (isShiftHeld && isCtrlHeld) { + setIsHighlighting(true) + } + } + + const handleKeyUp = (e: KeyboardEvent) => { + const isShiftHeld = e.shiftKey + const isCtrlHeld = e.ctrlKey || e.metaKey + if (!isShiftHeld || !isCtrlHeld) { + setIsHighlighting(false) + setCurrentElement(null) + } + } + const handleMouseMove = (e: MouseEvent) => { + if (!isHighlighting()) return + + const target = document.elementFromPoint(e.clientX, e.clientY) + + if (!(target instanceof HTMLElement)) { + return + } + + if (target === currentElement()) { + return + } + + const dataSource = target.getAttribute('data-tsd-source') + if (!dataSource) return + + setCurrentElement(target) + const rect = target.getBoundingClientRect() + setCurrentElementBounding({ + width: rect.width, + height: rect.height, + left: rect.left, + top: rect.top, + }) + } + + const openSourceHandler = (e: Event) => { + if (!isHighlighting()) return + + if (e.target instanceof HTMLElement) { + const dataSource = e.target.getAttribute('data-tsd-source') + window.getSelection()?.removeAllRanges() + if (dataSource) { + e.preventDefault() + e.stopPropagation() + fetch( + `${location.origin}/__tsd/open-source?source=${encodeURIComponent( + dataSource, + )}`, + ).catch(() => {}) + } + } + } + + window.addEventListener('keydown', handleKeyDown) + window.addEventListener('keyup', handleKeyUp) + window.addEventListener('mousemove', handleMouseMove) + window.addEventListener('click', openSourceHandler) + onCleanup(() => { + window.removeEventListener('keydown', handleKeyDown) + window.removeEventListener('keyup', handleKeyUp) + window.removeEventListener('mousemove', handleMouseMove) + window.removeEventListener('click', openSourceHandler) + }) + }) + + const currentElementBoxStyles = () => { + const element = currentElement() + if (element) { + const bounding = currentElementBounding() + return { + display: 'block', + width: `${bounding.width}px`, + height: `${bounding.height}px`, + left: `${bounding.left}px`, + top: `${bounding.top}px`, + + 'background-color': 'oklch(55.4% 0.046 257.417 /0.25)', + transition: 'all 0.05s linear', + position: 'fixed' as const, + 'z-index': 9999, + } + } + return { + display: 'none', + } + } + + return ( +
+ ) +} diff --git a/packages/devtools/src/devtools.tsx b/packages/devtools/src/devtools.tsx index 2ec3ee5d..9ca75eaf 100644 --- a/packages/devtools/src/devtools.tsx +++ b/packages/devtools/src/devtools.tsx @@ -1,4 +1,4 @@ -import { Show, createEffect, createSignal, onCleanup } from 'solid-js' +import { Show, createEffect, createSignal } from 'solid-js' import { createShortcut } from '@solid-primitives/keyboard' import { Portal } from 'solid-js/web' import { ThemeContextProvider } from '@tanstack/devtools-ui' @@ -18,6 +18,7 @@ import { TabContent } from './components/tab-content' import { keyboardModifiers } from './context/devtools-store' import { getAllPermutations } from './utils/sanitize' import { usePiPWindow } from './context/pip-context' +import { SourceInspector } from './components/source-inspector' export default function DevTools() { const { settings } = useDevtoolsSettings() @@ -159,33 +160,6 @@ export default function DevTools() { } }) - createEffect(() => { - // this will only work with the Vite plugin - const openSourceHandler = (e: Event) => { - const isShiftHeld = (e as KeyboardEvent).shiftKey - const isCtrlHeld = - (e as KeyboardEvent).ctrlKey || (e as KeyboardEvent).metaKey - if (!isShiftHeld || !isCtrlHeld) return - - if (e.target instanceof HTMLElement) { - const dataSource = e.target.getAttribute('data-tsd-source') - window.getSelection()?.removeAllRanges() - if (dataSource) { - e.preventDefault() - e.stopPropagation() - fetch( - `${location.origin}/__tsd/open-source?source=${encodeURIComponent( - dataSource, - )}`, - ).catch(() => {}) - } - } - } - window.addEventListener('click', openSourceHandler) - onCleanup(() => { - window.removeEventListener('click', openSourceHandler) - }) - }) const { theme } = useTheme() return ( @@ -216,6 +190,7 @@ export default function DevTools() { +
From 6be26fb7ab14b5f9612a3bc9d59b0a5547b14c36 Mon Sep 17 00:00:00 2001 From: axuj Date: Thu, 16 Oct 2025 01:18:38 +0800 Subject: [PATCH 2/6] feat(devtools): enhance source inspector with improved highlighting and name tags Refactor the SourceInspector component to use createStore for state management, integrate @solid-primitives for resize observer, keyboard, mouse, and event listener to improve performance and accuracy. Add a dynamic name tag displaying the file name near highlighted elements, with smart positioning to avoid screen edges. --- packages/devtools/package.json | 3 + .../src/components/source-inspector.tsx | 203 +++++++++++------- pnpm-lock.yaml | 71 ++++++ 3 files changed, 197 insertions(+), 80 deletions(-) diff --git a/packages/devtools/package.json b/packages/devtools/package.json index 5408efcd..3877a3e4 100644 --- a/packages/devtools/package.json +++ b/packages/devtools/package.json @@ -59,7 +59,10 @@ "build": "tsup" }, "dependencies": { + "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/keyboard": "^1.3.3", + "@solid-primitives/mouse": "^2.1.4", + "@solid-primitives/resize-observer": "^2.1.3", "@tanstack/devtools-event-bus": "workspace:*", "@tanstack/devtools-ui": "workspace:*", "clsx": "^2.1.1", diff --git a/packages/devtools/src/components/source-inspector.tsx b/packages/devtools/src/components/source-inspector.tsx index 2f0515b6..86af5d26 100644 --- a/packages/devtools/src/components/source-inspector.tsx +++ b/packages/devtools/src/components/source-inspector.tsx @@ -1,100 +1,96 @@ -import { createEffect, createSignal, onCleanup } from 'solid-js' +import { createEffect, createMemo, createSignal } from 'solid-js' +import { createStore } from 'solid-js/store' +import { createElementSize } from '@solid-primitives/resize-observer' +import { useKeyDownList } from '@solid-primitives/keyboard' +import { createMousePosition } from '@solid-primitives/mouse' +import { createEventListener } from '@solid-primitives/event-listener' export const SourceInspector = () => { - const [isHighlighting, setIsHighlighting] = createSignal(false) - const [currentElement, setCurrentElement] = createSignal( - null, - ) - const [currentElementBounding, setCurrentElementBounding] = createSignal({ - width: 0, - height: 0, - left: 0, - top: 0, + const highlightStateInit = { + element: null as HTMLElement | null, + bounding: { width: 0, height: 0, left: 0, top: 0 }, + dataSource: '', + } + + const [highlightState, setHighlightState] = createStore({ + ...highlightStateInit, }) + const resetHighlight = () => { + setHighlightState({ ...highlightStateInit }) + } - createEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - const isShiftHeld = e.shiftKey - const isCtrlHeld = e.ctrlKey || e.metaKey - if (isShiftHeld && isCtrlHeld) { - setIsHighlighting(true) - } - } + const [nameTagRef, setNameTagRef] = createSignal(null) + const nameTagSize = createElementSize(() => nameTagRef()) - const handleKeyUp = (e: KeyboardEvent) => { - const isShiftHeld = e.shiftKey - const isCtrlHeld = e.ctrlKey || e.metaKey - if (!isShiftHeld || !isCtrlHeld) { - setIsHighlighting(false) - setCurrentElement(null) - } + const mousePosition = createMousePosition() + + const downList = useKeyDownList() + const isHighlightingKeysHeld = createMemo(() => { + const keys = downList() + const isShiftHeld = keys.includes('SHIFT') + const isCtrlHeld = keys.includes('CONTROL') + const isMetaHeld = keys.includes('META') + return isShiftHeld && (isCtrlHeld || isMetaHeld) + }) + + createEffect(() => { + if (!isHighlightingKeysHeld()) { + resetHighlight() + return } - const handleMouseMove = (e: MouseEvent) => { - if (!isHighlighting()) return - const target = document.elementFromPoint(e.clientX, e.clientY) + const target = document.elementFromPoint(mousePosition.x, mousePosition.y) - if (!(target instanceof HTMLElement)) { - return - } + if (!(target instanceof HTMLElement)) { + resetHighlight() + return + } - if (target === currentElement()) { - return - } + if (target === highlightState.element) { + return + } - const dataSource = target.getAttribute('data-tsd-source') - if (!dataSource) return - - setCurrentElement(target) - const rect = target.getBoundingClientRect() - setCurrentElementBounding({ - width: rect.width, - height: rect.height, - left: rect.left, - top: rect.top, - }) + const dataSource = target.getAttribute('data-tsd-source') + if (!dataSource) { + resetHighlight() + return } - const openSourceHandler = (e: Event) => { - if (!isHighlighting()) return - - if (e.target instanceof HTMLElement) { - const dataSource = e.target.getAttribute('data-tsd-source') - window.getSelection()?.removeAllRanges() - if (dataSource) { - e.preventDefault() - e.stopPropagation() - fetch( - `${location.origin}/__tsd/open-source?source=${encodeURIComponent( - dataSource, - )}`, - ).catch(() => {}) - } - } + const rect = target.getBoundingClientRect() + const bounding = { + width: rect.width, + height: rect.height, + left: rect.left, + top: rect.top, } - window.addEventListener('keydown', handleKeyDown) - window.addEventListener('keyup', handleKeyUp) - window.addEventListener('mousemove', handleMouseMove) - window.addEventListener('click', openSourceHandler) - onCleanup(() => { - window.removeEventListener('keydown', handleKeyDown) - window.removeEventListener('keyup', handleKeyUp) - window.removeEventListener('mousemove', handleMouseMove) - window.removeEventListener('click', openSourceHandler) + setHighlightState({ + element: target, + bounding, + dataSource, }) }) - const currentElementBoxStyles = () => { - const element = currentElement() - if (element) { - const bounding = currentElementBounding() + createEventListener(window, 'click', (e: Event) => { + if (!highlightState.element) return + + e.preventDefault() + e.stopPropagation() + fetch( + `${location.origin}/__tsd/open-source?source=${encodeURIComponent( + highlightState.dataSource, + )}`, + ).catch(() => {}) + }) + + const currentElementBoxStyles = createMemo(() => { + if (highlightState.element) { return { display: 'block', - width: `${bounding.width}px`, - height: `${bounding.height}px`, - left: `${bounding.left}px`, - top: `${bounding.top}px`, + width: `${highlightState.bounding.width}px`, + height: `${highlightState.bounding.height}px`, + left: `${highlightState.bounding.left}px`, + top: `${highlightState.bounding.top}px`, 'background-color': 'oklch(55.4% 0.046 257.417 /0.25)', transition: 'all 0.05s linear', @@ -105,9 +101,56 @@ export const SourceInspector = () => { return { display: 'none', } - } + }) + + const fileNameStyles = createMemo(() => { + if (highlightState.element && nameTagRef()) { + const windowWidth = window.innerWidth + const nameTagHeight = nameTagSize.height || 26 + const nameTagWidth = nameTagSize.width || 0 + let left = highlightState.bounding.left + let top = highlightState.bounding.top - nameTagHeight - 4 + + if (top < 0) { + top = highlightState.bounding.top + highlightState.bounding.height + 4 + } + + if (left + nameTagWidth > windowWidth) { + left = windowWidth - nameTagWidth - 4 + } + + if (left < 0) { + left = 4 + } + + return { + position: 'fixed' as const, + left: `${left}px`, + top: `${top}px`, + 'background-color': 'oklch(55.4% 0.046 257.417 /0.80)', + color: 'white', + padding: '2px 4px', + fontSize: '12px', + 'border-radius': '2px', + 'z-index': 10000, + visibility: 'visible' as const, + transition: 'all 0.05s linear', + } + } + return { + display: 'none', + } + }) return ( -
+ <> +
+ {highlightState.dataSource.split(':')[0]} +
+
+ ) } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 97854d96..e835188a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -479,9 +479,18 @@ importers: packages/devtools: dependencies: + '@solid-primitives/event-listener': + specifier: ^2.4.3 + version: 2.4.3(solid-js@1.9.9) '@solid-primitives/keyboard': specifier: ^1.3.3 version: 1.3.3(solid-js@1.9.9) + '@solid-primitives/mouse': + specifier: ^2.1.4 + version: 2.1.4(solid-js@1.9.9) + '@solid-primitives/resize-observer': + specifier: ^2.1.3 + version: 2.1.3(solid-js@1.9.9) '@tanstack/devtools-event-bus': specifier: workspace:* version: link:../event-bus @@ -1889,21 +1898,25 @@ packages: resolution: {integrity: sha512-GQF/xjGeqt4tYWf9jT1D0GRPrcAjajTB1QpSavUaiT1jDkByuN11WvuWeTfBdPJpYWFxvH887+r+uMEg8zRE4A==} cpu: [arm64] os: [linux] + libc: [glibc] '@nx/nx-linux-arm64-musl@21.5.3': resolution: {integrity: sha512-C5j2pzfe0zoAJelHXzPdTGeU19VvvHVaoesiKPeH9EvJwLLb9FoeIn+6//x3jDUNUqIHdn4+63kMA6mdBQSpMQ==} cpu: [arm64] os: [linux] + libc: [musl] '@nx/nx-linux-x64-gnu@21.5.3': resolution: {integrity: sha512-HI+tdkvzFcnJQpU9m1FjvlxW7ZsJeF4os8OG4HSLRTQfFT8HCXXzp6b9sojTr+4Nfvp5r3T/J/UJM9tOJXW9Aw==} cpu: [x64] os: [linux] + libc: [glibc] '@nx/nx-linux-x64-musl@21.5.3': resolution: {integrity: sha512-ntlBqcO9wVajUjYwzBU5ru2iBORttO4nurKvjnpBbyCF1mOjSJ3uFcFMzktbp2cxpRE5JRAadGq9/pZisez1AQ==} cpu: [x64] os: [linux] + libc: [musl] '@nx/nx-win32-arm64-msvc@21.5.3': resolution: {integrity: sha512-VgX1VnKDRgWcjIMJ0V3zZ6OPuYkvA7rzgn8wbZWyBh2/1GFFffypJJsGeXRPCZBFHjF3UFYcwjzCMoStZ35b5g==} @@ -1981,41 +1994,49 @@ packages: resolution: {integrity: sha512-8g2Y72gavZ8fesZD22cKo0Z8g8epynwShu7M+wpAoOq432IGUyUxPUKB2/nvyogPToaAlb1OsRiX/za8W4h8Aw==} cpu: [arm64] os: [linux] + libc: [glibc] '@oxc-resolver/binding-linux-arm64-musl@11.8.2': resolution: {integrity: sha512-N3BPWnIDRmHn/xPDZGKnzFwWxwH1hvs3aVnw4jvMAYarPNDZfbAY+fjHSIwkypV+ozMoJ5lK5PzRO5BOtEx2oQ==} cpu: [arm64] os: [linux] + libc: [musl] '@oxc-resolver/binding-linux-ppc64-gnu@11.8.2': resolution: {integrity: sha512-AXW2AyjENmzNuZD3Z2TO1QWoZzfULWR1otDzw/+MAVMRXBy3W50XxDqNAflRiLB4o0aI0oDTwMfeyuhVv9Ur8Q==} cpu: [ppc64] os: [linux] + libc: [glibc] '@oxc-resolver/binding-linux-riscv64-gnu@11.8.2': resolution: {integrity: sha512-oX+qxJdqOfrJUkGWmcNpu7wiFs6E7KH6hqUORkMAgl4yW+LZxPTz5P4DHvTqTFMywbs9hXVu2KQrdD8ROrdhMQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@oxc-resolver/binding-linux-riscv64-musl@11.8.2': resolution: {integrity: sha512-TG7LpxXjqlpD1aWnAXw6vMgY74KNV92exPixzEj4AKm4LdGsfnSWYTTJcTQ7deFMYxvBGrZ+qEy8DjGx+5w9GQ==} cpu: [riscv64] os: [linux] + libc: [musl] '@oxc-resolver/binding-linux-s390x-gnu@11.8.2': resolution: {integrity: sha512-1PpXMq0KMD3CQPn3v/UqU4NM2JFjry+mLIH1d3iNVL2vlwRt9lxRfpXTiyiFJrtroUIyeKhw0QbHbF2UfnZVKQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@oxc-resolver/binding-linux-x64-gnu@11.8.2': resolution: {integrity: sha512-V1iYhEDbjQzj+o7JgTYVllRgNZ56Tjw0rPBWw03KJQ8Nphy00Vf7AySf22vV0K/93V1lPCgOSbI5/iunRnIfAw==} cpu: [x64] os: [linux] + libc: [glibc] '@oxc-resolver/binding-linux-x64-musl@11.8.2': resolution: {integrity: sha512-2hYNXEZSUM7qLEk4uuY3GmMqLU+860v+8PzbloVvRRjTWtHsLZyB5w+5p2gel38eaTcSYfZ2zvp3xcSpKDAbaw==} cpu: [x64] os: [linux] + libc: [musl] '@oxc-resolver/binding-wasm32-wasi@11.8.2': resolution: {integrity: sha512-TjFqB+1siSqhd+S64Hf2qbxqWqtFIlld4DDEVotxOjj5//rX/6uwAL1HWnUHSNIni+wpcyQoXPhO3fBgppCvuA==} @@ -2066,36 +2087,42 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.1': resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.1': resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.1': resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.1': resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.1': resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@parcel/watcher-wasm@2.3.0': resolution: {integrity: sha512-ejBAX8H0ZGsD8lSICDNyMbSEtPMWgDL0WFCt/0z7hyf5v8Imz4rAM8xY379mBsECkq/Wdqa5WEDLqtjZ+6NxfA==} @@ -2226,21 +2253,25 @@ packages: resolution: {integrity: sha512-hRZygRlaGCjcNTNY9GV7dDI18sG1dK3cc7ujHq72LoDad23zFDUGMQjiSxHWK+/r92iMV+j2MiHbvzayxqynsg==} cpu: [arm64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-arm64-musl@1.0.0-beta.32': resolution: {integrity: sha512-HzgT6h+CXLs+GKAU0Wvkt3rvcv0CmDBsDjlPhh4GHysOKbG9NjpKYX2zvjx671E9pGbTvcPpwy7gGsy7xpu+8g==} cpu: [arm64] os: [linux] + libc: [musl] '@rolldown/binding-linux-x64-gnu@1.0.0-beta.32': resolution: {integrity: sha512-Ab/wbf6gdzphDbsg51UaxsC93foQ7wxhtg0SVCXd25BrV4MAJ1HoDtKN/f4h0maFmJobkqYub2DlmoasUzkvBg==} cpu: [x64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-x64-musl@1.0.0-beta.32': resolution: {integrity: sha512-VoxqGEfh5A1Yx+zBp/FR5QwAbtzbuvky2SVc+ii4g1gLD4zww6mt/hPi5zG+b88zYPFBKHpxMtsz9cWqXU5V5Q==} cpu: [x64] os: [linux] + libc: [musl] '@rolldown/binding-openharmony-arm64@1.0.0-beta.32': resolution: {integrity: sha512-qZ1ViyOUDGbiZrSAJ/FIAhYUElDfVxxFW6DLT/w4KeoZN3HsF4jmRP95mXtl51/oGrqzU9l9Q2f7/P4O/o2ZZA==} @@ -2379,56 +2410,67 @@ packages: resolution: {integrity: sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.46.2': resolution: {integrity: sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.46.2': resolution: {integrity: sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.46.2': resolution: {integrity: sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.46.2': resolution: {integrity: sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-gnu@4.46.2': resolution: {integrity: sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.46.2': resolution: {integrity: sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.46.2': resolution: {integrity: sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.46.2': resolution: {integrity: sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.46.2': resolution: {integrity: sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.46.2': resolution: {integrity: sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.46.2': resolution: {integrity: sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==} @@ -2551,6 +2593,11 @@ packages: peerDependencies: solid-js: ^1.6.12 + '@solid-primitives/mouse@2.1.4': + resolution: {integrity: sha512-qZNh7FedzJMSrUX/BSd5Pjkv+Ct7vDr9TXVbXSdAKa4xMri0mwNi1eCbNbcEsq/FOsPVQLyt2d5oR7cQHBOg0A==} + peerDependencies: + solid-js: ^1.6.12 + '@solid-primitives/refs@1.1.2': resolution: {integrity: sha512-K7tf2thy7L+YJjdqXspXOg5xvNEOH8tgEWsp0+1mQk3obHBRD6hEjYZk7p7FlJphSZImS35je3UfmWuD7MhDfg==} peerDependencies: @@ -2655,24 +2702,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.1.13': resolution: {integrity: sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.1.13': resolution: {integrity: sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.1.13': resolution: {integrity: sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.1.13': resolution: {integrity: sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==} @@ -3207,41 +3258,49 @@ packages: resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.11.1': resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} cpu: [arm64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} cpu: [riscv64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} cpu: [s390x] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.11.1': resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} cpu: [x64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.11.1': resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} cpu: [x64] os: [linux] + libc: [musl] '@unrs/resolver-binding-wasm32-wasi@1.11.1': resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} @@ -5418,24 +5477,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.30.1: resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.30.1: resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.30.1: resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.30.1: resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} @@ -9639,6 +9702,14 @@ snapshots: '@solid-primitives/utils': 6.3.2(solid-js@1.9.9) solid-js: 1.9.9 + '@solid-primitives/mouse@2.1.4(solid-js@1.9.9)': + dependencies: + '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.9) + '@solid-primitives/rootless': 1.5.2(solid-js@1.9.9) + '@solid-primitives/static-store': 0.1.2(solid-js@1.9.9) + '@solid-primitives/utils': 6.3.2(solid-js@1.9.9) + solid-js: 1.9.9 + '@solid-primitives/refs@1.1.2(solid-js@1.9.9)': dependencies: '@solid-primitives/utils': 6.3.2(solid-js@1.9.9) From 19cc46cc74eab3f711b56296ab1470ab98808bae Mon Sep 17 00:00:00 2001 From: axuj Date: Thu, 16 Oct 2025 11:52:09 +0800 Subject: [PATCH 3/6] refactor(devtools): switch highlight state init to function-based approach in source inspector --- packages/devtools/src/components/source-inspector.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/devtools/src/components/source-inspector.tsx b/packages/devtools/src/components/source-inspector.tsx index 86af5d26..ecdf6e7e 100644 --- a/packages/devtools/src/components/source-inspector.tsx +++ b/packages/devtools/src/components/source-inspector.tsx @@ -6,17 +6,15 @@ import { createMousePosition } from '@solid-primitives/mouse' import { createEventListener } from '@solid-primitives/event-listener' export const SourceInspector = () => { - const highlightStateInit = { + const highlightStateInit = () => ({ element: null as HTMLElement | null, bounding: { width: 0, height: 0, left: 0, top: 0 }, dataSource: '', - } - - const [highlightState, setHighlightState] = createStore({ - ...highlightStateInit, }) + + const [highlightState, setHighlightState] = createStore(highlightStateInit()) const resetHighlight = () => { - setHighlightState({ ...highlightStateInit }) + setHighlightState(highlightStateInit()) } const [nameTagRef, setNameTagRef] = createSignal(null) From 6cc63dc8ac9d789c9f89def8a48c68092b5ebac1 Mon Sep 17 00:00:00 2001 From: axuj Date: Thu, 16 Oct 2025 13:26:31 +0800 Subject: [PATCH 4/6] refactor(devtools): replace mouse position tracking with custom implementation - Replace @solid-primitives/mouse with custom event listener using createEventListener - Use e.clientX and e.clientY for client position instead of page position - Explanation: @solid-primitives/mouse returns page position, but we need client position for accurate element highlighting in source inspector --- packages/devtools/package.json | 1 - .../devtools/src/components/source-inspector.tsx | 8 +++++--- pnpm-lock.yaml | 16 ---------------- 3 files changed, 5 insertions(+), 20 deletions(-) diff --git a/packages/devtools/package.json b/packages/devtools/package.json index 3877a3e4..cd4746f8 100644 --- a/packages/devtools/package.json +++ b/packages/devtools/package.json @@ -61,7 +61,6 @@ "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/keyboard": "^1.3.3", - "@solid-primitives/mouse": "^2.1.4", "@solid-primitives/resize-observer": "^2.1.3", "@tanstack/devtools-event-bus": "workspace:*", "@tanstack/devtools-ui": "workspace:*", diff --git a/packages/devtools/src/components/source-inspector.tsx b/packages/devtools/src/components/source-inspector.tsx index ecdf6e7e..52a98c29 100644 --- a/packages/devtools/src/components/source-inspector.tsx +++ b/packages/devtools/src/components/source-inspector.tsx @@ -2,7 +2,6 @@ import { createEffect, createMemo, createSignal } from 'solid-js' import { createStore } from 'solid-js/store' import { createElementSize } from '@solid-primitives/resize-observer' import { useKeyDownList } from '@solid-primitives/keyboard' -import { createMousePosition } from '@solid-primitives/mouse' import { createEventListener } from '@solid-primitives/event-listener' export const SourceInspector = () => { @@ -20,7 +19,10 @@ export const SourceInspector = () => { const [nameTagRef, setNameTagRef] = createSignal(null) const nameTagSize = createElementSize(() => nameTagRef()) - const mousePosition = createMousePosition() + const [mousePosition, setMousePosition] = createStore({ x: 0, y: 0 }) + createEventListener(document, 'mousemove', (e) => { + setMousePosition({ x: e.clientX, y: e.clientY }) + }) const downList = useKeyDownList() const isHighlightingKeysHeld = createMemo(() => { @@ -69,7 +71,7 @@ export const SourceInspector = () => { }) }) - createEventListener(window, 'click', (e: Event) => { + createEventListener(document, 'click', (e: Event) => { if (!highlightState.element) return e.preventDefault() diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e835188a..7f9c768e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -485,9 +485,6 @@ importers: '@solid-primitives/keyboard': specifier: ^1.3.3 version: 1.3.3(solid-js@1.9.9) - '@solid-primitives/mouse': - specifier: ^2.1.4 - version: 2.1.4(solid-js@1.9.9) '@solid-primitives/resize-observer': specifier: ^2.1.3 version: 2.1.3(solid-js@1.9.9) @@ -2593,11 +2590,6 @@ packages: peerDependencies: solid-js: ^1.6.12 - '@solid-primitives/mouse@2.1.4': - resolution: {integrity: sha512-qZNh7FedzJMSrUX/BSd5Pjkv+Ct7vDr9TXVbXSdAKa4xMri0mwNi1eCbNbcEsq/FOsPVQLyt2d5oR7cQHBOg0A==} - peerDependencies: - solid-js: ^1.6.12 - '@solid-primitives/refs@1.1.2': resolution: {integrity: sha512-K7tf2thy7L+YJjdqXspXOg5xvNEOH8tgEWsp0+1mQk3obHBRD6hEjYZk7p7FlJphSZImS35je3UfmWuD7MhDfg==} peerDependencies: @@ -9702,14 +9694,6 @@ snapshots: '@solid-primitives/utils': 6.3.2(solid-js@1.9.9) solid-js: 1.9.9 - '@solid-primitives/mouse@2.1.4(solid-js@1.9.9)': - dependencies: - '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.9) - '@solid-primitives/rootless': 1.5.2(solid-js@1.9.9) - '@solid-primitives/static-store': 0.1.2(solid-js@1.9.9) - '@solid-primitives/utils': 6.3.2(solid-js@1.9.9) - solid-js: 1.9.9 - '@solid-primitives/refs@1.1.2(solid-js@1.9.9)': dependencies: '@solid-primitives/utils': 6.3.2(solid-js@1.9.9) From deff9aebb7eb8d38d1f73d267fde5b17518fac1a Mon Sep 17 00:00:00 2001 From: axuj Date: Thu, 16 Oct 2025 13:34:47 +0800 Subject: [PATCH 5/6] refactor(devtools): clear text selection on click in source inspector --- packages/devtools/src/components/source-inspector.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/devtools/src/components/source-inspector.tsx b/packages/devtools/src/components/source-inspector.tsx index 52a98c29..eef21ae7 100644 --- a/packages/devtools/src/components/source-inspector.tsx +++ b/packages/devtools/src/components/source-inspector.tsx @@ -71,11 +71,13 @@ export const SourceInspector = () => { }) }) - createEventListener(document, 'click', (e: Event) => { + createEventListener(document, 'click', (e) => { if (!highlightState.element) return + window.getSelection()?.removeAllRanges() e.preventDefault() e.stopPropagation() + fetch( `${location.origin}/__tsd/open-source?source=${encodeURIComponent( highlightState.dataSource, From dd0ab3cf1fb0fa9e173ca4e8061ac819079798e1 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Fri, 17 Oct 2025 13:23:59 +0000 Subject: [PATCH 6/6] ci: apply automated fixes --- packages/devtools/src/components/source-inspector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devtools/src/components/source-inspector.tsx b/packages/devtools/src/components/source-inspector.tsx index eef21ae7..66121663 100644 --- a/packages/devtools/src/components/source-inspector.tsx +++ b/packages/devtools/src/components/source-inspector.tsx @@ -77,7 +77,7 @@ export const SourceInspector = () => { window.getSelection()?.removeAllRanges() e.preventDefault() e.stopPropagation() - + fetch( `${location.origin}/__tsd/open-source?source=${encodeURIComponent( highlightState.dataSource,