feat: replace agentation with draggable help button (#1008)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: ananaBMaster <68643891+ananaBMaster@users.noreply.github.com>
This commit is contained in:
MengXi
2026-02-25 17:26:44 -08:00
committed by GitHub
parent 5ae4de063c
commit 7e822cb738
7 changed files with 105 additions and 25 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@read-frog/extension": patch
---
feat: replace agentation dev toolbar with draggable help button
+1 -1
View File
@@ -18,7 +18,7 @@ fi
pnpm exec nx lint
pnpm exec nx type-check
pnpm exec nx build
# pnpm exec nx build
# Check if SKIP_FREE_API environment variable is set
if [ "$SKIP_FREE_API" = "true" ]; then
-1
View File
@@ -128,7 +128,6 @@
"@vitejs/plugin-react": "^5.1.4",
"@vitest/coverage-istanbul": "^4.0.18",
"@wxt-dev/module-react": "^1.1.5",
"agentation": "^2.2.1",
"autoprefixer": "^10.4.24",
"eruda": "^3.4.3",
"eslint": "9.39.3",
-19
View File
@@ -297,9 +297,6 @@ importers:
'@wxt-dev/module-react':
specifier: ^1.1.5
version: 1.1.5(vite@7.2.2(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2))(wxt@0.20.18(@types/node@20.19.25)(eslint@9.39.3(jiti@2.6.1))(jiti@2.6.1)(lightningcss@1.31.1)(rollup@4.53.2)(yaml@2.8.2))
agentation:
specifier: ^2.2.1
version: 2.2.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
autoprefixer:
specifier: ^10.4.24
version: 10.4.24(postcss@8.5.6)
@@ -2897,17 +2894,6 @@ packages:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
agentation@2.2.1:
resolution: {integrity: sha512-yV9P1DggI7M3SRaRwLwt+xqE5lXqg5l8xtqCr8KzEkbnH8Wa6eRATU97uKnD7cC8FrsJP62Mmw0Xf5Xi5KV50Q==}
peerDependencies:
react: '>=18.0.0'
react-dom: '>=18.0.0'
peerDependenciesMeta:
react:
optional: true
react-dom:
optional: true
ai@6.0.97:
resolution: {integrity: sha512-eZIAcBymwGhBwncRH/v9pillZNMeRCDkc4BwcvwXerXd7sxjVxRis3ZNCNCpP02pVH4NLs81ljm4cElC4vbNcQ==}
engines: {node: '>=18'}
@@ -10010,11 +9996,6 @@ snapshots:
agent-base@7.1.4: {}
agentation@2.2.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
optionalDependencies:
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
ai@6.0.97(zod@4.3.6):
dependencies:
'@ai-sdk/gateway': 3.0.53(zod@4.3.6)
+95
View File
@@ -0,0 +1,95 @@
import { Icon } from "@iconify/react"
import { useCallback, useRef, useState } from "react"
import { cn } from "@/utils/styles/utils"
type Corner = "bottom-right" | "top-right"
const STORAGE_KEY = "help-button-corner"
const OFFSET = 16
const DRAG_THRESHOLD = 5
const cornerStyles: Record<Corner, React.CSSProperties> = {
"bottom-right": { bottom: OFFSET, right: OFFSET, top: "auto", left: "auto" },
"top-right": { top: OFFSET, right: OFFSET, bottom: "auto", left: "auto" },
}
function getStoredCorner(): Corner {
try {
const stored = localStorage.getItem(STORAGE_KEY)
if (stored && stored in cornerStyles)
return stored as Corner
}
catch {}
return "bottom-right"
}
function getNearestCorner(_x: number, y: number): Corner {
const cy = window.innerHeight / 2
return y >= cy ? "bottom-right" : "top-right"
}
export function HelpButton() {
const [corner, setCorner] = useState<Corner>(getStoredCorner)
const [dragging, setDragging] = useState(false)
const [dragPos, setDragPos] = useState<{ x: number, y: number } | null>(null)
const hasDraggedRef = useRef(false)
const handleMouseDown = useCallback((e: React.MouseEvent) => {
e.preventDefault()
const startX = e.clientX
const startY = e.clientY
hasDraggedRef.current = false
function onMouseMove(ev: MouseEvent) {
const dx = ev.clientX - startX
const dy = ev.clientY - startY
if (!hasDraggedRef.current && Math.sqrt(dx * dx + dy * dy) < DRAG_THRESHOLD)
return
hasDraggedRef.current = true
setDragging(true)
setDragPos({ x: ev.clientX - 20, y: ev.clientY - 20 })
}
function onMouseUp(ev: MouseEvent) {
document.removeEventListener("mousemove", onMouseMove)
document.removeEventListener("mouseup", onMouseUp)
if (hasDraggedRef.current) {
const newCorner = getNearestCorner(ev.clientX, ev.clientY)
setCorner(newCorner)
localStorage.setItem(STORAGE_KEY, newCorner)
}
else {
window.open("https://github.com/mengxi-ream/read-frog/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen", "_blank")
}
hasDraggedRef.current = false
setDragging(false)
setDragPos(null)
}
document.addEventListener("mousemove", onMouseMove)
document.addEventListener("mouseup", onMouseUp)
}, [])
const style: React.CSSProperties = dragging && dragPos
? { position: "fixed", left: dragPos.x, top: dragPos.y, right: "auto", bottom: "auto", transition: "none" }
: { position: "fixed", ...cornerStyles[corner], transition: "all 300ms ease" }
return (
<button
type="button"
onMouseDown={handleMouseDown}
className={cn(
"z-50 flex size-10 cursor-grab items-center justify-center rounded-full",
"bg-muted-foreground/20 text-muted-foreground shadow-md",
"opacity-60 hover:opacity-100",
dragging && "cursor-grabbing opacity-100",
)}
style={style}
>
<Icon icon="tabler:message-2" className="size-5" />
</button>
)
}
+2 -2
View File
@@ -1,12 +1,12 @@
import type { Config } from "@/types/config/config"
import { QueryClientProvider } from "@tanstack/react-query"
import { Agentation } from "agentation"
import { Provider as JotaiProvider } from "jotai"
import { useHydrateAtoms } from "jotai/utils"
import * as React from "react"
import ReactDOM from "react-dom/client"
import { HashRouter } from "react-router"
import FrogToast from "@/components/frog-toast"
import { HelpButton } from "@/components/help-button"
import { ChartThemeProvider } from "@/components/providers/chart-theme-provider"
import { ThemeProvider } from "@/components/providers/theme-provider"
import { SidebarProvider } from "@/components/ui/base-ui/sidebar"
@@ -51,7 +51,7 @@ async function initApp() {
<AppSidebar />
<App />
<FrogToast />
{import.meta.env.DEV && <Agentation />}
<HelpButton />
</ChartThemeProvider>
</ThemeProvider>
</SidebarProvider>
+2 -2
View File
@@ -1,11 +1,11 @@
import type { Config } from "@/types/config/config"
import { QueryClientProvider } from "@tanstack/react-query"
import { Agentation } from "agentation"
import { Provider as JotaiProvider } from "jotai"
import { useHydrateAtoms } from "jotai/utils"
import * as React from "react"
import ReactDOM from "react-dom/client"
import FrogToast from "@/components/frog-toast"
import { HelpButton } from "@/components/help-button"
import { ThemeProvider } from "@/components/providers/theme-provider"
import { TooltipProvider } from "@/components/ui/base-ui/tooltip"
import { configAtom } from "@/utils/atoms/config"
@@ -39,8 +39,8 @@ async function initApp() {
<ThemeProvider>
<TooltipProvider>
<App />
{import.meta.env.DEV && <Agentation />}
<FrogToast />
<HelpButton />
</TooltipProvider>
</ThemeProvider>
</HydrateAtoms>