kiku-agent 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/README.md +459 -407
  2. package/package.json +27 -3
  3. package/server/agent/agentCore.js +635 -267
  4. package/server/agent/appPaths.js +2 -0
  5. package/server/agent/catalogEntityResolver.js +629 -0
  6. package/server/agent/citations.js +0 -21
  7. package/server/agent/clarificationAnalytics.js +1 -1
  8. package/server/agent/clarifyingQuestions.js +286 -216
  9. package/server/agent/cli.js +528 -73
  10. package/server/agent/commandFeatures.js +123 -24
  11. package/server/agent/commandRegistry.js +128 -62
  12. package/server/agent/commands.js +1410 -95
  13. package/server/agent/config.js +253 -163
  14. package/server/agent/contextCompactor.js +239 -25
  15. package/server/agent/dynamicCatalog.js +192 -0
  16. package/server/agent/envBootstrap.js +46 -0
  17. package/server/agent/envManager.js +317 -0
  18. package/server/agent/eventBus.js +3 -3
  19. package/server/agent/feedbackLearning.js +0 -8
  20. package/server/agent/gpuMetrics.js +188 -55
  21. package/server/agent/ingestionWorker.js +339 -0
  22. package/server/agent/lanDiscovery.js +369 -0
  23. package/server/agent/llmClient.js +111 -39
  24. package/server/agent/lmStudioWakeup.js +144 -0
  25. package/server/agent/mcpClient.js +2 -2
  26. package/server/agent/memory.js +635 -494
  27. package/server/agent/modelConfig.js +487 -0
  28. package/server/agent/newsIntelligence.js +219 -0
  29. package/server/agent/newsWatch.js +85 -0
  30. package/server/agent/prompts.js +146 -0
  31. package/server/agent/publicTools.js +7 -0
  32. package/server/agent/queryDetails.js +62 -1
  33. package/server/agent/research.js +314 -35
  34. package/server/agent/routingInterceptor.js +77 -0
  35. package/server/agent/rumoredProjects.js +15 -0
  36. package/server/agent/sanitizer.js +117 -17
  37. package/server/agent/subagent.js +52 -9
  38. package/server/agent/suggestionTui.js +33 -13
  39. package/server/agent/suggestions.js +29 -13
  40. package/server/agent/telemetry.js +0 -11
  41. package/server/agent/terminalImage.js +0 -1
  42. package/server/agent/textToolProtocol.js +190 -0
  43. package/server/agent/tokenTracker.js +220 -0
  44. package/server/agent/toolRouter.js +362 -216
  45. package/server/agent/tools.js +1155 -194
  46. package/server/agent/ui.js +144 -41
  47. package/server/agent/vramService.js +572 -0
  48. package/server/agent/webSearch.js +2279 -273
  49. package/server/agent/youtubeAuth.js +82 -73
  50. package/server/agent/youtubeTranscript.js +209 -0
  51. package/server/apiRouter.js +383 -179
  52. package/server/mcp/data/brands.js +5 -5
  53. package/server/mcp/data/discography.js +56 -45
  54. package/server/mcp/data/gallery.js +9 -0
  55. package/server/mcp/data/index.js +1 -0
  56. package/server/mcp/data/industryIndex.js +251 -0
  57. package/server/mcp/data/profile.js +5 -5
  58. package/server/mcp/data/timeline.js +2 -2
  59. package/server/mcp/data/works.js +2 -2
  60. package/server/mcp/handler.js +0 -2
  61. package/server/mcp/prompts.js +42 -2
  62. package/server/mcp/resources.js +5 -1
  63. package/server/mcp/security.js +25 -14
  64. package/server/mcp/server.js +92 -3
  65. package/server/mcp/tools.js +20 -11
  66. package/server/web-chat/core/connection.js +218 -194
  67. package/server/web-chat/core/conversationContext.js +1 -1
  68. package/server/web-chat/core/directCatalogEngine.js +190 -184
  69. package/server/web-chat/core/followUpEngine.js +88 -11
  70. package/server/web-chat/core/security.js +7 -4
  71. package/server/web-chat/router.js +54 -17
  72. package/server/web-chat/routes/bilibiliRoute.js +34 -0
  73. package/server/web-chat/routes/chatRoute.js +488 -397
  74. package/server/web-chat/routes/clarifyConfigRoute.js +1 -2
  75. package/server/web-chat/routes/exportRoute.js +2 -1
  76. package/server/web-chat/routes/lanDiscoveryRoute.js +90 -0
  77. package/server/web-chat/routes/newsRoute.js +36 -6
  78. package/server/web-chat/routes/providerRoute.js +87 -0
  79. package/server/web-chat/routes/staticRoute.js +61 -13
  80. package/server/web-chat/routes/statusRoute.js +23 -2
  81. package/server/web-chat/routes/vramRoute.js +22 -0
  82. package/server/web-chat/server.js +135 -106
  83. package/server/web-chat/ui/htmlBuilder.js +8 -8
  84. package/server/web-chat/ui/previews/modelSettings.preview.html +41 -0
  85. package/server/web-chat/ui/scripts/agentBubbleSystem.js +0 -5
  86. package/server/web-chat/ui/scripts/client.js +10526 -8397
  87. package/server/web-chat/ui/scripts/webchatI18n.js +190 -138
  88. package/server/web-chat/ui/styles/main.css +7471 -4246
  89. package/server/web-chat/ui/templates/body.html +305 -179
  90. package/src/data/brandsData.js +29 -6
  91. package/src/data/discographyData.js +918 -900
  92. package/src/data/galleryData.js +66 -0
  93. package/src/data/industryIndexData.js +292 -0
  94. package/src/data/musicVideoData.js +48 -5
  95. package/src/data/profileData.js +15 -1
  96. package/src/data/timelineData.js +4 -3
  97. package/src/data/worksData.js +536 -535
  98. package/src/lib/vram/estimator.js +464 -0
  99. package/src/lib/vram/modelRegistry.js +387 -0
  100. package/tokens.css +33 -2
