diff --git a/src/client/components/chat-ui/ChatInput.tsx b/src/client/components/chat-ui/ChatInput.tsx index dcd732008..cdb1a79ff 100644 --- a/src/client/components/chat-ui/ChatInput.tsx +++ b/src/client/components/chat-ui/ChatInput.tsx @@ -14,7 +14,7 @@ import { import { Button } from "../ui/button" import { Textarea } from "../ui/textarea" import { ScrollArea } from "../ui/scroll-area" -import { cn } from "../../lib/utils" +import { cn, generateUUID } from "../../lib/utils" import { useComposer } from "../../hooks/useComposer" import { useIsStandalone } from "../../hooks/useIsStandalone" import { useVoiceRecorder } from "../../hooks/useVoiceRecorder" @@ -540,7 +540,10 @@ const ChatInputInner = forwardRef(function ChatInput({ if (!file) break activeUploadsRef.current += 1 - const tempId = crypto.randomUUID() + // Not crypto.randomUUID(): that is secure-context only, so it is + // undefined when Kanna is opened over plain http on a LAN/Tailscale + // hostname, and every upload would throw before it started. + const tempId = generateUUID() const previewUrl = file.type.startsWith("image/") ? URL.createObjectURL(file) : undefined const generation = uploadGenerationRef.current diff --git a/src/client/lib/chat-navigation.ts b/src/client/lib/chat-navigation.ts index f4b9ce51d..1e8928ba0 100644 --- a/src/client/lib/chat-navigation.ts +++ b/src/client/lib/chat-navigation.ts @@ -1,3 +1,5 @@ +import { generateUUID } from "./utils" + /** * The router-state contract for opening a chat *at a message*. * @@ -29,7 +31,9 @@ export interface ChatJumpLocationState { } export function buildChatJumpLocationState(role: ChatJumpRole): ChatJumpLocationState { - return { jumpToRole: role, jumpRequestId: crypto.randomUUID() } + // generateUUID, not crypto.randomUUID: the latter is secure-context only and + // is undefined over plain http on a LAN/Tailscale hostname. + return { jumpToRole: role, jumpRequestId: generateUUID() } } /** Reads the jump out of an opaque `useLocation().state`, or null if absent. */