Update TTS test text defaults

This commit is contained in:
henrryyes
2026-01-18 02:48:18 +08:00
parent 9ebbecaf72
commit 6c18e438e0

View File

@@ -12,9 +12,8 @@
* { margin: 0; padding: 0; box-sizing: border-box; } * { margin: 0; padding: 0; box-sizing: border-box; }
:root { :root {
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? 鏋佺畝绉戞妧椋庨厤鑹?- 榛戠櫧鐏?+ 鍗曡壊鐐圭紑
极简科技风配色 - 黑白灰 + 单色点缀 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
═══════════════════════════════════════════════════════════════ */
/* 鑳屾櫙灞傛<E7819E> */ /* 鑳屾櫙灞傛<E7819E> */
--bg-primary: #0a0a0c; --bg-primary: #0a0a0c;
@@ -34,12 +33,12 @@
--border-light: rgba(255, 255, 255, 0.12); --border-light: rgba(255, 255, 255, 0.12);
--border-focus: rgba(140, 200, 255, 0.4); --border-focus: rgba(140, 200, 255, 0.4);
/* 唯一强调色 - 淡青蓝(科技感) */ /* <EFBFBD>竴寮鸿皟鑹?- 娣¢潚钃濓紙绉戞妧鎰燂級 */
--accent: #8cc8ff; --accent: #8cc8ff;
--accent-soft: rgba(140, 200, 255, 0.1); --accent-soft: rgba(140, 200, 255, 0.1);
--accent-glow: rgba(140, 200, 255, 0.15); --accent-glow: rgba(140, 200, 255, 0.15);
/* 功能色 - 极低饱和度 */ /* 鍔熻兘鑹?- 鏋佷綆楗卞拰搴?*/
--success: #90d4a0; --success: #90d4a0;
--success-soft: rgba(144, 212, 160, 0.08); --success-soft: rgba(144, 212, 160, 0.08);
--error: #e08080; --error: #e08080;
@@ -83,9 +82,8 @@ body {
min-height: -webkit-fill-available; min-height: -webkit-fill-available;
} }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Header
Header 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
═══════════════════════════════════════════════════════════════ */
.app-header { .app-header {
display: flex; display: flex;
@@ -167,9 +165,8 @@ body {
border-color: var(--border-light); border-color: var(--border-light);
} }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Layout
Layout 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
═══════════════════════════════════════════════════════════════ */
.app-body { .app-body {
display: flex; display: flex;
@@ -200,9 +197,8 @@ body {
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
} }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Navigation
Navigation 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
═══════════════════════════════════════════════════════════════ */
.nav-item { .nav-item {
display: flex; display: flex;
@@ -232,9 +228,8 @@ body {
margin: 8px 0; margin: 8px 0;
} }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Views
Views 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
═══════════════════════════════════════════════════════════════ */
.view { .view {
display: none; display: none;
@@ -263,9 +258,8 @@ body {
color: var(--text-muted); color: var(--text-muted);
} }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Cards
Cards 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
═══════════════════════════════════════════════════════════════ */
.card { .card {
background: var(--bg-secondary); background: var(--bg-secondary);
@@ -284,9 +278,8 @@ body {
letter-spacing: 0.08em; letter-spacing: 0.08em;
} }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Forms
Forms 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
═══════════════════════════════════════════════════════════════ */
.form-group { margin-bottom: 16px; } .form-group { margin-bottom: 16px; }
.form-group:last-child { margin-bottom: 0; } .form-group:last-child { margin-bottom: 0; }
@@ -354,9 +347,8 @@ select.input { cursor: pointer; }
color: var(--text-secondary); color: var(--text-secondary);
} }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Buttons
Buttons 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
═══════════════════════════════════════════════════════════════ */
.btn { .btn {
display: inline-flex; display: inline-flex;
@@ -414,9 +406,8 @@ select.input { cursor: pointer; }
pointer-events: none; pointer-events: none;
} }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Slider
Slider 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
═══════════════════════════════════════════════════════════════ */
.slider-row { display: flex; align-items: center; gap: 12px; } .slider-row { display: flex; align-items: center; gap: 12px; }
.slider-row input[type="range"] { .slider-row input[type="range"] {
@@ -435,9 +426,8 @@ select.input { cursor: pointer; }
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
} }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Rules Editor
Rules Editor 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
═══════════════════════════════════════════════════════════════ */
.rules-editor { .rules-editor {
border: 1px solid var(--border); border: 1px solid var(--border);
@@ -519,9 +509,7 @@ select.input { cursor: pointer; }
color: var(--error); color: var(--error);
} }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Current Voice Card - 鏋佺畝鐗? 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
Current Voice Card - 极简版
═══════════════════════════════════════════════════════════════ */
.current-voice-card { .current-voice-card {
background: var(--bg-tertiary); background: var(--bg-tertiary);
@@ -584,9 +572,7 @@ select.input { cursor: pointer; }
margin-top: 2px; margin-top: 2px;
} }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Source Badge - 鏋佺畝榛戠櫧鐗? 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
Source Badge - 极简黑白版
═══════════════════════════════════════════════════════════════ */
.source-badge { .source-badge {
display: inline-flex; display: inline-flex;
@@ -599,7 +585,7 @@ select.input { cursor: pointer; }
letter-spacing: 0.02em; letter-spacing: 0.02em;
} }
/* 试用 - 白色/浅色调 */ /* 璇曠敤 - 鐧借壊/娴呰壊璋?*/
.source-badge.trial { .source-badge.trial {
background: var(--tag-free-bg); background: var(--tag-free-bg);
color: var(--tag-free); color: var(--tag-free);
@@ -613,9 +599,7 @@ select.input { cursor: pointer; }
border: 1px solid rgba(140, 200, 255, 0.15); border: 1px solid rgba(140, 200, 255, 0.15);
} }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Voice Tabs - 鏋佺畝鐗? 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
Voice Tabs - 极简版
═══════════════════════════════════════════════════════════════ */
.voice-tabs { .voice-tabs {
display: flex; display: flex;
@@ -666,9 +650,8 @@ select.input { cursor: pointer; }
.voice-panel { display: none; } .voice-panel { display: none; }
.voice-panel.active { display: block; } .voice-panel.active { display: block; }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Test Voice Box
Test Voice Box 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
═══════════════════════════════════════════════════════════════ */
.test-voice-box { .test-voice-box {
background: var(--bg-tertiary); background: var(--bg-tertiary);
@@ -694,9 +677,8 @@ select.input { cursor: pointer; }
.test-voice-status.playing { color: var(--accent); } .test-voice-status.playing { color: var(--accent); }
.test-voice-status.error { color: var(--error); } .test-voice-status.error { color: var(--error); }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Voice Filters
Voice Filters 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
═══════════════════════════════════════════════════════════════ */
.voice-filters { .voice-filters {
display: flex; display: flex;
@@ -727,9 +709,7 @@ select.input { cursor: pointer; }
} }
.voice-search .input { flex: 1; } .voice-search .input { flex: 1; }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Voice List - 鏋佺畝鐗? 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
Voice List - 极简版
═══════════════════════════════════════════════════════════════ */
.voice-list { .voice-list {
display: flex; display: flex;
@@ -789,7 +769,7 @@ select.input { cursor: pointer; }
background: var(--accent); background: var(--accent);
} }
.voice-item.selected .voice-item-radio::after { .voice-item.selected .voice-item-radio::after {
content: '✓'; content: '?;
color: var(--bg-primary); color: var(--bg-primary);
font-size: 9px; font-size: 9px;
font-weight: bold; font-weight: bold;
@@ -874,9 +854,8 @@ select.input { cursor: pointer; }
font-size: 13px; font-size: 13px;
} }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? API Status Box
API Status Box 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
═══════════════════════════════════════════════════════════════ */
.api-status-box { .api-status-box {
display: flex; display: flex;
@@ -921,9 +900,8 @@ select.input { cursor: pointer; }
margin-top: 2px; margin-top: 2px;
} }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Stats Card
Stats Card 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
═══════════════════════════════════════════════════════════════ */
.stats-card { .stats-card {
display: flex; display: flex;
@@ -955,9 +933,7 @@ select.input { cursor: pointer; }
letter-spacing: 0.05em; letter-spacing: 0.05em;
} }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Tip Box - 鏋佺畝鐗? 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
Tip Box - 极简版
═══════════════════════════════════════════════════════════════ */
.tip-box { .tip-box {
display: flex; display: flex;
@@ -984,9 +960,8 @@ select.input { cursor: pointer; }
color: var(--accent); color: var(--accent);
} }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Guide Box
Guide Box 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
═══════════════════════════════════════════════════════════════ */
.guide-box { .guide-box {
background: var(--bg-tertiary); background: var(--bg-tertiary);
@@ -1074,9 +1049,8 @@ select.input { cursor: pointer; }
} }
.guide-link a { color: var(--accent); } .guide-link a { color: var(--accent); }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Mobile Navigation
Mobile Navigation 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
═══════════════════════════════════════════════════════════════ */
.mobile-nav { .mobile-nav {
display: none; display: none;
@@ -1119,9 +1093,8 @@ select.input { cursor: pointer; }
.mobile-nav-item:hover { color: var(--text-muted); } .mobile-nav-item:hover { color: var(--text-muted); }
.mobile-nav-item.active { color: var(--accent); } .mobile-nav-item.active { color: var(--accent); }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Responsive
Responsive 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
═══════════════════════════════════════════════════════════════ */
@media (max-width: 768px) { @media (max-width: 768px) {
.app-sidebar { display: none; } .app-sidebar { display: none; }
@@ -1178,9 +1151,8 @@ select.input { cursor: pointer; }
.mobile-nav-item { min-height: 44px; } .mobile-nav-item { min-height: 44px; }
} }
/* ═══════════════════════════════════════════════════════════════ /* 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺? Scrollbar
Scrollbar 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?*/
═══════════════════════════════════════════════════════════════ */
::-webkit-scrollbar { width: 6px; height: 6px; } ::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-track { background: transparent; }
@@ -1206,7 +1178,7 @@ select.input { cursor: pointer; }
<div id="badge_auth" class="header-badge"><i class="fa-solid fa-circle"></i><span>閴存潈</span></div> <div id="badge_auth" class="header-badge"><i class="fa-solid fa-circle"></i><span>閴存潈</span></div>
</div> </div>
<div class="header-spacer"></div> <div class="header-spacer"></div>
<button id="tts_close" class="header-close"></button> <button id="tts_close" class="header-close">鉁?/button>
</header> </header>
<div class="app-body"> <div class="app-body">
@@ -1226,15 +1198,14 @@ select.input { cursor: pointer; }
<div id="view-config" class="view active"> <div id="view-config" class="view active">
<div class="view-header"> <div class="view-header">
<h2 class="view-title">鍩虹<EFBFBD>閰嶇疆</h2> <h2 class="view-title">鍩虹<EFBFBD>閰嶇疆</h2>
<p class="view-desc">TTS 服务连接与朗读设置</p> <p class="view-desc">TTS 鏈嶅姟杩炴帴涓庢湕璇昏<EFBFBD>缃?/p>
</div> </div>
<div class="tip-box" style="margin-bottom: 16px;"> <div class="tip-box" style="margin-bottom: 16px;">
<i class="fa-solid fa-info-circle"></i> <i class="fa-solid fa-info-circle"></i>
<div> <div>
<strong>试用音色</strong> — 无需配置使用插件服务器11个音色<br> <strong>璇曠敤闊宠壊</strong> 鈥?鏃犻渶閰嶇疆锛屼娇鐢ㄦ彃浠舵湇鍔″櫒锛?1涓<31>煶鑹诧級<br>
<strong>鉴权音色</strong> — 需配置火山引擎 API200+ 音色 + 复刻) <strong>閴存潈闊宠壊</strong> 鈥?闇€閰嶇疆鐏<E79686>北寮曟搸 API锛?00+ 闊宠壊 + 澶嶅埢锛? </div>
</div>
</div> </div>
<div class="card"> <div class="card">
@@ -1242,7 +1213,7 @@ select.input { cursor: pointer; }
<div id="apiStatusBox" class="api-status-box not-configured"> <div id="apiStatusBox" class="api-status-box not-configured">
<div class="api-status-icon"><i class="fa-solid fa-link-slash"></i></div> <div class="api-status-icon"><i class="fa-solid fa-link-slash"></i></div>
<div class="api-status-info"> <div class="api-status-info">
<div class="api-status-title">未配置</div> <div class="api-status-title"><EFBFBD>厤缃?/div>
<div class="api-status-desc">閰嶇疆鍚庡彲浣跨敤棰勮<EFBFBD>闊宠壊搴撳拰澶嶅埢闊宠壊</div> <div class="api-status-desc">閰嶇疆鍚庡彲浣跨敤棰勮<EFBFBD>闊宠壊搴撳拰澶嶅埢闊宠壊</div>
</div> </div>
</div> </div>
@@ -1264,10 +1235,10 @@ select.input { cursor: pointer; }
<div class="card-title">鏈楄<EFBFBD>璁剧疆</div> <div class="card-title">鏈楄<EFBFBD>璁剧疆</div>
<div class="checkbox-row"> <div class="checkbox-row">
<input type="checkbox" id="autoSpeak" checked> <input type="checkbox" id="autoSpeak" checked>
<label for="autoSpeak">AI 回复后自动朗读</label> <label for="autoSpeak">AI 鍥炲<EFBFBD>鍚庤嚜鍔ㄦ湕璇?/label>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">语速</label> <label class="form-label"><EFBFBD>€?/label>
<div class="slider-row"> <div class="slider-row">
<input type="range" id="speechRate" min="0.5" max="2.0" step="0.1" value="1.0"> <input type="range" id="speechRate" min="0.5" max="2.0" step="0.1" value="1.0">
<span class="slider-val" id="speechRateValue">1.0x</span> <span class="slider-val" id="speechRateValue">1.0x</span>
@@ -1279,7 +1250,7 @@ select.input { cursor: pointer; }
<div class="card-title">鏂囨湰杩囨护</div> <div class="card-title">鏂囨湰杩囨护</div>
<div class="form-group"> <div class="form-group">
<label class="form-label">璺宠繃鍖洪棿</label> <label class="form-label">璺宠繃鍖洪棿</label>
<p class="form-hint" style="margin-bottom: 8px;">遇到「起始」后跳过,直到「结束」。起始或结束可单独留空。</p> <p class="form-hint" style="margin-bottom: 8px;">閬囧埌銆岃捣濮嬨€嶅悗璺宠繃锛岀洿鍒般€岀粨鏉熴€嶃€傝捣濮嬫垨缁撴潫鍙<EFBFBD>崟鐙<EFBFBD>暀绌恒€?/p>
<div class="rules-editor"> <div class="rules-editor">
<div class="rules-header"> <div class="rules-header">
<span class="rules-header-title">褰撳墠瑙勫垯</span> <span class="rules-header-title">褰撳墠瑙勫垯</span>
@@ -1291,9 +1262,9 @@ select.input { cursor: pointer; }
<div class="form-group"> <div class="form-group">
<div class="checkbox-row" style="margin-bottom: 8px;"> <div class="checkbox-row" style="margin-bottom: 8px;">
<input type="checkbox" id="readRangesEnabled"> <input type="checkbox" id="readRangesEnabled">
<label for="readRangesEnabled">启用只读区间(仅朗读匹配内容)</label> <label for="readRangesEnabled"><EFBFBD>敤鍙<EFBFBD><EFBFBD>鍖洪棿锛堜粎鏈楄<EFBFBD>鍖归厤鍐呭<EFBFBD>锛?/label>
</div> </div>
<p class="form-hint" style="margin-bottom: 8px;">起始或结束可单独留空。</p> <p class="form-hint" style="margin-bottom: 8px;">璧峰<EFBFBD>鎴栫粨鏉熷彲鍗曠嫭鐣欑┖銆?/p>
<div class="rules-editor"> <div class="rules-editor">
<div class="rules-header"> <div class="rules-header">
<span class="rules-header-title"><EFBFBD><EFBFBD>瑙勫垯</span> <span class="rules-header-title"><EFBFBD><EFBFBD>瑙勫垯</span>
@@ -1311,7 +1282,7 @@ select.input { cursor: pointer; }
<div id="view-voice" class="view"> <div id="view-voice" class="view">
<div class="view-header"> <div class="view-header">
<h2 class="view-title">闊宠壊绠$悊</h2> <h2 class="view-title">闊宠壊绠$悊</h2>
<p class="view-desc">将喜欢的音色重命名加入【我的音色】</p> <p class="view-desc">灏嗗枩娆㈢殑闊宠壊閲嶅懡鍚嶅姞鍏ャ€愭垜鐨勯煶鑹层€?/p>
</div> </div>
<div class="current-voice-card" id="currentVoiceCard"> <div class="current-voice-card" id="currentVoiceCard">
@@ -1334,8 +1305,7 @@ select.input { cursor: pointer; }
<i class="fa-solid fa-flask"></i> 璇曠敤 <i class="fa-solid fa-flask"></i> 璇曠敤
</button> </button>
<button class="voice-tab" data-panel="authVoice"> <button class="voice-tab" data-panel="authVoice">
<i class="fa-solid fa-list"></i> 预设库 <i class="fa-solid fa-list"></i> 棰勮<EFBFBD>搴? </button>
</button>
</div> </div>
<!-- 鎴戠殑闊宠壊闈㈡澘 --> <!-- 鎴戠殑闊宠壊闈㈡澘 -->
@@ -1343,27 +1313,26 @@ select.input { cursor: pointer; }
<div class="card"> <div class="card">
<div class="test-voice-box"> <div class="test-voice-box">
<div class="test-voice-row"> <div class="test-voice-row">
<input type="text" id="testTextMy" class="input" value="哼唱:...我能想到最浪漫的事" placeholder="输入测试文本"> <input type="text" id="testTextMy" class="input" value="&#9834; 我能想到最浪漫的事&#65374;&#9834;" placeholder="杈撳叆娴嬭瘯鏂囨湰">
<button class="btn btn-primary" id="testMyVoiceBtn"><i class="fa-solid fa-play"></i> 璇曞惉</button> <button class="btn btn-primary" id="testMyVoiceBtn"><i class="fa-solid fa-play"></i> 璇曞惉</button>
</div> </div>
<div class="test-voice-status" id="testMyStatus"></div> <div class="test-voice-status" id="testMyStatus"></div>
</div> </div>
<p class="form-hint" style="margin-bottom: 12px;"> <p class="form-hint" style="margin-bottom: 12px;">
点击选中设为默认。<span class="source-badge trial">试用</span> 无需配置,<span class="source-badge auth">鉴权</span> 需配置 API 鐐瑰嚮閫変腑璁句负榛樿<EFBFBD>銆?span class="source-badge trial">璇曠敤</span> 鏃犻渶閰嶇疆锛?span class="source-badge auth">閴存潈</span> 闇€閰嶇疆 API
</p> </p>
<div class="voice-list" id="myVoiceList"></div> <div class="voice-list" id="myVoiceList"></div>
<div id="myVoiceEmpty" class="rules-empty"> <div id="myVoiceEmpty" class="rules-empty">
<i class="fa-solid fa-inbox" style="font-size: 24px; margin-bottom: 8px; display: block; opacity: 0.5;"></i> <i class="fa-solid fa-inbox" style="font-size: 24px; margin-bottom: 8px; display: block; opacity: 0.5;"></i>
暂无音色,请从「试用」或「预设库」添加 鏆傛棤闊宠壊锛岃<EFBFBD>浠庛€岃瘯鐢ㄣ€嶆垨銆岄<EFBFBD>璁惧簱銆嶆坊鍔? </div>
</div>
<div class="voice-add-form"> <div class="voice-add-form">
<div class="form-label" style="margin-bottom: 8px;">鎵嬪姩娣诲姞澶嶅埢闊宠壊 <span class="source-badge auth">閴存潈</span></div> <div class="form-label" style="margin-bottom: 8px;">鎵嬪姩娣诲姞澶嶅埢闊宠壊 <span class="source-badge auth">閴存潈</span></div>
<div class="voice-add-row"> <div class="voice-add-row">
<div class="form-group"> <div class="form-group">
<label class="form-label">闊宠壊 ID</label> <label class="form-label">闊宠壊 ID</label>
<input type="text" id="newVoiceId" class="input" placeholder="S_xxx"> <input type="text" id="newVoiceId" class="input" placeholder="濡?S_xxx">
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">鍚嶇О</label> <label class="form-label">鍚嶇О</label>
@@ -1380,7 +1349,7 @@ select.input { cursor: pointer; }
<div class="card"> <div class="card">
<div class="test-voice-box"> <div class="test-voice-box">
<div class="test-voice-row"> <div class="test-voice-row">
<input type="text" id="testTextTrial" class="input" value="哼唱:...我能想到最浪漫的事" placeholder="输入测试文本"> <input type="text" id="testTextTrial" class="input" value="咳~&#9834;祝你生日快乐&#65374;" placeholder="杈撳叆娴嬭瘯鏂囨湰">
<button class="btn btn-primary" id="testTrialVoiceBtn"><i class="fa-solid fa-play"></i> 璇曞惉</button> <button class="btn btn-primary" id="testTrialVoiceBtn"><i class="fa-solid fa-play"></i> 璇曞惉</button>
</div> </div>
<div class="test-voice-status" id="testTrialStatus"></div> <div class="test-voice-status" id="testTrialStatus"></div>
@@ -1390,22 +1359,22 @@ select.input { cursor: pointer; }
<div class="preset-save-row"> <div class="preset-save-row">
<input type="text" id="saveAsNameTrial" class="input" placeholder="淇濆瓨鍚嶇О锛堝彲閫夛級"> <input type="text" id="saveAsNameTrial" class="input" placeholder="淇濆瓨鍚嶇О锛堝彲閫夛級">
<button class="btn btn-primary" id="saveToMyVoiceTrialBtn"><i class="fa-solid fa-plus"></i> 添加到我的音色</button> <button class="btn btn-primary" id="saveToMyVoiceTrialBtn"><i class="fa-solid fa-plus"></i> 娣诲姞鍒版垜鐨勯煶鑹?/button>
</div> </div>
</div> </div>
</div> </div>
<!-- 预设音色库面板 --> <!-- 棰勮<EFBFBD>闊宠壊搴撻潰鏉?-->
<div class="voice-panel" id="panel-authVoice"> <div class="voice-panel" id="panel-authVoice">
<div class="card"> <div class="card">
<div id="authVoiceNotice" class="tip-box warning" style="margin-bottom: 16px;"> <div id="authVoiceNotice" class="tip-box warning" style="margin-bottom: 16px;">
<i class="fa-solid fa-exclamation-triangle"></i> <i class="fa-solid fa-exclamation-triangle"></i>
<div>使用预设音色库需要先配置鉴权 API请前往「基础配置」页面设置。</div> <div>浣跨敤棰勮<EFBFBD>闊宠壊搴撻渶瑕佸厛閰嶇疆閴存潈 API锛岃<E9949B>鍓嶅線銆屽熀纭€閰嶇疆銆嶉〉闈㈣<E99788><EFBFBD>€?/div>
</div> </div>
<div class="test-voice-box"> <div class="test-voice-box">
<div class="test-voice-row"> <div class="test-voice-row">
<input type="text" id="testTextAuth" class="input" value="哼唱:...我能想到最浪漫的事" placeholder="输入测试文本"> <input type="text" id="testTextAuth" class="input" value="预备,唱:两只老虎,两只老虎..." placeholder="杈撳叆娴嬭瘯鏂囨湰">
<button class="btn btn-primary" id="testAuthVoiceBtn"><i class="fa-solid fa-play"></i> 璇曞惉</button> <button class="btn btn-primary" id="testAuthVoiceBtn"><i class="fa-solid fa-play"></i> 璇曞惉</button>
</div> </div>
<div class="test-voice-status" id="testAuthStatus"></div> <div class="test-voice-status" id="testAuthStatus"></div>
@@ -1442,7 +1411,7 @@ select.input { cursor: pointer; }
<div class="preset-save-row"> <div class="preset-save-row">
<input type="text" id="saveAsNameAuth" class="input" placeholder="淇濆瓨鍚嶇О锛堝彲閫夛級"> <input type="text" id="saveAsNameAuth" class="input" placeholder="淇濆瓨鍚嶇О锛堝彲閫夛級">
<button class="btn btn-primary" id="saveToMyVoiceAuthBtn"><i class="fa-solid fa-plus"></i> 添加到我的音色</button> <button class="btn btn-primary" id="saveToMyVoiceAuthBtn"><i class="fa-solid fa-plus"></i> 娣诲姞鍒版垜鐨勯煶鑹?/button>
</div> </div>
</div> </div>
</div> </div>
@@ -1458,30 +1427,30 @@ select.input { cursor: pointer; }
</div> </div>
<div class="card"> <div class="card">
<div class="card-title">计费与缓存</div> <div class="card-title">璁¤垂涓庣紦瀛?/div>
<div class="checkbox-row"> <div class="checkbox-row">
<input type="checkbox" id="usageReturn"> <input type="checkbox" id="usageReturn">
<label for="usageReturn">返回计费用量text_words</label> <label for="usageReturn">杩斿洖璁¤垂鐢ㄩ噺锛坱ext_words锛?/label>
</div> </div>
<div class="checkbox-row"> <div class="checkbox-row">
<input type="checkbox" id="serverCacheEnabled"> <input type="checkbox" id="serverCacheEnabled">
<label for="serverCacheEnabled">启用火山服务端缓存</label> <label for="serverCacheEnabled"><EFBFBD>敤鐏<EFBFBD>北鏈嶅姟绔<EFBFBD>紦瀛?/label>
</div> </div>
</div> </div>
<div class="card"> <div class="card">
<div class="card-title">过滤与识别</div> <div class="card-title">杩囨护涓庤瘑鍒?/div>
<div class="checkbox-row"> <div class="checkbox-row">
<input type="checkbox" id="disableMarkdownFilter"> <input type="checkbox" id="disableMarkdownFilter">
<label for="disableMarkdownFilter"><EFBFBD>敤 Markdown 杩囨护</label> <label for="disableMarkdownFilter"><EFBFBD>敤 Markdown 杩囨护</label>
</div> </div>
<div class="checkbox-row"> <div class="checkbox-row">
<input type="checkbox" id="useTts11" checked> <input type="checkbox" id="useTts11" checked>
<label for="useTts11">启用 1.1 模型(仅对 seed-tts-1.0 生效)</label> <label for="useTts11"><EFBFBD> 1.1 妯″瀷锛堜粎瀵?seed-tts-1.0 鐢熸晥锛?/label>
</div> </div>
<div class="checkbox-row"> <div class="checkbox-row">
<input type="checkbox" id="disableEmojiFilter"> <input type="checkbox" id="disableEmojiFilter">
<label for="disableEmojiFilter">不过滤 Emoji</label> <label for="disableEmojiFilter">涓嶈繃婊?Emoji</label>
</div> </div>
<div class="checkbox-row"> <div class="checkbox-row">
<input type="checkbox" id="enableLanguageDetector"> <input type="checkbox" id="enableLanguageDetector">
@@ -1510,7 +1479,7 @@ select.input { cursor: pointer; }
<div id="view-cache" class="view"> <div id="view-cache" class="view">
<div class="view-header"> <div class="view-header">
<h2 class="view-title">缂撳瓨绠$悊</h2> <h2 class="view-title">缂撳瓨绠$悊</h2>
<p class="view-desc">本地音频缓存统计与清理</p> <p class="view-desc"><EFBFBD>湴闊抽<EFBFBD>缂撳瓨缁熻<EFBFBD>涓庢竻鐞?/p>
</div> </div>
<div class="card"> <div class="card">
@@ -1530,7 +1499,7 @@ select.input { cursor: pointer; }
</div> </div>
<div class="stats-item"> <div class="stats-item">
<div class="stats-value" id="cacheMisses">0</div> <div class="stats-value" id="cacheMisses">0</div>
<div class="stats-label">未命中</div> <div class="stats-label"><EFBFBD>懡涓?/div>
</div> </div>
</div> </div>
</div> </div>
@@ -1544,11 +1513,11 @@ select.input { cursor: pointer; }
<input type="number" id="cacheDays" class="input" min="1" max="30"> <input type="number" id="cacheDays" class="input" min="1" max="30">
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">最大条数</label> <label class="form-label">鏈€澶ф潯鏁?/label>
<input type="number" id="cacheMaxEntries" class="input" min="10" max="5000"> <input type="number" id="cacheMaxEntries" class="input" min="10" max="5000">
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">最大容量 (MB)</label> <label class="form-label">鏈€澶у<EFBFBD>閲?(MB)</label>
<input type="number" id="cacheMaxMB" class="input" min="10" max="5000"> <input type="number" id="cacheMaxMB" class="input" min="10" max="5000">
</div> </div>
</div> </div>
@@ -1566,48 +1535,47 @@ select.input { cursor: pointer; }
<div id="view-guide" class="view"> <div id="view-guide" class="view">
<div class="view-header"> <div class="view-header">
<h2 class="view-title">浣跨敤璇存槑</h2> <h2 class="view-title">浣跨敤璇存槑</h2>
<p class="view-desc">配音指令与开通流程</p> <p class="view-desc">閰嶉煶鎸囦护涓庡紑閫氭祦绋?/p>
</div> </div>
<div class="guide-box"> <div class="guide-box">
<h3><i class="fa-solid fa-terminal"></i> 閰嶉煶鎸囦护</h3> <h3><i class="fa-solid fa-terminal"></i> 閰嶉煶鎸囦护</h3>
<p>格式:<code>[tts:speaker=音色名;emotion=情绪;context=语气提示]</code> 放在正文前一行</p> <p>鏍煎紡锛?code>[tts:speaker=闊宠壊鍚?emotion=鎯呯华;context=<EFBFBD>皵鎻愮ず]</code> 鏀惧湪姝f枃鍓嶄竴琛?/p>
<p>speaker、emotion、context 三个参数可任意组合、任意顺序,用分号分隔</p> <p>speaker銆乪motion銆乧ontext 涓変釜鍙傛暟鍙<EFBFBD>换鎰忕粍鍚堛€佷换鎰忛『搴忥紝鐢ㄥ垎鍙峰垎闅?/p>
<p>姣忛亣鍒颁竴涓<EFBFBD><code>[tts:...]</code> 鍧椾細鍒嗘<E98D92>鏈楄<E98F88>锛屾寜椤哄簭鎾<E7B0AD></p> <p>姣忛亣鍒颁竴涓<EFBFBD><code>[tts:...]</code> 鍧椾細鍒嗘<E98D92>鏈楄<E98F88>锛屾寜椤哄簭鎾<E7B0AD></p>
<p>未写 <code>speaker=</code> 的块使用当前选中的默认音色</p> <p><EFBFBD> <code>speaker=</code> 鐨勫潡浣跨敤褰撳墠閫変腑鐨勯粯璁ら煶鑹?/p>
<p style="margin-top: 12px;"><strong>音色speaker</strong></p> <p style="margin-top: 12px;"><strong>闊宠壊锛坰peaker锛?/strong></p>
<p>只能指定"我的音色"中保存的名称。例如保存了名为"小白"的音色,则可用 <code>speaker=小白</code></p> <p><EFBFBD>兘鎸囧畾"鎴戠殑闊宠壊"涓<>繚瀛樼殑鍚嶇О銆備緥濡備繚瀛樹簡鍚嶄负"灏忕櫧"鐨勯煶鑹诧紝鍒欏彲鐢?<code>speaker=灏忕櫧</code>銆?/p>
<p style="margin-top: 12px;"><strong>鎯呮劅锛坋motion锛夊彲鐢ㄥ€硷細</strong></p> <p style="margin-top: 12px;"><strong>鎯呮劅锛坋motion锛夊彲鐢ㄥ€硷細</strong></p>
<pre>中文:开心、悲伤、生气、惊讶、恐惧、厌恶、激动、冷漠、中性、沮丧、撒娇、害羞、安慰、鼓励、咆哮、焦急、温柔、讲故事、自然讲述、情感电台、磁性、广告营销、气泡音、低语、新闻播报、娱乐八卦、方言、对话、闲聊、温暖、深情、权威 <pre><EFBFBD>枃锛氬紑蹇冦€佹偛浼ゃ€佺敓姘斻€佹儕璁躲€佹亹鎯с€佸帉鎭躲€佹縺鍔ㄣ€佸喎婕犮€佷腑鎬с€佹伯涓с€佹拻濞囥€佸<EFBFBD>缇炪€佸畨鎱般€侀紦鍔便€佸拞鍝<EFBFBD>€佺劍鎬ャ€佹俯鏌斻€佽<EFBFBD>鏁呬簨銆佽嚜鐒惰<EFBFBD>杩般€佹儏鎰熺數鍙般€佺<EFBFBD>с€佸箍鍛婅惀閿€銆佹皵娉¢煶銆佷綆璇<EFBFBD>€佹柊闂绘挱鎶ャ€佸ū涔愬叓鍗︺€佹柟瑷€銆佸<EFBFBD>璇濄€侀棽鑱娿€佹俯鏆栥€佹繁鎯呫€佹潈濞?
鑻辨枃锛歨appy, sad, angry, surprised, fear, hate, excited, coldness, neutral, depressed, lovey-dovey, shy, comfort, tension, tender, storytelling, radio, magnetic, advertising, vocal-fry, asmr, news, entertainment, dialect, chat, warm, affectionate, authoritative</pre> 鑻辨枃锛歨appy, sad, angry, surprised, fear, hate, excited, coldness, neutral, depressed, lovey-dovey, shy, comfort, tension, tender, storytelling, radio, magnetic, advertising, vocal-fry, asmr, news, entertainment, dialect, chat, warm, affectionate, authoritative</pre>
<p style="margin-top: 12px;"><strong>语气提示context</strong>仅对 seed-tts-2.0 生效:</p> <p style="margin-top: 12px;"><strong><EFBFBD>皵鎻愮ず锛坈ontext锛?/strong>浠呭<E6B5A0> seed-tts-2.0 鐢熸晥锛?/p>
<p>例如:"用更委屈的语气"、"放慢一点,压低音量"</p> <p>渚嬪<EFBFBD>锛?鐢ㄦ洿濮斿眻鐨勮<E990A8>姘?銆?鏀炬參涓€鐐癸紝鍘嬩綆闊抽噺"</p>
</div> </div>
<div class="guide-box"> <div class="guide-box">
<h3><i class="fa-solid fa-user-plus"></i> 澶嶅埢闊宠壊浣跨敤</h3> <h3><i class="fa-solid fa-user-plus"></i> 澶嶅埢闊宠壊浣跨敤</h3>
<ol> <ol>
<li>在火山官网复刻音色</li> <li>鍦ㄧ伀灞卞畼缃戝<EFBFBD>鍒婚煶鑹?/li>
<li>获取音色ID格式 <code>S_xxxxxxxx</code></li> <li>鑾峰彇闊宠壊ID锛堟牸寮?<code>S_xxxxxxxx</code>锛?/li>
<li>在"音色管理" → "我的音色"中添加</li> <li>鍦?闊宠壊绠$悊" 鈫?"鎴戠殑闊宠壊"涓<>坊鍔?/li>
</ol> </ol>
</div> </div>
<div class="tip-box warning" style="margin-bottom: 16px;"> <div class="tip-box warning" style="margin-bottom: 16px;">
<i class="fa-solid fa-exclamation-triangle"></i> <i class="fa-solid fa-exclamation-triangle"></i>
<div><strong>以下是鉴权模式的开通教程</strong>,试用音色无需配置。</div> <div><strong>浠ヤ笅鏄<EFBFBD>壌鏉冩ā寮忕殑寮€閫氭暀绋?/strong>锛岃瘯鐢ㄩ煶鑹叉棤闇€閰嶇疆銆?/div>
</div> </div>
<div class="guide-box"> <div class="guide-box">
<h3><i class="fa-solid fa-server"></i> 开启 CORS 代理</h3> <h3><i class="fa-solid fa-server"></i> 寮€鍚?CORS 浠g悊</h3>
<ol> <ol>
<li>打开酒馆目录的 config.yaml</li> <li>鎵撳紑閰掗<EFBFBD><EFBFBD>綍鐨?config.yaml</li>
<li>enableCorsProxy 改为 true 并保存</li> <li>灏?enableCorsProxy 鏀逛负 true 骞朵繚瀛?/li>
<li>重启酒馆(重启容器/进程,不是 F5 刷新)</li> <li>閲嶅惎閰掗<EFBFBD>锛堥噸鍚<EFBFBD><EFBFBD>鍣?杩涚▼锛屼笉鏄?F5 鍒锋柊锛?/li>
</ol> </ol>
</div> </div>
@@ -1616,7 +1584,7 @@ select.input { cursor: pointer; }
<div class="guide-link"> <div class="guide-link">
<a href="https://console.volcengine.com/speech/new/setting/activate" target="_blank">https://console.volcengine.com/speech/new/setting/activate</a> <a href="https://console.volcengine.com/speech/new/setting/activate" target="_blank">https://console.volcengine.com/speech/new/setting/activate</a>
</div> </div>
<img class="guide-image" src="开通管理.png" alt="开通管理"> <img class="guide-image" src="寮€閫氱<EFBFBD>鐞?png" alt="寮€閫氱<EFBFBD>鐞?>
</div> </div>
<div class="guide-box"> <div class="guide-box">
@@ -1653,10 +1621,7 @@ select.input { cursor: pointer; }
<script src="tts-voices.js"></script> <script src="tts-voices.js"></script>
<script> <script>
// ═══════════════════════════════════════════════════════════════════════════ // 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?// 甯搁噺涓庣姸鎬?// 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?
// 常量与状态
// ═══════════════════════════════════════════════════════════════════════════
const PARENT_ORIGIN = (() => { const PARENT_ORIGIN = (() => {
try { return new URL(document.referrer).origin; } catch { return window.location.origin; } try { return new URL(document.referrer).origin; } catch { return window.location.origin; }
})(); })();
@@ -1674,11 +1639,11 @@ const TRIAL_VOICES = [
{ key: 'female_1', name: '妗冨き', tag: '鐢滆湝浠欏瓙', gender: 'female' }, { key: 'female_1', name: '妗冨き', tag: '鐢滆湝浠欏瓙', gender: 'female' },
{ key: 'female_2', name: '闇滃崕', tag: '娓呭喎浠欏瓙', gender: 'female' }, { key: 'female_2', name: '闇滃崕', tag: '娓呭喎浠欏瓙', gender: 'female' },
{ key: 'female_3', name: '椤惧<E6A4A4>', tag: '寰″<E5AFB0>鐑熷棑', gender: 'female' }, { key: 'female_3', name: '椤惧<E6A4A4>', tag: '寰″<E5AFB0>鐑熷棑', gender: 'female' },
{ key: 'female_4', name: '苏菲', tag: '优雅知性', gender: 'female' }, { key: 'female_4', name: '鑻忚彶', tag: '浼橀泤鐭ユ€?, gender: 'female' },
{ key: 'female_5', name: '鍢夋<EFBFBD>', tag: '<EFBFBD><EFBFBD>鐢滃績', gender: 'female' }, { key: 'female_5', name: '鍢夋<EFBFBD>', tag: '<EFBFBD><EFBFBD>鐢滃績', gender: 'female' },
{ key: 'female_6', name: '青梅', tag: '清秀少年音', gender: 'female' }, { key: 'female_6', name: '闈掓<EFBFBD>', tag: '娓呯<EFBFBD>灏戝勾闊?, gender: 'female' },
{ key: 'female_7', name: '鍙<>帀', tag: '濂堕煶钀濊帀', gender: 'female' }, { key: 'female_7', name: '鍙<>帀', tag: '濂堕煶钀濊帀', gender: 'female' },
{ key: 'male_1', name: '夜枭', tag: '磁性低音', gender: 'male' }, { key: 'male_1', name: '澶滄灜', tag: '纾佹€т綆闊?, gender: 'male' },
{ key: 'male_2', name: '鍚涙辰', tag: '鼎鍏<EFBFBD>', gender: 'male' }, { key: 'male_2', name: '鍚涙辰', tag: '鼎鍏<EFBFBD>', gender: 'male' },
{ key: 'male_3', name: '娌愰槼', tag: '娌夌ǔ鏆栫敺', gender: 'male' }, { key: 'male_3', name: '娌愰槼', tag: '娌夌ǔ鏆栫敺', gender: 'male' },
{ key: 'male_4', name: '姊撹緵', tag: '闈掓槬灏戝勾', gender: 'male' }, { key: 'male_4', name: '姊撹緵', tag: '闈掓槬灏戝勾', gender: 'male' },
@@ -1690,10 +1655,8 @@ const TTS2_VOICES = new Set((window.XB_TTS_TTS2_VOICE_INFO || []).map(item => it
let authVoiceList = []; let authVoiceList = [];
// ═══════════════════════════════════════════════════════════════════════════ // 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?// 宸ュ叿鍑芥暟
// 工具函数 // 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?
// ═══════════════════════════════════════════════════════════════════════════
const $ = id => document.getElementById(id); const $ = id => document.getElementById(id);
const $$ = sel => document.querySelectorAll(sel); const $$ = sel => document.querySelectorAll(sel);
@@ -1752,10 +1715,7 @@ function isInMyList(value) {
return mySpeakers.some(s => s.value === value); return mySpeakers.some(s => s.value === value);
} }
// ═══════════════════════════════════════════════════════════════════════════ // 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?// 瑙勫垯缂栬緫鍣?// 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?
// 规则编辑器
// ═══════════════════════════════════════════════════════════════════════════
function renderRulesList(listEl, rules, type) { function renderRulesList(listEl, rules, type) {
if (!rules?.length) { if (!rules?.length) {
listEl.innerHTML = `<div class="rules-empty">鏆傛棤瑙勫垯</div>`; listEl.innerHTML = `<div class="rules-empty">鏆傛棤瑙勫垯</div>`;
@@ -1763,9 +1723,9 @@ function renderRulesList(listEl, rules, type) {
} }
listEl.innerHTML = rules.map((rule, idx) => ` listEl.innerHTML = rules.map((rule, idx) => `
<div class="rule-item" data-idx="${idx}"> <div class="rule-item" data-idx="${idx}">
<input type="text" class="rule-input rule-start" value="${escapeHtml(rule.start || '')}" placeholder="起始(可为空)"> <input type="text" class="rule-input rule-start" value="${escapeHtml(rule.start || '')}" placeholder="璧峰<EFBFBD>锛堝彲涓虹┖锛?>
<span class="rule-arrow">→</span> <span class="rule-arrow">鈫?/span>
<input type="text" class="rule-input rule-end" value="${escapeHtml(rule.end || '')}" placeholder="结束(可为空)"> <input type="text" class="rule-input rule-end" value="${escapeHtml(rule.end || '')}" placeholder="缁撴潫锛堝彲涓虹┖锛?>
<button class="rule-delete" data-type="${type}" data-idx="${idx}"><i class="fa-solid fa-times"></i></button> <button class="rule-delete" data-type="${type}" data-idx="${idx}"><i class="fa-solid fa-times"></i></button>
</div> </div>
`).join(''); `).join('');
@@ -1804,10 +1764,8 @@ function initRulesEditors() {
}); });
} }
// ═══════════════════════════════════════════════════════════════════════════ // 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?// 闊宠壊澶勭悊
// 音色处理 // 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?
// ═══════════════════════════════════════════════════════════════════════════
function detectGenderByValue(value) { function detectGenderByValue(value) {
const v = String(value || '').toLowerCase(); const v = String(value || '').toLowerCase();
if (v.includes('female')) return 'female'; if (v.includes('female')) return 'female';
@@ -1821,7 +1779,7 @@ function detectTags(value, model) {
const tags = []; const tags = [];
if (model === 'tts2') tags.push('2.0'); if (model === 'tts2') tags.push('2.0');
if (model === 'tts1') tags.push('1.0'); if (model === 'tts1') tags.push('1.0');
if (String(value).includes('emo')) tags.push('多情感'); if (String(value).includes('emo')) tags.push('澶氭儏鎰?);
return tags; return tags;
} }
@@ -1830,7 +1788,7 @@ function buildAuthVoiceList() {
const value = item.value; const value = item.value;
const name = item.name || value; const name = item.name || value;
const gender = detectGenderByValue(value); const gender = detectGenderByValue(value);
const genderLabel = gender === 'female' ? '女' : gender === 'male' ? '男' : '其他'; const genderLabel = gender === 'female' ? '濂? : gender === 'male' ? '? : '鍏朵粬';
const model = detectModel(value); const model = detectModel(value);
const scene = String(item.scene || '').trim(); const scene = String(item.scene || '').trim();
const tags = detectTags(value, model); const tags = detectTags(value, model);
@@ -1853,10 +1811,8 @@ function buildAuthVoiceList() {
function formatTagLabel(tags) { return tags.length ? ` [${tags.join('/')}]` : ''; } function formatTagLabel(tags) { return tags.length ? ` [${tags.join('/')}]` : ''; }
// ═══════════════════════════════════════════════════════════════════════════ // 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?// UI 鏇存柊
// UI 更新 // 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?
// ═══════════════════════════════════════════════════════════════════════════
function updateApiStatus() { function updateApiStatus() {
const configured = isAuthConfigured(); const configured = isAuthConfigured();
const box = $('apiStatusBox'); const box = $('apiStatusBox');
@@ -1868,14 +1824,14 @@ function updateApiStatus() {
if (configured) { if (configured) {
box.className = 'api-status-box configured'; box.className = 'api-status-box configured';
icon.className = 'fa-solid fa-link'; icon.className = 'fa-solid fa-link';
title.textContent = '已配置'; title.textContent = '宸查厤缃?;
desc.textContent = '可使用预设音色库和复刻音色'; desc.textContent = '<EFBFBD>娇鐢ㄩ<EFBFBD>璁鹃煶鑹插簱鍜屽<EFBFBD>鍒婚煶鑹?;
badge.className = 'header-badge active'; badge.className = 'header-badge active';
$('authVoiceNotice').style.display = 'none'; $('authVoiceNotice').style.display = 'none';
} else { } else {
box.className = 'api-status-box not-configured'; box.className = 'api-status-box not-configured';
icon.className = 'fa-solid fa-link-slash'; icon.className = 'fa-solid fa-link-slash';
title.textContent = '未配置'; title.textContent = '<EFBFBD>厤缃?;
desc.textContent = '閰嶇疆鍚庡彲浣跨敤棰勮<EFBFBD>闊宠壊搴撳拰澶嶅埢闊宠壊'; desc.textContent = '閰嶇疆鍚庡彲浣跨敤棰勮<EFBFBD>闊宠壊搴撳拰澶嶅埢闊宠壊';
badge.className = 'header-badge'; badge.className = 'header-badge';
$('authVoiceNotice').style.display = 'flex'; $('authVoiceNotice').style.display = 'flex';
@@ -1912,10 +1868,8 @@ function updateCurrentVoiceDisplay() {
} }
} }
// ═══════════════════════════════════════════════════════════════════════════ // 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?// 娓叉煋闊宠壊鍒楄〃
// 渲染音色列表 // 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?
// ═══════════════════════════════════════════════════════════════════════════
function renderMyVoiceList() { function renderMyVoiceList() {
const listEl = $('myVoiceList'); const listEl = $('myVoiceList');
const emptyEl = $('myVoiceEmpty'); const emptyEl = $('myVoiceEmpty');
@@ -1950,7 +1904,7 @@ function renderMyVoiceList() {
<div class="voice-item-name">${escapeHtml(s.name || s.value)} ${sourceBadge}</div> <div class="voice-item-name">${escapeHtml(s.name || s.value)} ${sourceBadge}</div>
<div class="voice-item-meta">${!canPlay ? '闇€閰嶇疆閴存潈' : escapeHtml(s.value.slice(0, 25))}</div> <div class="voice-item-meta">${!canPlay ? '闇€閰嶇疆閴存潈' : escapeHtml(s.value.slice(0, 25))}</div>
<div class="voice-item-edit-form"> <div class="voice-item-edit-form">
<input type="text" class="input voice-edit-input" value="${escapeHtml(s.name || '')}" placeholder="输入新名称"> <input type="text" class="input voice-edit-input" value="${escapeHtml(s.name || '')}" placeholder="杈撳叆鏂板悕绉?>
<button class="btn btn-xs btn-primary voice-edit-save"><i class="fa-solid fa-check"></i></button> <button class="btn btn-xs btn-primary voice-edit-save"><i class="fa-solid fa-check"></i></button>
<button class="btn btn-xs voice-edit-cancel"><i class="fa-solid fa-times"></i></button> <button class="btn btn-xs voice-edit-cancel"><i class="fa-solid fa-times"></i></button>
</div> </div>
@@ -2021,7 +1975,7 @@ function bindMyVoiceEvents(listEl) {
e.stopPropagation(); e.stopPropagation();
const value = btn.dataset.value; const value = btn.dataset.value;
const item = mySpeakers.find(s => s.value === value); const item = mySpeakers.find(s => s.value === value);
if (confirm(`删除「${item?.name || value}」?`)) { if (confirm(`鍒犻櫎銆?{item?.name || value}銆嶏紵`)) {
mySpeakers = mySpeakers.filter(s => s.value !== value); mySpeakers = mySpeakers.filter(s => s.value !== value);
if (selectedVoiceValue === value) { if (selectedVoiceValue === value) {
selectedVoiceValue = mySpeakers[0]?.value || ''; selectedVoiceValue = mySpeakers[0]?.value || '';
@@ -2045,8 +1999,8 @@ function renderTrialVoiceList() {
<div class="voice-item${isSelected ? ' selected' : ''}${inMy ? ' in-my-list' : ''}" data-value="${v.key}"> <div class="voice-item${isSelected ? ' selected' : ''}${inMy ? ' in-my-list' : ''}" data-value="${v.key}">
<div class="voice-item-radio"></div> <div class="voice-item-radio"></div>
<div class="voice-item-info"> <div class="voice-item-info">
<div class="voice-item-name">${escapeHtml(v.name)}${inMy ? ' <span class="source-badge trial">已添加</span>' : ''}</div> <div class="voice-item-name">${escapeHtml(v.name)}${inMy ? ' <span class="source-badge trial">宸叉坊鍔?/span>' : ''}</div>
<div class="voice-item-meta">${escapeHtml(v.tag)} · ${v.gender === 'female' ? '女' : '男'}</div> <div class="voice-item-meta">${escapeHtml(v.tag)} ${v.gender === 'female' ? '濂? : '?}</div>
</div> </div>
</div>`; </div>`;
}).join(''); }).join('');
@@ -2077,7 +2031,7 @@ function renderAuthVoiceList() {
const listEl = $('authVoiceList'); const listEl = $('authVoiceList');
if (!list.length) { if (!list.length) {
listEl.innerHTML = '<div class="rules-empty">没有匹配的音色</div>'; listEl.innerHTML = '<div class="rules-empty">娌℃湁鍖归厤鐨勯煶鑹?/div>';
return; return;
} }
@@ -2088,7 +2042,7 @@ function renderAuthVoiceList() {
<div class="voice-item${isSelected ? ' selected' : ''}${inMy ? ' in-my-list' : ''}" data-value="${escapeHtml(item.value)}"> <div class="voice-item${isSelected ? ' selected' : ''}${inMy ? ' in-my-list' : ''}" data-value="${escapeHtml(item.value)}">
<div class="voice-item-radio"></div> <div class="voice-item-radio"></div>
<div class="voice-item-info"> <div class="voice-item-info">
<div class="voice-item-name">${escapeHtml(item.name)}${inMy ? ' <span class="source-badge auth">已添加</span>' : ''}</div> <div class="voice-item-name">${escapeHtml(item.name)}${inMy ? ' <span class="source-badge auth">宸叉坊鍔?/span>' : ''}</div>
<div class="voice-item-meta">${escapeHtml(item.genderLabel)}${formatTagLabel(item.tags)}</div> <div class="voice-item-meta">${escapeHtml(item.genderLabel)}${formatTagLabel(item.tags)}</div>
</div> </div>
</div>`; </div>`;
@@ -2102,10 +2056,8 @@ function renderAuthVoiceList() {
}); });
} }
// ═══════════════════════════════════════════════════════════════════════════ // 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?// 鏁版嵁澶勭悊
// 数据处理 // 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?
// ═══════════════════════════════════════════════════════════════════════════
function normalizeMySpeakers(list) { function normalizeMySpeakers(list) {
if (!Array.isArray(list)) return []; if (!Array.isArray(list)) return [];
return list.map(item => ({ return list.map(item => ({
@@ -2200,15 +2152,13 @@ function collectForm() {
}; };
} }
// ═══════════════════════════════════════════════════════════════════════════ // 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?// 璇曞惉鍔熻兘
// 试听功能 // 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?
// ═══════════════════════════════════════════════════════════════════════════
function doTestVoice(speaker, source, textElId, statusElId) { function doTestVoice(speaker, source, textElId, statusElId) {
const text = $(textElId)?.value?.trim() || '你好,这是一段测试语音。'; const text = $(textElId)?.value?.trim() || '浣犲ソ锛岃繖鏄<EFBFBD>竴娈垫祴璇曡<EFBFBD>闊炽€?;
if (!speaker) { if (!speaker) {
setTestStatus(statusElId, 'error', '请先选择一个音色'); setTestStatus(statusElId, 'error', '璇峰厛閫夋嫨涓<EFBFBD>煶鑹?);
return; return;
} }
@@ -2227,10 +2177,8 @@ function doTestVoice(speaker, source, textElId, statusElId) {
}); });
} }
// ═══════════════════════════════════════════════════════════════════════════ // 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?// 娑堟伅澶勭悊
// 消息处理 // 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?
// ═══════════════════════════════════════════════════════════════════════════
window.addEventListener('message', ev => { window.addEventListener('message', ev => {
if (ev.origin !== PARENT_ORIGIN || ev.source !== parent) return; if (ev.origin !== PARENT_ORIGIN || ev.source !== parent) return;
if (!ev.data?.type?.startsWith('xb-tts:')) return; if (!ev.data?.type?.startsWith('xb-tts:')) return;
@@ -2243,14 +2191,14 @@ window.addEventListener('message', ev => {
case 'xb-tts:config-saved': case 'xb-tts:config-saved':
fillForm(payload); fillForm(payload);
handleSaveResult(true); handleSaveResult(true);
post('xb-tts:toast', { type: 'success', message: '配置已保存' }); post('xb-tts:toast', { type: 'success', message: '閰嶇疆宸蹭繚瀛? });
break; break;
case 'xb-tts:config-save-error': case 'xb-tts:config-save-error':
handleSaveResult(false); handleSaveResult(false);
post('xb-tts:toast', { type: 'error', message: payload?.message || '淇濆瓨澶辫触' }); post('xb-tts:toast', { type: 'error', message: payload?.message || '淇濆瓨澶辫触' });
break; break;
case 'xb-tts:test-done': case 'xb-tts:test-done':
['testMyStatus', 'testTrialStatus', 'testAuthStatus'].forEach(id => setTestStatus(id, 'playing', '播放中...')); ['testMyStatus', 'testTrialStatus', 'testAuthStatus'].forEach(id => setTestStatus(id, 'playing', '<EFBFBD>斁涓?..'));
setTimeout(() => ['testMyStatus', 'testTrialStatus', 'testAuthStatus'].forEach(id => setTestStatus(id, '', '')), 3000); setTimeout(() => ['testMyStatus', 'testTrialStatus', 'testAuthStatus'].forEach(id => setTestStatus(id, '', '')), 3000);
break; break;
case 'xb-tts:test-error': case 'xb-tts:test-error':
@@ -2263,10 +2211,7 @@ window.addEventListener('message', ev => {
} }
}); });
// ═══════════════════════════════════════════════════════════════════════════ // 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?// 鍒濆<E98D92>鍖?// 鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺愨晲鈺?
// 初始化
// ═══════════════════════════════════════════════════════════════════════════
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
buildAuthVoiceList(); buildAuthVoiceList();
initRulesEditors(); initRulesEditors();
@@ -2327,7 +2272,7 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
$('saveToMyVoiceTrialBtn').addEventListener('click', () => { $('saveToMyVoiceTrialBtn').addEventListener('click', () => {
if (!selectedTrialVoiceValue) { post('xb-tts:toast', { type: 'error', message: '请先选择一个音色' }); return; } if (!selectedTrialVoiceValue) { post('xb-tts:toast', { type: 'error', message: '璇峰厛閫夋嫨涓€涓<EFBFBD>煶鑹? }); return; }
const tv = TRIAL_VOICES.find(v => v.key === selectedTrialVoiceValue); const tv = TRIAL_VOICES.find(v => v.key === selectedTrialVoiceValue);
const name = $('saveAsNameTrial').value.trim() || tv?.name || selectedTrialVoiceValue; const name = $('saveAsNameTrial').value.trim() || tv?.name || selectedTrialVoiceValue;
@@ -2351,7 +2296,7 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
$('saveToMyVoiceAuthBtn').addEventListener('click', () => { $('saveToMyVoiceAuthBtn').addEventListener('click', () => {
if (!selectedAuthVoiceValue) { post('xb-tts:toast', { type: 'error', message: '请先选择一个音色' }); return; } if (!selectedAuthVoiceValue) { post('xb-tts:toast', { type: 'error', message: '璇峰厛閫夋嫨涓<EFBFBD>煶鑹? }); return; }
const av = authVoiceList.find(v => v.value === selectedAuthVoiceValue); const av = authVoiceList.find(v => v.value === selectedAuthVoiceValue);
const name = $('saveAsNameAuth').value.trim() || av?.name || selectedAuthVoiceValue; const name = $('saveAsNameAuth').value.trim() || av?.name || selectedAuthVoiceValue;
@@ -2404,4 +2349,4 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
</script> </script>
</body> </body>
</html> </html>