@@ -25,7 +25,7 @@
25
25
  <svg aria-hidden="true" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.4-3.4"/></svg>
26
26
  <span data-sidebar-i18n="search">Search Chat</span>
27
27
  </button>
28
- <button type="button" class="sidebar-nav-item" id="sidebar-music-button" onclick="openMusicModal()" title="คลังเพลงและ Jukebox">
28
+ <button type="button" class="sidebar-nav-item" id="sidebar-music-button" onclick="openMusicModal()" title="คลังเพลงและ Jukebox">
29
29
  <svg aria-hidden="true" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg>
30
30
  <span data-sidebar-i18n="music">Music Jukebox</span>
31
31
  </button>
@@ -91,8 +91,8 @@
91
91
  <div class="sidebar-section-heading static">
92
92
  <span data-sidebar-i18n="controlsHeading">System Controls</span>
93
93
  </div>
94
- <button type="button" class="sidebar-nav-item" onclick="exportChat()" title="ส่งออกบทสนทนาเป็นไฟล์ Markdown">
95
- <svg aria-hidden="true" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
94
+ <button type="button" class="sidebar-nav-item" onclick="exportChat()" title="ส่งออกบทสนทนาเป็นไฟล์ Markdown">
95
+ <svg aria-hidden="true" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
96
96
  <span data-sidebar-i18n="export">Export Chat</span>
97
97
  </button>
98
98
  <button type="button" class="sidebar-nav-item" onclick="togglePetals()" title="เปิด/ปิด กลีบดอกซากุระ">
@@ -111,6 +111,10 @@
111
111
  <svg aria-hidden="true" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
112
112
  <span id="sidebar-lang-label" data-sidebar-i18n="lang">Language (TH)</span>
113
113
  </button>
114
+ <button type="button" class="sidebar-nav-item" id="btn-settings-toggle" onclick="openSettingsModal()" title="ตั้งค่าโมเดลและ AI Provider">
115
+ <svg aria-hidden="true" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
116
+ <span id="sidebar-settings-label" data-sidebar-i18n="settings">AI Settings (Model)</span>
117
+ </button>
114
118
  </section>
115
119
  </div>
116
120
  </aside>
@@ -120,98 +124,14 @@
120
124
  <button type="button" class="toolbar-sidebar-button" id="toolbar-sidebar-button" onclick="toggleAppSidebar()" title="เปิด/ปิดแถบเมนู (Sidebar)" aria-label="เปิดแถบเมนู" aria-expanded="false">
121
125
  <svg aria-hidden="true" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="4" y1="6" x2="20" y2="6"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="18" x2="20" y2="18"/></svg>
122
126
  </button>
127
+ <span class="toolbar-title" style="font-weight: 700; font-size: 13px; color: var(--text);">KIKU AGENT</span>
123
128
  </div>
124
- </header>
125
-
126
- <div class="settings-drawer" id="settings-drawer">
127
- <div class="settings-container">
128
- <div class="settings-header-row">
129
- <div class="settings-title-group">
130
- <span class="settings-title-icon">⚙️</span>
131
- <div>
132
- <h4 class="settings-title">AI Engine &amp; Model Configuration</h4>
133
- <p class="settings-subtitle">กำหนดค่า AI Provider, Model ID, และพารามิเตอร์การประมวลผล</p>
134
- </div>
135
- </div>
136
- <div class="settings-status-pill" id="settings-status-pill">
137
- <span class="status-dot-pulse"></span>
138
- <span id="settings-provider-badge">LM Studio (Local)</span>
139
- </div>
140
- </div>
141
-
142
- <div class="settings-grid-cards">
143
-
144
- <div class="settings-card">
145
- <div class="settings-card-label">
146
- <svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg>
147
- <span>โมเดลและผู้ให้บริการ (AI Provider &amp; Model)</span>
148
- </div>
149
- <div class="settings-card-body">
150
- <div class="form-group">
151
- <label for="cfg-provider" id="lbl-cfg-provider">AI Provider</label>
152
- <div class="form-control-wrap">
153
- <select id="cfg-provider" class="form-control" onchange="onProviderChange()">
154
- <option value="lmstudio">LM Studio (Local gemma-4-e4b / Qwen)</option>
155
- <option value="gemini">Google Gemini (Gemini 3.7 Flash / Pro)</option>
156
- <option value="openai">OpenAI (GPT-4o / o1 / o3)</option>
157
- <option value="groq">Groq (Llama 3.3 70B Fast)</option>
158
- <option value="deepseek">DeepSeek AI (V3 / R1)</option>
159
- <option value="openrouter">OpenRouter (Unified Gateway)</option>
160
- </select>
161
- </div>
162
- </div>
163
- <div class="form-group">
164
- <label for="cfg-model" id="lbl-cfg-model">Model Name / ID</label>
165
- <div class="form-control-wrap">
166
- <input type="text" id="cfg-model" class="form-control" placeholder="google/gemma-4-e4b" value="google/gemma-4-e4b" onchange="autoSaveSettings()">
167
- </div>
168
- </div>
169
- </div>
170
- </div>
171
-
172
- <div class="settings-card">
173
- <div class="settings-card-label">
174
- <svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
175
- <span>คีย์และการประมวลผล (Security &amp; Inference)</span>
176
- </div>
177
- <div class="settings-card-body">
178
- <div class="form-group" id="group-cfg-apikey">
179
- <label for="cfg-apikey" id="lbl-cfg-apikey">Cloud API Key</label>
180
- <div class="form-control-wrap with-toggle">
181
- <input type="password" id="cfg-apikey" class="form-control" placeholder="sk-... / AIza... (เว้นว่างหากใช้ LM Studio)" onchange="autoSaveSettings()">
182
- <button type="button" id="btn-toggle-apikey-vis" class="btn-input-icon" onclick="toggleApiKeyVisibility()" title="แสดง/ซ่อน API Key">
183
- <svg aria-hidden="true" id="eye-icon" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
184
- </button>
185
- </div>
186
- <div id="cfg-apikey-help" class="form-help-text">🔒 ไม่จำเป็นต้องใส่หากใช้ Local LM Studio</div>
187
- </div>
188
-
189
- <div class="form-row-dual">
190
- <div class="form-group" style="flex: 1;">
191
- <label for="cfg-thinking" id="lbl-cfg-thinking">Thinking Mode</label>
192
- <div class="form-control-wrap">
193
- <select id="cfg-thinking" class="form-control" onchange="autoSaveSettings()">
194
- <option value="show">แสดงทั้งหมด (Deep Thinking)</option>
195
- <option value="collapse">ย่อเริ่มต้น (Auto Collapse)</option>
196
- <option value="hide">ซ่อนทั้งหมด (Direct Answer)</option>
197
- </select>
198
- </div>
199
- </div>
200
- <div class="form-group" style="flex: 1;">
201
- <div class="label-with-badge">
202
- <label for="cfg-temperature" id="lbl-cfg-temp">Creativity</label>
203
- <span class="temp-badge" id="temp-val">0.7</span>
204
- </div>
205
- <div class="form-control-wrap slider-wrap">
206
- <input type="range" id="cfg-temperature" min="0" max="1" step="0.05" value="0.7" class="form-slider" oninput="document.getElementById('temp-val').textContent = this.value; autoSaveSettings();">
207
- </div>
208
- </div>
209
- </div>
210
- </div>
211
- </div>
212
- </div>
129
+ <div class="toolbar-trailing" style="display: flex; align-items: center; gap: 8px;">
130
+ <button type="button" class="toolbar-sidebar-button" id="btn-mobile-settings-toggle" onclick="openSettingsModal()" title="ตั้งค่าโมเดลและ AI Provider" aria-label="ตั้งค่าโมเดล">
131
+ <svg aria-hidden="true" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
132
+ </button>
213
133
  </div>
