From 59a52f1e2ffb7bd1e9793025553a7c1d0b3d46ac Mon Sep 17 00:00:00 2001 From: viettranx Date: Mon, 23 Mar 2026 16:12:07 +0700 Subject: [PATCH] fix(ui): replace bare crypto.randomUUID with uniqueId fallback and show WS disconnect error - use-chat-sessions, skill-upload-dialog: replace crypto.randomUUID() with uniqueId() which has a UUID v4 fallback for non-secure contexts (HTTP without localhost) where crypto.randomUUID is unavailable - use-chat-send: show localized error message when WebSocket is not connected instead of silently dropping the send - Add error.notConnected i18n key to en/vi/zh chat locale files Closes #352 --- ui/web/src/i18n/locales/en/chat.json | 5 ++++- ui/web/src/i18n/locales/vi/chat.json | 5 ++++- ui/web/src/i18n/locales/zh/chat.json | 5 ++++- ui/web/src/pages/chat/hooks/use-chat-send.ts | 8 +++++++- ui/web/src/pages/chat/hooks/use-chat-sessions.ts | 3 ++- ui/web/src/pages/skills/skill-upload-dialog.tsx | 3 ++- 6 files changed, 23 insertions(+), 6 deletions(-) diff --git a/ui/web/src/i18n/locales/en/chat.json b/ui/web/src/i18n/locales/en/chat.json index 2c8e7c20..a8d91aaa 100644 --- a/ui/web/src/i18n/locales/en/chat.json +++ b/ui/web/src/i18n/locales/en/chat.json @@ -18,5 +18,8 @@ "attached": "attached" }, "forwardedFrom": "Forwarded from", - "replyingTo": "Replying to" + "replyingTo": "Replying to", + "error": { + "notConnected": "Not connected. Please wait for the connection to be established." + } } diff --git a/ui/web/src/i18n/locales/vi/chat.json b/ui/web/src/i18n/locales/vi/chat.json index 5441be2b..2ec92c20 100644 --- a/ui/web/src/i18n/locales/vi/chat.json +++ b/ui/web/src/i18n/locales/vi/chat.json @@ -18,5 +18,8 @@ "newChat": "Trò chuyện mới", "openSessions": "Mở phiên", "readOnly": "Chỉ đọc — phiên này thuộc về người dùng khác", - "replyingTo": "Đang trả lời" + "replyingTo": "Đang trả lời", + "error": { + "notConnected": "Chưa kết nối. Vui lòng chờ kết nối được thiết lập." + } } diff --git a/ui/web/src/i18n/locales/zh/chat.json b/ui/web/src/i18n/locales/zh/chat.json index a4b6e867..59e20741 100644 --- a/ui/web/src/i18n/locales/zh/chat.json +++ b/ui/web/src/i18n/locales/zh/chat.json @@ -18,5 +18,8 @@ "newChat": "新建对话", "openSessions": "打开Session", "readOnly": "只读 — 此Session属于其他用户", - "replyingTo": "回复" + "replyingTo": "回复", + "error": { + "notConnected": "未连接。请等待连接建立。" + } } diff --git a/ui/web/src/pages/chat/hooks/use-chat-send.ts b/ui/web/src/pages/chat/hooks/use-chat-send.ts index 679c056a..5bdc2d43 100644 --- a/ui/web/src/pages/chat/hooks/use-chat-send.ts +++ b/ui/web/src/pages/chat/hooks/use-chat-send.ts @@ -1,4 +1,5 @@ import { useState, useCallback } from "react"; +import { useTranslation } from "react-i18next"; import { useWs, useHttp } from "@/hooks/use-ws"; import { Methods } from "@/api/protocol"; import type { ChatMessage } from "@/types/chat"; @@ -25,6 +26,7 @@ export function useChatSend({ onMessageAdded, onExpectRun, }: UseChatSendOptions) { + const { t } = useTranslation("chat"); const ws = useWs(); const http = useHttp(); const [sending, setSending] = useState(false); @@ -34,7 +36,11 @@ export function useChatSend({ async (message: string, sessionKey: string, files?: AttachedFile[]) => { const hasMessage = message.trim().length > 0; const hasFiles = files && files.length > 0; - if (!ws.isConnected || (!hasMessage && !hasFiles) || !sessionKey) return; + if (!ws.isConnected) { + setError(t("error.notConnected")); + return; + } + if ((!hasMessage && !hasFiles) || !sessionKey) return; const trimmed = message.trim(); setError(null); diff --git a/ui/web/src/pages/chat/hooks/use-chat-sessions.ts b/ui/web/src/pages/chat/hooks/use-chat-sessions.ts index 164bc9fb..a9a4a02b 100644 --- a/ui/web/src/pages/chat/hooks/use-chat-sessions.ts +++ b/ui/web/src/pages/chat/hooks/use-chat-sessions.ts @@ -7,6 +7,7 @@ import { useAuthStore } from "@/stores/use-auth-store"; import { toast } from "@/stores/use-toast-store"; import i18next from "i18next"; import { userFriendlyError } from "@/lib/error-utils"; +import { uniqueId } from "@/lib/utils"; /** * Manages the session list for the chat sidebar. @@ -45,7 +46,7 @@ export function useChatSessions(agentId: string) { }, [loadSessions]); const buildNewSessionKey = useCallback(() => { - const convId = crypto.randomUUID(); + const convId = uniqueId(); return `agent:${agentId}:ws:direct:${convId}`; }, [agentId]); diff --git a/ui/web/src/pages/skills/skill-upload-dialog.tsx b/ui/web/src/pages/skills/skill-upload-dialog.tsx index dd45a47e..5bb29d71 100644 --- a/ui/web/src/pages/skills/skill-upload-dialog.tsx +++ b/ui/web/src/pages/skills/skill-upload-dialog.tsx @@ -11,6 +11,7 @@ import { } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { validateSkillZip } from "./lib/validate-skill-zip"; +import { uniqueId } from "@/lib/utils"; type FileStatus = "validating" | "valid" | "invalid" | "uploading" | "success" | "error"; @@ -46,7 +47,7 @@ export function SkillUploadDialog({ open, onOpenChange, onUpload }: SkillUploadD if (fresh.length === 0) return; const pending: FileEntry[] = fresh.map((f) => ({ - id: crypto.randomUUID(), + id: uniqueId(), file: f, status: "validating" as const, }));