diff --git a/client/src/locales/data-de.json b/client/src/locales/data-de.json index b19b0a9..e610cdd 100644 --- a/client/src/locales/data-de.json +++ b/client/src/locales/data-de.json @@ -878,6 +878,7 @@ "cleared": "Aktueller Chat gelöscht", "clearFailed": "Löschen fehlgeschlagen: {message}", "deviceRequired": "Bitte verbinden Sie zuerst ein Gerät", + "inputTooLong": "Nachricht zu lang, bitte auf maximal {max} Zeichen begrenzen", "deviceDisconnected": "Gerät getrennt", "loadHistoryFailed": "Laden des Verlaufs fehlgeschlagen: {message}", "loadChatFailed": "Laden des Chats fehlgeschlagen: {message}", diff --git a/client/src/locales/data-en.json b/client/src/locales/data-en.json index 1ba761d..bbefab8 100644 --- a/client/src/locales/data-en.json +++ b/client/src/locales/data-en.json @@ -883,6 +883,7 @@ "cleared": "Current chat cleared", "clearFailed": "Clear failed: {message}", "deviceRequired": "Please connect a device first", + "inputTooLong": "Message too long, please keep it under {max} characters", "deviceDisconnected": "Device disconnected", "loadHistoryFailed": "Failed to load history: {message}", "loadChatFailed": "Failed to load chat: {message}", diff --git a/client/src/locales/data-es.json b/client/src/locales/data-es.json index 3b7cef0..32db59f 100644 --- a/client/src/locales/data-es.json +++ b/client/src/locales/data-es.json @@ -878,6 +878,7 @@ "cleared": "Chat actual borrado", "clearFailed": "Error al borrar: {message}", "deviceRequired": "Conecte primero un dispositivo", + "inputTooLong": "Mensaje demasiado largo, límite de {max} caracteres", "deviceDisconnected": "Dispositivo desconectado", "loadHistoryFailed": "Error al cargar el historial: {message}", "loadChatFailed": "Error al cargar el chat: {message}", diff --git a/client/src/locales/data-fr.json b/client/src/locales/data-fr.json index 3f45d9c..e7f15da 100644 --- a/client/src/locales/data-fr.json +++ b/client/src/locales/data-fr.json @@ -878,6 +878,7 @@ "cleared": "Chat actuel effacé", "clearFailed": "Échec de l'effacement : {message}", "deviceRequired": "Veuillez d'abord connecter un appareil", + "inputTooLong": "Message trop long, veuillez le limiter à {max} caractères", "deviceDisconnected": "Appareil déconnecté", "loadHistoryFailed": "Échec du chargement de l'historique : {message}", "loadChatFailed": "Échec du chargement du chat : {message}", diff --git a/client/src/locales/data-zh-HK.json b/client/src/locales/data-zh-HK.json index 9ff09e8..b0d89df 100644 --- a/client/src/locales/data-zh-HK.json +++ b/client/src/locales/data-zh-HK.json @@ -883,6 +883,7 @@ "cleared": "已清空目前會話", "clearFailed": "清空失敗:{message}", "deviceRequired": "請先連接裝置", + "inputTooLong": "內容過長,請將輸入限制在 {max} 字以內", "deviceDisconnected": "裝置未連接", "loadHistoryFailed": "載入歷史失敗:{message}", "loadChatFailed": "載入會話失敗:{message}", diff --git a/client/src/locales/data-zh.json b/client/src/locales/data-zh.json index af3bfdc..4c5c108 100644 --- a/client/src/locales/data-zh.json +++ b/client/src/locales/data-zh.json @@ -883,6 +883,7 @@ "cleared": "已清空当前会话", "clearFailed": "清空失败:{message}", "deviceRequired": "请先连接设备", + "inputTooLong": "内容过长,请将输入限制在 {max} 字以内", "deviceDisconnected": "设备未连接", "loadHistoryFailed": "加载历史失败:{message}", "loadChatFailed": "加载会话失败:{message}", diff --git a/client/src/pages/AIPage.tsx b/client/src/pages/AIPage.tsx index be80501..c353e01 100644 --- a/client/src/pages/AIPage.tsx +++ b/client/src/pages/AIPage.tsx @@ -202,6 +202,8 @@ function makeId() { return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; } +const MAX_INPUT_LENGTH = 1000; + export type AIPageVariant = "page" | "split"; export interface AIPageProps { @@ -422,6 +424,10 @@ export default function AIPage({ variant = "page", onClose }: AIPageProps = {}) (question: string) => { const trimmed = question.trim(); if (!trimmed || sending) return; + if (trimmed.length > MAX_INPUT_LENGTH) { + toast.error(formatText(aiText.inputTooLong, { max: MAX_INPUT_LENGTH })); + return; + } if (!mac || !deviceState || !api) { toast.error(aiText.deviceRequired); return; @@ -595,12 +601,27 @@ export default function AIPage({ variant = "page", onClose }: AIPageProps = {}) [input, send], ); - const onInputChange = useCallback((e: React.ChangeEvent) => { - setInput(e.target.value); - const el = e.target; - el.style.height = "auto"; - el.style.height = `${Math.min(el.scrollHeight, 160)}px`; - }, []); + const overLimitToastShown = useRef(false); + + const onInputChange = useCallback( + (e: React.ChangeEvent) => { + const raw = e.target.value; + if (raw.length > MAX_INPUT_LENGTH) { + if (!overLimitToastShown.current) { + toast.error(formatText(aiText.inputTooLong, { max: MAX_INPUT_LENGTH })); + overLimitToastShown.current = true; + } + setInput(raw.slice(0, MAX_INPUT_LENGTH)); + } else { + overLimitToastShown.current = false; + setInput(raw); + } + const el = e.target; + el.style.height = "auto"; + el.style.height = `${Math.min(el.scrollHeight, 160)}px`; + }, + [aiText], + ); // ── 优化记录:对比对话框 & 应用/回滚 ── const [compareMsg, setCompareMsg] = useState(null); @@ -880,6 +901,7 @@ export default function AIPage({ variant = "page", onClose }: AIPageProps = {}) onChange={onInputChange} onKeyDown={onKeyDown} rows={1} + maxLength={MAX_INPUT_LENGTH} placeholder={aiText.inputPlaceholder} className="flex-1 bg-transparent outline-none resize-none text-[15px] text-white placeholder:text-white/35 py-1.5 px-1 max-h-40 leading-[1.4]" />