214
- </div>
134
+ </header>
215
135
 
216
136
  <main class="main-chat-workspace" id="main-chat-workspace">
217
137
  <div class="main-inner">
@@ -244,11 +164,6 @@
244
164
  </div>
245
165
  </div>
246
166
 
247
- <button type="button" id="btn-scroll-bottom" class="btn-scroll-bottom" onclick="scrollToBottomSmooth(true)" title="เลื่อนลงล่างสุด">
248
- <svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M19 12l-7 7-7-7"/></svg>
249
- <span>เลื่อนลงล่างสุด</span>
250
- </button>
251
-
252
167
  <div class="suggestions-drawer" id="suggestions-drawer">
253
168
  <div class="suggestions-header-bar">
254
169
  <span class="suggestions-title">
@@ -269,79 +184,6 @@
269
184
  <div class="chips-container" id="chips-container"></div>
270
185
  </div>
271
186
 
272
- <div id="attachment-tray" class="attachment-tray" style="display: none;"></div>
273
-
274
- <!-- Futuristic Glassmorphic Voice HUD (การรับฟังเสียง AI โฉมใหม่) -->
275
- <div id="voice-hud" class="voice-hud luxury-voice-hud" style="display: none;" role="region" aria-label="อินเทอร์เฟซการรับฟังเสียง">
276
- <div class="voice-hud-inner">
277
- <!-- Top Row: Status, Dynamic Orb, Language Controls -->
278
- <div class="voice-hud-header">
279
- <div class="voice-hud-identity">
280
- <div class="voice-live-orb" title="ไมโครโฟนทำงานอยู่">
281
- <span class="voice-orb-ring ring-1"></span>
282
- <span class="voice-orb-ring ring-2"></span>
283
- <span class="voice-orb-core">
284
- <svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/></svg>
285
- </span>
286
- </div>
287
- <div class="voice-identity-meta">
288
- <div class="voice-identity-title-row">
289
- <span class="voice-live-title">กำลังรับฟังเสียงสด...</span>
290
- <span id="voice-lang-badge" class="voice-lang-badge" title="ภาษาที่ตรวจพบขณะนี้">🇹🇭 ภาษาไทย</span>
291
- </div>
292
- <span class="voice-live-sub">พูดคำถามหรือเรื่องที่ต้องการค้นหาเกี่ยวกับจวีจิ้งอี</span>
293
- </div>
294
- </div>
295
-
296
- <!-- Language Pills Bar -->
297
- <div class="voice-lang-pills">
298
- <button type="button" class="voice-pill active" id="vpill-th" onclick="setVoiceRecognitionLang('th-TH')" title="ภาษาไทย">🇹🇭 ไทย</button>
299
- <button type="button" class="voice-pill" id="vpill-zh" onclick="setVoiceRecognitionLang('zh-CN')" title="ภาษาจีน (中文)">🇨🇳 中文</button>
300
- <button type="button" class="voice-pill" id="vpill-en" onclick="setVoiceRecognitionLang('en-US')" title="English">🇺🇸 EN</button>
301
- </div>
302
- </div>
303
-
304
- <!-- Middle Row: Interactive Waveform Canvas + Live Transcript Bubble -->
305
- <div class="voice-hud-body">
306
- <div class="voice-visualizer-wrap">
307
- <canvas id="voice-canvas" class="voice-canvas" width="540" height="42" aria-hidden="true"></canvas>
308
- </div>
309
-
310
- <div class="voice-transcript-card" id="voice-transcript-card">
311
- <div class="voice-transcript-content">
312
- <span id="voice-hud-text" class="voice-transcript-text">“กำลังรับฟังเสียงสด... กรุณาพูดคำถามของคุณ”</span>
313
- <span class="voice-streaming-cursor" aria-hidden="true"></span>
314
- </div>
315
- </div>
316
- </div>
317
-
318
- <!-- Bottom Row: Quick Actions -->
319
- <div class="voice-hud-footer">
320
- <div class="voice-hud-hint">
321
- <span class="voice-hint-bullet">●</span>
322
- <span>พูดเสร็จแล้วคลิกนำไปใช้ หรือส่งคำถามทันที</span>
323
- </div>
324
-
325
- <div class="voice-hud-actions">
326
- <button type="button" class="btn-voice-action btn-voice-discard" onclick="cancelSpeechRecognition()" title="ยกเลิกการบันทึกเสียง">
327
- <svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
328
- <span>ยกเลิก</span>
329
- </button>
330
-
331
- <button type="button" class="btn-voice-action btn-voice-finish" onclick="toggleSpeechRecognition()" title="เสร็จสิ้น นำข้อความลงในช่องพิมพ์">
332
- <svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><polyline points="20 6 9 17 4 12"/></svg>
333
- <span>นำไปใช้</span>
334
- </button>
335
-
336
- <button type="button" class="btn-voice-action btn-voice-send" id="btn-voice-send" onclick="sendVoiceQueryDirectly()" title="ส่งคำถามนี้ไปยัง AI ทันที">
337
- <svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><polygon points="6 3 20 12 6 21 6 3"/></svg>
338
- <span>ส่งคำถามทันที</span>
339
- </button>
340
- </div>
341
- </div>
342
- </div>
343
- </div>
344
-
345
187
  <!-- Chat-Integrated Mini Player Dock (ผนวกเข้ากับ UX/UI ช่องแชท) -->
