Sergy2077 commited on
Commit
458e8a9
·
verified ·
1 Parent(s): 9424680

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +6 -4
  2. index.html +1764 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Vtuber Test
3
- emoji: 🦀
4
- colorFrom: purple
5
  colorTo: blue
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: vtuber-test
3
+ emoji: 🐳
4
+ colorFrom: blue
5
  colorTo: blue
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,1764 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AI VTuber Creator Pro - Complete Studio</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ /* Animation styles */
11
+ @keyframes pulse {
12
+ 0% { opacity: 0.7; }
13
+ 50% { opacity: 1; }
14
+ 100% { opacity: 0.7; }
15
+ }
16
+
17
+ .pulse-connecting {
18
+ animation: pulse 1.5s infinite;
19
+ }
20
+
21
+ @keyframes liveFeedback {
22
+ 0% { transform: scale(1); }
23
+ 50% { transform: scale(1.05); }
24
+ 100% { transform: scale(1); }
25
+ }
26
+
27
+ .live-feedback {
28
+ animation: liveFeedback 2s infinite;
29
+ }
30
+
31
+ .debug-console {
32
+ font-family: monospace;
33
+ font-size: 13px;
34
+ background-color: rgba(0, 0, 0, 0.7);
35
+ border-left: 3px solid #4ade80;
36
+ }
37
+
38
+ /* Custom scrollbar */
39
+ ::-webkit-scrollbar {
40
+ width: 8px;
41
+ }
42
+
43
+ ::-webkit-scrollbar-track {
44
+ background: #1f2937;
45
+ }
46
+
47
+ ::-webkit-scrollbar-thumb {
48
+ background: #4b5563;
49
+ border-radius: 4px;
50
+ }
51
+
52
+ /* Tabs */
53
+ .tab-content {
54
+ display: none;
55
+ }
56
+
57
+ .tab-content.active {
58
+ display: block;
59
+ animation: fadeIn 0.3s ease-in-out;
60
+ }
61
+
62
+ @keyframes fadeIn {
63
+ from { opacity: 0; }
64
+ to { opacity: 1; }
65
+ }
66
+
67
+ /* Text-to-speech visualization */
68
+ .tts-visualizer {
69
+ display: flex;
70
+ justify-content: center;
71
+ align-items: flex-end;
72
+ height: 100px;
73
+ gap: 2px;
74
+ }
75
+
76
+ .tts-bar {
77
+ width: 6px;
78
+ background: #8b5cf6;
79
+ border-radius: 3px;
80
+ transition: height 0.1s ease;
81
+ }
82
+
83
+ /* Status ring */
84
+ .status-ring {
85
+ box-shadow: 0 0 10px currentColor;
86
+ }
87
+ </style>
88
+ </head>
89
+ <body class="bg-gray-900 text-gray-200 min-h-screen">
90
+ <!-- Debug Panel -->
91
+ <div id="debugPanel" class="fixed bottom-0 right-0 w-96 bg-gray-800 rounded-tl-lg shadow-xl z-50 hidden">
92
+ <div class="bg-gray-700 px-4 py-2 rounded-tl-lg flex justify-between items-center">
93
+ <h3 class="font-bold"><i class="fas fa-bug mr-2"></i>System Debugger</h3>
94
+ <button id="closeDebug" class="text-gray-400 hover:text-white">
95
+ <i class="fas fa-times"></i>
96
+ </button>
97
+ </div>
98
+ <div id="debugConsole" class="debug-console p-3 h-64 overflow-y-auto">
99
+ <div class="text-green-400">System debug console initialized...</div>
100
+ </div>
101
+ <div class="bg-gray-700 px-4 py-2 flex space-x-2">
102
+ <button id="fullDiagnostic" class="bg-orange-600 hover:bg-orange-700 px-3 py-1 rounded text-sm">
103
+ <i class="fas fa-stethoscope mr-1"></i>Full Diagnostic
104
+ </button>
105
+ <button id="exportLogs" class="bg-gray-600 hover:bg-gray-700 px-3 py-1 rounded text-sm">
106
+ <i class="fas fa-file-export mr-1"></i>Export Logs
107
+ </button>
108
+ </div>
109
+ </div>
110
+
111
+ <div class="container mx-auto px-4 py-6">
112
+ <header class="flex justify-between items-center mb-8">
113
+ <div>
114
+ <h1 class="text-4xl font-bold bg-gradient-to-r from-purple-500 to-pink-500 bg-clip-text text-transparent">
115
+ <i class="fas fa-robot mr-2"></i>AI VTuber Creator Pro
116
+ </h1>
117
+ <p class="text-gray-400">Complete streaming AI personality solution</p>
118
+ </div>
119
+ <div class="flex space-x-4">
120
+ <button id="saveConfig" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-lg">
121
+ <i class="fas fa-save mr-2"></i>Save Config
122
+ </button>
123
+ <button id="debugToggle" class="bg-gray-700 hover:bg-gray-600 px-4 py-2 rounded-lg">
124
+ <i class="fas fa-bug mr-2"></i>Debug
125
+ </button>
126
+ </div>
127
+ </header>
128
+
129
+ <!-- Status Bar -->
130
+ <div id="statusBar" class="bg-gray-800 rounded-lg p-3 mb-6 grid grid-cols-1 md:grid-cols-4 gap-4">
131
+ <div class="flex items-center space-x-2">
132
+ <div class="w-3 h-3 rounded-full bg-gray-600 status-ring" id="connectionStatusDot"></div>
133
+ <span class="text-gray-400">VTube Studio:</span>
134
+ <span id="vtsStatus" class="px-2 py-1 rounded bg-gray-700 text-sm">Not Connected</span>
135
+ </div>
136
+ <div class="flex items-center space-x-2">
137
+ <div class="w-3 h-3 rounded-full bg-gray-600" id="twitchStatusDot"></div>
138
+ <span class="text-gray-400">Twitch:</span>
139
+ <span id="twitchStatus" class="px-2 py-1 rounded bg-gray-700 text-sm">Disconnected</span>
140
+ </div>
141
+ <div class="flex items-center space-x-2">
142
+ <div class="w-3 h-3 rounded-full bg-gray-600" id="discordStatusDot"></div>
143
+ <span class="text-gray-400">Discord:</span>
144
+ <span id="discordStatus" class="px-2 py-1 rounded bg-gray-700 text-sm">Disconnected</span>
145
+ </div>
146
+ <div class="flex items-center space-x-2">
147
+ <div class="w-3 h-3 rounded-full bg-gray-600" id="aiStatusDot"></div>
148
+ <span class="text-gray-400">AI Personality:</span>
149
+ <span id="aiStatus" class="px-2 py-1 rounded bg-gray-700 text-sm">Inactive</span>
150
+ </div>
151
+ </div>
152
+
153
+ <!-- Main Content -->
154
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
155
+ <!-- Left Panel - Connections -->
156
+ <div class="lg:col-span-1 space-y-6">
157
+ <!-- VTube Studio Connection Panel -->
158
+ <div class="bg-gray-800 rounded-xl p-6 shadow-lg">
159
+ <h2 class="text-xl font-semibold mb-4 border-b border-gray-700 pb-2 flex items-center">
160
+ <i class="fas fa-project-diagram mr-2 text-purple-400"></i>VTube Studio
161
+ <span class="ml-auto w-3 h-3 rounded-full bg-gray-600 status-ring" id="vtsConnectionDot"></span>
162
+ </h2>
163
+
164
+ <div class="space-y-4">
165
+ <div>
166
+ <label class="block text-sm font-medium mb-1">API Settings</label>
167
+ <div class="grid grid-cols-2 gap-2">
168
+ <div>
169
+ <input type="number" id="vtsPort" value="8001" class="w-full bg-gray-700 rounded-lg px-3 py-2" placeholder="Port">
170
+ </div>
171
+ <button id="connectVTS" class="bg-purple-600 hover:bg-purple-700 px-3 py-2 rounded-lg">
172
+ <i class="fas fa-plug mr-2"></i>Connect
173
+ </button>
174
+ </div>
175
+ </div>
176
+
177
+ <div>
178
+ <label class="block text-sm font-medium mb-1">Model Status</label>
179
+ <div id="modelIndicator" class="p-2 bg-gray-700 rounded-lg flex items-center justify-between">
180
+ <div class="flex items-center">
181
+ <span class="w-3 h-3 rounded-full bg-gray-600 mr-2" id="modelStatusDot"></span>
182
+ <span id="modelName" class="text-gray-400">No model loaded</span>
183
+ </div>
184
+ <div class="flex space-x-2">
185
+ <button id="reloadModel" class="text-gray-400 hover:text-purple-400" title="Reload Model">
186
+ <i class="fas fa-redo"></i>
187
+ </button>
188
+ <button id="modelSettings" class="text-gray-400 hover:text-purple-400" title="Model Settings">
189
+ <i class="fas fa-cog"></i>
190
+ </button>
191
+ </div>
192
+ </div>
193
+ </div>
194
+
195
+ <div class="grid grid-cols-2 gap-2 pt-2">
196
+ <button id="testVTS" class="bg-blue-600 hover:bg-blue-700 px-3 py-2 rounded-lg">
197
+ <i class="fas fa-vial mr-2"></i>Test
198
+ </button>
199
+ <button id="vtsHotkeys" class="bg-purple-600 hover:bg-purple-700 px-3 py-2 rounded-lg">
200
+ <i class="fas fa-keyboard mr-2"></i>Hotkeys
201
+ </button>
202
+ </div>
203
+ </div>
204
+ </div>
205
+
206
+ <!-- Twitch Integration Panel -->
207
+ <div class="bg-gray-800 rounded-xl p-6 shadow-lg">
208
+ <h2 class="text-xl font-semibold mb-4 border-b border-gray-700 pb-2 flex items-center">
209
+ <i class="fab fa-twitch mr-2 text-purple-400"></i>Twitch
210
+ <span class="ml-auto w-3 h-3 rounded-full bg-gray-600 status-ring" id="twitchDot"></span>
211
+ </h2>
212
+
213
+ <div class="space-y-4">
214
+ <div>
215
+ <label class="block text-sm font-medium mb-1">Channel</label>
216
+ <div class="flex">
217
+ <span class="inline-flex items-center px-3 rounded-l-lg bg-gray-700 text-gray-400">twitch.tv/</span>
218
+ <input type="text" id="twitchChannel" class="flex-1 bg-gray-700 rounded-r-lg px-3 py-2" placeholder="yourchannel">
219
+ </div>
220
+ </div>
221
+
222
+ <div id="twitchAuthSection">
223
+ <label class="block text-sm font-medium mb-1">OAuth Token</label>
224
+ <div class="flex">
225
+ <input type="password" id="twitchOAuth" class="flex-1 bg-gray-700 rounded-l-lg px-3 py-2" placeholder="oauth:xxxxxxxx">
226
+ <button id="showOAuth" class="bg-gray-600 hover:bg-gray-700 rounded-r-lg px-3">
227
+ <i class="fas fa-eye"></i>
228
+ </button>
229
+ </div>
230
+ <div class="text-xs text-gray-500 mt-1">
231
+ Get token from <a href="https://twitchtokengenerator.com" target="_blank" class="text-purple-400 hover:underline">Twitch Token Generator</a>
232
+ </div>
233
+ </div>
234
+
235
+ <div class="grid grid-cols-2 gap-2 pt-2">
236
+ <button id="connectTwitch" class="bg-purple-600 hover:bg-purple-700 px-3 py-2 rounded-lg">
237
+ <i class="fas fa-plug mr-2"></i>Connect
238
+ </button>
239
+ <button id="twitchTest" class="bg-gray-600 hover:bg-gray-700 px-3 py-2 rounded-lg">
240
+ <i class="fas fa-comment-alt mr-2"></i>Test Chat
241
+ </button>
242
+ </div>
243
+ </div>
244
+ </div>
245
+
246
+ <!-- Discord Integration Panel -->
247
+ <div class="bg-gray-800 rounded-xl p-6 shadow-lg">
248
+ <h2 class="text-xl font-semibold mb-4 border-b border-gray-700 pb-2 flex items-center">
249
+ <i class="fab fa-discord mr-2 text-purple-400"></i>Discord
250
+ <span class="ml-auto w-3 h-3 rounded-full bg-gray-600 status-ring" id="discordDot"></span>
251
+ </h2>
252
+
253
+ <div class="space-y-4">
254
+ <div>
255
+ <label class="block text-sm font-medium mb-1">Bot Token</label>
256
+ <div class="flex">
257
+ <input type="password" id="discordToken" class="flex-1 bg-gray-700 rounded-l-lg px-3 py-2" placeholder="xxxxxxxx.xxxx.xxxx">
258
+ <button id="showDiscordToken" class="bg-gray-600 hover:bg-gray-700 rounded-r-lg px-3">
259
+ <i class="fas fa-eye"></i>
260
+ </button>
261
+ </div>
262
+ <div class="text-xs text-gray-500 mt-1">
263
+ Create a bot at <a href="https://discord.com/developers" target="_blank" class="text-purple-400 hover:underline">Discord Developer Portal</a>
264
+ </div>
265
+ </div>
266
+
267
+ <div>
268
+ <label class="block text-sm font-medium mb-1">Channel ID</label>
269
+ <input type="text" id="discordChannel" class="w-full bg-gray-700 rounded-lg px-3 py-2" placeholder="1234567890">
270
+ </div>
271
+
272
+ <div class="grid grid-cols-2 gap-2 pt-2">
273
+ <button id="connectDiscord" class="bg-purple-600 hover:bg-purple-700 px-3 py-2 rounded-lg">
274
+ <i class="fas fa-plug mr-2"></i>Connect
275
+ </button>
276
+ <button id="discordTest" class="bg-gray-600 hover:bg-gray-700 px-3 py-2 rounded-lg">
277
+ <i class="fas fa-comment-alt mr-2"></i>Test Message
278
+ </button>
279
+ </div>
280
+ </div>
281
+ </div>
282
+ </div>
283
+
284
+ <!-- Center Panel - AI Configuration -->
285
+ <div class="lg:col-span-1">
286
+ <div class="bg-gray-800 rounded-xl p-6 shadow-lg h-full">
287
+ <!-- Tab Navigation -->
288
+ <div class="flex border-b border-gray-700 mb-4">
289
+ <button class="tab-btn px-4 py-2 font-medium text-purple-400 border-b-2 border-purple-500" data-tab="aiConfig">AI Config</button>
290
+ <button class="tab-btn px-4 py-2 font-medium text-gray-400 hover:text-white" data-tab="personality">Personality</button>
291
+ <button class="tab-btn px-4 py-2 font-medium text-gray-400 hover:text-white" data-tab="voice">Voice</button>
292
+ </div>
293
+
294
+ <!-- Tab Contents -->
295
+ <div class="tab-content active" id="aiConfig">
296
+ <h2 class="text-xl font-semibold mb-4 flex items-center">
297
+ <i class="fas fa-brain mr-2 text-pink-400"></i>AI Settings
298
+ <span class="ml-auto w-3 h-3 rounded-full bg-gray-600 status-ring" id="aiConfigDot"></span>
299
+ </h2>
300
+
301
+ <div class="space-y-4">
302
+ <div>
303
+ <label class="block text-sm font-medium mb-1">AI Model</label>
304
+ <select id="aiModel" class="w-full bg-gray-700 rounded-lg px-3 py-2">
305
+ <option value="gpt4">GPT-4 Turbo (Recommended)</option>
306
+ <option value="claude">Claude 3 Opus</option>
307
+ <option value="llama3">Llama 3 70B</option>
308
+ <option value="mistral">Mistral 7B</option>
309
+ <option value="local">Local Model (Custom)</option>
310
+ </select>
311
+
312
+ <div id="localModelConfig" class="mt-2 p-3 bg-gray-700 rounded-lg hidden">
313
+ <div class="space-y-3">
314
+ <div>
315
+ <label class="block text-xs font-medium mb-1">Model Path</label>
316
+ <div class="flex">
317
+ <input type="text" id="modelPath" class="flex-1 bg-gray-600 rounded-l-lg px-3 py-2 text-sm" placeholder="/path/to/model.gguf">
318
+ <button class="bg-gray-700 hover:bg-gray-600 rounded-r-lg px-3 text-sm">
319
+ <i class="fas fa-folder-open"></i>
320
+ </button>
321
+ </div>
322
+ </div>
323
+ <div class="grid grid-cols-2 gap-2">
324
+ <div>
325
+ <label class="block text-xs font-medium mb-1">Context Size</label>
326
+ <input type="number" id="contextSize" class="w-full bg-gray-600 rounded-lg px-3 py-2 text-sm" value="4096">
327
+ </div>
328
+ <div>
329
+ <label class="block text-xs font-medium mb-1">GPU Layers</label>
330
+ <input type="number" id="gpuLayers" class="w-full bg-gray-600 rounded-lg px-3 py-2 text-sm" value="20">
331
+ </div>
332
+ </div>
333
+ </div>
334
+ </div>
335
+ </div>
336
+
337
+ <div>
338
+ <label class="block text-sm font-medium mb-1">Prompt Template</label>
339
+ <select id="promptTemplate" class="w-full bg-gray-700 rounded-lg px-3 py-2">
340
+ <option value="vtuber">VTuber (Default)</option>
341
+ <option value="assistant">Helpful Assistant</option>
342
+ <option value="tsundere">Tsundere Personality</option>
343
+ <option value="yandere">Yandere Personality</option>
344
+ <option value="custom">Custom Template</option>
345
+ </select>
346
+
347
+ <div id="customTemplate" class="mt-2 hidden">
348
+ <textarea id="customPrompt" class="w-full bg-gray-700 rounded-lg px-3 py-2 h-32 text-sm" placeholder="Enter your custom prompt template..."></textarea>
349
+ </div>
350
+ </div>
351
+
352
+ <div>
353
+ <label class="block text-sm font-medium mb-1">Response Settings</label>
354
+ <div class="space-y-3">
355
+ <div class="flex items-center justify-between">
356
+ <span class="text-sm">Creativity</span>
357
+ <input type="range" id="creativity" min="0" max="2" step="0.1" value="1" class="w-32">
358
+ <span class="text-sm text-purple-400">Balanced</span>
359
+ </div>
360
+
361
+ <div class="flex items-center justify-between">
362
+ <span class="text-sm">Response Length</span>
363
+ <input type="range" id="responseLength" min="1" max="5" value="3" class="w-32">
364
+ <span class="text-sm text-purple-400">Medium</span>
365
+ </div>
366
+
367
+ <div class="flex items-center space-x-2">
368
+ <input type="checkbox" id="allowMemory" class="rounded bg-gray-700" checked>
369
+ <label class="text-sm">Enable Conversation Memory</label>
370
+ </div>
371
+ </div>
372
+ </div>
373
+
374
+ <div class="pt-4">
375
+ <button id="testAI" class="w-full bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-lg">
376
+ <i class="fas fa-vial mr-2"></i>Test AI Response
377
+ </button>
378
+ </div>
379
+ </div>
380
+ </div>
381
+
382
+ <div class="tab-content" id="personality">
383
+ <h2 class="text-xl font-semibold mb-4"><i class="fas fa-user-tie mr-2 text-pink-400"></i>Personality</h2>
384
+ <div class="space-y-4">
385
+ <div>
386
+ <label class="block text-sm font-medium mb-1">Character Name</label>
387
+ <input type="text" id="charName" class="w-full bg-gray-700 rounded-lg px-3 py-2" placeholder="Your VTuber's name">
388
+ </div>
389
+
390
+ <div>
391
+ <label class="block text-sm font-medium mb-1">Personality Traits</label>
392
+ <div id="traitsContainer" class="space-y-2">
393
+ <div class="flex items-center space-x-2">
394
+ <input type="checkbox" id="traitFriendly" class="rounded bg-gray-700" checked>
395
+ <label class="text-sm">Friendly/Warm</label>
396
+ </div>
397
+ <div class="flex items-center space-x-2">
398
+ <input type="checkbox" id="traitShy" class="rounded bg-gray-700">
399
+ <label class="text-sm">Shy/Reserved</label>
400
+ </div>
401
+ <div class="flex items-center space-x-2">
402
+ <input type="checkbox" id="traitEnergetic" class="rounded bg-gray-700">
403
+ <label class="text-sm">Energetic/Excitable</label>
404
+ </div>
405
+ <div class="flex items-center space-x-2">
406
+ <input type="checkbox" id="traitSerious" class="rounded bg-gray-700">
407
+ <label class="text-sm">Serious/Professional</label>
408
+ </div>
409
+ <div class="flex items-center space-x-2">
410
+ <input type="checkbox" id="traitGloomy" class="rounded bg-gray-700">
411
+ <label class="text-sm">Gloomy/Pessimistic</label>
412
+ </div>
413
+ <div class="flex items-center space-x-2">
414
+ <input type="checkbox" id="traitOptimistic" class="rounded bg-gray-700" checked>
415
+ <label class="text-sm">Optimistic/Cheerful</label>
416
+ </div>
417
+ <div class="flex items-center space-x-2">
418
+ <input type="checkbox" id="traitBlunt" class="rounded bg-gray-700">
419
+ <label class="text-sm">Blunt/Direct</label>
420
+ </div>
421
+ <div class="flex items-center space-x-2">
422
+ <input type="checkbox" id="traitPlayful" class="rounded bg-gray-700" checked>
423
+ <label class="text-sm">Playful/Teasing</label>
424
+ </div>
425
+ </div>
426
+ </div>
427
+
428
+ <div>
429
+ <label class="block text-sm font-medium mb-1">Special Characteristics</label>
430
+ <textarea id="specialTraits" class="w-full bg-gray-700 rounded-lg px-3 py-2 h-24" placeholder="Unique quirks, speech patterns, catchphrases etc."></textarea>
431
+ </div>
432
+
433
+ <div>
434
+ <label class="block text-sm font-medium mb-1">Dialog Examples</label>
435
+ <textarea id="dialogExamples" class="w-full bg-gray-700 rounded-lg px-3 py-2 h-32" placeholder="Example responses you'd like the AI to emulate"></textarea>
436
+ </div>
437
+ </div>
438
+ </div>
439
+
440
+ <div class="tab-content" id="voice">
441
+ <h2 class="text-xl font-semibold mb-4 flex items-center">
442
+ <i class="fas fa-volume-up mr-2 text-pink-400"></i>Voice Settings
443
+ <span class="ml-auto w-3 h-3 rounded-full bg-gray-600 status-ring" id="voiceDot"></span>
444
+ </h2>
445
+
446
+ <div class="space-y-4">
447
+ <div>
448
+ <label class="block text-sm font-medium mb-1">TTS Engine</label>
449
+ <select id="ttsEngine" class="w-full bg-gray-700 rounded-lg px-3 py-2">
450
+ <option value="elevenlabs">ElevenLabs (Recommended)</option>
451
+ <option value="azure">Azure TTS</option>
452
+ <option value="google">Google TTS</option>
453
+ <option value="coqui">Coqui TTS</option>
454
+ <option value="local">Local TTS</option>
455
+ </select>
456
+
457
+ <div id="elevenLabsConfig" class="mt-2 p-3 bg-gray-700 rounded-lg">
458
+ <div class="space-y-3">
459
+ <div>
460
+ <label class="block text-xs font-medium mb-1">API Key</label>
461
+ <div class="flex">
462
+ <input type="password" id="elevenLabsKey" class="flex-1 bg-gray-600 rounded-l-lg px-3 py-2 text-sm" placeholder="xxxxxxxx">
463
+ <button id="showElevenLabsKey" class="bg-gray-700 hover:bg-gray-600 rounded-r-lg px-3 text-sm">
464
+ <i class="fas fa-eye"></i>
465
+ </button>
466
+ </div>
467
+ </div>
468
+ <div>
469
+ <label class="block text-xs font-medium mb-1">Voice Preset</label>
470
+ <select id="elevenLabsVoice" class="w-full bg-gray-600 rounded-lg px-3 py-2 text-sm">
471
+ <option value="rachel">Rachel (Female, Clear)</option>
472
+ <option value="dave">Dave (Male, Warm)</option>
473
+ <option value="clyde">Clyde (Male, Deep)</option>
474
+ <option value="sarah">Sarah (Female, Energetic)</option>
475
+ <option value="custom">Custom Voice ID</option>
476
+ </select>
477
+ </div>
478
+ <div id="elevenLabsCustomDiv" class="hidden">
479
+ <label class="block text-xs font-medium mb-1">Custom Voice ID</label>
480
+ <input type="text" id="elevenLabsCustom" class="w-full bg-gray-600 rounded-lg px-3 py-2 text-sm" placeholder="XXXXXXXXXX">
481
+ </div>
482
+ </div>
483
+ </div>
484
+
485
+ <div id="localTTSConfig" class="mt-2 p-3 bg-gray-700 rounded-lg hidden">
486
+ <div class="space-y-3">
487
+ <div>
488
+ <label class="block text-xs font-medium mb-1">Model Path</label>
489
+ <div class="flex">
490
+ <input type="text" id="localTTSModel" class="flex-1 bg-gray-600 rounded-l-lg px-3 py-2 text-sm" placeholder="/path/to/model">
491
+ <button class="bg-gray-700 hover:bg-gray-600 rounded-r-lg px-3 text-sm">
492
+ <i class="fas fa-folder-open"></i>
493
+ </button>
494
+ </div>
495
+ </div>
496
+ <div class="grid grid-cols-2 gap-2">
497
+ <div>
498
+ <label class="block text-xs font-medium mb-1">Speech Rate</label>
499
+ <input type="number" id="ttsSpeed" min="0.5" max="2" step="0.1" value="1" class="w-full bg-gray-600 rounded-lg px-3 py-2 text-sm">
500
+ </div>
501
+ <div>
502
+ <label class="block text-xs font-medium mb-1">Pitch</label>
503
+ <input type="number" id="ttsPitch" min="0.5" max="1.5" step="0.1" value="1" class="w-full bg-gray-600 rounded-lg px-3 py-2 text-sm">
504
+ </div>
505
+ </div>
506
+ </div>
507
+ </div>
508
+ </div>
509
+
510
+ <div>
511
+ <label class="block text-sm font-medium mb-1">Voice Settings</label>
512
+ <div class="space-y-3">
513
+ <div class="flex items-center justify-between">
514
+ <span class="text-sm">Speech Rate</span>
515
+ <input type="range" id="speechRate" min="0.5" max="1.5" step="0.1" value="1" class="w-32">
516
+ <span class="text-sm text-purple-400">Normal</span>
517
+ </div>
518
+
519
+ <div class="flex items-center justify-between">
520
+ <span class="text-sm">Voice Pitch</span>
521
+ <input type="range" id="voicePitch" min="0.8" max="1.2" step="0.05" value="1" class="w-32">
522
+ <span class="text-sm text-purple-400">Normal</span>
523
+ </div>
524
+
525
+ <div class="flex items-center space-x-2">
526
+ <input type="checkbox" id="voiceEffects" class="rounded bg-gray-700" checked>
527
+ <label class="text-sm">Enable Voice Effects</label>
528
+ </div>
529
+ </div>
530
+ </div>
531
+
532
+ <div>
533
+ <label class="block text-sm font-medium mb-1">Test Voice</label>
534
+ <div class="flex space-x-2">
535
+ <input type="text" id="ttsTestText" class="flex-1 bg-gray-700 rounded-lg px-3 py-2" placeholder="Enter text to test voice" value="Hello everyone! How are you doing today?">
536
+ <button id="testTTS" class="bg-purple-600 hover:bg-purple-700 px-3 py-2 rounded-lg">
537
+ <i class="fas fa-play"></i>
538
+ </button>
539
+ </div>
540
+ </div>
541
+
542
+ <div id="ttsVisualization" class="h-20 bg-gray-700 rounded-lg flex items-center justify-center hidden">
543
+ <div class="tts-visualizer" id="ttsVisualizer">
544
+ <div class="tts-bar"></div>
545
+ <div class="tts-bar"></div>
546
+ <div class="tts-bar"></div>
547
+ <div class="tts-bar"></div>
548
+ <div class="tts-bar"></div>
549
+ <div class="tts-bar"></div>
550
+ <div class="tts-bar"></div>
551
+ <div class="tts-bar"></div>
552
+ <div class="tts-bar"></div>
553
+ <div class="tts-bar"></div>
554
+ <div class="tts-bar"></div>
555
+ <div class="tts-bar"></div>
556
+ </div>
557
+ </div>
558
+ </div>
559
+ </div>
560
+ </div>
561
+ </div>
562
+
563
+ <!-- Right Panel - Preview & Interactions -->
564
+ <div class="lg:col-span-1 space-y-6">
565
+ <!-- Model Preview -->
566
+ <div class="bg-gray-800 rounded-xl p-6 shadow-lg">
567
+ <h2 class="text-xl font-semibold mb-4 border-b border-gray-700 pb-2 flex justify-between items-center">
568
+ <span><i class="fas fa-user mr-2"></i>Live Preview</span>
569
+ <span id="previewStatus" class="text-sm px-2 py-1 rounded bg-gray-700">Not Connected</span>
570
+ </h2>
571
+
572
+ <div class="relative">
573
+ <div class="bg-black rounded-lg w-full aspect-[4/5] flex items-center justify-center mb-4">
574
+ <div id="modelPreview" class="relative w-full h-full flex items-center justify-center">
575
+ <div id="previewPlaceholder" class="absolute inset-0 flex flex-col items-center justify-center p-6 text-center">
576
+ <i class="fas fa-user-ninja text-5xl mb-4 text-gray-500"></i>
577
+ <p class="text-gray-500">VTuber preview will appear here when connected</p>
578
+ <p class="text-sm text-gray-600 mt-2">Ensure VTube Studio is running with your model</p>
579
+ </div>
580
+ <img src="" id="vtuberPreview" class="absolute inset-0 w-full h-full object-contain hidden">
581
+ </div>
582
+ </div>
583
+
584
+ <!-- Preview Controls -->
585
+ <div class="grid grid-cols-3 gap-2">
586
+ <button id="resetPose" class="bg-gray-700 hover:bg-gray-600 px-3 py-2 rounded-lg" disabled>
587
+ <i class="fas fa-undo mr-2"></i>Reset
588
+ </button>
589
+ <button id="toggleTracking" class="bg-purple-600 hover:bg-purple-700 px-3 py-2 rounded-lg" disabled>
590
+ <i class="fas fa-eye mr-2"></i>Tracking
591
+ </button>
592
+ <button id="triggerHotkey" class="bg-pink-600 hover:bg-pink-700 px-3 py-2 rounded-lg" disabled>
593
+ <i class="fas fa-magic mr-2"></i>Hotkey
594
+ </button>
595
+ </div>
596
+ </div>
597
+ </div>
598
+
599
+ <!-- Interactions Panel -->
600
+ <div class="bg-gray-800 rounded-xl p-6 shadow-lg">
601
+ <h2 class="text-xl font-semibold mb-4 border-b border-gray-700 pb-2">
602
+ <i class="fas fa-comments mr-2"></i>Interactions
603
+ </h2>
604
+
605
+ <div class="space-y-4">
606
+ <div>
607
+ <label class="block text-sm font-medium mb-1">Chat Message</label>
608
+ <div class="flex space-x-2">
609
+ <input type="text" id="chatMessage" class="flex-1 bg-gray-700 rounded-lg px-3 py-2" placeholder="Send a test message...">
610
+ <button id="sendChat" class="bg-purple-600 hover:bg-purple-700 px-3 py-2 rounded-lg">
611
+ <i class="fas fa-paper-plane"></i>
612
+ </button>
613
+ </div>
614
+ </div>
615
+
616
+ <div>
617
+ <label class="block text-sm font-medium mb-1">AI Response</label>
618
+ <div id="aiResponse" class="bg-gray-700 rounded-lg p-3 h-32 overflow-y-auto text-sm">
619
+ <p class="text-gray-400">AI responses will appear here...</p>
620
+ </div>
621
+ </div>
622
+
623
+ <div class="grid grid-cols-3 gap-2">
624
+ <button id="triggerHappy" class="bg-yellow-500 hover:bg-yellow-600 px-3 py-2 rounded-lg">
625
+ <i class="fas fa-smile mr-2"></i>Happy
626
+ </button>
627
+ <button id="triggerAngry" class="bg-red-500 hover:bg-red-600 px-3 py-2 rounded-lg">
628
+ <i class="fas fa-angry mr-2"></i>Angry
629
+ </button>
630
+ <button id="triggerSurprise" class="bg-blue-500 hover:bg-blue-600 px-3 py-2 rounded-lg">
631
+ <i class="fas fa-surprise mr-2"></i>Surprise
632
+ </button>
633
+ </div>
634
+ </div>
635
+ </div>
636
+ </div>
637
+ </div>
638
+ </div>
639
+
640
+ <script>
641
+ // DOM Elements
642
+ // Status elements
643
+ const vtsStatus = document.getElementById('vtsStatus');
644
+ const twitchStatus = document.getElementById('twitchStatus');
645
+ const discordStatus = document.getElementById('discordStatus');
646
+ const aiStatus = document.getElementById('aiStatus');
647
+
648
+ // Status dots
649
+ const connectionStatusDot = document.getElementById('connectionStatusDot');
650
+ const twitchStatusDot = document.getElementById('twitchStatusDot');
651
+ const discordStatusDot = document.getElementById('discordStatusDot');
652
+ const aiStatusDot = document.getElementById('aiStatusDot');
653
+
654
+ // VTube Studio elements
655
+ const connectVTS = document.getElementById('connectVTS');
656
+ const vtsPort = document.getElementById('vtsPort');
657
+ const testVTS = document.getElementById('testVTS');
658
+ const vtsHotkeys = document.getElementById('vtsHotkeys');
659
+ const reloadModel = document.getElementById('reloadModel');
660
+ const modelSettings = document.getElementById('modelSettings');
661
+
662
+ // Model preview elements
663
+ const vtuberPreview = document.getElementById('vtuberPreview');
664
+ const previewPlaceholder = document.getElementById('previewPlaceholder');
665
+ const previewStatus = document.getElementById('previewStatus');
666
+ const resetPose = document.getElementById('resetPose');
667
+ const toggleTracking = document.getElementById('toggleTracking');
668
+ const triggerHotkey = document.getElementById('triggerHotkey');
669
+
670
+ // Twitch elements
671
+ const twitchChannel = document.getElementById('twitchChannel');
672
+ const twitchOAuth = document.getElementById('twitchOAuth');
673
+ const connectTwitch = document.getElementById('connectTwitch');
674
+ const twitchTest = document.getElementById('twitchTest');
675
+ const showOAuth = document.getElementById('showOAuth');
676
+
677
+ // Discord elements
678
+ const discordToken = document.getElementById('discordToken');
679
+ const discordChannel = document.getElementById('discordChannel');
680
+ const connectDiscord = document.getElementById('connectDiscord');
681
+ const discordTest = document.getElementById('discordTest');
682
+ const showDiscordToken = document.getElementById('showDiscordToken');
683
+
684
+ // AI Configuration elements
685
+ const aiModel = document.getElementById('aiModel');
686
+ const promptTemplate = document.getElementById('promptTemplate');
687
+ const customTemplate = document.getElementById('customTemplate');
688
+ const customPrompt = document.getElementById('customPrompt');
689
+ const creativity = document.getElementById('creativity');
690
+ const responseLength = document.getElementById('responseLength');
691
+ const allowMemory = document.getElementById('allowMemory');
692
+ const testAI = document.getElementById('testAI');
693
+ const localModelConfig = document.getElementById('localModelConfig');
694
+
695
+ // Personality elements
696
+ const charName = document.getElementById('charName');
697
+ const specialTraits = document.getElementById('specialTraits');
698
+ const dialogExamples = document.getElementById('dialogExamples');
699
+
700
+ // Voice elements
701
+ const ttsEngine = document.getElementById('ttsEngine');
702
+ const elevenLabsConfig = document.getElementById('elevenLabsConfig');
703
+ const localTTSConfig = document.getElementById('localTTSConfig');
704
+ const speechRate = document.getElementById('speechRate');
705
+ const voicePitch = document.getElementById('voicePitch');
706
+ const voiceEffects = document.getElementById('voiceEffects');
707
+ const ttsTestText = document.getElementById('ttsTestText');
708
+ const testTTS = document.getElementById('testTTS');
709
+ const ttsVisualization = document.getElementById('ttsVisualization');
710
+ const ttsVisualizer = document.getElementById('ttsVisualizer');
711
+
712
+ // Debug panel elements
713
+ const debugPanel = document.getElementById('debugPanel');
714
+ const debugConsole = document.getElementById('debugConsole');
715
+ const debugToggle = document.getElementById('debugToggle');
716
+ const closeDebug = document.getElementById('closeDebug');
717
+ const fullDiagnostic = document.getElementById('fullDiagnostic');
718
+ const exportLogs = document.getElementById('exportLogs');
719
+
720
+ // Interaction elements
721
+ const chatMessage = document.getElementById('chatMessage');
722
+ const sendChat = document.getElementById('sendChat');
723
+ const aiResponse = document.getElementById('aiResponse');
724
+ const triggerHappy = document.getElementById('triggerHappy');
725
+ const triggerAngry = document.getElementById('triggerAngry');
726
+ const triggerSurprise = document.getElementById('triggerSurprise');
727
+
728
+ // App state
729
+ let isVTSConnected = false;
730
+ let isTwitchConnected = false;
731
+ let isDiscordConnected = false;
732
+ let isAIActive = false;
733
+ let isTTSTesting = false;
734
+ let debugLogs = [];
735
+
736
+ // Initialize app
737
+ function init() {
738
+ setupEventListeners();
739
+ setupTabNavigation();
740
+ loadDefaultConfig();
741
+
742
+ // Check for debug mode in URL
743
+ const params = new URLSearchParams(window.location.search);
744
+ if (params.has('debug')) {
745
+ toggleDebugPanel();
746
+ }
747
+ }
748
+
749
+ // Set up event listeners
750
+ function setupEventListeners() {
751
+ // VTube Studio connection
752
+ connectVTS.addEventListener('click', toggleVTSConnection);
753
+ testVTS.addEventListener('click', testVTSConnection);
754
+
755
+ // Model controls
756
+ resetPose.addEventListener('click', resetModelPose);
757
+ toggleTracking.addEventListener('click', toggleFaceTracking);
758
+ reloadModel.addEventListener('click', reloadCurrentModel);
759
+ modelSettings.addEventListener('click', openModelSettings);
760
+ vtsHotkeys.addEventListener('click', configureHotkeys);
761
+
762
+ // Twitch connection
763
+ connectTwitch.addEventListener('click', toggleTwitchConnection);
764
+ twitchTest.addEventListener('click', testTwitchChat);
765
+ showOAuth.addEventListener('click', () => {
766
+ twitchOAuth.type = twitchOAuth.type === 'password' ? 'text' : 'password';
767
+ });
768
+
769
+ // Discord connection
770
+ connectDiscord.addEventListener('click', toggleDiscordConnection);
771
+ discordTest.addEventListener('click', testDiscordMessage);
772
+ showDiscordToken.addEventListener('click', () => {
773
+ discordToken.type = discordToken.type === 'password' ? 'text' : 'password';
774
+ });
775
+
776
+ // AI configuration
777
+ aiModel.addEventListener('change', updateModelConfig);
778
+ promptTemplate.addEventListener('change', updatePromptTemplate);
779
+ testAI.addEventListener('click', testAIResponse);
780
+
781
+ // Voice configuration
782
+ ttsEngine.addEventListener('change', updateTTSConfig);
783
+ speechRate.addEventListener('input', updateSpeechRateLabel);
784
+ voicePitch.addEventListener('input', updateVoicePitchLabel);
785
+ testTTS.addEventListener('click', testTTSVoice);
786
+
787
+ // Debug panel
788
+ debugToggle.addEventListener('click', toggleDebugPanel);
789
+ closeDebug.addEventListener('click', toggleDebugPanel);
790
+ fullDiagnostic.addEventListener('click', runFullDiagnostic);
791
+ exportLogs.addEventListener('click', exportDebugLogs);
792
+
793
+ // Interactions
794
+ sendChat.addEventListener('click', sendTestMessage);
795
+ triggerHappy.addEventListener('click', () => triggerEmotion('happy'));
796
+ triggerAngry.addEventListener('click', () => triggerEmotion('angry'));
797
+ triggerSurprise.addEventListener('click', () => triggerEmotion('surprise'));
798
+
799
+ // Text input enter key
800
+ chatMessage.addEventListener('keypress', (e) => {
801
+ if (e.key === 'Enter') sendTestMessage();
802
+ });
803
+
804
+ // TTS visualization bars
805
+ const bars = ttsVisualizer.querySelectorAll('.tts-bar');
806
+ bars.forEach(bar => {
807
+ bar.style.height = `${Math.random() * 80 + 20}%`;
808
+ });
809
+ }
810
+
811
+ // Set up tab navigation
812
+ function setupTabNavigation() {
813
+ const tabButtons = document.querySelectorAll('.tab-btn');
814
+ const tabContents = document.querySelectorAll('.tab-content');
815
+
816
+ tabButtons.forEach(button => {
817
+ button.addEventListener('click', () => {
818
+ // Remove active class from all buttons and contents
819
+ tabButtons.forEach(btn => {
820
+ btn.classList.remove('text-purple-400', 'border-purple-500');
821
+ btn.classList.add('text-gray-400');
822
+ });
823
+
824
+ tabContents.forEach(content => {
825
+ content.classList.remove('active');
826
+ });
827
+
828
+ // Add active class to clicked button and corresponding content
829
+ button.classList.add('text-purple-400', 'border-purple-500');
830
+ button.classList.remove('text-gray-400');
831
+
832
+ const tabId = button.getAttribute('data-tab');
833
+ document.getElementById(tabId).classList.add('active');
834
+ });
835
+ });
836
+ }
837
+
838
+ // Load default configuration
839
+ function loadDefaultConfig() {
840
+ // Set up event listeners for configuration changes
841
+ aiModel.value = 'gpt4';
842
+ updateModelConfig();
843
+
844
+ promptTemplate.value = 'vtuber';
845
+ updatePromptTemplate();
846
+
847
+ ttsEngine.value = 'elevenlabs';
848
+ updateTTSConfig();
849
+
850
+ // Set default character name if empty
851
+ if (!charName.value) charName.value = 'Aiko';
852
+
853
+ // Load creativity and response length labels
854
+ updateCreativityLabel();
855
+ updateResponseLengthLabel();
856
+
857
+ creativity.addEventListener('input', updateCreativityLabel);
858
+ responseLength.addEventListener('input', updateResponseLengthLabel);
859
+
860
+ // Initialize ElevenLabs key visibility toggle
861
+ document.getElementById('elevenLabsVoice').addEventListener('change', function() {
862
+ const customDiv = document.getElementById('elevenLabsCustomDiv');
863
+ customDiv.classList.toggle('hidden', this.value !== 'custom');
864
+ });
865
+ }
866
+
867
+ // Update model configuration UI
868
+ function updateModelConfig() {
869
+ const model = aiModel.value;
870
+ localModelConfig.classList.toggle('hidden', model !== 'local');
871
+
872
+ if (model === 'gpt4') {
873
+ logToConsole("Selected model: GPT-4 Turbo", "info");
874
+ } else if (model === 'claude') {
875
+ logToConsole("Selected model: Claude 3 Opus", "info");
876
+ } else if (model === 'local') {
877
+ logToConsole("Selected model: Local model", "info");
878
+ }
879
+ }
880
+
881
+ // Update prompt template UI
882
+ function updatePromptTemplate() {
883
+ const template = promptTemplate.value;
884
+ customTemplate.classList.toggle('hidden', template !== 'custom');
885
+
886
+ if (template === 'vtuber') {
887
+ logToConsole("Loaded default VTuber prompt template", "info");
888
+ } else if (template === 'custom') {
889
+ logToConsole("Using custom prompt template", "info");
890
+ }
891
+ }
892
+
893
+ // Update TTS configuration UI
894
+ function updateTTSConfig() {
895
+ const engine = ttsEngine.value;
896
+
897
+ elevenLabsConfig.classList.toggle('hidden', engine !== 'elevenlabs');
898
+ localTTSConfig.classList.toggle('hidden', engine !== 'local');
899
+
900
+ if (engine === 'elevenlabs') {
901
+ logToConsole("Selected TTS engine: ElevenLabs", "info");
902
+ } else if (engine === 'local') {
903
+ logToConsole("Selected TTS engine: Local TTS", "info");
904
+ }
905
+ }
906
+
907
+ // Update creativity label
908
+ function updateCreativityLabel() {
909
+ const value = parseFloat(creativity.value);
910
+ const label = creativity.nextElementSibling;
911
+
912
+ if (value < 0.7) {
913
+ label.textContent = "Precise";
914
+ label.className = "text-sm text-blue-400";
915
+ } else if (value < 1.3) {
916
+ label.textContent = "Balanced";
917
+ label.className = "text-sm text-purple-400";
918
+ } else {
919
+ label.textContent = "Creative";
920
+ label.className = "text-sm text-pink-400";
921
+ }
922
+ }
923
+
924
+ // Update response length label
925
+ function updateResponseLengthLabel() {
926
+ const value = parseInt(responseLength.value);
927
+ const label = responseLength.nextElementSibling;
928
+
929
+ if (value === 1) {
930
+ label.textContent = "Short";
931
+ } else if (value === 2) {
932
+ label.textContent = "Medium-Short";
933
+ } else if (value === 3) {
934
+ label.textContent = "Medium";
935
+ } else if (value === 4) {
936
+ label.textContent = "Medium-Long";
937
+ } else {
938
+ label.textContent = "Long";
939
+ }
940
+
941
+ label.className = "text-sm text-purple-400";
942
+ }
943
+
944
+ // Update speech rate label
945
+ function updateSpeechRateLabel() {
946
+ const value = parseFloat(speechRate.value);
947
+ const label = speechRate.nextElementSibling;
948
+
949
+ if (value < 0.8) {
950
+ label.textContent = "Slow";
951
+ } else if (value < 1.2) {
952
+ label.textContent = "Normal";
953
+ } else {
954
+ label.textContent = "Fast";
955
+ }
956
+
957
+ label.className = "text-sm text-purple-400";
958
+ }
959
+
960
+ // Update voice pitch label
961
+ function updateVoicePitchLabel() {
962
+ const value = parseFloat(voicePitch.value);
963
+ const label = voicePitch.nextElementSibling;
964
+
965
+ if (value < 0.95) {
966
+ label.textContent = "Low";
967
+ } else if (value < 1.05) {
968
+ label.textContent = "Normal";
969
+ } else {
970
+ label.textContent = "High";
971
+ }
972
+
973
+ label.className = "text-sm text-purple-400";
974
+ }
975
+
976
+ // Toggle VTube Studio connection
977
+ function toggleVTSConnection() {
978
+ if (isVTSConnected) {
979
+ disconnectVTS();
980
+ } else {
981
+ connectVTS.textContent = "Connecting...";
982
+ connectVTS.classList.add('pulse-connecting');
983
+ vtsStatus.textContent = "Connecting...";
984
+ vtsStatus.className = "px-2 py-1 rounded bg-yellow-700 text-sm";
985
+ previewStatus.textContent = "Connecting...";
986
+ previewStatus.className = "text-sm px-2 py-1 rounded bg-yellow-700";
987
+ logToConsole("Attempting to connect to VTube Studio API...");
988
+
989
+ // Simulate connection delay
990
+ setTimeout(() => {
991
+ connectToVTS();
992
+ }, 1500 + Math.random() * 1000);
993
+ }
994
+ }
995
+
996
+ // Connect to VTube Studio
997
+ function connectToVTS() {
998
+ // For demo purposes, we'll simulate a connection that sometimes fails
999
+ const shouldFail = Math.random() < 0.2; // 20% chance of failure
1000
+
1001
+ if (shouldFail) {
1002
+ showError("Failed to connect to VTube Studio API! Check if VTube Studio is running with API enabled.");
1003
+ logToConsole("Failed to connect to VTube Studio API", "error");
1004
+
1005
+ connectVTS.textContent = "Connect";
1006
+ connectVTS.classList.remove('pulse-connecting');
1007
+ vtsStatus.textContent = "Connection Failed";
1008
+ vtsStatus.className = "px-2 py-1 rounded bg-red-700 text-sm";
1009
+ previewStatus.textContent = "Connection failed";
1010
+ previewStatus.className = "text-sm px-2 py-1 rounded bg-red-700";
1011
+ } else {
1012
+ isVTSConnected = true;
1013
+
1014
+ connectVTS.textContent = "Disconnect";
1015
+ connectVTS.classList.remove('pulse-connecting');
1016
+ vtsStatus.textContent = "Connected";
1017
+ vtsStatus.className = "px-2 py-1 rounded bg-green-700 text-sm";
1018
+ previewStatus.textContent = "Live";
1019
+ previewStatus.className = "text-sm px-2 py-1 rounded bg-green-700 animate-pulse";
1020
+
1021
+ // Enable model controls
1022
+ resetPose.disabled = false;
1023
+ toggleTracking.disabled = false;
1024
+ reloadModel.disabled = false;
1025
+ modelSettings.disabled = false;
1026
+ triggerHotkey.disabled = false;
1027
+
1028
+ // Show model preview
1029
+ previewPlaceholder.classList.add('hidden');
1030
+ vtuberPreview.classList.remove('hidden');
1031
+ vtuberPreview.src = "https://via.placeholder.com/800x1000/663399/ffffff?text=Live+VTuber";
1032
+
1033
+ // Update status dot
1034
+ connectionStatusDot.className = "w-3 h-3 rounded-full bg-green-500 status-ring";
1035
+ document.getElementById('vtsConnectionDot').className = "w-3 h-3 rounded-full bg-green-500 status-ring";
1036
+ document.getElementById('modelStatusDot').className = "w-3 h-3 rounded-full bg-green-500";
1037
+
1038
+ // Update model name
1039
+ document.getElementById('modelName').textContent = "Aiko (Default)";
1040
+ document.getElementById('modelName').className = "text-green-400";
1041
+
1042
+ showSuccess("Successfully connected to VTube Studio!");
1043
+ logToConsole("Successfully connected to VTube Studio", "success");
1044
+ logToConsole(`Model loaded: Aiko (Default)`);
1045
+ logToConsole(`Tracking resolution: 1280x720`);
1046
+ logToConsole(`API version: 1.8.2`);
1047
+ }
1048
+ }
1049
+
1050
+ // Disconnect from VTube Studio
1051
+ function disconnectVTS() {
1052
+ isVTSConnected = false;
1053
+
1054
+ connectVTS.textContent = "Connect";
1055
+ vtsStatus.textContent = "Not Connected";
1056
+ vtsStatus.className = "px-2 py-1 rounded bg-gray-700 text-sm";
1057
+ previewStatus.textContent = "Disconnected";
1058
+ previewStatus.className = "text-sm px-2 py-1 rounded bg-gray-700";
1059
+
1060
+ // Disable model controls
1061
+ resetPose.disabled = true;
1062
+ toggleTracking.disabled = true;
1063
+ reloadModel.disabled = true;
1064
+ modelSettings.disabled = true;
1065
+ triggerHotkey.disabled = true;
1066
+
1067
+ // Hide model preview
1068
+ previewPlaceholder.classList.remove('hidden');
1069
+ vtuberPreview.classList.add('hidden');
1070
+
1071
+ // Update status dot
1072
+ connectionStatusDot.className = "w-3 h-3 rounded-full bg-gray-600";
1073
+ document.getElementById('vtsConnectionDot').className = "w-3 h-3 rounded-full bg-gray-600";
1074
+ document.getElementById('modelStatusDot').className = "w-3 h-3 rounded-full bg-gray-600";
1075
+
1076
+ // Update model name
1077
+ document.getElementById('modelName').textContent = "No model loaded";
1078
+ document.getElementById('modelName').className = "text-gray-400";
1079
+
1080
+ logToConsole("Disconnected from VTube Studio", "info");
1081
+ }
1082
+
1083
+ // Test VTube Studio connection
1084
+ function testVTSConnection() {
1085
+ const port = vtsPort.value;
1086
+
1087
+ if (!port) {
1088
+ showError("Please enter a port number");
1089
+ return;
1090
+ }
1091
+
1092
+ testVTS.textContent = "Testing...";
1093
+ testVTS.disabled = true;
1094
+ logToConsole(`Testing connection to port ${port}...`);
1095
+
1096
+ // Simulate test connection
1097
+ setTimeout(() => {
1098
+ if (Math.random() > 0.2) { // 80% chance of success
1099
+ showSuccess(`Success! Port ${port} is available for connection`);
1100
+ logToConsole(`Port ${port} is available`, "success");
1101
+ } else {
1102
+ showError(`Failed to connect to port ${port}. Check if VTube Studio is running.`);
1103
+ logToConsole(`Failed to connect to port ${port}`, "error");
1104
+ }
1105
+
1106
+ testVTS.textContent = "Test";
1107
+ testVTS.disabled = false;
1108
+ }, 1500);
1109
+ }
1110
+
1111
+ // Reset model pose
1112
+ function resetModelPose() {
1113
+ logToConsole("Sending model reset command...");
1114
+
1115
+ // Visual feedback
1116
+ vtuberPreview.style.filter = "brightness(1.5)";
1117
+ setTimeout(() => {
1118
+ vtuberPreview.style.filter = "";
1119
+ }, 300);
1120
+
1121
+ logToConsole("Model pose reset", "success");
1122
+ }
1123
+
1124
+ // Toggle face tracking
1125
+ function toggleFaceTracking() {
1126
+ if (toggleTracking.textContent.includes("Enable")) {
1127
+ toggleTracking.innerHTML = '<i class="fas fa-eye-slash mr-2"></i>Disable';
1128
+ toggleTracking.className = "bg-red-600 hover:bg-red-700 px-3 py-2 rounded-lg";
1129
+ logToConsole("Face tracking enabled", "success");
1130
+ } else {
1131
+ toggleTracking.innerHTML = '<i class="fas fa-eye mr-2"></i>Enable';
1132
+ toggleTracking.className = "bg-purple-600 hover:bg-purple-700 px-3 py-2 rounded-lg";
1133
+ logToConsole("Face tracking disabled", "info");
1134
+ }
1135
+ }
1136
+
1137
+ // Reload current model
1138
+ function reloadCurrentModel() {
1139
+ logToConsole("Reloading current model...");
1140
+ reloadModel.innerHTML = '<i class="fas fa-spinner fa-spin mr-1"></i>';
1141
+
1142
+ // Simulate reload delay
1143
+ setTimeout(() => {
1144
+ showSuccess("Model reloaded successfully");
1145
+ logToConsole("Model reloaded successfully", "success");
1146
+ reloadModel.innerHTML = '<i class="fas fa-redo"></i>';
1147
+
1148
+ // Visual feedback
1149
+ vtuberPreview.style.opacity = "0.5";
1150
+ setTimeout(() => {
1151
+ vtuberPreview.style.opacity = "1";
1152
+ }, 300);
1153
+ }, 2000);
1154
+ }
1155
+
1156
+ // Open model settings
1157
+ function openModelSettings() {
1158
+ logToConsole("Opening model settings...");
1159
+ showInfo("Model settings would open in a separate dialog in the full application");
1160
+ }
1161
+
1162
+ // Configure hotkeys
1163
+ function configureHotkeys() {
1164
+ logToConsole("Opening hotkey configuration...");
1165
+ showInfo("Hotkey configuration would open in a separate dialog in the full application");
1166
+ }
1167
+
1168
+ // Toggle Twitch connection
1169
+ function toggleTwitchConnection() {
1170
+ if (isTwitchConnected) {
1171
+ disconnectTwitch();
1172
+ } else {
1173
+ const channel = twitchChannel.value;
1174
+ const oauth = twitchOAuth.value;
1175
+
1176
+ if (!channel || !oauth) {
1177
+ showError("Please enter both channel name and OAuth token");
1178
+ return;
1179
+ }
1180
+
1181
+ connectTwitch.textContent = "Connecting...";
1182
+ connectTwitch.classList.add('pulse-connecting');
1183
+ twitchStatus.textContent = "Connecting...";
1184
+ twitchStatus.className = "px-2 py-1 rounded bg-yellow-700 text-sm";
1185
+ logToConsole("Attempting to connect to Twitch...");
1186
+
1187
+ // Simulate connection delay
1188
+ setTimeout(() => {
1189
+ connectToTwitch(channel, oauth);
1190
+ }, 1500);
1191
+ }
1192
+ }
1193
+
1194
+ // Connect to Twitch
1195
+ function connectToTwitch(channel, oauth) {
1196
+ // For demo purposes, we'll simulate a connection that sometimes fails
1197
+ const shouldFail = Math.random() < 0.2; // 20% chance of failure
1198
+
1199
+ if (shouldFail) {
1200
+ showError("Failed to connect to Twitch! Check your OAuth token and channel name.");
1201
+ logToConsole("Failed to connect to Twitch", "error");
1202
+
1203
+ connectTwitch.textContent = "Connect";
1204
+ connectTwitch.classList.remove('pulse-connecting');
1205
+ twitchStatus.textContent = "Connection Failed";
1206
+ twitchStatus.className = "px-2 py-1 rounded bg-red-700 text-sm";
1207
+ } else {
1208
+ isTwitchConnected = true;
1209
+
1210
+ connectTwitch.textContent = "Disconnect";
1211
+ connectTwitch.classList.remove('pulse-connecting');
1212
+ twitchStatus.textContent = "Connected";
1213
+ twitchStatus.className = "px-2 py-1 rounded bg-green-700 text-sm";
1214
+
1215
+ // Update status dot
1216
+ twitchStatusDot.className = "w-3 h-3 rounded-full bg-green-500";
1217
+ document.getElementById('twitchDot').className = "w-3 h-3 rounded-full bg-green-500 status-ring";
1218
+
1219
+ showSuccess(`Successfully connected to Twitch channel ${channel}!`);
1220
+ logToConsole("Successfully connected to Twitch", "success");
1221
+ logToConsole(`Listening to channel: ${channel}`);
1222
+
1223
+ // For demo, simulate a chat message
1224
+ setTimeout(() => {
1225
+ logToConsole(`[Twitch] User123: Hello from chat!`, "chat");
1226
+ }, 2000);
1227
+ }
1228
+ }
1229
+
1230
+ // Disconnect from Twitch
1231
+ function disconnectTwitch() {
1232
+ isTwitchConnected = false;
1233
+
1234
+ connectTwitch.textContent = "Connect";
1235
+ twitchStatus.textContent = "Disconnected";
1236
+ twitchStatus.className = "px-2 py-1 rounded bg-gray-700 text-sm";
1237
+
1238
+ // Update status dot
1239
+ twitchStatusDot.className = "w-3 h-3 rounded-full bg-gray-600";
1240
+ document.getElementById('twitchDot').className = "w-3 h-3 rounded-full bg-gray-600 status-ring";
1241
+
1242
+ logToConsole("Disconnected from Twitch", "info");
1243
+ }
1244
+
1245
+ // Test Twitch chat
1246
+ function testTwitchChat() {
1247
+ logToConsole("Sending test Twitch chat message...");
1248
+ twitchTest.textContent = "Sending...";
1249
+ twitchTest.disabled = true;
1250
+
1251
+ setTimeout(() => {
1252
+ logToConsole(`[Twitch] TestUser: This is a test message from ${twitchChannel.value || "your channel"}!`, "chat");
1253
+ showSuccess("Test chat message sent!");
1254
+ twitchTest.textContent = "Test Chat";
1255
+ twitchTest.disabled = false;
1256
+ }, 1000);
1257
+ }
1258
+
1259
+ // Toggle Discord connection
1260
+ function toggleDiscordConnection() {
1261
+ if (isDiscordConnected) {
1262
+ disconnectDiscord();
1263
+ } else {
1264
+ const token = discordToken.value;
1265
+ const channel = discordChannel.value;
1266
+
1267
+ if (!token || !channel) {
1268
+ showError("Please enter both bot token and channel ID");
1269
+ return;
1270
+ }
1271
+
1272
+ connectDiscord.textContent = "Connecting...";
1273
+ connectDiscord.classList.add('pulse-connecting');
1274
+ discordStatus.textContent = "Connecting...";
1275
+ discordStatus.className = "px-2 py-1 rounded bg-yellow-700 text-sm";
1276
+ logToConsole("Attempting to connect to Discord...");
1277
+
1278
+ // Simulate connection delay
1279
+ setTimeout(() => {
1280
+ connectToDiscord(token, channel);
1281
+ }, 1500);
1282
+ }
1283
+ }
1284
+
1285
+ // Connect to Discord
1286
+ function connectToDiscord(token, channel) {
1287
+ // For demo purposes, we'll simulate a connection that sometimes fails
1288
+ const shouldFail = Math.random() < 0.2; // 20% chance of failure
1289
+
1290
+ if (shouldFail) {
1291
+ showError("Failed to connect to Discord! Check your bot token and channel ID.");
1292
+ logToConsole("Failed to connect to Discord", "error");
1293
+
1294
+ connectDiscord.textContent = "Connect";
1295
+ connectDiscord.classList.remove('pulse-connecting');
1296
+ discordStatus.textContent = "Connection Failed";
1297
+ discordStatus.className = "px-2 py-1 rounded bg-red-700 text-sm";
1298
+ } else {
1299
+ isDiscordConnected = true;
1300
+
1301
+ connectDiscord.textContent = "Disconnect";
1302
+ connectDiscord.classList.remove('pulse-connecting');
1303
+ discordStatus.textContent = "Connected";
1304
+ discordStatus.className = "px-2 py-1 rounded bg-green-700 text-sm";
1305
+
1306
+ // Update status dot
1307
+ discordStatusDot.className = "w-3 h-3 rounded-full bg-green-500";
1308
+ document.getElementById('discordDot').className = "w-3 h-3 rounded-full bg-green-500 status-ring";
1309
+
1310
+ showSuccess("Successfully connected to Discord!");
1311
+ logToConsole("Successfully connected to Discord", "success");
1312
+ logToConsole(`Listening to channel ID: ${channel}`);
1313
+
1314
+ // For demo, simulate a message
1315
+ setTimeout(() => {
1316
+ logToConsole(`[Discord] User456: Hello from Discord!`, "chat");
1317
+ }, 2000);
1318
+ }
1319
+ }
1320
+
1321
+ // Disconnect from Discord
1322
+ function disconnectDiscord() {
1323
+ isDiscordConnected = false;
1324
+
1325
+ connectDiscord.textContent = "Connect";
1326
+ discordStatus.textContent = "Disconnected";
1327
+ discordStatus.className = "px-2 py-1 rounded bg-gray-700 text-sm";
1328
+
1329
+ // Update status dot
1330
+ discordStatusDot.className = "w-3 h-3 rounded-full bg-gray-600";
1331
+ document.getElementById('discordDot').className = "w-3 h-3 rounded-full bg-gray-600 status-ring";
1332
+
1333
+ logToConsole("Disconnected from Discord", "info");
1334
+ }
1335
+
1336
+ // Test Discord message
1337
+ function testDiscordMessage() {
1338
+ logToConsole("Sending test Discord message...");
1339
+ discordTest.textContent = "Sending...";
1340
+ discordTest.disabled = true;
1341
+
1342
+ setTimeout(() => {
1343
+ logToConsole(`[Discord] TestBot: This is a test message to channel ${discordChannel.value || "your channel"}!`, "chat");
1344
+ showSuccess("Test message sent to Discord!");
1345
+ discordTest.textContent = "Test Message";
1346
+ discordTest.disabled = false;
1347
+ }, 1000);
1348
+ }
1349
+
1350
+ // Test AI response
1351
+ function testAIResponse() {
1352
+ const model = aiModel.value;
1353
+ const prompt = promptTemplate.value;
1354
+
1355
+ logToConsole(`Testing AI response with ${model} and ${prompt} template...`);
1356
+ testAI.textContent = "Thinking...";
1357
+ testAI.disabled = true;
1358
+
1359
+ // Generate a random response based on model
1360
+ const responses = {
1361
+ gpt4: [
1362
+ "Hello everyone! I'm your cheerful VTuber assistant, ready to chat and have fun today!",
1363
+ "Oh? You want to test my responses? Well, I'm happy to oblige~!",
1364
+ "How's everyone doing today? I'm all set up and ready to entertain you!"
1365
+ ],
1366
+ claude: [
1367
+ "Greetings! I'm here to engage in thoughtful conversation and provide helpful insights.",
1368
+ "This is a test response demonstrating my natural language capabilities.",
1369
+ "As an AI assistant, I aim to be helpful, harmless, and honest in our interactions."
1370
+ ],
1371
+ llama3: [
1372
+ "Hola hola! Testing 1 2 3! *waves energetically* Can you hear me okay?",
1373
+ "Oh wow, you're testing me? That's so cool! I can't wait to chat properly!",
1374
+ "*adjusts virtual hair* Hello hello~! Just testing my responses here!"
1375
+ ]
1376
+ };
1377
+
1378
+ // Select random response
1379
+ setTimeout(() => {
1380
+ const modelResponses = responses[model] || responses.gpt4;
1381
+ const randomResponse = modelResponses[Math.floor(Math.random() * modelResponses.length)];
1382
+
1383
+ aiResponse.innerHTML = `<p class="text-green-400">${randomResponse}</p>`;
1384
+ logToConsole(`AI response test successful: "${randomResponse}"`, "success");
1385
+
1386
+ testAI.textContent = "Test AI Response";
1387
+ testAI.disabled = false;
1388
+
1389
+ // If connected to TTS, trigger a test voice
1390
+ if (Math.random() > 0.5) {
1391
+ simulateTTS(randomResponse);
1392
+ }
1393
+ }, 1500 + Math.random() * 1000);
1394
+ }
1395
+
1396
+ // Test TTS voice
1397
+ function testTTSVoice() {
1398
+ const text = ttsTestText.value || "Hello everyone! How are you doing today?";
1399
+
1400
+ if (isTTSTesting) return;
1401
+ isTTSTesting = true;
1402
+
1403
+ logToConsole(`Testing TTS with: "${text}"`);
1404
+ testTTS.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
1405
+
1406
+ // Show visualization
1407
+ ttsVisualization.classList.remove('hidden');
1408
+
1409
+ // Animate the visualization bars
1410
+ const bars = ttsVisualizer.querySelectorAll('.tts-bar');
1411
+ const animationInterval = setInterval(() => {
1412
+ bars.forEach(bar => {
1413
+ bar.style.height = `${Math.random() * 80 + 20}%`;
1414
+ });
1415
+ }, 100);
1416
+
1417
+ // Simulate TTS processing
1418
+ setTimeout(() => {
1419
+ clearInterval(animationInterval);
1420
+ isTTSTesting = false;
1421
+ testTTS.innerHTML = '<i class="fas fa-play"></i>';
1422
+
1423
+ // Reset bars to idle state
1424
+ bars.forEach(bar => {
1425
+ bar.style.height = `${Math.random() * 30 + 10}%`;
1426
+ });
1427
+
1428
+ logToConsole("TTS test completed successfully", "success");
1429
+ showSuccess("Voice test completed. Check your audio output!");
1430
+ }, 2000 + text.length * 50);
1431
+ }
1432
+
1433
+ // Simulate TTS for AI responses
1434
+ function simulateTTS(text) {
1435
+ // Just log it for the demo
1436
+ logToConsole(`[TTS] Speaking: "${text}"`, "tts");
1437
+
1438
+ // For demo, just show visualization briefly
1439
+ ttsVisualization.classList.remove('hidden');
1440
+ const bars = ttsVisualizer.querySelectorAll('.tts-bar');
1441
+
1442
+ const animationInterval = setInterval(() => {
1443
+ bars.forEach(bar => {
1444
+ bar.style.height = `${Math.random() * 80 + 20}%`;
1445
+ });
1446
+ }, 100);
1447
+
1448
+ setTimeout(() => {
1449
+ clearInterval(animationInterval);
1450
+
1451
+ // Reset bars to idle state
1452
+ bars.forEach(bar => {
1453
+ bar.style.height = `${Math.random() * 30 + 10}%`;
1454
+ });
1455
+ }, 1500);
1456
+ }
1457
+
1458
+ // Send test message to AI
1459
+ function sendTestMessage() {
1460
+ const message = chatMessage.value;
1461
+
1462
+ if (!message) {
1463
+ showError("Please enter a message to send");
1464
+ return;
1465
+ }
1466
+
1467
+ logToConsole(`Sending test message: "${message}"`, "chat");
1468
+ aiResponse.innerHTML = `<p class="text-gray-400">Thinking...</p>`;
1469
+ sendChat.disabled = true;
1470
+ chatMessage.disabled = true;
1471
+
1472
+ // Simulate AI processing time
1473
+ setTimeout(() => {
1474
+ // Generate a response based on the message
1475
+ let response;
1476
+
1477
+ if (message.toLowerCase().includes('hello') || message.toLowerCase().includes('hi')) {
1478
+ response = `Oh hello there! *waves energetically* Thanks for saying "${message}" to me!`;
1479
+ } else if (message.toLowerCase().includes('how are you')) {
1480
+ response = "I'm doing great, thanks for asking! All my systems are functioning perfectly~";
1481
+ } else if (message.toLowerCase().includes('name')) {
1482
+ response = `My name is ${charName.value || "Aiko"}! Nice to meet you! *bows politely*`;
1483
+ } else {
1484
+ const randomResponses = [
1485
+ `Hmm, "${message}" is an interesting thing to say! What do you mean by that?`,
1486
+ "*tilts head* I'm not sure I understand completely, but I'd love to chat more about it!",
1487
+ `Oh! You said "${message}"! That's really cool~!`,
1488
+ "I'm still learning, but I'll do my best to respond thoughtfully!"
1489
+ ];
1490
+ response = randomResponses[Math.floor(Math.random() * randomResponses.length)];
1491
+ }
1492
+
1493
+ aiResponse.innerHTML = `<p class="text-green-400">${response}</p>`;
1494
+ logToConsole(`AI response: "${response}"`, "ai");
1495
+
1496
+ sendChat.disabled = false;
1497
+ chatMessage.disabled = false;
1498
+ chatMessage.value = "";
1499
+
1500
+ // If connected to TTS, trigger the voice
1501
+ if (Math.random() > 0.3) {
1502
+ simulateTTS(response);
1503
+ }
1504
+ }, 1000 + Math.random() * 2000);
1505
+ }
1506
+
1507
+ // Trigger emotion reaction
1508
+ function triggerEmotion(emotion) {
1509
+ if (!isVTSConnected) {
1510
+ showError("Connect to VTube Studio first to trigger emotions");
1511
+ return;
1512
+ }
1513
+
1514
+ logToConsole(`Triggering ${emotion} emotion...`, "emotion");
1515
+
1516
+ // Visual feedback on the preview
1517
+ let filter = "";
1518
+ let emoji = "";
1519
+
1520
+ switch(emotion) {
1521
+ case 'happy':
1522
+ filter = "brightness(1.2) saturate(1.3)";
1523
+ emoji = "😊";
1524
+ break;
1525
+ case 'angry':
1526
+ filter = "hue-rotate(-20deg) brightness(1.1) saturate(1.5)";
1527
+ emoji = "😠";
1528
+ break;
1529
+ case 'surprise':
1530
+ filter = "brightness(1.4) contrast(1.2)";
1531
+ emoji = "😲";
1532
+ break;
1533
+ }
1534
+
1535
+ vtuberPreview.style.filter = filter;
1536
+ aiResponse.innerHTML = `<p class="text-yellow-400">${emoji} ${charName.value || "VTuber"} looks ${emotion} now!</p>`;
1537
+
1538
+ setTimeout(() => {
1539
+ vtuberPreview.style.filter = "";
1540
+ }, 2000);
1541
+
1542
+ logToConsole(`Emotion triggered: ${emotion}`, "success");
1543
+ }
1544
+
1545
+ // Toggle debug panel
1546
+ function toggleDebugPanel() {
1547
+ debugPanel.classList.toggle('hidden');
1548
+ debugToggle.classList.toggle('bg-blue-600');
1549
+
1550
+ if (!debugPanel.classList.contains('hidden')) {
1551
+ logToConsole("Debug console opened", "info");
1552
+ }
1553
+ }
1554
+
1555
+ // Log to debug console
1556
+ function logToConsole(message, type = "info") {
1557
+ const timestamp = new Date().toLocaleTimeString();
1558
+ const messageDiv = document.createElement('div');
1559
+
1560
+ // Format the log message with type and timestamp
1561
+ switch(type) {
1562
+ case "error":
1563
+ messageDiv.className = "text-red-400";
1564
+ message = `[${timestamp}] ERROR: ${message}`;
1565
+ break;
1566
+ case "success":
1567
+ messageDiv.className = "text-green-400";
1568
+ message = `[${timestamp}] SUCCESS: ${message}`;
1569
+ break;
1570
+ case "warning":
1571
+ messageDiv.className = "text-yellow-400";
1572
+ message = `[${timestamp}] WARNING: ${message}`;
1573
+ break;
1574
+ case "chat":
1575
+ messageDiv.className = "text-blue-300";
1576
+ message = `[${timestamp}] CHAT: ${message}`;
1577
+ break;
1578
+ case "ai":
1579
+ messageDiv.className = "text-purple-300";
1580
+ message = `[${timestamp}] AI: ${message}`;
1581
+ break;
1582
+ case "tts":
1583
+ messageDiv.className = "text-pink-300";
1584
+ message = `[${timestamp}] TTS: ${message}`;
1585
+ break;
1586
+ case "emotion":
1587
+ messageDiv.className = "text-yellow-300";
1588
+ message = `[${timestamp}] EMOTION: ${message}`;
1589
+ break;
1590
+ default:
1591
+ messageDiv.className = "text-gray-400";
1592
+ message = `[${timestamp}] INFO: ${message}`;
1593
+ }
1594
+
1595
+ // Add to console and scroll to bottom
1596
+ messageDiv.textContent = message;
1597
+ debugConsole.appendChild(messageDiv);
1598
+ debugConsole.scrollTop = debugConsole.scrollHeight;
1599
+
1600
+ // Save to debug logs
1601
+ debugLogs.push({
1602
+ timestamp: new Date().toISOString(),
1603
+ type,
1604
+ message: message.replace(/^\[.*?\]\s*/, '') // Remove timestamp for storage
1605
+ });
1606
+ }
1607
+
1608
+ // Run full diagnostic
1609
+ function runFullDiagnostic() {
1610
+ logToConsole("Running full system diagnostic...", "info");
1611
+ fullDiagnostic.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Diagnosing...';
1612
+ fullDiagnostic.disabled = true;
1613
+
1614
+ // Start with all systems
1615
+ const systems = [
1616
+ "VTube Studio Connection",
1617
+ "Twitch Integration",
1618
+ "Discord Integration",
1619
+ "AI Personality System",
1620
+ "TTS Engine",
1621
+ "Speech Processing"
1622
+ ];
1623
+
1624
+ let delay = 1000;
1625
+ systems.forEach((system, index) => {
1626
+ setTimeout(() => {
1627
+ // Random pass/fail
1628
+ const passed = Math.random() > 0.3;
1629
+
1630
+ if (passed) {
1631
+ logToConsole(`${system}: OK`, "success");
1632
+ } else {
1633
+ logToConsole(`${system}: Warning - ${randomIssue(system)}`, "warning");
1634
+ }
1635
+
1636
+ // On last system, complete diagnostic
1637
+ if (index === systems.length - 1) {
1638
+ setTimeout(() => {
1639
+ logToConsole("System diagnostic completed", "info");
1640
+ showSuccess("System diagnostic completed");
1641
+
1642
+ fullDiagnostic.innerHTML = '<i class="fas fa-stethoscope mr-2"></i>Full Diagnostic';
1643
+ fullDiagnostic.disabled = false;
1644
+ }, 500);
1645
+ }
1646
+ }, delay);
1647
+
1648
+ delay += 800 + Math.random() * 700;
1649
+ });
1650
+ }
1651
+
1652
+ // Generate random issue for diagnostic
1653
+ function randomIssue(system) {
1654
+ const issues = {
1655
+ "VTube Studio Connection": [
1656
+ "High latency detected",
1657
+ "Model loading slowly",
1658
+ "API version mismatch"
1659
+ ],
1660
+ "Twitch Integration": [
1661
+ "Chat connection unstable",
1662
+ "Moderation filters not configured",
1663
+ "Rate limit approaching"
1664
+ ],
1665
+ "Discord Integration": [
1666
+ "Bot permissions need review",
1667
+ "Channel visibility issue",
1668
+ "Message queue backlog detected"
1669
+ ],
1670
+ "AI Personality System": [
1671
+ "Context memory fragmented",
1672
+ "Response latency above threshold",
1673
+ "Creativity scoring inconsistent"
1674
+ ],
1675
+ "TTS Engine": [
1676
+ "Voice artifacts detected",
1677
+ "Pronunciation dictionary needs updates",
1678
+ "Pitch modulation unstable"
1679
+ ],
1680
+ "Speech Processing": [
1681
+ "Background noise detected",
1682
+ "Voice activation sensitivity too high",
1683
+ "Speech-to-text confidence low"
1684
+ ]
1685
+ };
1686
+
1687
+ return issues[system][Math.floor(Math.random() * issues[system].length)];
1688
+ }
1689
+
1690
+ // Export debug logs
1691
+ function exportDebugLogs() {
1692
+ logToConsole("Exporting debug logs...", "info");
1693
+
1694
+ // Convert logs to JSON
1695
+ const logsJSON = JSON.stringify(debugLogs, null, 2);
1696
+
1697
+ // Create download link
1698
+ const blob = new Blob([logsJSON], { type: 'application/json' });
1699
+ const url = URL.createObjectURL(blob);
1700
+ const a = document.createElement('a');
1701
+ a.href = url;
1702
+ a.download = `vtuber-debug-${new Date().toISOString().replace(/[:.]/g, '-')}.json`;
1703
+ document.body.appendChild(a);
1704
+ a.click();
1705
+ document.body.removeChild(a);
1706
+ URL.revokeObjectURL(url);
1707
+
1708
+ showSuccess("Debug logs exported successfully");
1709
+ logToConsole("Debug logs exported successfully", "success");
1710
+ }
1711
+
1712
+ // Show error message
1713
+ function showError(message) {
1714
+ const errorDiv = document.createElement('div');
1715
+ errorDiv.className = "bg-red-900 text-white rounded-lg px-4 py-2 absolute bottom-4 right-4 animate-fade-in";
1716
+ errorDiv.innerHTML = `<i class="fas fa-exclamation-circle mr-2"></i> ${message}`;
1717
+
1718
+ document.body.appendChild(errorDiv);
1719
+
1720
+ setTimeout(() => {
1721
+ errorDiv.classList.add('animate-fade-out');
1722
+ setTimeout(() => {
1723
+ document.body.removeChild(errorDiv);
1724
+ }, 500);
1725
+ }, 5000);
1726
+ }
1727
+
1728
+ // Show success message
1729
+ function showSuccess(message) {
1730
+ const successDiv = document.createElement('div');
1731
+ successDiv.className = "bg-green-900 text-white rounded-lg px-4 py-2 absolute bottom-4 right-4 animate-fade-in";
1732
+ successDiv.innerHTML = `<i class="fas fa-check-circle mr-2"></i> ${message}`;
1733
+
1734
+ document.body.appendChild(successDiv);
1735
+
1736
+ setTimeout(() => {
1737
+ successDiv.classList.add('animate-fade-out');
1738
+ setTimeout(() => {
1739
+ document.body.removeChild(successDiv);
1740
+ }, 500);
1741
+ }, 5000);
1742
+ }
1743
+
1744
+ // Show info message
1745
+ function showInfo(message) {
1746
+ const infoDiv = document.createElement('div');
1747
+ infoDiv.className = "bg-blue-900 text-white rounded-lg px-4 py-2 absolute bottom-4 right-4 animate-fade-in";
1748
+ infoDiv.innerHTML = `<i class="fas fa-info-circle mr-2"></i> ${message}`;
1749
+
1750
+ document.body.appendChild(infoDiv);
1751
+
1752
+ setTimeout(() => {
1753
+ infoDiv.classList.add('animate-fade-out');
1754
+ setTimeout(() => {
1755
+ document.body.removeChild(infoDiv);
1756
+ }, 500);
1757
+ }, 5000);
1758
+ }
1759
+
1760
+ // Initialize the app
1761
+ document.addEventListener('DOMContentLoaded', init);
1762
+ </script>
1763
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - <a href="https://enzostvs-deepsite.hf.space?remix=Sergy2077/vtuber-test" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
1764
+ </html>