1- import { Layout , Space , Badge , Spin , Tooltip , Dropdown , Popover } from "antd" ;
1+ import {
2+ Layout ,
3+ Space ,
4+ Badge ,
5+ Spin ,
6+ Tooltip ,
7+ Dropdown ,
8+ Popover ,
9+ message ,
10+ } from "antd" ;
211import type { MenuProps } from "antd" ;
312import LanguageSwitcher , {
413 LANGUAGE_LIST ,
@@ -23,7 +32,8 @@ import {
2332 compareVersions ,
2433} from "./constants" ;
2534import { useTheme } from "../contexts/ThemeContext" ;
26- import { useState , useEffect } from "react" ;
35+ import { useState , useEffect , useRef } from "react" ;
36+ import { invoke } from "@tauri-apps/api/core" ;
2737import { Slot } from "../plugins/registry/Slot" ;
2838import ReactMarkdown from "react-markdown" ;
2939import remarkGfm from "remark-gfm" ;
@@ -80,6 +90,7 @@ export default function Header() {
8090 const [ latestVersion , setLatestVersion ] = useState < string > ( "" ) ;
8191 const [ updateModalOpen , setUpdateModalOpen ] = useState ( false ) ;
8292 const [ updateMarkdown , setUpdateMarkdown ] = useState < string > ( "" ) ;
93+ const logoClicksRef = useRef < number [ ] > ( [ ] ) ;
8394
8495 useEffect ( ( ) => {
8596 api
@@ -88,6 +99,34 @@ export default function Header() {
8899 . catch ( ( ) => { } ) ;
89100 } , [ ] ) ;
90101
102+ // Hidden gesture: 8 rapid clicks on the logo within 3 seconds toggles DevTools
103+ // in the Tauri desktop build. This keeps DevTools inaccessible via the default
104+ // context menu or keyboard shortcuts while still allowing support/debugging.
105+ const handleLogoClick = ( ) => {
106+ if ( ! onDesktop ) return ;
107+ const now = Date . now ( ) ;
108+ const windowStart = now - 3000 ;
109+ logoClicksRef . current = logoClicksRef . current . filter (
110+ ( time ) => time > windowStart ,
111+ ) ;
112+ logoClicksRef . current . push ( now ) ;
113+ if ( logoClicksRef . current . length >= 8 ) {
114+ logoClicksRef . current = [ ] ;
115+ invoke ( "open_devtools" )
116+ . then ( ( ) => message . success ( "DevTools opened" ) )
117+ . catch ( ( err : unknown ) => {
118+ const errMsg =
119+ err instanceof Error
120+ ? err . message
121+ : typeof err === "string"
122+ ? err
123+ : JSON . stringify ( err ) ;
124+ console . error ( "Failed to open DevTools:" , errMsg ) ;
125+ message . error ( `DevTools error: ${ errMsg } ` ) ;
126+ } ) ;
127+ }
128+ } ;
129+
91130 // Web-only PyPI fallback: desktop path is owned by DesktopUpdateContext.
92131 useEffect ( ( ) => {
93132 if ( onDesktop ) return ;
@@ -297,7 +336,7 @@ export default function Header() {
297336 return (
298337 < >
299338 < AntHeader className = { styles . header } >
300- < div className = { styles . logoWrapper } >
339+ < div className = { styles . logoWrapper } onClick = { handleLogoClick } >
301340 { /*
302341 Slot lets a plugin replace the brand logo (e.g. a per-agent
303342 branding override). When no plugin registers a replacement —
0 commit comments