346
188
  <aside id="mini-player-dock" class="mini-player-draggable-dock chat-integrated-dock" aria-label="Ju Jingyi Mini Player">
347
189
  <div class="mini-player-card" id="mini-player-card">
@@ -454,9 +296,55 @@
454
296
  </button>
455
297
  </aside>
456
298
 
299
+ <!-- Slash Commands Autocomplete Menu -->
300
+ <div class="slash-commands-menu" id="slash-commands-menu" role="menu" aria-label="เมนูคำสั่งลัด" style="display: none;"></div>
301
+
457
302
  <div class="input-box" id="input-box-container">
303
+ <!-- Integrated Voice HUD (แถบสั่งงานด้วยเสียง ผสานรวมในกรอบ input) -->
304
+ <div id="voice-hud" class="voice-hud voice-hud-integrated" style="display: none;" role="region" aria-label="อินเทอร์เฟซการรับฟังเสียง">
305
+ <div class="voice-hud-inner">
306
+ <div class="voice-hud-left">
307
+ <div class="voice-live-orb" title="ไมโครโฟนทำงานอยู่">
308
+ <span class="voice-orb-ring ring-1"></span>
309
+ <span class="voice-orb-core">
310
+ <svg aria-hidden="true" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/></svg>
311
+ </span>
312
+ </div>
313
+ <span class="voice-live-title">กำลังฟัง...</span>
314
+ <span id="voice-lang-badge" class="voice-lang-badge" title="ภาษาที่กำลังรับฟัง — คลิกปุ่มด้านขวาเพื่อสลับภาษา">🇹🇭 ฟังภาษาไทย (TH)</span>
315
+ <button type="button" id="voice-mismatch-hint" class="voice-mismatch-hint" style="display: none;" onclick="switchVoiceToDetectedLang()" title="สลับภาษาไมโครโฟนตามข้อความที่ตรวจพบ"></button>
316
+ <div class="voice-visualizer-wrap">
317
+ <canvas id="voice-canvas" class="voice-canvas" width="130" height="20" aria-hidden="true"></canvas>
318
+ </div>
319
+ <!-- Preserved for JS and test inspection -->
320
+ <span id="voice-hud-text" class="voice-transcript-text" style="display: none;">“กำลังรับฟังเสียงสด... กรุณาพูดคำถามของคุณ”</span>
321
+ </div>
322
+
323
+ <div class="voice-hud-right">
324
+ <div class="voice-lang-pills" role="radiogroup" aria-label="เลือกภาษาไมโครโฟน">
325
+ <button type="button" id="vpill-th" class="voice-pill active" onclick="setVoiceRecognitionLang('th-TH')" title="สลับฟังภาษาไทย (Thai Speech Recognition)">🇹🇭 ไทย</button>
326
+ <button type="button" id="vpill-zh" class="voice-pill" onclick="setVoiceRecognitionLang('zh-CN')" title="切换中文语音识别 (Chinese Speech Recognition)">🇨🇳 中文</button>
327
+ <button type="button" id="vpill-en" class="voice-pill" onclick="setVoiceRecognitionLang('en-US')" title="Switch to English Speech (English Speech Recognition)">🇺🇸 EN</button>
328
+ </div>
329
+ <div class="voice-hud-actions">
330
+ <button type="button" class="btn-voice-mini btn-voice-discard" onclick="cancelSpeechRecognition()" title="ยกเลิกการบันทึกเสียง">
331
+ <svg aria-hidden="true" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
332
+ <span>ยกเลิก</span>
333
+ </button>
334
+ <button type="button" class="btn-voice-mini btn-voice-send" id="btn-voice-send" onclick="sendVoiceQueryDirectly()" title="ส่งคำถามนี้ไปยัง AI ทันที">
335
+ <svg aria-hidden="true" width="11" height="11" viewBox="0 0 24 24" fill="currentColor"><polygon points="6 3 20 12 6 21 6 3"/></svg>
336
+ <span>ส่งให้ AI</span>
337
+ </button>
338
+ </div>
339
+ </div>
340
+ </div>
341
+ </div>
342
+
343
+ <!-- Attached Files & Images Tray (วางติดกับช่องพิมพ์ข้อความ) -->
344
+ <div id="attachment-tray" class="attachment-tray" style="display: none;"></div>
345
+
458
346
  <div class="input-box-top">
