diff --git a/ui/web/index.html b/ui/web/index.html index 0a3f4294..2b0e5358 100644 --- a/ui/web/index.html +++ b/ui/web/index.html @@ -13,12 +13,13 @@ - style-src 'self' 'unsafe-inline' — Tailwind/inline loader keyframes + Google Fonts stylesheet require inline styles. - font-src self + gstatic — Google Fonts woff2 files. - - img-src self + data: — base64 avatars/thumbnails. + - img-src self + data: + blob: — base64 avatars/thumbnails, blob previews. + - media-src self + blob: — TTS audio playback via blob URLs. - connect-src self + ws:/wss: — websocket to gateway. Same-origin only; if the gateway is hosted on a different domain in production, add the explicit origin here rather than wildcard-allowing all http(s):. --> - + diff --git a/ui/web/src/components/voice-picker.tsx b/ui/web/src/components/voice-picker.tsx index 416e46eb..4f103c76 100644 --- a/ui/web/src/components/voice-picker.tsx +++ b/ui/web/src/components/voice-picker.tsx @@ -236,7 +236,7 @@ function DynamicVoicePicker({ ) : filtered.length === 0 ? (
- {voices.length === 0 ? t("voice_no_voices") : search ? t("voice_no_voices") : t("voice_loading")} + {voices.length === 0 ? t("voice_save_config_first") : search ? t("voice_no_voices") : t("voice_loading")}
) : ( filtered.map((voice) => ( diff --git a/ui/web/src/i18n/locales/en/tts.json b/ui/web/src/i18n/locales/en/tts.json index acd52193..d94c5aa6 100644 --- a/ui/web/src/i18n/locales/en/tts.json +++ b/ui/web/src/i18n/locales/en/tts.json @@ -69,6 +69,7 @@ "voice_preview": "Preview", "voice_stop_preview": "Stop", "voice_no_voices": "No voices available", + "voice_save_config_first": "Save API key first to load voices", "voice_loading": "Loading voices…", "voice_preview_error": "Preview failed — refreshing list", "model_label": "Model", diff --git a/ui/web/src/i18n/locales/vi/tts.json b/ui/web/src/i18n/locales/vi/tts.json index 26f0a549..cc5ee5c5 100644 --- a/ui/web/src/i18n/locales/vi/tts.json +++ b/ui/web/src/i18n/locales/vi/tts.json @@ -38,6 +38,7 @@ "voice_preview": "Nghe thử", "voice_stop_preview": "Dừng", "voice_no_voices": "Chưa có giọng đọc", + "voice_save_config_first": "Lưu API key trước để tải giọng đọc", "voice_loading": "Đang tải giọng đọc…", "voice_preview_error": "Không nghe thử được — đang làm mới", "model_label": "Mô hình", diff --git a/ui/web/src/i18n/locales/zh/tts.json b/ui/web/src/i18n/locales/zh/tts.json index 631ddfd7..09eb022c 100644 --- a/ui/web/src/i18n/locales/zh/tts.json +++ b/ui/web/src/i18n/locales/zh/tts.json @@ -38,6 +38,7 @@ "voice_preview": "试听", "voice_stop_preview": "停止", "voice_no_voices": "暂无声音", + "voice_save_config_first": "请先保存 API Key 以加载声音", "voice_loading": "加载声音中…", "voice_preview_error": "试听失败 — 正在刷新", "model_label": "模型", diff --git a/ui/web/src/pages/tts/sections/behavior-section.tsx b/ui/web/src/pages/tts/sections/behavior-section.tsx index 3cc3c274..41dd2d7f 100644 --- a/ui/web/src/pages/tts/sections/behavior-section.tsx +++ b/ui/web/src/pages/tts/sections/behavior-section.tsx @@ -1,7 +1,7 @@ /** * Behavior section — collapsible "Advanced settings" panel. * Contains: auto mode, reply mode, max_length, timeout_ms. - * Collapsed by default to reduce visual noise for the common 4-step flow. + * Expanded by default for visibility; users can collapse if needed. * Uses local useState + chevron (no Radix Collapsible dependency needed). */ import { useState } from "react"; @@ -24,7 +24,7 @@ interface Props { export function BehaviorSection({ draft, onUpdate }: Props) { const { t } = useTranslation("tts"); - const [open, setOpen] = useState(false); + const [open, setOpen] = useState(true); return (