优化事件处理
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
import { useTranslations } from "next-intl"
|
||||
import { acpListAgents } from "@/lib/tauri"
|
||||
import { disposeTauriListener } from "@/lib/tauri-listener"
|
||||
import type { AgentType, AcpAgentInfo } from "@/lib/types"
|
||||
import { AGENT_LABELS } from "@/lib/types"
|
||||
import { AgentIcon } from "@/components/agent-icon"
|
||||
@@ -100,7 +101,7 @@ export function AgentSelector({
|
||||
)
|
||||
.then((dispose) => {
|
||||
if (cancelled) {
|
||||
dispose()
|
||||
disposeTauriListener(dispose, "AgentSelector.agentsUpdated")
|
||||
return
|
||||
}
|
||||
unlisten = dispose
|
||||
@@ -112,9 +113,7 @@ export function AgentSelector({
|
||||
return () => {
|
||||
cancelled = true
|
||||
window.removeEventListener("focus", onWindowFocus)
|
||||
if (unlisten) {
|
||||
unlisten()
|
||||
}
|
||||
disposeTauriListener(unlisten, "AgentSelector.agentsUpdated")
|
||||
}
|
||||
}, [defaultAgentType])
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||
import { TauriEvent } from "@tauri-apps/api/event"
|
||||
import { getCurrentWebview } from "@tauri-apps/api/webview"
|
||||
import { open } from "@tauri-apps/plugin-dialog"
|
||||
import Image from "next/image"
|
||||
@@ -10,6 +11,7 @@ import { Textarea } from "@/components/ui/textarea"
|
||||
import { FileSearch, Plus, Send, Square, X } from "lucide-react"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { readFileBase64 } from "@/lib/tauri"
|
||||
import { disposeTauriListener } from "@/lib/tauri-listener"
|
||||
import type {
|
||||
AvailableCommandInfo,
|
||||
PromptCapabilitiesInfo,
|
||||
@@ -686,46 +688,109 @@ export function MessageInput({
|
||||
}, [appendResourceAttachments, attachmentTabId])
|
||||
|
||||
useEffect(() => {
|
||||
let unlisten: (() => void) | null = null
|
||||
let cancelled = false
|
||||
const unlisteners: Array<() => void | Promise<void>> = []
|
||||
|
||||
getCurrentWebview()
|
||||
.onDragDropEvent((event) => {
|
||||
const host = containerRef.current
|
||||
if (!host) return
|
||||
const payload = event.payload
|
||||
if (payload.type === "leave") {
|
||||
setIsDragActive(false)
|
||||
return
|
||||
const cleanupListeners = () => {
|
||||
for (const fn of unlisteners.splice(0)) {
|
||||
disposeTauriListener(fn, "MessageInput.dragDrop")
|
||||
}
|
||||
}
|
||||
|
||||
type DragDropPayload =
|
||||
| {
|
||||
type: "enter" | "drop"
|
||||
paths: string[]
|
||||
position: { x: number; y: number }
|
||||
}
|
||||
const inside = pointWithinElement(payload.position, host)
|
||||
if (payload.type === "drop") {
|
||||
setIsDragActive(false)
|
||||
if (Date.now() - lastDomDropAtRef.current < 250) return
|
||||
if (!inside || disabled || isPrompting) return
|
||||
void appendPathsFromDrop(payload.paths).catch((error) => {
|
||||
console.error("[MessageInput] drag drop paths failed:", error)
|
||||
| {
|
||||
type: "over"
|
||||
position: { x: number; y: number }
|
||||
}
|
||||
| { type: "leave" }
|
||||
|
||||
const handlePayload = (payload: DragDropPayload) => {
|
||||
const host = containerRef.current
|
||||
if (!host) return
|
||||
if (payload.type === "leave") {
|
||||
setIsDragActive(false)
|
||||
return
|
||||
}
|
||||
const inside = pointWithinElement(payload.position, host)
|
||||
if (payload.type === "drop") {
|
||||
setIsDragActive(false)
|
||||
if (Date.now() - lastDomDropAtRef.current < 250) return
|
||||
if (!inside || disabled || isPrompting) return
|
||||
void appendPathsFromDrop(payload.paths).catch((error) => {
|
||||
console.error("[MessageInput] drag drop paths failed:", error)
|
||||
})
|
||||
return
|
||||
}
|
||||
setIsDragActive(inside && !disabled && !isPrompting)
|
||||
}
|
||||
|
||||
const setup = async () => {
|
||||
const webview = getCurrentWebview()
|
||||
try {
|
||||
const unlistenEnter = await webview.listen<{
|
||||
paths: string[]
|
||||
position: { x: number; y: number }
|
||||
}>(TauriEvent.DRAG_ENTER, (event) => {
|
||||
if (cancelled) return
|
||||
handlePayload({
|
||||
type: "enter",
|
||||
paths: event.payload.paths,
|
||||
position: event.payload.position,
|
||||
})
|
||||
return
|
||||
}
|
||||
setIsDragActive(inside && !disabled && !isPrompting)
|
||||
})
|
||||
.then((fn) => {
|
||||
if (cancelled) {
|
||||
fn()
|
||||
} else {
|
||||
unlisten = fn
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
})
|
||||
unlisteners.push(unlistenEnter)
|
||||
|
||||
const unlistenOver = await webview.listen<{
|
||||
position: { x: number; y: number }
|
||||
}>(TauriEvent.DRAG_OVER, (event) => {
|
||||
if (cancelled) return
|
||||
handlePayload({
|
||||
type: "over",
|
||||
position: event.payload.position,
|
||||
})
|
||||
})
|
||||
unlisteners.push(unlistenOver)
|
||||
|
||||
const unlistenDrop = await webview.listen<{
|
||||
paths: string[]
|
||||
position: { x: number; y: number }
|
||||
}>(TauriEvent.DRAG_DROP, (event) => {
|
||||
if (cancelled) return
|
||||
handlePayload({
|
||||
type: "drop",
|
||||
paths: event.payload.paths,
|
||||
position: event.payload.position,
|
||||
})
|
||||
})
|
||||
unlisteners.push(unlistenDrop)
|
||||
|
||||
const unlistenLeave = await webview.listen(
|
||||
TauriEvent.DRAG_LEAVE,
|
||||
() => {
|
||||
if (cancelled) return
|
||||
handlePayload({ type: "leave" })
|
||||
}
|
||||
)
|
||||
unlisteners.push(unlistenLeave)
|
||||
} catch {
|
||||
// Ignore non-Tauri environments.
|
||||
})
|
||||
} finally {
|
||||
if (cancelled) {
|
||||
cleanupListeners()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup()
|
||||
|
||||
return () => {
|
||||
cancelled = true
|
||||
if (unlisten) {
|
||||
unlisten()
|
||||
}
|
||||
cleanupListeners()
|
||||
}
|
||||
}, [appendPathsFromDrop, disabled, isPrompting])
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
updateConversationStatus,
|
||||
updateConversationExternalId,
|
||||
} from "@/lib/tauri"
|
||||
import { disposeTauriListener } from "@/lib/tauri-listener"
|
||||
import { AgentSelector } from "@/components/chat/agent-selector"
|
||||
import { LiveMessageBlock } from "@/components/chat/live-message-block"
|
||||
import { AgentPlanOverlay } from "@/components/chat/agent-plan-overlay"
|
||||
@@ -448,7 +449,7 @@ export function WelcomeInputPanel({
|
||||
)
|
||||
.then((dispose) => {
|
||||
if (cancelled) {
|
||||
dispose()
|
||||
disposeTauriListener(dispose, "WelcomeInputPanel.agentsUpdated")
|
||||
return
|
||||
}
|
||||
unlisten = dispose
|
||||
@@ -463,9 +464,7 @@ export function WelcomeInputPanel({
|
||||
clearTimeout(agentStatusRefreshTimerRef.current)
|
||||
agentStatusRefreshTimerRef.current = null
|
||||
}
|
||||
if (unlisten) {
|
||||
unlisten()
|
||||
}
|
||||
disposeTauriListener(unlisten, "WelcomeInputPanel.agentsUpdated")
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user