459
- <textarea id="prompt-input" rows="1" placeholder="ถามอะไรเกี่ยวกับจวีจิ้งอีก็ได้... (กด Enter เพื่อส่ง, Shift + Enter เพื่อขึ้นบรรทัดใหม่)" onkeydown="handleInputKeyDown(event)" oninput="autoGrowTextarea(this)" aria-label="พิมพ์ข้อความถาม AI"></textarea>
347
+ <textarea id="prompt-input" rows="1" placeholder="ถามอะไรเกี่ยวกับจวีจิ้งอีก็ได้... (กด Enter เพื่อส่ง, Shift + Enter เพื่อขึ้นบรรทัดใหม่, พิมพ์ / เพื่อดูคำสั่ง)" onkeydown="handleInputKeyDown(event)" oninput="autoGrowTextarea(this)" aria-label="พิมพ์ข้อความถาม AI"></textarea>
460
348
  </div>
461
349
 
462
350
  <div class="input-box-bottom">
@@ -467,10 +355,11 @@
467
355
  </span>
468
356
  </button>
469
357
  <div class="quick-action-menu" id="quick-action-menu" role="menu" aria-label="เครื่องมือด่วน">
470
- <button type="button" role="menuitem" onclick="handleImageUpload(); closeQuickActions()"><span class="quick-item-icon"></span><span>แนบรูปภาพ / ไฟล์</span></button>
471
- <button type="button" role="menuitem" onclick="toggleSpeechRecognition(); closeQuickActions()"><span class="quick-item-icon"></span><span>สั่งงานด้วยเสียง</span></button>
472
- <button type="button" role="menuitem" onclick="openQuizModal(); closeQuickActions()"><span class="quick-item-icon"></span><span>Fan Quiz Challenge</span></button>
473
- <button type="button" role="menuitem" onclick="openMusicModal(); closeQuickActions()"><span class="quick-item-icon"></span><span>คลังเพลง Jukebox</span></button>
358
+ <button type="button" role="menuitem" onclick="handleImageUpload(); closeQuickActions()"><span class="quick-item-icon" aria-hidden="true"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg></span><span>แนบรูปภาพ / ไฟล์</span></button>
359
+ <button type="button" role="menuitem" onclick="toggleSpeechRecognition(); closeQuickActions()"><span class="quick-item-icon" aria-hidden="true"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="22"/></svg></span><span>สั่งงานด้วยเสียง</span></button>
360
+ <button type="button" role="menuitem" onclick="openQuizModal(); closeQuickActions()"><span class="quick-item-icon" aria-hidden="true"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9H4.5a2.5 2.5 0 0 1 0-5H6"/><path d="M18 9h1.5a2.5 2.5 0 0 0 0-5H18"/><path d="M4 22h16"/><path d="M10 14.66V17c0 .55-.47.98-.97 1.21C7.85 18.75 7 20.24 7 22"/><path d="M14 14.66V17c0 .55.47.98.97 1.21C16.15 18.75 17 20.24 17 22"/><path d="M18 2H6v7a6 6 0 0 0 12 0V2Z"/></svg></span><span>Fan Quiz Challenge</span></button>
361
+ <button type="button" role="menuitem" onclick="openMusicModal(); closeQuickActions()"><span class="quick-item-icon" aria-hidden="true"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg></span><span>คลังเพลง Jukebox</span></button>
362
+ <button type="button" role="menuitem" onclick="openSettingsModal(); closeQuickActions()"><span class="quick-item-icon" aria-hidden="true"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h10M18 7h2M4 17h2M10 17h10M14 4v6M7 14v6"/></svg></span><span>ตั้งค่าโมเดล AI (Model Settings)</span></button>
474
363
  </div>
475
364
 
476
365
  <button type="button" id="btn-attach" class="btn-input-tool" title="แนบรูปภาพ" onclick="handleImageUpload()">
@@ -479,6 +368,13 @@
479
368
  <button type="button" id="btn-mic" class="btn-input-tool btn-mic" title="สั่งงานด้วยเสียง" onclick="toggleSpeechRecognition()">
480
369
  <svg aria-hidden="true" width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="22"/></svg>
481
370
  </button>
371
+
372
+ <!-- Active Model Status Chip -->
373
+ <button type="button" id="input-model-chip" class="input-model-chip" onclick="openSettingsModal()" title="คลิกเพื่อเปลี่ยนโมเดล AI หรือตั้งค่า">
374
+ <span class="model-chip-dot" id="input-model-chip-dot" aria-hidden="true"></span>
375
+ <span id="input-model-chip-icon" class="model-chip-icon">🌸</span>
376
+ <span id="input-model-chip-label" class="model-chip-label">Gemma 4 E4B</span>
377
+ </button>
482
378
  </div>
483
379
 
484
380
  <div class="input-actions-right">
@@ -487,7 +383,7 @@
487
383
  <svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><rect x="4" y="4" width="16" height="16" rx="3"/></svg>
488
384
  </span>
489
385
  </button>
490
- <button type="button" id="btn-send" class="btn-send" title="ส่งข้อความ (Enter)" onclick="submitQuery()" aria-label="ส่งข้อความ">
386
+ <button type="button" id="btn-send" class="btn-send btn-send-muted" title="ส่งข้อความ (Enter)" onclick="submitQuery()" aria-label="ส่งข้อความ">
491
387
  <span class="btn-send-icon">
492
388
  <svg aria-hidden="true" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
