mirror of
https://github.com/EthanMarti/infio-copilot.git
synced 2026-06-11 07:21:00 +00:00
update chatview
This commit is contained in:
75
src/utils/clipboard.ts
Normal file
75
src/utils/clipboard.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react"
|
||||
|
||||
/**
|
||||
* Options for copying text to clipboard
|
||||
*/
|
||||
interface CopyOptions {
|
||||
/** Duration in ms to show success feedback (default: 2000) */
|
||||
feedbackDuration?: number
|
||||
/** Optional callback when copy succeeds */
|
||||
onSuccess?: () => void
|
||||
/** Optional callback when copy fails */
|
||||
onError?: (error: Error) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy text to clipboard with error handling
|
||||
*/
|
||||
export const copyToClipboard = async (text: string, options?: CopyOptions): Promise<boolean> => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text)
|
||||
options?.onSuccess?.()
|
||||
return true
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error("Failed to copy to clipboard")
|
||||
options?.onError?.(err)
|
||||
console.error("Failed to copy to clipboard:", err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* React hook for managing clipboard copy state with feedback
|
||||
*/
|
||||
export const useCopyToClipboard = (feedbackDuration = 2000) => {
|
||||
const [showCopyFeedback, setShowCopyFeedback] = useState(false)
|
||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null)
|
||||
|
||||
const copyWithFeedback = useCallback(
|
||||
async (text: string, e?: React.MouseEvent) => {
|
||||
e?.stopPropagation()
|
||||
|
||||
// Clear any existing timeout
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current)
|
||||
}
|
||||
|
||||
const success = await copyToClipboard(text, {
|
||||
onSuccess: () => {
|
||||
setShowCopyFeedback(true)
|
||||
timeoutRef.current = setTimeout(() => {
|
||||
setShowCopyFeedback(false)
|
||||
timeoutRef.current = null
|
||||
}, feedbackDuration)
|
||||
},
|
||||
})
|
||||
|
||||
return success
|
||||
},
|
||||
[feedbackDuration],
|
||||
)
|
||||
|
||||
// Cleanup timeout on unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
return {
|
||||
showCopyFeedback,
|
||||
copyWithFeedback,
|
||||
}
|
||||
}
|
||||
42
src/utils/useDebounceEffect.ts
Normal file
42
src/utils/useDebounceEffect.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { useEffect, useRef } from "react"
|
||||
|
||||
type VoidFn = () => void
|
||||
|
||||
/**
|
||||
* Runs `effectRef.current()` after `delay` ms whenever any of the `deps` change,
|
||||
* but cancels/re-schedules if they change again before the delay.
|
||||
*/
|
||||
export function useDebounceEffect(effect: VoidFn, delay: number, deps: any[]) {
|
||||
const callbackRef = useRef<VoidFn>(effect)
|
||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null)
|
||||
|
||||
// Keep callbackRef current
|
||||
useEffect(() => {
|
||||
callbackRef.current = effect
|
||||
}, [effect])
|
||||
|
||||
useEffect(() => {
|
||||
// Clear any queued call
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current)
|
||||
}
|
||||
|
||||
// Schedule a new call
|
||||
timeoutRef.current = setTimeout(() => {
|
||||
// always call the *latest* version of effect
|
||||
callbackRef.current()
|
||||
}, delay)
|
||||
|
||||
// Cleanup on unmount or next effect
|
||||
return () => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current)
|
||||
}
|
||||
}
|
||||
|
||||
// We want to re‐schedule if any item in `deps` changed,
|
||||
// or if `delay` changed.
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [delay, ...deps])
|
||||
}
|
||||
Reference in New Issue
Block a user