mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-01-08 01:02:29 +08:00
Compare commits
3 Commits
v0.3.0
...
fix/bug-fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6931077f4 | ||
|
|
46567cb0b8 | ||
|
|
9f77199272 |
32
app/api/verify-access-code/route.ts
Normal file
32
app/api/verify-access-code/route.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
export async function POST(req: Request) {
|
||||||
|
const accessCodes =
|
||||||
|
process.env.ACCESS_CODE_LIST?.split(",")
|
||||||
|
.map((code) => code.trim())
|
||||||
|
.filter(Boolean) || []
|
||||||
|
|
||||||
|
// If no access codes configured, verification always passes
|
||||||
|
if (accessCodes.length === 0) {
|
||||||
|
return Response.json({
|
||||||
|
valid: true,
|
||||||
|
message: "No access code required",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessCodeHeader = req.headers.get("x-access-code")
|
||||||
|
|
||||||
|
if (!accessCodeHeader) {
|
||||||
|
return Response.json(
|
||||||
|
{ valid: false, message: "Access code is required" },
|
||||||
|
{ status: 401 },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!accessCodes.includes(accessCodeHeader)) {
|
||||||
|
return Response.json(
|
||||||
|
{ valid: false, message: "Invalid access code" },
|
||||||
|
{ status: 401 },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.json({ valid: true, message: "Access code is valid" })
|
||||||
|
}
|
||||||
13
app/page.tsx
13
app/page.tsx
@@ -3,6 +3,7 @@ import { useEffect, useRef, useState } from "react"
|
|||||||
import { DrawIoEmbed } from "react-drawio"
|
import { DrawIoEmbed } from "react-drawio"
|
||||||
import type { ImperativePanelHandle } from "react-resizable-panels"
|
import type { ImperativePanelHandle } from "react-resizable-panels"
|
||||||
import ChatPanel from "@/components/chat-panel"
|
import ChatPanel from "@/components/chat-panel"
|
||||||
|
import { STORAGE_CLOSE_PROTECTION_KEY } from "@/components/settings-dialog"
|
||||||
import {
|
import {
|
||||||
ResizableHandle,
|
ResizableHandle,
|
||||||
ResizablePanel,
|
ResizablePanel,
|
||||||
@@ -21,6 +22,13 @@ export default function Home() {
|
|||||||
}
|
}
|
||||||
return "min"
|
return "min"
|
||||||
})
|
})
|
||||||
|
const [closeProtection, setCloseProtection] = useState(() => {
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
const saved = localStorage.getItem(STORAGE_CLOSE_PROTECTION_KEY)
|
||||||
|
return saved !== "false" // Default to true
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
const chatPanelRef = useRef<ImperativePanelHandle>(null)
|
const chatPanelRef = useRef<ImperativePanelHandle>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -61,6 +69,8 @@ export default function Home() {
|
|||||||
// Show confirmation dialog when user tries to leave the page
|
// Show confirmation dialog when user tries to leave the page
|
||||||
// This helps prevent accidental navigation from browser back gestures
|
// This helps prevent accidental navigation from browser back gestures
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!closeProtection) return
|
||||||
|
|
||||||
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
|
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
return ""
|
return ""
|
||||||
@@ -69,7 +79,7 @@ export default function Home() {
|
|||||||
window.addEventListener("beforeunload", handleBeforeUnload)
|
window.addEventListener("beforeunload", handleBeforeUnload)
|
||||||
return () =>
|
return () =>
|
||||||
window.removeEventListener("beforeunload", handleBeforeUnload)
|
window.removeEventListener("beforeunload", handleBeforeUnload)
|
||||||
}, [])
|
}, [closeProtection])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-screen bg-background relative overflow-hidden">
|
<div className="h-screen bg-background relative overflow-hidden">
|
||||||
@@ -127,6 +137,7 @@ export default function Home() {
|
|||||||
setDrawioUi(newTheme)
|
setDrawioUi(newTheme)
|
||||||
}}
|
}}
|
||||||
isMobile={isMobile}
|
isMobile={isMobile}
|
||||||
|
onCloseProtectionChange={setCloseProtection}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ResizablePanel>
|
</ResizablePanel>
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ function EditDiffDisplay({ edits }: { edits: EditPair[] }) {
|
|||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{edits.map((edit, index) => (
|
{edits.map((edit, index) => (
|
||||||
<div
|
<div
|
||||||
key={`${edit.search.slice(0, 50)}-${edit.replace.slice(0, 50)}-${index}`}
|
key={`${(edit.search || "").slice(0, 50)}-${(edit.replace || "").slice(0, 50)}-${index}`}
|
||||||
className="rounded-lg border border-border/50 overflow-hidden bg-background/50"
|
className="rounded-lg border border-border/50 overflow-hidden bg-background/50"
|
||||||
>
|
>
|
||||||
<div className="px-3 py-1.5 bg-muted/40 border-b border-border/30 flex items-center gap-2">
|
<div className="px-3 py-1.5 bg-muted/40 border-b border-border/30 flex items-center gap-2">
|
||||||
@@ -177,7 +177,10 @@ export function ChatMessageDisplay({
|
|||||||
const currentXml = xml || ""
|
const currentXml = xml || ""
|
||||||
const convertedXml = convertToLegalXml(currentXml)
|
const convertedXml = convertToLegalXml(currentXml)
|
||||||
if (convertedXml !== previousXML.current) {
|
if (convertedXml !== previousXML.current) {
|
||||||
const replacedXML = replaceNodes(chartXML, convertedXml)
|
// If chartXML is empty, use the converted XML directly
|
||||||
|
const replacedXML = chartXML
|
||||||
|
? replaceNodes(chartXML, convertedXml)
|
||||||
|
: convertedXml
|
||||||
|
|
||||||
const validationError = validateMxCellStructure(replacedXML)
|
const validationError = validateMxCellStructure(replacedXML)
|
||||||
if (!validationError) {
|
if (!validationError) {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ interface ChatPanelProps {
|
|||||||
drawioUi: "min" | "sketch"
|
drawioUi: "min" | "sketch"
|
||||||
onToggleDrawioUi: () => void
|
onToggleDrawioUi: () => void
|
||||||
isMobile?: boolean
|
isMobile?: boolean
|
||||||
|
onCloseProtectionChange?: (enabled: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ChatPanel({
|
export default function ChatPanel({
|
||||||
@@ -39,6 +40,7 @@ export default function ChatPanel({
|
|||||||
drawioUi,
|
drawioUi,
|
||||||
onToggleDrawioUi,
|
onToggleDrawioUi,
|
||||||
isMobile = false,
|
isMobile = false,
|
||||||
|
onCloseProtectionChange,
|
||||||
}: ChatPanelProps) {
|
}: ChatPanelProps) {
|
||||||
const {
|
const {
|
||||||
loadDiagram: onDisplayChart,
|
loadDiagram: onDisplayChart,
|
||||||
@@ -345,6 +347,7 @@ Please retry with an adjusted search pattern or use display_diagram if retries a
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Now send the message after state is guaranteed to be updated
|
// Now send the message after state is guaranteed to be updated
|
||||||
|
const accessCode = localStorage.getItem(STORAGE_ACCESS_CODE_KEY) || ""
|
||||||
sendMessage(
|
sendMessage(
|
||||||
{ parts: userParts },
|
{ parts: userParts },
|
||||||
{
|
{
|
||||||
@@ -352,6 +355,9 @@ Please retry with an adjusted search pattern or use display_diagram if retries a
|
|||||||
xml: savedXml,
|
xml: savedXml,
|
||||||
sessionId,
|
sessionId,
|
||||||
},
|
},
|
||||||
|
headers: {
|
||||||
|
"x-access-code": accessCode,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -402,6 +408,7 @@ Please retry with an adjusted search pattern or use display_diagram if retries a
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Now send the edited message after state is guaranteed to be updated
|
// Now send the edited message after state is guaranteed to be updated
|
||||||
|
const accessCode = localStorage.getItem(STORAGE_ACCESS_CODE_KEY) || ""
|
||||||
sendMessage(
|
sendMessage(
|
||||||
{ parts: newParts },
|
{ parts: newParts },
|
||||||
{
|
{
|
||||||
@@ -409,6 +416,9 @@ Please retry with an adjusted search pattern or use display_diagram if retries a
|
|||||||
xml: savedXml,
|
xml: savedXml,
|
||||||
sessionId,
|
sessionId,
|
||||||
},
|
},
|
||||||
|
headers: {
|
||||||
|
"x-access-code": accessCode,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -497,19 +507,17 @@ Please retry with an adjusted search pattern or use display_diagram if retries a
|
|||||||
className={`${isMobile ? "w-4 h-4" : "w-5 h-5"}`}
|
className={`${isMobile ? "w-4 h-4" : "w-5 h-5"}`}
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
{accessCodeRequired && (
|
<ButtonWithTooltip
|
||||||
<ButtonWithTooltip
|
tooltipContent="Settings"
|
||||||
tooltipContent="Settings"
|
variant="ghost"
|
||||||
variant="ghost"
|
size="icon"
|
||||||
size="icon"
|
onClick={() => setShowSettingsDialog(true)}
|
||||||
onClick={() => setShowSettingsDialog(true)}
|
className="hover:bg-accent"
|
||||||
className="hover:bg-accent"
|
>
|
||||||
>
|
<Settings
|
||||||
<Settings
|
className={`${isMobile ? "h-4 w-4" : "h-5 w-5"} text-muted-foreground`}
|
||||||
className={`${isMobile ? "h-4 w-4" : "h-5 w-5"} text-muted-foreground`}
|
/>
|
||||||
/>
|
</ButtonWithTooltip>
|
||||||
</ButtonWithTooltip>
|
|
||||||
)}
|
|
||||||
{!isMobile && (
|
{!isMobile && (
|
||||||
<ButtonWithTooltip
|
<ButtonWithTooltip
|
||||||
tooltipContent="Hide chat panel (Ctrl+B)"
|
tooltipContent="Hide chat panel (Ctrl+B)"
|
||||||
@@ -570,6 +578,7 @@ Please retry with an adjusted search pattern or use display_diagram if retries a
|
|||||||
<SettingsDialog
|
<SettingsDialog
|
||||||
open={showSettingsDialog}
|
open={showSettingsDialog}
|
||||||
onOpenChange={setShowSettingsDialog}
|
onOpenChange={setShowSettingsDialog}
|
||||||
|
onCloseProtectionChange={onCloseProtectionChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,28 +11,77 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/components/ui/dialog"
|
} from "@/components/ui/dialog"
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
|
import { Label } from "@/components/ui/label"
|
||||||
|
import { Switch } from "@/components/ui/switch"
|
||||||
|
|
||||||
interface SettingsDialogProps {
|
interface SettingsDialogProps {
|
||||||
open: boolean
|
open: boolean
|
||||||
onOpenChange: (open: boolean) => void
|
onOpenChange: (open: boolean) => void
|
||||||
|
onCloseProtectionChange?: (enabled: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const STORAGE_ACCESS_CODE_KEY = "next-ai-draw-io-access-code"
|
export const STORAGE_ACCESS_CODE_KEY = "next-ai-draw-io-access-code"
|
||||||
|
export const STORAGE_CLOSE_PROTECTION_KEY = "next-ai-draw-io-close-protection"
|
||||||
|
|
||||||
export function SettingsDialog({ open, onOpenChange }: SettingsDialogProps) {
|
export function SettingsDialog({
|
||||||
|
open,
|
||||||
|
onOpenChange,
|
||||||
|
onCloseProtectionChange,
|
||||||
|
}: SettingsDialogProps) {
|
||||||
const [accessCode, setAccessCode] = useState("")
|
const [accessCode, setAccessCode] = useState("")
|
||||||
|
const [closeProtection, setCloseProtection] = useState(true)
|
||||||
|
const [isVerifying, setIsVerifying] = useState(false)
|
||||||
|
const [error, setError] = useState("")
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
const storedCode =
|
const storedCode =
|
||||||
localStorage.getItem(STORAGE_ACCESS_CODE_KEY) || ""
|
localStorage.getItem(STORAGE_ACCESS_CODE_KEY) || ""
|
||||||
setAccessCode(storedCode)
|
setAccessCode(storedCode)
|
||||||
|
|
||||||
|
const storedCloseProtection = localStorage.getItem(
|
||||||
|
STORAGE_CLOSE_PROTECTION_KEY,
|
||||||
|
)
|
||||||
|
// Default to true if not set
|
||||||
|
setCloseProtection(storedCloseProtection !== "false")
|
||||||
|
setError("")
|
||||||
}
|
}
|
||||||
}, [open])
|
}, [open])
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = async () => {
|
||||||
localStorage.setItem(STORAGE_ACCESS_CODE_KEY, accessCode.trim())
|
setError("")
|
||||||
onOpenChange(false)
|
setIsVerifying(true)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Verify access code with server
|
||||||
|
const response = await fetch("/api/verify-access-code", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"x-access-code": accessCode.trim(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const data = await response.json()
|
||||||
|
|
||||||
|
if (!data.valid) {
|
||||||
|
setError(data.message || "Invalid access code")
|
||||||
|
setIsVerifying(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save settings only if verification passes
|
||||||
|
localStorage.setItem(STORAGE_ACCESS_CODE_KEY, accessCode.trim())
|
||||||
|
localStorage.setItem(
|
||||||
|
STORAGE_CLOSE_PROTECTION_KEY,
|
||||||
|
closeProtection.toString(),
|
||||||
|
)
|
||||||
|
onCloseProtectionChange?.(closeProtection)
|
||||||
|
onOpenChange(false)
|
||||||
|
} catch {
|
||||||
|
setError("Failed to verify access code")
|
||||||
|
} finally {
|
||||||
|
setIsVerifying(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||||
@@ -67,6 +116,26 @@ export function SettingsDialog({ open, onOpenChange }: SettingsDialogProps) {
|
|||||||
<p className="text-[0.8rem] text-muted-foreground">
|
<p className="text-[0.8rem] text-muted-foreground">
|
||||||
Required if the server has enabled access control.
|
Required if the server has enabled access control.
|
||||||
</p>
|
</p>
|
||||||
|
{error && (
|
||||||
|
<p className="text-[0.8rem] text-destructive">
|
||||||
|
{error}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<Label htmlFor="close-protection">
|
||||||
|
Close Protection
|
||||||
|
</Label>
|
||||||
|
<p className="text-[0.8rem] text-muted-foreground">
|
||||||
|
Show confirmation when leaving the page.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Switch
|
||||||
|
id="close-protection"
|
||||||
|
checked={closeProtection}
|
||||||
|
onCheckedChange={setCloseProtection}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
@@ -76,7 +145,9 @@ export function SettingsDialog({ open, onOpenChange }: SettingsDialogProps) {
|
|||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleSave}>Save</Button>
|
<Button onClick={handleSave} disabled={isVerifying}>
|
||||||
|
{isVerifying ? "Verifying..." : "Save"}
|
||||||
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
24
components/ui/label.tsx
Normal file
24
components/ui/label.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Label({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
||||||
|
return (
|
||||||
|
<LabelPrimitive.Root
|
||||||
|
data-slot="label"
|
||||||
|
className={cn(
|
||||||
|
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Label }
|
||||||
31
components/ui/switch.tsx
Normal file
31
components/ui/switch.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as SwitchPrimitive from "@radix-ui/react-switch"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Switch({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof SwitchPrimitive.Root>) {
|
||||||
|
return (
|
||||||
|
<SwitchPrimitive.Root
|
||||||
|
data-slot="switch"
|
||||||
|
className={cn(
|
||||||
|
"peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<SwitchPrimitive.Thumb
|
||||||
|
data-slot="switch-thumb"
|
||||||
|
className={cn(
|
||||||
|
"bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</SwitchPrimitive.Root>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Switch }
|
||||||
239
package-lock.json
generated
239
package-lock.json
generated
@@ -24,9 +24,11 @@
|
|||||||
"@openrouter/ai-sdk-provider": "^1.2.3",
|
"@openrouter/ai-sdk-provider": "^1.2.3",
|
||||||
"@opentelemetry/sdk-trace-node": "^2.2.0",
|
"@opentelemetry/sdk-trace-node": "^2.2.0",
|
||||||
"@radix-ui/react-dialog": "^1.1.6",
|
"@radix-ui/react-dialog": "^1.1.6",
|
||||||
|
"@radix-ui/react-label": "^2.1.8",
|
||||||
"@radix-ui/react-scroll-area": "^1.2.3",
|
"@radix-ui/react-scroll-area": "^1.2.3",
|
||||||
"@radix-ui/react-select": "^2.2.6",
|
"@radix-ui/react-select": "^2.2.6",
|
||||||
"@radix-ui/react-slot": "^1.1.2",
|
"@radix-ui/react-slot": "^1.1.2",
|
||||||
|
"@radix-ui/react-switch": "^1.2.6",
|
||||||
"@radix-ui/react-tooltip": "^1.1.8",
|
"@radix-ui/react-tooltip": "^1.1.8",
|
||||||
"@vercel/analytics": "^1.5.0",
|
"@vercel/analytics": "^1.5.0",
|
||||||
"@xmldom/xmldom": "^0.9.8",
|
"@xmldom/xmldom": "^0.9.8",
|
||||||
@@ -3400,6 +3402,85 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-label": {
|
||||||
|
"version": "2.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.8.tgz",
|
||||||
|
"integrity": "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-primitive": "2.1.4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-compose-refs": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz",
|
||||||
|
"integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.2.4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz",
|
||||||
|
"integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-popper": {
|
"node_modules/@radix-ui/react-popper": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.2.tgz",
|
||||||
@@ -3989,6 +4070,164 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-switch": {
|
||||||
|
"version": "1.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz",
|
||||||
|
"integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.3",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
|
"@radix-ui/react-context": "1.1.2",
|
||||||
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||||
|
"@radix-ui/react-use-previous": "1.1.1",
|
||||||
|
"@radix-ui/react-use-size": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-switch/node_modules/@radix-ui/primitive": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-switch/node_modules/@radix-ui/react-compose-refs": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-switch/node_modules/@radix-ui/react-context": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-switch/node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.2.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-switch/node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-switch/node_modules/@radix-ui/react-use-controllable-state": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-effect-event": "0.0.2",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-switch/node_modules/@radix-ui/react-use-layout-effect": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-switch/node_modules/@radix-ui/react-use-size": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-tooltip": {
|
"node_modules/@radix-ui/react-tooltip": {
|
||||||
"version": "1.1.8",
|
"version": "1.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.8.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.8.tgz",
|
||||||
|
|||||||
@@ -28,9 +28,11 @@
|
|||||||
"@openrouter/ai-sdk-provider": "^1.2.3",
|
"@openrouter/ai-sdk-provider": "^1.2.3",
|
||||||
"@opentelemetry/sdk-trace-node": "^2.2.0",
|
"@opentelemetry/sdk-trace-node": "^2.2.0",
|
||||||
"@radix-ui/react-dialog": "^1.1.6",
|
"@radix-ui/react-dialog": "^1.1.6",
|
||||||
|
"@radix-ui/react-label": "^2.1.8",
|
||||||
"@radix-ui/react-scroll-area": "^1.2.3",
|
"@radix-ui/react-scroll-area": "^1.2.3",
|
||||||
"@radix-ui/react-select": "^2.2.6",
|
"@radix-ui/react-select": "^2.2.6",
|
||||||
"@radix-ui/react-slot": "^1.1.2",
|
"@radix-ui/react-slot": "^1.1.2",
|
||||||
|
"@radix-ui/react-switch": "^1.2.6",
|
||||||
"@radix-ui/react-tooltip": "^1.1.8",
|
"@radix-ui/react-tooltip": "^1.1.8",
|
||||||
"@vercel/analytics": "^1.5.0",
|
"@vercel/analytics": "^1.5.0",
|
||||||
"@xmldom/xmldom": "^0.9.8",
|
"@xmldom/xmldom": "^0.9.8",
|
||||||
|
|||||||
Reference in New Issue
Block a user