mirror of
https://github.com/mengxi-ream/read-frog.git
synced 2026-09-01 05:28:49 +08:00
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:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@read-frog/extension": patch
|
||||
---
|
||||
|
||||
feat: replace agentation dev toolbar with draggable help button
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
Generated
-19
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user