mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-01-07 08:42:28 +08:00
* feat: add URL content extraction for AI diagram generation * Changes made as recommended by Claude: 1. Added a request timeout to prevent server resources from being tied up (route.ts) 2. Implemented runtime validation for the API response shape (url-utils.ts) 3. Removed hardcoded English error messages and replaced them with localized strings (url-input-dialog.tsx) 4. Fixed the incorrect i18n namespace (changed from pdf.* to url.*) (url-input-dialog.tsx and en/ja/zh.json) * chore: restore package.json and package-lock.json * fix: use i18n strings for URL dialog error messages --------- Co-authored-by: dayuan.jiang <jdy.toh@gmail.com>
117 lines
3.4 KiB
TypeScript
117 lines
3.4 KiB
TypeScript
"use client"
|
|
|
|
import { Link, Loader2 } from "lucide-react"
|
|
import { useState } from "react"
|
|
import { Button } from "@/components/ui/button"
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from "@/components/ui/dialog"
|
|
import { Input } from "@/components/ui/input"
|
|
import { useDictionary } from "@/hooks/use-dictionary"
|
|
|
|
interface UrlInputDialogProps {
|
|
open: boolean
|
|
onOpenChange: (open: boolean) => void
|
|
onSubmit: (url: string) => void
|
|
isExtracting: boolean
|
|
}
|
|
|
|
export function UrlInputDialog({
|
|
open,
|
|
onOpenChange,
|
|
onSubmit,
|
|
isExtracting,
|
|
}: UrlInputDialogProps) {
|
|
const dict = useDictionary()
|
|
const [url, setUrl] = useState("")
|
|
const [error, setError] = useState("")
|
|
|
|
const handleSubmit = () => {
|
|
setError("")
|
|
|
|
if (!url.trim()) {
|
|
setError(dict.url.enterUrl)
|
|
return
|
|
}
|
|
|
|
try {
|
|
new URL(url)
|
|
} catch {
|
|
setError(dict.url.invalidFormat)
|
|
return
|
|
}
|
|
|
|
onSubmit(url.trim())
|
|
}
|
|
|
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
if (e.key === "Enter" && !isExtracting) {
|
|
e.preventDefault()
|
|
handleSubmit()
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent className="sm:max-w-md">
|
|
<DialogHeader>
|
|
<DialogTitle>{dict.url.title}</DialogTitle>
|
|
<DialogDescription>
|
|
{dict.url.description}
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<div className="space-y-4">
|
|
<div className="space-y-2">
|
|
<Input
|
|
value={url}
|
|
onChange={(e) => {
|
|
setUrl(e.target.value)
|
|
setError("")
|
|
}}
|
|
onKeyDown={handleKeyDown}
|
|
placeholder="https://example.com/article"
|
|
disabled={isExtracting}
|
|
autoFocus
|
|
/>
|
|
{error && (
|
|
<p className="text-sm text-destructive">{error}</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<DialogFooter>
|
|
<Button
|
|
variant="outline"
|
|
onClick={() => onOpenChange(false)}
|
|
disabled={isExtracting}
|
|
>
|
|
{dict.url.Cancel}
|
|
</Button>
|
|
<Button
|
|
onClick={handleSubmit}
|
|
disabled={isExtracting || !url.trim()}
|
|
>
|
|
{isExtracting ? (
|
|
<>
|
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
{dict.url.Extracting}
|
|
</>
|
|
) : (
|
|
<>
|
|
<Link className="mr-2 h-4 w-4" />
|
|
{dict.url.extract}
|
|
</>
|
|
)}
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|