diff --git a/.changeset/icy-states-chew.md b/.changeset/icy-states-chew.md new file mode 100644 index 00000000..5e445e0c --- /dev/null +++ b/.changeset/icy-states-chew.md @@ -0,0 +1,5 @@ +--- +'@tanstack/devtools': minor +--- + +Adds copy path feature and config to devtools source inspector diff --git a/docs/source-inspector.md b/docs/source-inspector.md index e49535db..44b26946 100644 --- a/docs/source-inspector.md +++ b/docs/source-inspector.md @@ -67,6 +67,25 @@ export default { Both `files` and `components` accept arrays of strings (exact match) or RegExp patterns. +## Click Action + +By default, clicking an inspected element opens the file in your editor. You can change this to copy the source path to the clipboard instead using the `sourceAction` setting: + +```ts + +``` + +| Value | Behavior | +| --- | --- | +| `"ide-warp"` | Opens the file in your editor at the exact line (default) | +| `"copy-path"` | Copies the `filepath:line:column` string to the clipboard | + +This is useful in environments where the Vite dev server cannot reach your editor, or when you want to paste the path elsewhere. + ## Editor Configuration Most popular editors work out of the box via the `launch-editor` package. Supported editors include VS Code, WebStorm, Sublime Text, Atom, and more ([full list](https://github.com/yyx990803/launch-editor?tab=readme-ov-file#supported-editors)). diff --git a/examples/react/basic/src/setup.tsx b/examples/react/basic/src/setup.tsx index a2839d99..d164bd3b 100644 --- a/examples/react/basic/src/setup.tsx +++ b/examples/react/basic/src/setup.tsx @@ -59,9 +59,7 @@ export default function DevtoolsExample() { return ( <> { const downList = useKeyDownList() + const [disabledAfterClick, setDisabledAfterClick] = createSignal(false) + const isHighlightingKeysHeld = createMemo(() => { return isHotkeyCombinationPressed(downList(), settings().inspectHotkey) }) createEffect(() => { if (!isHighlightingKeysHeld()) { + setDisabledAfterClick(false) + } + }) + + const isActive = createMemo( + () => isHighlightingKeysHeld() && !disabledAfterClick(), + ) + + createEffect(() => { + if (isActive()) { + document.body.style.cursor = 'pointer' + } else { + document.body.style.cursor = '' + } + }) + + createEffect(() => { + if (!isActive()) { resetHighlight() return } @@ -78,6 +98,12 @@ export const SourceInspector = () => { window.getSelection()?.removeAllRanges() e.preventDefault() e.stopPropagation() + setDisabledAfterClick(true) + + if (settings().sourceAction === 'copy-path') { + navigator.clipboard.writeText(highlightState.dataSource).catch(() => {}) + return + } // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition const baseUrl = new URL(import.meta.env?.BASE_URL ?? '/', location.origin) diff --git a/packages/devtools/src/context/devtools-store.ts b/packages/devtools/src/context/devtools-store.ts index e4e908de..be9cdd87 100644 --- a/packages/devtools/src/context/devtools-store.ts +++ b/packages/devtools/src/context/devtools-store.ts @@ -74,6 +74,13 @@ export type DevtoolsStore = { */ theme: TanStackDevtoolsTheme + /** + * The action to perform when clicking a source-inspected element + * - "ide-warp": open the file in the IDE via the Vite middleware + * - "copy-path": copy the file path to the clipboard + * @default "ide-warp" + */ + sourceAction: 'ide-warp' | 'copy-path' /** * Whether the trigger should be completely hidden or not (you can still open with the hotkey) */ @@ -110,6 +117,7 @@ export const initialState: DevtoolsStore = { window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light', + sourceAction: 'ide-warp', triggerHidden: false, customTrigger: undefined, },