493
389
  </span>
@@ -528,7 +424,7 @@
528
424
  <button type="button" class="jukebox-search-clear" id="music-search-clear" onclick="clearMusicSearch()" title="ล้างการค้นหา">✕</button>
529
425
  </div>
530
426
  <div class="music-tabs">
531
- <button type="button" class="music-tab-pill active" id="pill-music-all" onclick="setJukeboxCategory('all')">ทั้งหมด</button>
427
+ <button type="button" class="music-tab-pill active" id="pill-music-all" onclick="setJukeboxCategory('all')">ทั้งหมด</button>
532
428
  <button type="button" class="music-tab-pill" id="pill-music-new" onclick="setJukeboxCategory('new')"> New 2026-2024</button>
533
429
  <button type="button" class="music-tab-pill" id="pill-music-ost" onclick="setJukeboxCategory('ost')"> เพลงซีรีส์ (OST)</button>
534
430
  <button type="button" class="music-tab-pill" id="pill-music-single" onclick="setJukeboxCategory('single')"> ซิงเกิลเดี่ยว</button>
@@ -793,4 +689,234 @@
793
689
  <button type="button" class="btn-msg-action" onclick="closeSponsorsModal()" style="padding:6px 14px; border-radius:8px;">ปิด</button>
794
690
  </div>
795
691
  </div>
796
- </div>
692
+ </div>
693
+
694
+ <!-- AI Engine & Model Configuration Modal Popup -->
695
+ <div class="modal-backdrop" id="settings-modal" onclick="if(event.target===this) closeSettingsModal()" onkeydown="if(event.key==='Escape') closeSettingsModal()" style="display:none;" role="dialog" aria-modal="true" aria-label="AI Engine & Model Configuration" tabindex="-1">
696
+ <div class="modal-dialog settings-modal-dialog">
697
+ <div class="modal-header settings-modal-header">
698
+ <div class="modal-title-wrap settings-heading-block">
699
+ <span class="settings-kicker">Inference control</span>
700
+ <h3 class="settings-modal-title">AI Engine &amp; Model Configuration</h3>
701
+ <p class="settings-modal-desc">กำหนดเส้นทางโมเดล การตอบสนอง และทรัพยากรของเครื่องในที่เดียว</p>
702
+ </div>
703
+ <div class="settings-modal-header-actions">
704
+ <button type="button" class="settings-status-pill" id="settings-status-pill" title="คลิกเพื่อซิงก์ตาม CLI / Server" onclick="resetToCliProvider()">
705
+ <span class="settings-status-mark" aria-hidden="true"></span>
706
+ <span id="settings-provider-badge">Auto (CLI Sync)</span>
707
+ </button>
708
+ <button type="button" class="btn-close-drawer settings-close-button" onclick="closeSettingsModal()" aria-label="ปิดหน้าต่าง"><svg aria-hidden="true" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
709
+ </div>
710
+ </div>
711
+
712
+ <div class="modal-body settings-modal-body">
713
+ <!-- Card 1: AI Provider & Model -->
714
+ <div class="settings-glass-panel">
715
+ <div class="settings-panel-header">
716
+ <svg aria-hidden="true" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg>
717
+ <span>ผู้ให้บริการและโมเดล (AI Provider &amp; Model)</span>
718
+ </div>
719
+ <div class="settings-panel-content">
720
+ <div class="form-group">
721
+ <label for="cfg-provider" id="lbl-cfg-provider">AI Provider</label>
722
+ <div class="form-control-wrap">
723
+ <select id="cfg-provider" class="form-control" onchange="onProviderChange()">
724
+ <option value="auto">ตาม CLI / Server (Auto Sync)</option>
725
+ <option value="lmstudio">LM Studio (Local gemma-4-e4b / Qwen)</option>
726
+ <option value="vllm">vLLM (Local / WSL - Port 8000)</option>
727
+ <option value="custom">Custom / LAN Server (Ollama, llama.cpp, gateway)</option>
728
+ <option value="gemini">Google Gemini (Gemini 3.7 Flash / Pro)</option>
729
+ <option value="openai">OpenAI (GPT-4o / o1 / o3)</option>
730
+ <option value="groq">Groq (Llama 3.3 70B Fast)</option>
731
+ <option value="deepseek">DeepSeek AI (V3 / R1)</option>
732
+ <option value="openrouter">OpenRouter (Unified Gateway)</option>
733
+ </select>
734
+ </div>
735
+ </div>
736
+ <div class="form-group">
737
+ <label for="cfg-model" id="lbl-cfg-model">Model Name / ID</label>
738
+ <div class="form-control-wrap">
739
+ <input type="text" id="cfg-model" class="form-control" placeholder="google/gemma-4-e4b" value="google/gemma-4-e4b" onchange="autoSaveSettings()">
740
+ </div>
741
+ </div>
742
+ <div class="form-group">
743
+ <label for="cfg-baseurl" id="lbl-cfg-baseurl">API Base URL</label>
744
+ <div class="form-control-wrap">
745
+ <input type="text" id="cfg-baseurl" class="form-control" placeholder="http://127.0.0.1:8000/v1" onchange="autoSaveSettings()">
746
+ </div>
747
+ <small class="form-hint" id="cfg-baseurl-help">LM Studio: :1234, vLLM: :8000 หรือ URL สำหรับ WSL/Network</small>
748
+ </div>
749
+ <div class="form-group lan-scan-group">
750
+ <label for="lan-scan-results" id="lbl-lan-scan">LAN Discovery — ค้นหา LM Server ในเครือข่าย</label>
751
+ <div class="lan-scan-row">
752
+ <button type="button" id="btn-lan-scan" class="btn-sync-cli" onclick="scanLanEndpoints()" title="สแกนหา LM Studio / vLLM / Ollama ในเครือข่ายและแสดงรายชื่อโมเดล">
753
+ <svg aria-hidden="true" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="2"/><path d="M16.24 7.76a6 6 0 0 1 0 8.49m-8.48-.01a6 6 0 0 1 0-8.49m11.31-2.82a10 10 0 0 1 0 14.14m-14.14 0a10 10 0 0 1 0-14.14"/></svg>
754
+ <span id="lan-scan-label">Scan LAN</span>
755
+ </button>
756
+ <small class="form-hint lan-scan-status" id="lan-scan-status"></small>
757
+ </div>
758
+ <input type="text" id="lan-scan-targets" class="form-control lan-scan-targets" placeholder="ระบุวงเอง (เว้นว่าง = อัตโนมัติ) เช่น 192.168.0.0/24, 10.0.0.1-10.0.0.50" onkeydown="if(event.key==='Enter'){event.preventDefault();scanLanEndpoints();}">
759
+ <small class="form-hint">รองรับ CIDR (192.168.0.0/24), ช่วง IP (10.0.0.1-10.0.0.50), IP เดี่ยว — คั่นด้วยเครื่องหมายจุลภาค</small>
760
+ <div id="lan-scan-results" class="lan-scan-results" hidden></div>
761
+ </div>
762
+ <div class="settings-quick-sync-row">
763
+ <button type="button" class="btn-sync-cli" onclick="resetToCliProvider()" title="รีเซ็ตกลับไปใช้โมเดลตามที่ CLI / Server กำหนด">
764
+ <svg aria-hidden="true" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 11a8.1 8.1 0 0 0-15.5-2M4 4v5h5M4 13a8.1 8.1 0 0 0 15.5 2M20 20v-5h-5"/></svg>
765
+ <span>Use CLI defaults</span>
766
+ </button>
767
+ </div>
768
+ </div>
769
+ </div>
770
+
771
+ <!-- Card 2: Security & Inference Parameters -->
772
+ <div class="settings-glass-panel">
773
+ <div class="settings-panel-header">
774
+ <svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
775
+ <span>ความปลอดภัยและพารามิเตอร์ (Security &amp; Inference)</span>
776
+ </div>
777
+ <div class="settings-panel-content">
778
+ <div class="form-group" id="group-cfg-apikey">
779
+ <label for="cfg-apikey" id="lbl-cfg-apikey">Cloud API Key</label>
780
+ <form class="form-control-wrap with-toggle" autocomplete="off" onsubmit="return false;" style="margin:0;">
781
+ <input type="text" id="cfg-apikey-username" name="username" autocomplete="username" value="kiku-cloud-user" hidden readonly tabindex="-1" aria-hidden="true" style="display:none;">
782
+ <input type="password" id="cfg-apikey" name="kiku-cloud-apikey" class="form-control" placeholder="ระบุ API Key (เว้นว่างหากใช้ตาม CLI / Local)" autocomplete="new-password" spellcheck="false" autocapitalize="off" onchange="autoSaveSettings()">
783
+ <button type="button" id="btn-toggle-apikey-vis" class="btn-input-icon" onclick="toggleApiKeyVisibility()" title="แสดง/ซ่อน API Key">
784
+ <svg aria-hidden="true" id="eye-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
785
+ </button>
786
+ </form>
787
+ <div id="cfg-apikey-help" class="form-help-text">เก็บไว้ในหน่วยความจำของเบราว์เซอร์สำหรับเซสชันนี้เท่านั้น</div>
788
+ </div>
789
+
790
+ <div class="form-group">
791
+ <label for="cfg-thinking" id="lbl-cfg-thinking">Thinking Mode</label>
792
+ <div class="form-control-wrap">
793
+ <select id="cfg-thinking" class="form-control" onchange="autoSaveSettings()">
794
+ <option value="show">แสดงทั้งหมด (Deep Thinking)</option>
795
+ <option value="collapse">ย่อเริ่มต้น (Auto Collapse)</option>
796
+ <option value="hide">ซ่อนทั้งหมด (Direct Answer)</option>
797
+ </select>
798
+ </div>
799
+ </div>
800
+
801
+ <div class="form-group">
802
+ <div class="label-with-badge">
803
+ <label for="cfg-temperature" id="lbl-cfg-temp">Creativity (Temp)</label>
804
+ <span class="temp-badge" id="temp-val">0.7</span>
805
+ </div>
806
+ <div class="form-control-wrap slider-wrap">
807
+ <input type="range" id="cfg-temperature" min="0" max="1" step="0.05" value="0.7" class="form-slider" oninput="onTemperatureInput(this)">
808
+ </div>
809
+ </div>
810
+ </div>
811
+ </div>
812
+
813
+ <!-- Card 3: Model Memory Footprint Calculator (Predictive VRAM Analyzer) -->
814
+ <div class="settings-glass-panel settings-vram-panel">
815
+ <div class="settings-panel-header">
816
+ <svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v-2"/><path d="M9 13v-2"/></svg>
817
+ <span>โมเดลและหน่วยความจำ (Model Memory Footprint Calculator)</span>
818
+ </div>
819
+ <div class="settings-panel-content" id="vram-calc">
820
+ <div id="vram-cloud-notice" class="vram-cloud-notice" style="display:none;">
821
+ <div class="vram-cloud-text">
822
+ <strong>โหมดแชท Cloud (<span id="vram-cloud-prov-name">Gemini</span>):</strong>
823
+ <span>ประมวลผลบนเซิร์ฟเวอร์ผู้ให้บริการ ไม่ใช้ VRAM ของการ์ดจอในเครื่อง | ตารางคำนวณด้านล่างใช้สำหรับจำลอง/ประเมินโมเดล Local</span>
824
+ </div>
825
+ </div>
826
+ <div class="form-group">
827
+ <label for="vram-model">โมเดลเป้าหมายสำหรับคำนวณ (Target Model)</label>
828
+ <div class="form-control-wrap">
829
+ <select id="vram-model" class="form-control" onchange="onVramModelChange()">
830
+ <option value="auto" selected>Auto: active local model</option>
831
+ </select>
832
+ </div>
833
+ <div id="vram-model-custom-wrap" style="display:none; margin-top: 8px;">
834
+ <input type="text" id="vram-model-custom" class="form-control" placeholder="พิมพ์ชื่อโมเดล HuggingFace หรือ path ไฟล์ .gguf" oninput="scheduleVramEstimate()">
835
+ </div>
836
+ <div id="vram-model-note" class="form-help-text">เลือกโมเดล Local / Preset ที่ต้องการประเมิน VRAM</div>
837
+ </div>
838
+ <div class="form-group">
839
+ <label for="vram-hardware">Hardware Target</label>
840
+ <div class="form-control-wrap">
841
+ <select id="vram-hardware" class="form-control" onchange="updateVramEstimate()">
842
+ <option value="auto">Auto-detect hardware</option>
843
+ </select>
844
+ </div>
845
+ <div id="vram-hardware-note" class="form-help-text">กำลังตรวจจับ GPU…</div>
846
+ </div>
847
+ <div class="form-group">
848
+ <div class="label-with-badge">
849
+ <label for="vram-ctx">Context Length</label>
850
+ <div class="vram-ctx-badge-group" style="display: inline-flex; align-items: center; gap: 6px;">
851
+ <button type="button" id="btn-vram-ctx-auto" class="btn-sync-inline" onclick="resetVramCtxToAuto()" style="display: none;" title="รีเซ็ตกลับเป็นโหมด Auto">
852
+ <svg aria-hidden="true" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M20 11a8.1 8.1 0 0 0-15.5-2M4 4v5h5M4 13a8.1 8.1 0 0 0 15.5 2M20 20v-5h-5"/></svg>
853
+ <span>Auto</span>
854
+ </button>
855
+ <span class="temp-badge" id="vram-ctx-val">Auto (16,384)</span>
856
+ </div>
857
+ </div>
858
+ <div class="form-control-wrap slider-wrap">
859
+ <input type="range" id="vram-ctx" min="1024" max="131072" step="1024" value="16384" class="form-slider" oninput="onVramCtxInput(this)">
860
+ </div>
861
+ </div>
862
+ <div class="vram-calc-grid">
863
+ <div class="form-group">
864
+ <label for="vram-quant">Quantization</label>
865
+ <div class="form-control-wrap">
866
+ <select id="vram-quant" class="form-control" onchange="updateVramEstimate()">
867
+ <option value="fp16">FP16 / BF16 (16.0 bpw)</option>
868
+ <option value="q8_0">Q8_0 / INT8 (8.0 bpw)</option>
869
+ <option value="q6_k">Q6_K (6.56 bpw)</option>
870
+ <option value="q5_k_m">Q5_K_M (5.5 bpw)</option>
871
+ <option value="q4_k_m" selected>Q4_K_M (4.5 bpw)</option>
872
+ <option value="q4_0">Q4_0 / Q4_K_S (4.2 bpw)</option>
873
+ <option value="q3_k_m">Q3_K_M (3.4 bpw)</option>
874
+ <option value="q2_k">Q2_K (2.6 bpw)</option>
875
+ </select>
876
+ </div>
877
+ </div>
878
+ <div class="form-group">
879
+ <label for="vram-cache">Cache Precision</label>
880
+ <div class="form-control-wrap">
881
+ <select id="vram-cache" class="form-control" onchange="updateVramEstimate()">
882
+ <option value="16" selected>FP16 (16-bit)</option>
883
+ <option value="8">Q8_0 (8-bit)</option>
884
+ </select>
885
+ </div>
886
+ </div>
887
+ </div>
888
+ <div class="vram-breakdown" id="vram-breakdown">
889
+ <div class="vram-row"><span>Model Weights</span><b id="vram-weights">—</b></div>
890
+ <div class="vram-row"><span>KV Cache Memory</span><b id="vram-kv">—</b></div>
891
+ <div class="vram-row"><span>Static Overhead</span><b id="vram-overhead">—</b></div>
892
+ <div class="vram-row vram-total"><span>Total VRAM Required</span><b id="vram-total">—</b></div>
893
+ </div>
894
+ <div class="vram-gauge-wrap">
895
+ <div class="vram-gauge"><div class="vram-gauge-fill" id="vram-gauge-fill" style="width:0%"></div></div>
896
+ <div class="vram-gauge-meta">
897
+ <span id="vram-gauge-pct">—</span>
898
+ <span class="vram-status-pill vram-zone-unknown" id="vram-status-pill">UNVERIFIED</span>
899
+ </div>
900
+ </div>
901
+ <div id="vram-remediation" class="form-help-text vram-remediation"></div>
902
+ <label class="vram-override-row" id="vram-override-row" style="display:none;">
903
+ <input type="checkbox" id="vram-override" onchange="onVramOverrideChange()">
904
+ <span>Allow CPU offloading. This avoids an out-of-memory stop but adds substantial latency.</span>
905
+ </label>
906
+ </div>
907
+ </div>
908
+ </div>
909
+
910
+ <div class="modal-footer settings-modal-footer">
911
+ <div class="settings-modal-footer-hint">
912
+ <span>Changes apply immediately and save automatically.</span>
913
+ </div>
914
+ <div style="display: flex; gap: 8px;">
915
+ <button type="button" class="btn-primary-pill" id="settings-done-btn" onclick="closeSettingsModal()" style="min-width: 120px; justify-content: center;">
916
+ <span>Close configuration</span>
917
+ </button>
918
+ </div>
919
+ </div>
920
+ </div>
921
+ </div>
922
+