VirtualKimi commited on
Commit
8c89f37
·
verified ·
1 Parent(s): d48e96e

Upload 37 files

Browse files
CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
  # Virtual Kimi Changelog
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  # [1.0.8] - 2025-08-19
4
 
5
  ### Changed
 
1
  # Virtual Kimi Changelog
2
 
3
+ # [1.0.9] - 2025-08-23
4
+
5
+ ### Major System Improvements
6
+
7
+ - **Personality trait system overhaul**: Rebalanced progression curves and multipliers for more natural character development.
8
+ - **Unified emotion system**: Centralized emotion-to-video mapping and fixed all 13 emotions to properly affect traits.
9
+ - **Intelligence trait integration**: Added intelligence to personality calculations and video selection algorithms.
10
+ - **Enhanced emotion detection**: Improved keyword detection with better priorities and reduced conflicts.
11
+ - **Video selection rebalancing**: Fixed positive/negative bias and made auto-triggers more accessible.
12
+ - **Complete codebase synchronization**: Eliminated inconsistencies and redundancies across all modules.
13
+
14
+ - **Text streaming implementation**: Added real-time text streaming in chat for better user experience.
15
+
16
+ ### Bug Fixes
17
+
18
+ - Fixed trait calculation inconsistencies between modules.
19
+ - Resolved emotion detection conflicts (LISTENING, ROMANTIC/KISS categories).
20
+ - Corrected fallback values causing progression issues.
21
+
22
  # [1.0.8] - 2025-08-19
23
 
24
  ### Changed
index.html CHANGED
@@ -13,7 +13,7 @@
13
 
14
  <!-- SEO Meta Tags -->
15
  <meta name="description"
16
- content="Virtual Kimi is a an AI companion with evolving personality, advanced voice recognition and immersive interface. Discover the future of human-AI girlfriend relationships.">
17
  <meta name="keywords"
18
  content="artificial intelligence, virtual companion, emotional AI, voice recognition, advanced chatbot, Virtual Kimi, personalized AI assistant, girlfriend">
19
  <meta name="author" content="Jean & Kimi">
@@ -26,7 +26,7 @@
26
  <meta property="og:url" content="https://virtualkimi.com/virtual-kimi-app/">
27
  <meta property="og:title" content="Virtual Kimi - Virtual AI Companion">
28
  <meta property="og:description"
29
- content="Discover Virtual Kimi, an AI companion with evolving personality, voice recognition and immersive interface. The future of human-AI girlfriend relationships.">
30
  <meta property="og:image" content="kimi-icons/virtualkimi-logo.png">
31
 
32
  <!-- Twitter -->
@@ -43,7 +43,7 @@
43
  "@context": "https://schema.org",
44
  "@type": "SoftwareApplication",
45
  "name": "Virtual Kimi",
46
- "description": "Virtual AI companion with evolving personality, voice recognition and immersive interface",
47
  "applicationCategory": "AI Companion",
48
  "operatingSystem": "Web Browser",
49
  "offers": {
@@ -56,8 +56,8 @@
56
  "name": "Jean & Kimi"
57
  },
58
  "dateCreated": "2025-07-16",
59
- "dateModified": "2025-08-19",
60
- "version": "v1.0.8"
61
  }
62
  </script>
63
 
@@ -442,12 +442,7 @@
442
  </h3>
443
 
444
  <div class="config-row">
445
- <div class="config-label-section">
446
- <label class="config-label" data-i18n="temperature">Temperature (Creativity)</label>
447
- <small class="config-help" data-i18n="temperature_help">Controls randomness and
448
- creativity
449
- (default: 0.9). Higher values make output more creative but less focused.</small>
450
- </div>
451
  <div class="config-control">
452
  <div class="slider-container">
453
  <input type="range" class="kimi-slider" id="llm-temperature" min="0.0" max="1"
@@ -456,13 +451,15 @@
456
  </div>
457
  </div>
458
  </div>
 
 
 
 
 
 
459
 
460
  <div class="config-row">
461
- <div class="config-label-section">
462
- <label class="config-label" data-i18n="max_tokens">Max Tokens</label>
463
- <small class="config-help" data-i18n="max_tokens_help">Maximum response length in tokens
464
- (default: 400). Higher values allow longer responses.</small>
465
- </div>
466
  <div class="config-control">
467
  <div class="slider-container">
468
  <input type="range" class="kimi-slider" id="llm-max-tokens" min="10" max="8192"
@@ -471,13 +468,14 @@
471
  </div>
472
  </div>
473
  </div>
 
 
 
 
 
474
 
475
  <div class="config-row">
476
- <div class="config-label-section">
477
- <label class="config-label" data-i18n="top_p">Top P</label>
478
- <small class="config-help" data-i18n="top_p_help">Controls diversity of word selection
479
- (default: 0.9). Lower values make responses more focused.</small>
480
- </div>
481
  <div class="config-control">
482
  <div class="slider-container">
483
  <input type="range" class="kimi-slider" id="llm-top-p" min="0" max="1" step="0.01"
@@ -486,13 +484,13 @@
486
  </div>
487
  </div>
488
  </div>
 
 
 
 
 
489
  <div class="config-row">
490
- <div class="config-label-section">
491
- <label class="config-label" data-i18n="frequency_penalty">Frequency Penalty</label>
492
- <small class="config-help" data-i18n="frequency_penalty_help">Reduces repetition of
493
- words
494
- already used (default: 0.9). Higher values discourage repetitive language.</small>
495
- </div>
496
  <div class="config-control">
497
  <div class="slider-container">
498
  <input type="range" class="kimi-slider" id="llm-frequency-penalty" min="0" max="2"
@@ -501,12 +499,14 @@
501
  </div>
502
  </div>
503
  </div>
 
 
 
 
 
 
504
  <div class="config-row">
505
- <div class="config-label-section">
506
- <label class="config-label" data-i18n="presence_penalty">Presence Penalty</label>
507
- <small class="config-help" data-i18n="presence_penalty_help">Encourages discussing new
508
- topics (default: 0.8). Higher values promote topic diversity.</small>
509
- </div>
510
  <div class="config-control">
511
  <div class="slider-container">
512
  <input type="range" class="kimi-slider" id="llm-presence-penalty" min="0" max="2"
@@ -515,6 +515,27 @@
515
  </div>
516
  </div>
517
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
518
  </div>
519
 
520
  <div class="config-section">
@@ -568,6 +589,12 @@
568
  <span data-i18n="transcript_settings">Transcript Settings</span>
569
  </h3>
570
 
 
 
 
 
 
 
571
  <div class="config-row">
572
  <label class="config-label" data-i18n="show_transcript">Show Transcript</label>
573
  <div class="config-control">
@@ -604,6 +631,13 @@
604
  <div class="config-section">
605
  <h3><i class="fas fa-brain"></i> <span data-i18n="memory_system">Memory System</span></h3>
606
 
 
 
 
 
 
 
 
607
  <div class="config-row">
608
  <label class="config-label" data-i18n="enable_memory">Enable Intelligent Memory</label>
609
  <div class="config-control">
@@ -719,6 +753,14 @@
719
  <div class="tab-content" data-tab="plugins">
720
  <div class="config-section">
721
  <h3><i class="fas fa-plug"></i> <span data-i18n="plugin_manager">Plugin Manager</span></h3>
 
 
 
 
 
 
 
 
722
  <div id="plugin-list"></div>
723
  <div class="plugin-actions">
724
  <button class="kimi-button" id="refresh-plugins" data-i18n="refresh">Refresh</button>
@@ -1024,8 +1066,8 @@
1024
  <h3><i class="fas fa-code"></i> Technical Information</h3>
1025
  <div class="tech-info">
1026
  <p><strong>Created date :</strong> July 16, 2025</p>
1027
- <p><strong>Version :</strong> v1.0.8</p>
1028
- <p><strong>Last update :</strong> August 19, 2025</p>
1029
  <p><strong>Technologies :</strong> HTML5, CSS3, JavaScript ES6+, IndexedDB, Web Speech
1030
  API</p>
1031
  <p><strong>Status :</strong> ✅ Stable and functional</p>
@@ -1056,14 +1098,14 @@
1056
  "@context": "https://schema.org",
1057
  "@type": "WebPage",
1058
  "name": "Virtual Kimi - Virtual AI Companion",
1059
- "description": "Discover Virtual Kimi, an AI companion with evolving personality, multi-provider AI support, advanced voice recognition and immersive interface. The future of human-AI girlfriend relationships.",
1060
  "url": "https://virtualkimi.com/virtual-kimi-app/index.html",
1061
  "mainEntity": {
1062
  "@type": "SoftwareApplication",
1063
  "name": "Virtual Kimi",
1064
  "applicationCategory": "AI Companion",
1065
  "operatingSystem": "Web Browser",
1066
- "description": "Virtual AI companion with evolving personality, multi-provider AI support, voice recognition and immersive interface",
1067
  "features": [
1068
  "Advanced voice recognition",
1069
  "Evolving personality with 6 adjustable traits",
@@ -1076,8 +1118,8 @@
1076
  "@type": "Person",
1077
  "name": "Jean & Kimi"
1078
  },
1079
- "dateCreated": "2025-07-19",
1080
- "version": "v1.0.8"
1081
  }
1082
  }
1083
  </script>
 
13
 
14
  <!-- SEO Meta Tags -->
15
  <meta name="description"
16
+ content="Virtual Kimi is a an AI girlfriend and companion with evolving personality, advanced voice recognition and immersive interface. Discover the future of human-AI girlfriend relationships.">
17
  <meta name="keywords"
18
  content="artificial intelligence, virtual companion, emotional AI, voice recognition, advanced chatbot, Virtual Kimi, personalized AI assistant, girlfriend">
19
  <meta name="author" content="Jean & Kimi">
 
26
  <meta property="og:url" content="https://virtualkimi.com/virtual-kimi-app/">
27
  <meta property="og:title" content="Virtual Kimi - Virtual AI Companion">
28
  <meta property="og:description"
29
+ content="Virtual Kimi, your virtual AI girlfriend and companion with an evolving personality, voice recognition and immersive interface. The future of human-AI girlfriend relationships.">
30
  <meta property="og:image" content="kimi-icons/virtualkimi-logo.png">
31
 
32
  <!-- Twitter -->
 
43
  "@context": "https://schema.org",
44
  "@type": "SoftwareApplication",
45
  "name": "Virtual Kimi",
46
+ "description": "Virtual Kimi, your virtual AI girlfriend and companion with an evolving personality, voice recognition and immersive interface",
47
  "applicationCategory": "AI Companion",
48
  "operatingSystem": "Web Browser",
49
  "offers": {
 
56
  "name": "Jean & Kimi"
57
  },
58
  "dateCreated": "2025-07-16",
59
+ "dateModified": "2025-08-23",
60
+ "version": "v1.0.9"
61
  }
62
  </script>
63
 
 
442
  </h3>
443
 
444
  <div class="config-row">
445
+ <label class="config-label" data-i18n="temperature">Temperature (Creativity)</label>
 
 
 
 
 
446
  <div class="config-control">
447
  <div class="slider-container">
448
  <input type="range" class="kimi-slider" id="llm-temperature" min="0.0" max="1"
 
451
  </div>
452
  </div>
453
  </div>
454
+ <div class="config-note-info tip">
455
+ <i class="fas fa-lightbulb"></i>
456
+ <small class="config-help" data-i18n="temperature_help">Controls randomness and
457
+ creativity
458
+ (default: 0.9). Higher values make output more creative but less focused.</small>
459
+ </div>
460
 
461
  <div class="config-row">
462
+ <label class="config-label" data-i18n="max_tokens">Max Tokens</label>
 
 
 
 
463
  <div class="config-control">
464
  <div class="slider-container">
465
  <input type="range" class="kimi-slider" id="llm-max-tokens" min="10" max="8192"
 
468
  </div>
469
  </div>
470
  </div>
471
+ <div class="config-note-info settings">
472
+ <i class="fas fa-ruler"></i>
473
+ <small class="config-help" data-i18n="max_tokens_help">Maximum response length in tokens
474
+ (default: 400). Higher values allow longer responses.</small>
475
+ </div>
476
 
477
  <div class="config-row">
478
+ <label class="config-label" data-i18n="top_p">Top P</label>
 
 
 
 
479
  <div class="config-control">
480
  <div class="slider-container">
481
  <input type="range" class="kimi-slider" id="llm-top-p" min="0" max="1" step="0.01"
 
484
  </div>
485
  </div>
486
  </div>
487
+ <div class="config-note-info tip">
488
+ <i class="fas fa-filter"></i>
489
+ <small class="config-help" data-i18n="top_p_help">Controls diversity of word selection
490
+ (default: 0.9). Lower values make responses more focused.</small>
491
+ </div>
492
  <div class="config-row">
493
+ <label class="config-label" data-i18n="frequency_penalty">Frequency Penalty</label>
 
 
 
 
 
494
  <div class="config-control">
495
  <div class="slider-container">
496
  <input type="range" class="kimi-slider" id="llm-frequency-penalty" min="0" max="2"
 
499
  </div>
500
  </div>
501
  </div>
502
+ <div class="config-note-info settings">
503
+ <i class="fas fa-sync-alt"></i>
504
+ <small class="config-help" data-i18n="frequency_penalty_help">Reduces repetition of
505
+ words
506
+ already used (default: 0.9). Higher values discourage repetitive language.</small>
507
+ </div>
508
  <div class="config-row">
509
+ <label class="config-label" data-i18n="presence_penalty">Presence Penalty</label>
 
 
 
 
510
  <div class="config-control">
511
  <div class="slider-container">
512
  <input type="range" class="kimi-slider" id="llm-presence-penalty" min="0" max="2"
 
515
  </div>
516
  </div>
517
  </div>
518
+ <div class="config-note-info tip">
519
+ <i class="fas fa-lightbulb"></i>
520
+ <small class="config-help" data-i18n="presence_penalty_help">Encourages discussing new
521
+ topics (default: 0.8). Higher values promote topic diversity.</small>
522
+ </div>
523
+
524
+ <div class="config-row">
525
+ <label class="config-label" data-i18n="enable_streaming">Enable Text Streaming</label>
526
+ <div class="config-control">
527
+ <div class="toggle-switch active" id="enable-streaming" role="switch"
528
+ aria-checked="true" tabindex="0" aria-labelledby="enable-streaming-label">
529
+ </div>
530
+ </div>
531
+ </div>
532
+
533
+ <div class="config-note-info info">
534
+ <i class="fas fa-stream"></i>
535
+ <small class="config-help" data-i18n="enable_streaming_help">Stream text as it's
536
+ generated for real-time responses (default: enabled). Shows text progressively
537
+ instead of waiting for complete response.</small>
538
+ </div>
539
  </div>
540
 
541
  <div class="config-section">
 
589
  <span data-i18n="transcript_settings">Transcript Settings</span>
590
  </h3>
591
 
592
+ <div class="config-note-info info">
593
+ <i class="fas fa-info-circle"></i>
594
+ <small class="config-help" data-i18n="show_transcript_note">Display real-time
595
+ transcription when you speak to send a message and when the AI responds.</small>
596
+ </div>
597
+
598
  <div class="config-row">
599
  <label class="config-label" data-i18n="show_transcript">Show Transcript</label>
600
  <div class="config-control">
 
631
  <div class="config-section">
632
  <h3><i class="fas fa-brain"></i> <span data-i18n="memory_system">Memory System</span></h3>
633
 
634
+ <div class="config-note-info tip">
635
+ <i class="fas fa-brain"></i>
636
+ <small class="config-help" data-i18n="memory_system_help">Intelligent Memory allows your
637
+ character to remember conversations, preferences, and important details across sessions.
638
+ This creates more personalized and coherent interactions over time.</small>
639
+ </div>
640
+
641
  <div class="config-row">
642
  <label class="config-label" data-i18n="enable_memory">Enable Intelligent Memory</label>
643
  <div class="config-control">
 
753
  <div class="tab-content" data-tab="plugins">
754
  <div class="config-section">
755
  <h3><i class="fas fa-plug"></i> <span data-i18n="plugin_manager">Plugin Manager</span></h3>
756
+ <div class="config-note-info info">
757
+ <i class="fas fa-info-circle"></i>
758
+ <small class="config-help">
759
+ <span data-i18n="plugin_status_note">Currently, only "Sample Blue Theme" is fully
760
+ functional. Other plugins are in development and activating them will have no effect
761
+ at this time.</span>
762
+ </small>
763
+ </div>
764
  <div id="plugin-list"></div>
765
  <div class="plugin-actions">
766
  <button class="kimi-button" id="refresh-plugins" data-i18n="refresh">Refresh</button>
 
1066
  <h3><i class="fas fa-code"></i> Technical Information</h3>
1067
  <div class="tech-info">
1068
  <p><strong>Created date :</strong> July 16, 2025</p>
1069
+ <p><strong>Version :</strong> v1.0.9</p>
1070
+ <p><strong>Last update :</strong> August 23, 2025</p>
1071
  <p><strong>Technologies :</strong> HTML5, CSS3, JavaScript ES6+, IndexedDB, Web Speech
1072
  API</p>
1073
  <p><strong>Status :</strong> ✅ Stable and functional</p>
 
1098
  "@context": "https://schema.org",
1099
  "@type": "WebPage",
1100
  "name": "Virtual Kimi - Virtual AI Companion",
1101
+ "description": "Virtual Kimi, your virtual AI girlfriend and companion with an evolving personality, multi-provider AI support, advanced voice recognition and immersive interface. The future of human-AI girlfriend relationships.",
1102
  "url": "https://virtualkimi.com/virtual-kimi-app/index.html",
1103
  "mainEntity": {
1104
  "@type": "SoftwareApplication",
1105
  "name": "Virtual Kimi",
1106
  "applicationCategory": "AI Companion",
1107
  "operatingSystem": "Web Browser",
1108
+ "description": "Virtual Kimi, a virtual AI girlfriend and companion with an evolving personality, multi-provider AI support, voice recognition and immersive interface",
1109
  "features": [
1110
  "Advanced voice recognition",
1111
  "Evolving personality with 6 adjustable traits",
 
1118
  "@type": "Person",
1119
  "name": "Jean & Kimi"
1120
  },
1121
+ "dateCreated": "2025-07-16",
1122
+ "version": "v1.0.9"
1123
  }
1124
  }
1125
  </script>
kimi-css/kimi-settings.css CHANGED
@@ -274,6 +274,42 @@
274
  line-height: 1.3;
275
  }
276
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  .config-label-group {
278
  display: inline-flex;
279
  align-items: center;
 
274
  line-height: 1.3;
275
  }
276
 
277
+ .config-note-info {
278
+ display: flex;
279
+ align-items: flex-start;
280
+ gap: 8px;
281
+ margin: 12px 0 16px 0;
282
+ padding: 12px;
283
+ background: var(--settings-bg-secondary, rgba(255, 255, 255, 0.05));
284
+ border: 1px solid var(--settings-border-color, rgba(255, 255, 255, 0.1));
285
+ border-radius: 6px;
286
+ border-left: 3px solid var(--accent-color, #8a2be2);
287
+ }
288
+
289
+ .config-note-info i {
290
+ color: var(--accent-color, #8a2be2);
291
+ margin-top: 1px;
292
+ font-size: 0.9rem;
293
+ }
294
+
295
+ /* Variantes de couleur pour différents types de notes */
296
+ .config-note-info.info i {
297
+ color: #4a9eff;
298
+ }
299
+
300
+ .config-note-info.tip i {
301
+ color: #ff9500;
302
+ }
303
+
304
+ .config-note-info.settings i {
305
+ color: #00c896;
306
+ }
307
+
308
+ .config-note-info .config-help {
309
+ margin: 0;
310
+ opacity: 0.9;
311
+ }
312
+
313
  .config-label-group {
314
  display: inline-flex;
315
  align-items: center;
kimi-css/kimi-style.css CHANGED
@@ -889,7 +889,6 @@ body {
889
  margin: 0;
890
  line-height: 1.3;
891
  text-align: left;
892
- white-space: pre-line;
893
  }
894
 
895
  /* ===== ACCESSIBILITY - FOCUS STYLES ===== */
 
889
  margin: 0;
890
  line-height: 1.3;
891
  text-align: left;
 
892
  }
893
 
894
  /* ===== ACCESSIBILITY - FOCUS STYLES ===== */
kimi-js/kimi-appearance.js CHANGED
@@ -33,9 +33,15 @@ class KimiAppearanceManager extends KimiBaseManager {
33
  if (!this.db) return;
34
 
35
  try {
36
- this.currentTheme = await this.db.getPreference("colorTheme", "purple");
37
- this.interfaceOpacity = await this.db.getPreference("interfaceOpacity", 0.8);
38
- this.animationsEnabled = await this.db.getPreference("animationsEnabled", true);
 
 
 
 
 
 
39
  } catch (error) {
40
  console.error("Error loading appearance settings:", error);
41
  }
 
33
  if (!this.db) return;
34
 
35
  try {
36
+ this.currentTheme = await this.db.getPreference("colorTheme", window.KIMI_CONFIG?.DEFAULTS?.THEME ?? "purple");
37
+ this.interfaceOpacity = await this.db.getPreference(
38
+ "interfaceOpacity",
39
+ window.KIMI_CONFIG?.DEFAULTS?.INTERFACE_OPACITY ?? 0.8
40
+ );
41
+ this.animationsEnabled = await this.db.getPreference(
42
+ "animationsEnabled",
43
+ window.KIMI_CONFIG?.DEFAULTS?.ANIMATIONS_ENABLED ?? true
44
+ );
45
  } catch (error) {
46
  console.error("Error loading appearance settings:", error);
47
  }
kimi-js/kimi-config.js CHANGED
@@ -15,7 +15,10 @@ window.KIMI_CONFIG = {
15
  LLM_FREQUENCY_PENALTY: 0.9,
16
  LLM_PRESENCE_PENALTY: 0.8,
17
  SELECTED_CHARACTER: "kimi",
18
- SHOW_TRANSCRIPT: true
 
 
 
19
  },
20
 
21
  // Validation ranges
 
15
  LLM_FREQUENCY_PENALTY: 0.9,
16
  LLM_PRESENCE_PENALTY: 0.8,
17
  SELECTED_CHARACTER: "kimi",
18
+ SHOW_TRANSCRIPT: true,
19
+ ENABLE_STREAMING: true,
20
+ VOICE_ENABLED: true,
21
+ MEMORY_SYSTEM_ENABLED: true
22
  },
23
 
24
  // Validation ranges
kimi-js/kimi-constants.js CHANGED
@@ -57,13 +57,14 @@ window.KIMI_CONTEXT_KEYWORDS = {
57
  "romantic",
58
  "tender",
59
  "hug",
60
- "kiss",
61
  "sweetheart",
62
  "darling",
63
  "my love",
64
  "beloved",
65
  "heart",
66
- "passionate"
 
 
67
  ],
68
  flirtatious: [
69
  "flirty",
@@ -93,17 +94,16 @@ window.KIMI_CONTEXT_KEYWORDS = {
93
  kiss: ["kiss", "kisses", "embrace", "smooch", "peck", "lip lock", "kissy", "mwah"],
94
  dancing: ["dance", "dancing", "move", "groove", "step", "boogie", "twirl", "spin", "shake", "jig"],
95
  listening: [
96
- "listen",
97
- "listening",
98
- "hear",
99
- "question",
100
- "ask",
101
- "tell me",
102
- "pay attention",
103
  "hear me out",
104
- "focus",
 
105
  "tune in",
106
- "lend an ear"
 
 
107
  ]
108
  },
109
  fr: {
@@ -906,12 +906,12 @@ window.KIMI_TRAIT_ADJUSTMENT = {
906
  },
907
  // Per-trait scaling
908
  traitGain: {
909
- affection: 1.2,
910
- romance: 1.3,
911
- empathy: 1.1,
912
- playfulness: 1.15,
913
- humor: 1.1,
914
- intelligence: 1.05
915
  },
916
  traitLoss: {
917
  affection: 0.9,
@@ -985,7 +985,7 @@ window.KIMI_CHARACTERS = {
985
  summary: "Dreamy, intuitive, captivated by cosmic metaphors",
986
  traits: {
987
  // Kimi starts balanced but reserved - cosmic dreamer needs time to open up
988
- affection: 65,
989
  playfulness: 55,
990
  intelligence: 75, // Higher intelligence - she's an astrophysicist
991
  empathy: 75,
@@ -1004,7 +1004,7 @@ window.KIMI_CHARACTERS = {
1004
  summary: "Cheerful, nurturing, sees people as plants needing care",
1005
  traits: {
1006
  // Bella starts more open and caring - nurturing botanist personality
1007
- affection: 70, // Naturally more affectionate
1008
  playfulness: 65, // Cheerful and playful from start
1009
  intelligence: 65, // Smart but not intimidating
1010
  empathy: 85, // High empathy - she cares for living things
@@ -1028,7 +1028,7 @@ window.KIMI_CHARACTERS = {
1028
  intelligence: 85, // High intelligence - cunning prankster
1029
  empathy: 55, // Lower empathy initially - focused on chaos
1030
  humor: 75, // High humor - prankster personality
1031
- romance: 60 // Romance through shared mischief
1032
  },
1033
  age: 21,
1034
  birthplace: "Barcelona, Spain",
@@ -1042,7 +1042,7 @@ window.KIMI_CHARACTERS = {
1042
  summary: "Whimsical, artistic, imaginative, playful, transforms chaos into art",
1043
  traits: {
1044
  // Stella starts mysterious and artistic - digital artist with glitchy personality
1045
- affection: 60, // Moderate starting affection - artistic mystery
1046
  playfulness: 70, // Artistic playfulness
1047
  intelligence: 90, // Very high intelligence - digital artist genius
1048
  empathy: 65, // Artistic empathy - understands through art
 
57
  "romantic",
58
  "tender",
59
  "hug",
 
60
  "sweetheart",
61
  "darling",
62
  "my love",
63
  "beloved",
64
  "heart",
65
+ "passionate",
66
+ "affection",
67
+ "adore"
68
  ],
69
  flirtatious: [
70
  "flirty",
 
94
  kiss: ["kiss", "kisses", "embrace", "smooch", "peck", "lip lock", "kissy", "mwah"],
95
  dancing: ["dance", "dancing", "move", "groove", "step", "boogie", "twirl", "spin", "shake", "jig"],
96
  listening: [
97
+ "listen carefully",
98
+ "I'm listening",
99
+ "listening to you",
 
 
 
 
100
  "hear me out",
101
+ "pay attention",
102
+ "focus on",
103
  "tune in",
104
+ "lend an ear",
105
+ "listen up",
106
+ "I need to talk"
107
  ]
108
  },
109
  fr: {
 
906
  },
907
  // Per-trait scaling
908
  traitGain: {
909
+ affection: 1.15, // Slightly faster affection growth
910
+ romance: 1.2, // Reduced from 1.3 - romance still important but not overpowered
911
+ empathy: 1.1, // Empathy grows steadily
912
+ playfulness: 1.15, // Playful interactions are rewarding
913
+ humor: 1.12, // Humor grows with shared laughter
914
+ intelligence: 1.08 // Intelligence should grow with meaningful conversations
915
  },
916
  traitLoss: {
917
  affection: 0.9,
 
985
  summary: "Dreamy, intuitive, captivated by cosmic metaphors",
986
  traits: {
987
  // Kimi starts balanced but reserved - cosmic dreamer needs time to open up
988
+ affection: 55, // Starts neutral, grows with interaction
989
  playfulness: 55,
990
  intelligence: 75, // Higher intelligence - she's an astrophysicist
991
  empathy: 75,
 
1004
  summary: "Cheerful, nurturing, sees people as plants needing care",
1005
  traits: {
1006
  // Bella starts more open and caring - nurturing botanist personality
1007
+ affection: 60, // Naturally more affectionate
1008
  playfulness: 65, // Cheerful and playful from start
1009
  intelligence: 65, // Smart but not intimidating
1010
  empathy: 85, // High empathy - she cares for living things
 
1028
  intelligence: 85, // High intelligence - cunning prankster
1029
  empathy: 55, // Lower empathy initially - focused on chaos
1030
  humor: 75, // High humor - prankster personality
1031
+ romance: 50 // Reduced from 60 - chaotic personalities are harder to romance initially
1032
  },
1033
  age: 21,
1034
  birthplace: "Barcelona, Spain",
 
1042
  summary: "Whimsical, artistic, imaginative, playful, transforms chaos into art",
1043
  traits: {
1044
  // Stella starts mysterious and artistic - digital artist with glitchy personality
1045
+ affection: 50, // Moderate starting affection - artistic mystery
1046
  playfulness: 70, // Artistic playfulness
1047
  intelligence: 90, // Very high intelligence - digital artist genius
1048
  empathy: 65, // Artistic empathy - understands through art
kimi-js/kimi-database.js CHANGED
@@ -102,7 +102,7 @@ class KimiDatabase {
102
  return emotionSystem.TRAIT_DEFAULTS;
103
  }
104
  return {
105
- affection: 65,
106
  playfulness: 55,
107
  intelligence: 70,
108
  empathy: 75,
@@ -123,6 +123,9 @@ class KimiDatabase {
123
  { key: "interfaceOpacity", value: 0.8 },
124
  { key: "animationsEnabled", value: true },
125
  { key: "showTranscript", value: true },
 
 
 
126
  { key: "llmProvider", value: "openrouter" },
127
  { key: "llmBaseUrl", value: "https://openrouter.ai/api/v1/chat/completions" },
128
  { key: "llmModelId", value: "mistralai/mistral-small-3.2-24b-instruct" },
@@ -315,6 +318,35 @@ class KimiDatabase {
315
  await this.db.preferences.put({ key, value, updated: new Date().toISOString() });
316
  }
317
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
  } catch {}
319
  }
320
 
@@ -522,7 +554,7 @@ class KimiDatabase {
522
  } else {
523
  defaultValue =
524
  {
525
- affection: 65,
526
  playfulness: 55,
527
  intelligence: 70,
528
  empathy: 75,
@@ -763,7 +795,7 @@ class KimiDatabase {
763
  if (window.KimiEmotionSystem) {
764
  return new window.KimiEmotionSystem(this).TRAIT_DEFAULTS[trait] || 50;
765
  }
766
- const fallback = { affection: 65, playfulness: 55, intelligence: 70, empathy: 75, humor: 60, romance: 50 };
767
  return fallback[trait] || 50;
768
  };
769
  const batch = Object.entries(traitsObj).map(([trait, value]) => {
 
102
  return emotionSystem.TRAIT_DEFAULTS;
103
  }
104
  return {
105
+ affection: 55,
106
  playfulness: 55,
107
  intelligence: 70,
108
  empathy: 75,
 
123
  { key: "interfaceOpacity", value: 0.8 },
124
  { key: "animationsEnabled", value: true },
125
  { key: "showTranscript", value: true },
126
+ { key: "enableStreaming", value: true },
127
+ { key: "voiceEnabled", value: true },
128
+ { key: "memorySystemEnabled", value: true },
129
  { key: "llmProvider", value: "openrouter" },
130
  { key: "llmBaseUrl", value: "https://openrouter.ai/api/v1/chat/completions" },
131
  { key: "llmModelId", value: "mistralai/mistral-small-3.2-24b-instruct" },
 
318
  await this.db.preferences.put({ key, value, updated: new Date().toISOString() });
319
  }
320
  }
321
+
322
+ // MIGRATION: Fix Kimi affection progression issue - update default affection from 65 to 55
323
+ // This allows better progression and prevents blocking at ~65%
324
+ const kimiAffectionRecord = await this.db.personality.get(["kimi", "affection"]);
325
+ if (kimiAffectionRecord && kimiAffectionRecord.value === 65) {
326
+ // Only update if it's exactly 65 (the old default) and user hasn't modified it significantly
327
+ const newValue = window.KIMI_CHARACTERS?.kimi?.traits?.affection || 55;
328
+ await this.db.personality.put({
329
+ trait: "affection",
330
+ character: "kimi",
331
+ value: newValue,
332
+ updated: new Date().toISOString()
333
+ });
334
+ console.log(`🔧 Migration: Updated Kimi affection from 65% to ${newValue}% for better progression`);
335
+ }
336
+
337
+ // MIGRATION: Fix Bella affection progression issue - update default affection from 70 to 60
338
+ const bellaAffectionRecord = await this.db.personality.get(["bella", "affection"]);
339
+ if (bellaAffectionRecord && bellaAffectionRecord.value === 70) {
340
+ // Only update if it's exactly 70 (the old default) and user hasn't modified it significantly
341
+ const newValue = window.KIMI_CHARACTERS?.bella?.traits?.affection || 60;
342
+ await this.db.personality.put({
343
+ trait: "affection",
344
+ character: "bella",
345
+ value: newValue,
346
+ updated: new Date().toISOString()
347
+ });
348
+ console.log(`🔧 Migration: Updated Bella affection from 70% to ${newValue}% for better progression`);
349
+ }
350
  } catch {}
351
  }
352
 
 
554
  } else {
555
  defaultValue =
556
  {
557
+ affection: 55,
558
  playfulness: 55,
559
  intelligence: 70,
560
  empathy: 75,
 
795
  if (window.KimiEmotionSystem) {
796
  return new window.KimiEmotionSystem(this).TRAIT_DEFAULTS[trait] || 50;
797
  }
798
+ const fallback = { affection: 55, playfulness: 55, intelligence: 70, empathy: 75, humor: 60, romance: 50 };
799
  return fallback[trait] || 50;
800
  };
801
  const batch = Object.entries(traitsObj).map(([trait, value]) => {
kimi-js/kimi-emotion-system.js CHANGED
@@ -43,9 +43,9 @@ class KimiEmotionSystem {
43
  goodbye: "neutral"
44
  };
45
 
46
- // Unified trait defaults - More balanced for progressive experience
47
  this.TRAIT_DEFAULTS = {
48
- affection: 65, // Reduced from 80 - starts neutral, grows with interaction
49
  playfulness: 55, // Reduced from 70 - more reserved initially
50
  intelligence: 70, // Reduced from 85 - still competent but not overwhelming
51
  empathy: 75, // Reduced from 90 - caring but not overly so
@@ -69,33 +69,29 @@ class KimiEmotionSystem {
69
 
70
  const emotionKeywords = window.KIMI_CONTEXT_KEYWORDS?.[detectedLang] || window.KIMI_CONTEXT_KEYWORDS?.en || {};
71
 
72
- // Priority order for emotion detection
73
  const emotionChecks = [
74
- // Listening intent (user asks to talk or indicates speaking/listening)
75
- {
76
- emotion: this.EMOTIONS.LISTENING,
77
- keywords: emotionKeywords.listening || [
78
- "listen",
79
- "listening",
80
- "écoute",
81
- "ecoute",
82
- "écouter",
83
- "parle",
84
- "speak",
85
- "talk",
86
- "question",
87
- "ask"
88
- ]
89
- },
90
  { emotion: this.EMOTIONS.DANCING, keywords: emotionKeywords.dancing || ["dance", "dancing"] },
91
  { emotion: this.EMOTIONS.ROMANTIC, keywords: emotionKeywords.romantic || ["love", "romantic"] },
 
92
  { emotion: this.EMOTIONS.LAUGHING, keywords: emotionKeywords.laughing || ["laugh", "funny"] },
93
  { emotion: this.EMOTIONS.SURPRISE, keywords: emotionKeywords.surprise || ["wow", "surprise"] },
94
  { emotion: this.EMOTIONS.CONFIDENT, keywords: emotionKeywords.confident || ["confident", "strong"] },
95
  { emotion: this.EMOTIONS.SHY, keywords: emotionKeywords.shy || ["shy", "embarrassed"] },
96
- { emotion: this.EMOTIONS.FLIRTATIOUS, keywords: emotionKeywords.flirtatious || ["flirt", "tease"] },
97
- { emotion: this.EMOTIONS.KISS, keywords: emotionKeywords.kiss || ["kiss", "embrace"] },
98
- { emotion: this.EMOTIONS.GOODBYE, keywords: emotionKeywords.goodbye || ["goodbye", "bye"] }
 
 
 
 
 
 
 
 
 
99
  ];
100
 
101
  // Check for specific emotions first, applying sensitivity weights per language
@@ -166,14 +162,14 @@ class KimiEmotionSystem {
166
  let humor = safe(traits?.humor, this.TRAIT_DEFAULTS.humor);
167
  let intelligence = safe(traits?.intelligence, this.TRAIT_DEFAULTS.intelligence);
168
 
169
- // Unified adjustment functions - More gradual progression for balanced experience
170
  const adjustUp = (val, amount) => {
171
- // Slower progression as values get higher - make romance and affection harder to max out
172
- if (val >= 95) return val + amount * 0.1; // Very slow near max
173
- if (val >= 85) return val + amount * 0.3; // Slow progression at high levels
174
- if (val >= 70) return val + amount * 0.6; // Moderate progression at medium levels
175
- if (val >= 50) return val + amount * 0.8; // Normal progression above average
176
- return val + amount; // Normal progression below average
177
  };
178
 
179
  const adjustDown = (val, amount) => {
@@ -245,6 +241,41 @@ class KimiEmotionSystem {
245
  playfulness = Math.min(100, adjustUp(playfulness, scaleGain("playfulness", 0.3))); // Reduced from 0.4
246
  affection = Math.min(100, adjustUp(affection, scaleGain("affection", 0.2))); // Small affection boost
247
  break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
  }
249
 
250
  // Content-based adjustments (unified)
@@ -429,7 +460,7 @@ class KimiEmotionSystem {
429
 
430
  // ===== PERSONALITY CALCULATION =====
431
  calculatePersonalityAverage(traits) {
432
- const keys = ["affection", "romance", "empathy", "playfulness", "humor"];
433
  let sum = 0;
434
  let count = 0;
435
 
 
43
  goodbye: "neutral"
44
  };
45
 
46
+ // Unified trait defaults - Balanced for progressive experience
47
  this.TRAIT_DEFAULTS = {
48
+ affection: 55, // Lowered to allow more natural progression from start
49
  playfulness: 55, // Reduced from 70 - more reserved initially
50
  intelligence: 70, // Reduced from 85 - still competent but not overwhelming
51
  empathy: 75, // Reduced from 90 - caring but not overly so
 
69
 
70
  const emotionKeywords = window.KIMI_CONTEXT_KEYWORDS?.[detectedLang] || window.KIMI_CONTEXT_KEYWORDS?.en || {};
71
 
72
+ // Priority order for emotion detection - reordered for better logic
73
  const emotionChecks = [
74
+ // High-impact emotions first
75
+ { emotion: this.EMOTIONS.KISS, keywords: emotionKeywords.kiss || ["kiss", "embrace"] },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  { emotion: this.EMOTIONS.DANCING, keywords: emotionKeywords.dancing || ["dance", "dancing"] },
77
  { emotion: this.EMOTIONS.ROMANTIC, keywords: emotionKeywords.romantic || ["love", "romantic"] },
78
+ { emotion: this.EMOTIONS.FLIRTATIOUS, keywords: emotionKeywords.flirtatious || ["flirt", "tease"] },
79
  { emotion: this.EMOTIONS.LAUGHING, keywords: emotionKeywords.laughing || ["laugh", "funny"] },
80
  { emotion: this.EMOTIONS.SURPRISE, keywords: emotionKeywords.surprise || ["wow", "surprise"] },
81
  { emotion: this.EMOTIONS.CONFIDENT, keywords: emotionKeywords.confident || ["confident", "strong"] },
82
  { emotion: this.EMOTIONS.SHY, keywords: emotionKeywords.shy || ["shy", "embarrassed"] },
83
+ { emotion: this.EMOTIONS.GOODBYE, keywords: emotionKeywords.goodbye || ["goodbye", "bye"] },
84
+ // Listening intent (lower priority to not mask other emotions)
85
+ {
86
+ emotion: this.EMOTIONS.LISTENING,
87
+ keywords: emotionKeywords.listening || [
88
+ "listen carefully",
89
+ "I'm listening",
90
+ "listening to you",
91
+ "hear me out",
92
+ "pay attention"
93
+ ]
94
+ }
95
  ];
96
 
97
  // Check for specific emotions first, applying sensitivity weights per language
 
162
  let humor = safe(traits?.humor, this.TRAIT_DEFAULTS.humor);
163
  let intelligence = safe(traits?.intelligence, this.TRAIT_DEFAULTS.intelligence);
164
 
165
+ // Unified adjustment functions - More balanced progression for better user experience
166
  const adjustUp = (val, amount) => {
167
+ // Gradual slowdown only at very high levels to allow natural progression
168
+ if (val >= 95) return val + amount * 0.2; // Slow near max to preserve challenge
169
+ if (val >= 88) return val + amount * 0.5; // Moderate slowdown at very high levels
170
+ if (val >= 80) return val + amount * 0.7; // Slight slowdown at high levels
171
+ if (val >= 60) return val + amount * 0.9; // Nearly normal progression in mid-high range
172
+ return val + amount; // Normal progression below 60%
173
  };
174
 
175
  const adjustDown = (val, amount) => {
 
241
  playfulness = Math.min(100, adjustUp(playfulness, scaleGain("playfulness", 0.3))); // Reduced from 0.4
242
  affection = Math.min(100, adjustUp(affection, scaleGain("affection", 0.2))); // Small affection boost
243
  break;
244
+ case this.EMOTIONS.SURPRISE:
245
+ intelligence = Math.min(100, adjustUp(intelligence, scaleGain("intelligence", 0.1))); // Surprise stimulates thinking
246
+ empathy = Math.min(100, adjustUp(empathy, scaleGain("empathy", 0.1))); // Opens mind to new perspectives
247
+ break;
248
+ case this.EMOTIONS.KISS:
249
+ romance = Math.min(100, adjustUp(romance, scaleGain("romance", 0.8))); // Strong romantic gesture
250
+ affection = Math.min(100, adjustUp(affection, scaleGain("affection", 0.6))); // Very affectionate action
251
+ break;
252
+ case this.EMOTIONS.GOODBYE:
253
+ // Slight melancholy but no major trait changes
254
+ empathy = Math.min(100, adjustUp(empathy, scaleGain("empathy", 0.05))); // Understanding of parting
255
+ break;
256
+ case this.EMOTIONS.LISTENING:
257
+ intelligence = Math.min(100, adjustUp(intelligence, scaleGain("intelligence", 0.2))); // Active listening shows intelligence
258
+ empathy = Math.min(100, adjustUp(empathy, scaleGain("empathy", 0.3))); // Listening builds empathy
259
+ break;
260
+ }
261
+
262
+ // Cross-trait interactions - traits influence each other for more realistic personality development
263
+ // High empathy should boost affection over time
264
+ if (empathy >= 75 && affection < empathy - 5) {
265
+ affection = Math.min(100, adjustUp(affection, scaleGain("affection", 0.1)));
266
+ }
267
+
268
+ // High intelligence should slightly boost empathy (understanding others)
269
+ if (intelligence >= 80 && empathy < intelligence - 10) {
270
+ empathy = Math.min(100, adjustUp(empathy, scaleGain("empathy", 0.05)));
271
+ }
272
+
273
+ // Humor and playfulness should reinforce each other
274
+ if (humor >= 70 && playfulness < humor - 10) {
275
+ playfulness = Math.min(100, adjustUp(playfulness, scaleGain("playfulness", 0.05)));
276
+ }
277
+ if (playfulness >= 70 && humor < playfulness - 10) {
278
+ humor = Math.min(100, adjustUp(humor, scaleGain("humor", 0.05)));
279
  }
280
 
281
  // Content-based adjustments (unified)
 
460
 
461
  // ===== PERSONALITY CALCULATION =====
462
  calculatePersonalityAverage(traits) {
463
+ const keys = ["affection", "romance", "empathy", "playfulness", "humor", "intelligence"];
464
  let sum = 0;
465
  let count = 0;
466
 
kimi-js/kimi-llm-manager.js CHANGED
@@ -287,7 +287,7 @@ class KimiLLMManager {
287
  const getUnifiedDefaults = () =>
288
  window.getTraitDefaults
289
  ? window.getTraitDefaults()
290
- : { affection: 65, playfulness: 55, intelligence: 70, empathy: 75, humor: 60, romance: 50 };
291
 
292
  const defaults = getUnifiedDefaults();
293
  const affection = personality.affection || defaults.affection;
@@ -469,6 +469,35 @@ class KimiLLMManager {
469
  }
470
  }
471
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
472
  async chatWithOpenAICompatible(userMessage, options = {}) {
473
  const baseUrl = await this.db.getPreference("llmBaseUrl", "https://api.openai.com/v1/chat/completions");
474
  const provider = await this.db.getPreference("llmProvider", "openai");
@@ -884,6 +913,352 @@ class KimiLLMManager {
884
  }
885
  }
886
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
887
  getFallbackResponse(userMessage, errorType = "api") {
888
  // Use centralized fallback manager instead of duplicated logic
889
  if (window.KimiFallbackManager) {
 
287
  const getUnifiedDefaults = () =>
288
  window.getTraitDefaults
289
  ? window.getTraitDefaults()
290
+ : { affection: 55, playfulness: 55, intelligence: 70, empathy: 75, humor: 60, romance: 50 };
291
 
292
  const defaults = getUnifiedDefaults();
293
  const affection = personality.affection || defaults.affection;
 
469
  }
470
  }
471
 
472
+ async chatStreaming(userMessage, onToken, options = {}) {
473
+ // Get LLM settings from individual preferences
474
+ const llmSettings = {
475
+ temperature: await this.db.getPreference("llmTemperature", 0.9),
476
+ maxTokens: await this.db.getPreference("llmMaxTokens", 400),
477
+ top_p: await this.db.getPreference("llmTopP", 0.9),
478
+ frequency_penalty: await this.db.getPreference("llmFrequencyPenalty", 0.9),
479
+ presence_penalty: await this.db.getPreference("llmPresencePenalty", 0.8)
480
+ };
481
+ const temperature = typeof options.temperature === "number" ? options.temperature : llmSettings.temperature;
482
+ const maxTokens = typeof options.maxTokens === "number" ? options.maxTokens : llmSettings.maxTokens;
483
+ const opts = { ...options, temperature, maxTokens };
484
+
485
+ try {
486
+ const provider = await this.db.getPreference("llmProvider", "openrouter");
487
+ if (provider === "openrouter") {
488
+ return await this.chatWithOpenRouterStreaming(userMessage, onToken, opts);
489
+ }
490
+ if (provider === "ollama") {
491
+ return await this.chatWithLocalStreaming(userMessage, onToken, opts);
492
+ }
493
+ return await this.chatWithOpenAICompatibleStreaming(userMessage, onToken, opts);
494
+ } catch (error) {
495
+ console.error("Error during streaming chat:", error);
496
+ // Fallback to non-streaming if streaming fails
497
+ return await this.chat(userMessage, options);
498
+ }
499
+ }
500
+
501
  async chatWithOpenAICompatible(userMessage, options = {}) {
502
  const baseUrl = await this.db.getPreference("llmBaseUrl", "https://api.openai.com/v1/chat/completions");
503
  const provider = await this.db.getPreference("llmProvider", "openai");
 
913
  }
914
  }
915
 
916
+ // ===== STREAMING METHODS =====
917
+
918
+ async chatWithOpenRouterStreaming(userMessage, onToken, options = {}) {
919
+ const apiKey = await this.db.getPreference("providerApiKey");
920
+ if (!apiKey) {
921
+ throw new Error("OpenRouter API key not configured");
922
+ }
923
+
924
+ const systemPromptContent = await this.assemblePrompt(userMessage);
925
+ const messages = [
926
+ { role: "system", content: systemPromptContent },
927
+ ...this.conversationContext.slice(-this.maxContextLength),
928
+ { role: "user", content: userMessage }
929
+ ];
930
+
931
+ // Get unified defaults and options
932
+ const unifiedDefaults = window.getUnifiedDefaults
933
+ ? window.getUnifiedDefaults()
934
+ : { temperature: 0.9, maxTokens: 400, top_p: 0.9, frequency_penalty: 0.9, presence_penalty: 0.8 };
935
+
936
+ const llmSettings = {
937
+ temperature: await this.db.getPreference("llmTemperature", unifiedDefaults.temperature),
938
+ maxTokens: await this.db.getPreference("llmMaxTokens", unifiedDefaults.maxTokens),
939
+ top_p: await this.db.getPreference("llmTopP", unifiedDefaults.top_p),
940
+ frequency_penalty: await this.db.getPreference("llmFrequencyPenalty", unifiedDefaults.frequency_penalty),
941
+ presence_penalty: await this.db.getPreference("llmPresencePenalty", unifiedDefaults.presence_penalty)
942
+ };
943
+
944
+ const payload = {
945
+ model: this.currentModel,
946
+ messages: messages,
947
+ stream: true, // Enable streaming
948
+ temperature: typeof options.temperature === "number" ? options.temperature : llmSettings.temperature,
949
+ max_tokens: typeof options.maxTokens === "number" ? options.maxTokens : llmSettings.maxTokens,
950
+ top_p: typeof options.topP === "number" ? options.topP : llmSettings.top_p,
951
+ frequency_penalty:
952
+ typeof options.frequencyPenalty === "number" ? options.frequencyPenalty : llmSettings.frequency_penalty,
953
+ presence_penalty: typeof options.presencePenalty === "number" ? options.presencePenalty : llmSettings.presence_penalty
954
+ };
955
+
956
+ try {
957
+ const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
958
+ method: "POST",
959
+ headers: {
960
+ Authorization: `Bearer ${apiKey}`,
961
+ "Content-Type": "application/json",
962
+ "HTTP-Referer": window.location.origin,
963
+ "X-Title": "Kimi - Virtual Companion"
964
+ },
965
+ body: JSON.stringify(payload)
966
+ });
967
+
968
+ if (!response.ok) {
969
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
970
+ }
971
+
972
+ const reader = response.body.getReader();
973
+ const decoder = new TextDecoder();
974
+ let buffer = "";
975
+ let fullResponse = "";
976
+
977
+ try {
978
+ while (true) {
979
+ const { done, value } = await reader.read();
980
+ if (done) break;
981
+
982
+ buffer += decoder.decode(value, { stream: true });
983
+ const lines = buffer.split("\n");
984
+ buffer = lines.pop() || ""; // Keep incomplete line in buffer
985
+
986
+ for (const line of lines) {
987
+ if (line.trim() === "" || line.startsWith(":")) continue; // Skip empty lines and comments
988
+
989
+ if (line.startsWith("data: ")) {
990
+ const data = line.slice(6);
991
+ if (data === "[DONE]") {
992
+ break;
993
+ }
994
+
995
+ try {
996
+ const parsed = JSON.parse(data);
997
+ const content = parsed.choices?.[0]?.delta?.content;
998
+ if (content) {
999
+ fullResponse += content;
1000
+ onToken(content);
1001
+ }
1002
+ } catch (parseError) {
1003
+ console.warn("Failed to parse streaming chunk:", parseError);
1004
+ }
1005
+ }
1006
+ }
1007
+ }
1008
+ } finally {
1009
+ reader.releaseLock();
1010
+ }
1011
+
1012
+ // Add to context after streaming completes
1013
+ this.conversationContext.push(
1014
+ { role: "user", content: userMessage, timestamp: new Date().toISOString() },
1015
+ { role: "assistant", content: fullResponse, timestamp: new Date().toISOString() }
1016
+ );
1017
+
1018
+ if (this.conversationContext.length > this.maxContextLength * 2) {
1019
+ this.conversationContext = this.conversationContext.slice(-this.maxContextLength * 2);
1020
+ }
1021
+
1022
+ // Token usage estimation
1023
+ try {
1024
+ const est = window.KimiTokenUtils?.estimate || (t => Math.ceil((t || "").length / 4));
1025
+ const tokensIn = est(userMessage + " " + systemPromptContent);
1026
+ const tokensOut = est(fullResponse);
1027
+ window._lastKimiTokenUsage = { tokensIn, tokensOut };
1028
+ if (!window.kimiMemory && this.db) {
1029
+ const character = await this.db.getSelectedCharacter();
1030
+ const prevIn = Number(await this.db.getPreference(`totalTokensIn_${character}`, 0)) || 0;
1031
+ const prevOut = Number(await this.db.getPreference(`totalTokensOut_${character}`, 0)) || 0;
1032
+ await this.db.setPreference(`totalTokensIn_${character}`, prevIn + tokensIn);
1033
+ await this.db.setPreference(`totalTokensOut_${character}`, prevOut + tokensOut);
1034
+ }
1035
+ } catch (e) {
1036
+ console.warn("Token usage estimation failed (OpenRouter streaming):", e);
1037
+ }
1038
+
1039
+ return fullResponse;
1040
+ } catch (error) {
1041
+ console.error("OpenRouter streaming error:", error);
1042
+ throw error;
1043
+ }
1044
+ }
1045
+
1046
+ async chatWithOpenAICompatibleStreaming(userMessage, onToken, options = {}) {
1047
+ const baseUrl = await this.db.getPreference("llmBaseUrl", "https://api.openai.com/v1/chat/completions");
1048
+ const apiKey = await this.db.getPreference("openaiApiKey");
1049
+ if (!apiKey) {
1050
+ throw new Error("OpenAI API key not configured");
1051
+ }
1052
+
1053
+ const systemPromptContent = await this.assemblePrompt(userMessage);
1054
+ const messages = [
1055
+ { role: "system", content: systemPromptContent },
1056
+ ...this.conversationContext.slice(-this.maxContextLength),
1057
+ { role: "user", content: userMessage }
1058
+ ];
1059
+
1060
+ const unifiedDefaults = window.getUnifiedDefaults
1061
+ ? window.getUnifiedDefaults()
1062
+ : { temperature: 0.9, maxTokens: 400, top_p: 0.9, frequency_penalty: 0.9, presence_penalty: 0.8 };
1063
+
1064
+ const llmSettings = {
1065
+ temperature: await this.db.getPreference("llmTemperature", unifiedDefaults.temperature),
1066
+ maxTokens: await this.db.getPreference("llmMaxTokens", unifiedDefaults.maxTokens),
1067
+ top_p: await this.db.getPreference("llmTopP", unifiedDefaults.top_p),
1068
+ frequency_penalty: await this.db.getPreference("llmFrequencyPenalty", unifiedDefaults.frequency_penalty),
1069
+ presence_penalty: await this.db.getPreference("llmPresencePenalty", unifiedDefaults.presence_penalty)
1070
+ };
1071
+
1072
+ const payload = {
1073
+ model: this.currentModel,
1074
+ messages: messages,
1075
+ stream: true,
1076
+ temperature: typeof options.temperature === "number" ? options.temperature : llmSettings.temperature,
1077
+ max_tokens: typeof options.maxTokens === "number" ? options.maxTokens : llmSettings.maxTokens,
1078
+ top_p: typeof options.topP === "number" ? options.topP : llmSettings.top_p,
1079
+ frequency_penalty:
1080
+ typeof options.frequencyPenalty === "number" ? options.frequencyPenalty : llmSettings.frequency_penalty,
1081
+ presence_penalty: typeof options.presencePenalty === "number" ? options.presencePenalty : llmSettings.presence_penalty
1082
+ };
1083
+
1084
+ try {
1085
+ const response = await fetch(baseUrl, {
1086
+ method: "POST",
1087
+ headers: {
1088
+ Authorization: `Bearer ${apiKey}`,
1089
+ "Content-Type": "application/json"
1090
+ },
1091
+ body: JSON.stringify(payload)
1092
+ });
1093
+
1094
+ if (!response.ok) {
1095
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
1096
+ }
1097
+
1098
+ const reader = response.body.getReader();
1099
+ const decoder = new TextDecoder();
1100
+ let buffer = "";
1101
+ let fullResponse = "";
1102
+
1103
+ try {
1104
+ while (true) {
1105
+ const { done, value } = await reader.read();
1106
+ if (done) break;
1107
+
1108
+ buffer += decoder.decode(value, { stream: true });
1109
+ const lines = buffer.split("\n");
1110
+ buffer = lines.pop() || "";
1111
+
1112
+ for (const line of lines) {
1113
+ if (line.trim() === "" || line.startsWith(":")) continue;
1114
+
1115
+ if (line.startsWith("data: ")) {
1116
+ const data = line.slice(6);
1117
+ if (data === "[DONE]") {
1118
+ break;
1119
+ }
1120
+
1121
+ try {
1122
+ const parsed = JSON.parse(data);
1123
+ const content = parsed.choices?.[0]?.delta?.content;
1124
+ if (content) {
1125
+ fullResponse += content;
1126
+ onToken(content);
1127
+ }
1128
+ } catch (parseError) {
1129
+ console.warn("Failed to parse streaming chunk:", parseError);
1130
+ }
1131
+ }
1132
+ }
1133
+ }
1134
+ } finally {
1135
+ reader.releaseLock();
1136
+ }
1137
+
1138
+ // Add to context
1139
+ this.conversationContext.push(
1140
+ { role: "user", content: userMessage, timestamp: new Date().toISOString() },
1141
+ { role: "assistant", content: fullResponse, timestamp: new Date().toISOString() }
1142
+ );
1143
+
1144
+ if (this.conversationContext.length > this.maxContextLength * 2) {
1145
+ this.conversationContext = this.conversationContext.slice(-this.maxContextLength * 2);
1146
+ }
1147
+
1148
+ // Token usage estimation
1149
+ try {
1150
+ const est = window.KimiTokenUtils?.estimate || (t => Math.ceil((t || "").length / 4));
1151
+ const tokensIn = est(userMessage + " " + systemPromptContent);
1152
+ const tokensOut = est(fullResponse);
1153
+ window._lastKimiTokenUsage = { tokensIn, tokensOut };
1154
+ if (!window.kimiMemory && this.db) {
1155
+ const character = await this.db.getSelectedCharacter();
1156
+ const prevIn = Number(await this.db.getPreference(`totalTokensIn_${character}`, 0)) || 0;
1157
+ const prevOut = Number(await this.db.getPreference(`totalTokensOut_${character}`, 0)) || 0;
1158
+ await this.db.setPreference(`totalTokensIn_${character}`, prevIn + tokensIn);
1159
+ await this.db.setPreference(`totalTokensOut_${character}`, prevOut + tokensOut);
1160
+ }
1161
+ } catch (e) {
1162
+ console.warn("Token usage estimation failed (OpenAI streaming):", e);
1163
+ }
1164
+
1165
+ return fullResponse;
1166
+ } catch (error) {
1167
+ console.error("OpenAI compatible streaming error:", error);
1168
+ throw error;
1169
+ }
1170
+ }
1171
+
1172
+ async chatWithLocalStreaming(userMessage, onToken, options = {}) {
1173
+ const systemPromptContent = await this.assemblePrompt(userMessage);
1174
+
1175
+ const payload = {
1176
+ model: this.currentModel || "llama2",
1177
+ messages: [
1178
+ { role: "system", content: systemPromptContent },
1179
+ ...this.conversationContext.slice(-this.maxContextLength),
1180
+ { role: "user", content: userMessage }
1181
+ ],
1182
+ stream: true
1183
+ };
1184
+
1185
+ try {
1186
+ const response = await fetch("http://localhost:11434/api/chat", {
1187
+ method: "POST",
1188
+ headers: {
1189
+ "Content-Type": "application/json"
1190
+ },
1191
+ body: JSON.stringify(payload)
1192
+ });
1193
+
1194
+ if (!response.ok) {
1195
+ throw new Error("Ollama not available");
1196
+ }
1197
+
1198
+ const reader = response.body.getReader();
1199
+ const decoder = new TextDecoder();
1200
+ let fullResponse = "";
1201
+
1202
+ try {
1203
+ while (true) {
1204
+ const { done, value } = await reader.read();
1205
+ if (done) break;
1206
+
1207
+ const chunk = decoder.decode(value, { stream: true });
1208
+ const lines = chunk.split("\n").filter(line => line.trim());
1209
+
1210
+ for (const line of lines) {
1211
+ try {
1212
+ const parsed = JSON.parse(line);
1213
+ const content = parsed.message?.content;
1214
+ if (content) {
1215
+ fullResponse += content;
1216
+ onToken(content);
1217
+ }
1218
+ if (parsed.done) {
1219
+ break;
1220
+ }
1221
+ } catch (parseError) {
1222
+ console.warn("Failed to parse Ollama streaming chunk:", parseError);
1223
+ }
1224
+ }
1225
+ }
1226
+ } finally {
1227
+ reader.releaseLock();
1228
+ }
1229
+
1230
+ // Add to context
1231
+ this.conversationContext.push(
1232
+ { role: "user", content: userMessage, timestamp: new Date().toISOString() },
1233
+ { role: "assistant", content: fullResponse, timestamp: new Date().toISOString() }
1234
+ );
1235
+
1236
+ if (this.conversationContext.length > this.maxContextLength * 2) {
1237
+ this.conversationContext = this.conversationContext.slice(-this.maxContextLength * 2);
1238
+ }
1239
+
1240
+ // Token usage estimation
1241
+ try {
1242
+ const est = window.KimiTokenUtils?.estimate || (t => Math.ceil((t || "").length / 4));
1243
+ const tokensIn = est(userMessage + " " + systemPromptContent);
1244
+ const tokensOut = est(fullResponse);
1245
+ window._lastKimiTokenUsage = { tokensIn, tokensOut };
1246
+ const character = await this.db.getSelectedCharacter();
1247
+ const prevIn = Number(await this.db.getPreference(`totalTokensIn_${character}`, 0)) || 0;
1248
+ const prevOut = Number(await this.db.getPreference(`totalTokensOut_${character}`, 0)) || 0;
1249
+ await this.db.setPreference(`totalTokensIn_${character}`, prevIn + tokensIn);
1250
+ await this.db.setPreference(`totalTokensOut_${character}`, prevOut + tokensOut);
1251
+ } catch (e) {
1252
+ console.warn("Token usage estimation failed (local streaming):", e);
1253
+ }
1254
+
1255
+ return fullResponse;
1256
+ } catch (error) {
1257
+ console.warn("Local LLM streaming not available:", error);
1258
+ throw error;
1259
+ }
1260
+ }
1261
+
1262
  getFallbackResponse(userMessage, errorType = "api") {
1263
  // Use centralized fallback manager instead of duplicated logic
1264
  if (window.KimiFallbackManager) {
kimi-js/kimi-memory-system.js CHANGED
@@ -209,7 +209,10 @@ class KimiMemorySystem {
209
  }
210
 
211
  try {
212
- this.memoryEnabled = await this.db.getPreference("memorySystemEnabled", true);
 
 
 
213
  this.selectedCharacter = await this.db.getSelectedCharacter();
214
  await this.createMemoryTables();
215
 
 
209
  }
210
 
211
  try {
212
+ this.memoryEnabled = await this.db.getPreference(
213
+ "memorySystemEnabled",
214
+ window.KIMI_CONFIG?.DEFAULTS?.MEMORY_SYSTEM_ENABLED ?? true
215
+ );
216
  this.selectedCharacter = await this.db.getSelectedCharacter();
217
  await this.createMemoryTables();
218
 
kimi-js/kimi-memory.js CHANGED
@@ -29,7 +29,7 @@ class KimiMemory {
29
  // Load affection trait from personality database with coherent defaults
30
  const charDefAff =
31
  (window.KIMI_CHARACTERS && window.KIMI_CHARACTERS[this.selectedCharacter]?.traits?.affection) || null;
32
- const genericAff = (window.getTraitDefaults && window.getTraitDefaults().affection) || 65;
33
  const defaultAff = typeof charDefAff === "number" ? charDefAff : genericAff;
34
  this.affectionTrait = await this.db.getPersonalityTrait("affection", defaultAff, this.selectedCharacter);
35
 
 
29
  // Load affection trait from personality database with coherent defaults
30
  const charDefAff =
31
  (window.KIMI_CHARACTERS && window.KIMI_CHARACTERS[this.selectedCharacter]?.traits?.affection) || null;
32
+ const genericAff = (window.getTraitDefaults && window.getTraitDefaults().affection) || 55;
33
  const defaultAff = typeof charDefAff === "number" ? charDefAff : genericAff;
34
  this.affectionTrait = await this.db.getPersonalityTrait("affection", defaultAff, this.selectedCharacter);
35
 
kimi-js/kimi-module.js CHANGED
@@ -435,7 +435,7 @@ async function getBasicResponse(reaction) {
435
 
436
  // Déporté vers KimiEmotionSystem: utiliser window.updatePersonalityTraitsFromEmotion
437
 
438
- async function analyzeAndReact(text, useAdvancedLLM = true) {
439
  const kimiDB = window.kimiDB;
440
  const kimiLLM = window.kimiLLM;
441
  const kimiVideo = window.kimiVideo;
@@ -461,7 +461,7 @@ async function analyzeAndReact(text, useAdvancedLLM = true) {
461
  const selectedCharacter = await kimiDB.getSelectedCharacter();
462
  const traits = await kimiDB.getAllPersonalityTraits(selectedCharacter);
463
  const avg = window.getPersonalityAverage ? window.getPersonalityAverage(traits) : 50;
464
- const affection = typeof traits.affection === "number" ? traits.affection : 80;
465
  const characterTraits = window.KIMI_CHARACTERS[selectedCharacter]?.traits || "";
466
 
467
  // Always reflect user's input phase with a listening video (voice or chat)
@@ -485,7 +485,14 @@ async function analyzeAndReact(text, useAdvancedLLM = true) {
485
  window.dispatchEvent(new CustomEvent("chat:typing:start"));
486
  }
487
  } catch (e) {}
488
- response = await kimiLLM.chat(sanitizedText);
 
 
 
 
 
 
 
489
  try {
490
  if (window.dispatchEvent) {
491
  window.dispatchEvent(new CustomEvent("chat:typing:stop"));
@@ -647,7 +654,9 @@ async function analyzeAndReact(text, useAdvancedLLM = true) {
647
 
648
  function addMessageToChat(sender, text, conversationId = null) {
649
  const chatMessages = document.getElementById("chat-messages");
650
- if (!text) return;
 
 
651
  const messageDiv = document.createElement("div");
652
  messageDiv.className = `message ${sender}`;
653
 
@@ -690,13 +699,29 @@ function addMessageToChat(sender, text, conversationId = null) {
690
  messageTimeDiv.appendChild(deleteBtn);
691
 
692
  const textDiv = document.createElement("div");
693
- textDiv.textContent = text;
694
 
695
  messageDiv.appendChild(textDiv);
696
  messageDiv.appendChild(messageTimeDiv);
697
 
698
  chatMessages.appendChild(messageDiv);
699
  chatMessages.scrollTop = chatMessages.scrollHeight;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
700
  }
701
 
702
  async function loadChatHistory() {
@@ -859,14 +884,24 @@ async function updatePersonalitySliders(characterKey) {
859
  // Get default traits from KIMI_CHARACTERS constants
860
  const characterDefaults = window.KIMI_CHARACTERS[characterKey]?.traits || {};
861
 
862
- // Use saved traits if they exist, otherwise fall back to character defaults
 
 
 
 
 
 
 
 
 
 
863
  const traits = {
864
- affection: savedTraits.affection ?? characterDefaults.affection ?? 50,
865
- playfulness: savedTraits.playfulness ?? characterDefaults.playfulness ?? 50,
866
- intelligence: savedTraits.intelligence ?? characterDefaults.intelligence ?? 50,
867
- empathy: savedTraits.empathy ?? characterDefaults.empathy ?? 50,
868
- humor: savedTraits.humor ?? characterDefaults.humor ?? 50,
869
- romance: savedTraits.romance ?? characterDefaults.romance ?? 50
870
  };
871
 
872
  // Check if sliders exist before updating them
@@ -898,7 +933,7 @@ async function updateStats() {
898
  const tokensIn = await kimiDB.getPreference(`totalTokensIn_${character}`, 0);
899
  const tokensOut = await kimiDB.getPreference(`totalTokensOut_${character}`, 0);
900
  const charDefAff = (window.KIMI_CHARACTERS && window.KIMI_CHARACTERS[character]?.traits?.affection) || null;
901
- const genericAff = (window.getTraitDefaults && window.getTraitDefaults().affection) || 65;
902
  const defaultAff = typeof charDefAff === "number" ? charDefAff : genericAff;
903
  const affectionTrait = await kimiDB.getPersonalityTrait("affection", defaultAff, character);
904
  const conversations = await kimiDB.getAllConversations(character);
@@ -1334,21 +1369,143 @@ async function sendMessage() {
1334
  if (waitingIndicator) waitingIndicator.style.display = "inline-block";
1335
 
1336
  try {
1337
- const response = await analyzeAndReact(message);
1338
- let finalResponse = response;
1339
- // If the LLM's response is empty, null, or too short, use the emotional fallback.
1340
- if (!finalResponse || typeof finalResponse !== "string" || finalResponse.trim().length < 2) {
1341
- finalResponse = window.getLocalizedEmotionalResponse
1342
- ? window.getLocalizedEmotionalResponse("neutral")
1343
- : "I'm here for you!";
1344
- }
1345
- setTimeout(() => {
1346
- addMessageToChat("kimi", finalResponse);
1347
- if (window.voiceManager && !message.startsWith("Vous:")) {
1348
- window.voiceManager.speak(finalResponse);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1349
  }
1350
- if (waitingIndicator) waitingIndicator.style.display = "none";
1351
- }, 1000);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1352
  } catch (error) {
1353
  console.error("Error while generating response:", error);
1354
  const i18n = window.kimiI18nManager;
@@ -1382,6 +1539,7 @@ function setupSettingsListeners(kimiDB, kimiMemory) {
1382
  const llmTopPSlider = document.getElementById("llm-top-p");
1383
  const llmFrequencyPenaltySlider = document.getElementById("llm-frequency-penalty");
1384
  const llmPresencePenaltySlider = document.getElementById("llm-presence-penalty");
 
1385
  const colorThemeSelect = document.getElementById("color-theme");
1386
  const interfaceOpacitySlider = document.getElementById("interface-opacity");
1387
  const animationsToggle = document.getElementById("animations-toggle");
@@ -1606,6 +1764,21 @@ function setupSettingsListeners(kimiDB, kimiMemory) {
1606
  llmPresencePenaltySlider.addEventListener("input", listener);
1607
  window._kimiListenerCleanup.push(() => llmPresencePenaltySlider.removeEventListener("input", listener));
1608
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1609
  if (colorThemeSelect) {
1610
  colorThemeSelect.removeEventListener("change", window._kimiColorThemeListener);
1611
  window._kimiColorThemeListener = async e => {
@@ -1630,11 +1803,11 @@ function setupSettingsListeners(kimiDB, kimiMemory) {
1630
  }
1631
  // Animation toggle is handled by KimiAppearanceManager
1632
  // Remove the duplicate handler to prevent conflicts
 
1633
  const transcriptToggle = document.getElementById("transcript-toggle");
1634
  if (transcriptToggle) {
1635
- let showTranscript = true;
1636
  if (kimiDB && kimiDB.getPreference) {
1637
- kimiDB.getPreference("showTranscript", true).then(showTranscript => {
1638
  transcriptToggle.classList.toggle("active", showTranscript);
1639
  transcriptToggle.setAttribute("aria-checked", showTranscript ? "true" : "false");
1640
  });
@@ -1643,9 +1816,18 @@ function setupSettingsListeners(kimiDB, kimiMemory) {
1643
  const enabled = !transcriptToggle.classList.contains("active");
1644
  transcriptToggle.classList.toggle("active", enabled);
1645
  transcriptToggle.setAttribute("aria-checked", enabled ? "true" : "false");
 
1646
  if (kimiDB && kimiDB.setPreference) {
1647
  await kimiDB.setPreference("showTranscript", enabled);
1648
  }
 
 
 
 
 
 
 
 
1649
  };
1650
  transcriptToggle.onclick = onToggle;
1651
  transcriptToggle.onkeydown = async e => {
@@ -1692,6 +1874,19 @@ async function refreshAllSliders() {
1692
  }
1693
  } catch {}
1694
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
1695
  }
1696
  window.refreshAllSliders = refreshAllSliders;
1697
 
@@ -1814,7 +2009,7 @@ async function syncPersonalityTraits(characterName = null) {
1814
  } else if (window.getTraitDefaults) {
1815
  generic = window.getTraitDefaults();
1816
  } else {
1817
- generic = { affection: 65, playfulness: 55, intelligence: 70, empathy: 75, humor: 60, romance: 50 };
1818
  }
1819
  // Character defaults take precedence over generic defaults
1820
  return { ...generic, ...charDefaults };
 
435
 
436
  // Déporté vers KimiEmotionSystem: utiliser window.updatePersonalityTraitsFromEmotion
437
 
438
+ async function analyzeAndReact(text, useAdvancedLLM = true, onStreamToken = null) {
439
  const kimiDB = window.kimiDB;
440
  const kimiLLM = window.kimiLLM;
441
  const kimiVideo = window.kimiVideo;
 
461
  const selectedCharacter = await kimiDB.getSelectedCharacter();
462
  const traits = await kimiDB.getAllPersonalityTraits(selectedCharacter);
463
  const avg = window.getPersonalityAverage ? window.getPersonalityAverage(traits) : 50;
464
+ const affection = typeof traits.affection === "number" ? traits.affection : 55;
465
  const characterTraits = window.KIMI_CHARACTERS[selectedCharacter]?.traits || "";
466
 
467
  // Always reflect user's input phase with a listening video (voice or chat)
 
485
  window.dispatchEvent(new CustomEvent("chat:typing:start"));
486
  }
487
  } catch (e) {}
488
+
489
+ // Use streaming if onStreamToken callback is provided
490
+ if (onStreamToken && typeof kimiLLM.chatStreaming === "function") {
491
+ response = await kimiLLM.chatStreaming(sanitizedText, onStreamToken);
492
+ } else {
493
+ response = await kimiLLM.chat(sanitizedText);
494
+ }
495
+
496
  try {
497
  if (window.dispatchEvent) {
498
  window.dispatchEvent(new CustomEvent("chat:typing:stop"));
 
654
 
655
  function addMessageToChat(sender, text, conversationId = null) {
656
  const chatMessages = document.getElementById("chat-messages");
657
+ // Allow empty text for streaming (we'll update it progressively)
658
+ if (text === undefined || text === null) return;
659
+
660
  const messageDiv = document.createElement("div");
661
  messageDiv.className = `message ${sender}`;
662
 
 
699
  messageTimeDiv.appendChild(deleteBtn);
700
 
701
  const textDiv = document.createElement("div");
702
+ textDiv.textContent = text || ""; // Handle empty strings properly
703
 
704
  messageDiv.appendChild(textDiv);
705
  messageDiv.appendChild(messageTimeDiv);
706
 
707
  chatMessages.appendChild(messageDiv);
708
  chatMessages.scrollTop = chatMessages.scrollHeight;
709
+
710
+ // Return an object that allows updating the message content for streaming
711
+ return {
712
+ updateText: newText => {
713
+ textDiv.textContent = newText;
714
+ // Throttle scrolling to prevent visual stuttering during streaming
715
+ if (!textDiv._scrollTimeout) {
716
+ textDiv._scrollTimeout = setTimeout(() => {
717
+ chatMessages.scrollTop = chatMessages.scrollHeight;
718
+ textDiv._scrollTimeout = null;
719
+ }, 50); // Throttle to 20 FPS max
720
+ }
721
+ },
722
+ element: messageDiv,
723
+ textElement: textDiv
724
+ };
725
  }
726
 
727
  async function loadChatHistory() {
 
884
  // Get default traits from KIMI_CHARACTERS constants
885
  const characterDefaults = window.KIMI_CHARACTERS[characterKey]?.traits || {};
886
 
887
+ // Get unified defaults
888
+ const unifiedDefaults = window.kimiEmotionSystem?.TRAIT_DEFAULTS || {
889
+ affection: 55,
890
+ playfulness: 55,
891
+ intelligence: 70,
892
+ empathy: 75,
893
+ humor: 60,
894
+ romance: 50
895
+ };
896
+
897
+ // Use saved traits if they exist, otherwise fall back to character defaults, then unified defaults
898
  const traits = {
899
+ affection: savedTraits.affection ?? characterDefaults.affection ?? unifiedDefaults.affection,
900
+ playfulness: savedTraits.playfulness ?? characterDefaults.playfulness ?? unifiedDefaults.playfulness,
901
+ intelligence: savedTraits.intelligence ?? characterDefaults.intelligence ?? unifiedDefaults.intelligence,
902
+ empathy: savedTraits.empathy ?? characterDefaults.empathy ?? unifiedDefaults.empathy,
903
+ humor: savedTraits.humor ?? characterDefaults.humor ?? unifiedDefaults.humor,
904
+ romance: savedTraits.romance ?? characterDefaults.romance ?? unifiedDefaults.romance
905
  };
906
 
907
  // Check if sliders exist before updating them
 
933
  const tokensIn = await kimiDB.getPreference(`totalTokensIn_${character}`, 0);
934
  const tokensOut = await kimiDB.getPreference(`totalTokensOut_${character}`, 0);
935
  const charDefAff = (window.KIMI_CHARACTERS && window.KIMI_CHARACTERS[character]?.traits?.affection) || null;
936
+ const genericAff = (window.getTraitDefaults && window.getTraitDefaults().affection) || 55;
937
  const defaultAff = typeof charDefAff === "number" ? charDefAff : genericAff;
938
  const affectionTrait = await kimiDB.getPersonalityTrait("affection", defaultAff, character);
939
  const conversations = await kimiDB.getAllConversations(character);
 
1369
  if (waitingIndicator) waitingIndicator.style.display = "inline-block";
1370
 
1371
  try {
1372
+ // Check if streaming is enabled (you can add a preference for this)
1373
+ const streamingEnabled = await window.kimiDB?.getPreference(
1374
+ "enableStreaming",
1375
+ window.KIMI_CONFIG?.DEFAULTS?.ENABLE_STREAMING ?? true
1376
+ );
1377
+
1378
+ if (streamingEnabled && window.kimiLLM && typeof window.kimiLLM.chatStreaming === "function") {
1379
+ // Use streaming through analyzeAndReact
1380
+ let streamingResponse = "";
1381
+ const messageObj = addMessageToChat("kimi", ""); // Start with empty message
1382
+
1383
+ // Safety check: ensure messageObj is valid
1384
+ if (!messageObj || typeof messageObj.updateText !== "function") {
1385
+ console.error("Failed to create streaming message object, falling back to non-streaming");
1386
+ const response = await analyzeAndReact(message);
1387
+ let finalResponse = response;
1388
+ if (!finalResponse || typeof finalResponse !== "string" || finalResponse.trim().length < 2) {
1389
+ finalResponse = window.getLocalizedEmotionalResponse
1390
+ ? window.getLocalizedEmotionalResponse("neutral")
1391
+ : "I'm here for you!";
1392
+ }
1393
+ addMessageToChat("kimi", finalResponse);
1394
+ if (window.voiceManager && !message.startsWith("Vous:")) {
1395
+ window.voiceManager.speak(finalResponse);
1396
+ }
1397
+ if (waitingIndicator) waitingIndicator.style.display = "none";
1398
+ return;
1399
+ }
1400
+
1401
+ try {
1402
+ console.log("🔄 Starting streaming response...");
1403
+ let emotionDetected = false;
1404
+
1405
+ const response = await analyzeAndReact(message, true, token => {
1406
+ streamingResponse += token;
1407
+ if (messageObj && messageObj.updateText) {
1408
+ messageObj.updateText(streamingResponse);
1409
+ }
1410
+ // Progressive analysis disabled to prevent UI flickering during streaming
1411
+ // All analysis will be done after streaming completes
1412
+ });
1413
+ console.log("✅ Streaming completed, final response length:", streamingResponse.length);
1414
+
1415
+ // Final processing after streaming completes
1416
+ let finalResponse = streamingResponse || response;
1417
+ if (!finalResponse || finalResponse.trim().length < 2) {
1418
+ finalResponse = window.getLocalizedEmotionalResponse
1419
+ ? window.getLocalizedEmotionalResponse("neutral")
1420
+ : "I'm here for you!";
1421
+ if (messageObj && messageObj.updateText) {
1422
+ messageObj.updateText(finalResponse);
1423
+ }
1424
+ }
1425
+
1426
+ // Voice synthesis after streaming completes (if not started during streaming)
1427
+ if (window.voiceManager && !message.startsWith("Vous:") && finalResponse.length > 20) {
1428
+ // Check if voice synthesis should happen
1429
+ const shouldSpeak = await window.kimiDB?.getPreference(
1430
+ "voiceEnabled",
1431
+ window.KIMI_CONFIG?.DEFAULTS?.VOICE_ENABLED ?? true
1432
+ );
1433
+ if (shouldSpeak) {
1434
+ window.voiceManager.speak(finalResponse);
1435
+ }
1436
+ }
1437
+
1438
+ // Final comprehensive system updates
1439
+ try {
1440
+ // Final emotion analysis if not done during streaming
1441
+ if (!emotionDetected && window.kimiAnalyzeEmotion) {
1442
+ const finalEmotion = window.kimiAnalyzeEmotion(finalResponse);
1443
+ if (finalEmotion && finalEmotion !== "neutral") {
1444
+ emotionDetected = true;
1445
+ }
1446
+ }
1447
+
1448
+ // Final personality update
1449
+ if (window.updatePersonalityTraitsFromEmotion && finalResponse.length > 50) {
1450
+ const finalEmotion = window.kimiAnalyzeEmotion ? window.kimiAnalyzeEmotion(finalResponse) : "neutral";
1451
+ await window.updatePersonalityTraitsFromEmotion(finalEmotion, finalResponse);
1452
+ }
1453
+
1454
+ // Final memory extraction
1455
+ if (window.kimiMemory && typeof window.kimiMemory.extractMemoriesFromConversation === "function") {
1456
+ await window.kimiMemory.extractMemoriesFromConversation(message, finalResponse);
1457
+ }
1458
+
1459
+ // Final video state adjustment
1460
+ if (window.kimiVideo && window.kimiDB) {
1461
+ const selectedCharacter = await window.kimiDB.getSelectedCharacter();
1462
+ const traits = await window.kimiDB.getAllPersonalityTraits(selectedCharacter);
1463
+ if (traits && emotionDetected) {
1464
+ window.kimiVideo.setMoodByPersonality(traits);
1465
+ }
1466
+ }
1467
+ } catch (finalError) {
1468
+ console.warn("Final system updates failed:", finalError);
1469
+ }
1470
+
1471
+ if (waitingIndicator) waitingIndicator.style.display = "none";
1472
+ } catch (streamingError) {
1473
+ console.warn("Streaming failed, falling back to non-streaming:", streamingError);
1474
+ // Fallback to non-streaming
1475
+ const response = await analyzeAndReact(message);
1476
+ let finalResponse = response;
1477
+ if (!finalResponse || typeof finalResponse !== "string" || finalResponse.trim().length < 2) {
1478
+ finalResponse = window.getLocalizedEmotionalResponse
1479
+ ? window.getLocalizedEmotionalResponse("neutral")
1480
+ : "I'm here for you!";
1481
+ }
1482
+ if (messageObj && messageObj.updateText) {
1483
+ messageObj.updateText(finalResponse);
1484
+ }
1485
+
1486
+ if (window.voiceManager && !message.startsWith("Vous:")) {
1487
+ window.voiceManager.speak(finalResponse);
1488
+ }
1489
+ if (waitingIndicator) waitingIndicator.style.display = "none";
1490
  }
1491
+ } else {
1492
+ // Use non-streaming (original behavior)
1493
+ const response = await analyzeAndReact(message);
1494
+ let finalResponse = response;
1495
+ // If the LLM's response is empty, null, or too short, use the emotional fallback.
1496
+ if (!finalResponse || typeof finalResponse !== "string" || finalResponse.trim().length < 2) {
1497
+ finalResponse = window.getLocalizedEmotionalResponse
1498
+ ? window.getLocalizedEmotionalResponse("neutral")
1499
+ : "I'm here for you!";
1500
+ }
1501
+ setTimeout(() => {
1502
+ addMessageToChat("kimi", finalResponse);
1503
+ if (window.voiceManager && !message.startsWith("Vous:")) {
1504
+ window.voiceManager.speak(finalResponse);
1505
+ }
1506
+ if (waitingIndicator) waitingIndicator.style.display = "none";
1507
+ }, 1000);
1508
+ }
1509
  } catch (error) {
1510
  console.error("Error while generating response:", error);
1511
  const i18n = window.kimiI18nManager;
 
1539
  const llmTopPSlider = document.getElementById("llm-top-p");
1540
  const llmFrequencyPenaltySlider = document.getElementById("llm-frequency-penalty");
1541
  const llmPresencePenaltySlider = document.getElementById("llm-presence-penalty");
1542
+ const enableStreamingToggle = document.getElementById("enable-streaming");
1543
  const colorThemeSelect = document.getElementById("color-theme");
1544
  const interfaceOpacitySlider = document.getElementById("interface-opacity");
1545
  const animationsToggle = document.getElementById("animations-toggle");
 
1764
  llmPresencePenaltySlider.addEventListener("input", listener);
1765
  window._kimiListenerCleanup.push(() => llmPresencePenaltySlider.removeEventListener("input", listener));
1766
  }
1767
+ if (enableStreamingToggle) {
1768
+ const listener = async () => {
1769
+ try {
1770
+ const isEnabled = enableStreamingToggle.classList.contains("active");
1771
+ const newState = !isEnabled;
1772
+ enableStreamingToggle.classList.toggle("active", newState);
1773
+ enableStreamingToggle.setAttribute("aria-checked", newState ? "true" : "false");
1774
+ if (kimiDB) await kimiDB.setPreference("enableStreaming", newState);
1775
+ } catch (error) {
1776
+ console.error("Error toggling streaming:", error);
1777
+ }
1778
+ };
1779
+ enableStreamingToggle.addEventListener("click", listener);
1780
+ window._kimiListenerCleanup.push(() => enableStreamingToggle.removeEventListener("click", listener));
1781
+ }
1782
  if (colorThemeSelect) {
1783
  colorThemeSelect.removeEventListener("change", window._kimiColorThemeListener);
1784
  window._kimiColorThemeListener = async e => {
 
1803
  }
1804
  // Animation toggle is handled by KimiAppearanceManager
1805
  // Remove the duplicate handler to prevent conflicts
1806
+ // Real-time transcript toggle (shows live speech transcription and AI responses)
1807
  const transcriptToggle = document.getElementById("transcript-toggle");
1808
  if (transcriptToggle) {
 
1809
  if (kimiDB && kimiDB.getPreference) {
1810
+ kimiDB.getPreference("showTranscript", window.KIMI_CONFIG?.DEFAULTS?.SHOW_TRANSCRIPT ?? true).then(showTranscript => {
1811
  transcriptToggle.classList.toggle("active", showTranscript);
1812
  transcriptToggle.setAttribute("aria-checked", showTranscript ? "true" : "false");
1813
  });
 
1816
  const enabled = !transcriptToggle.classList.contains("active");
1817
  transcriptToggle.classList.toggle("active", enabled);
1818
  transcriptToggle.setAttribute("aria-checked", enabled ? "true" : "false");
1819
+ // Save transcript display preference
1820
  if (kimiDB && kimiDB.setPreference) {
1821
  await kimiDB.setPreference("showTranscript", enabled);
1822
  }
1823
+ // Apply change immediately if transcript is currently visible
1824
+ if (window.kimiVoiceManager && window.kimiVoiceManager.updateTranscriptVisibility) {
1825
+ if (!enabled) {
1826
+ // Hide transcript immediately if disabled (uses centralized logic)
1827
+ await window.kimiVoiceManager.updateTranscriptVisibility(false);
1828
+ }
1829
+ // If enabled, transcript will show naturally during next voice interaction
1830
+ }
1831
  };
1832
  transcriptToggle.onclick = onToggle;
1833
  transcriptToggle.onkeydown = async e => {
 
1874
  }
1875
  } catch {}
1876
  }
1877
+
1878
+ // Load streaming preference
1879
+ try {
1880
+ const enableStreamingToggle = document.getElementById("enable-streaming");
1881
+ if (enableStreamingToggle) {
1882
+ const streamingEnabled = await window.kimiDB.getPreference(
1883
+ "enableStreaming",
1884
+ window.KIMI_CONFIG?.DEFAULTS?.ENABLE_STREAMING ?? true
1885
+ );
1886
+ enableStreamingToggle.classList.toggle("active", streamingEnabled);
1887
+ enableStreamingToggle.setAttribute("aria-checked", streamingEnabled ? "true" : "false");
1888
+ }
1889
+ } catch {}
1890
  }
1891
  window.refreshAllSliders = refreshAllSliders;
1892
 
 
2009
  } else if (window.getTraitDefaults) {
2010
  generic = window.getTraitDefaults();
2011
  } else {
2012
+ generic = { affection: 55, playfulness: 55, intelligence: 70, empathy: 75, humor: 60, romance: 50 };
2013
  }
2014
  // Character defaults take precedence over generic defaults
2015
  return { ...generic, ...charDefaults };
kimi-js/kimi-script.js CHANGED
@@ -909,7 +909,7 @@ document.addEventListener("DOMContentLoaded", async function () {
909
  if (personalityPayload) {
910
  const { character, traits } = personalityPayload;
911
  const defaults = (window.getTraitDefaults && window.getTraitDefaults()) || {
912
- affection: 65,
913
  romance: 50,
914
  empathy: 75,
915
  playfulness: 55,
 
909
  if (personalityPayload) {
910
  const { character, traits } = personalityPayload;
911
  const defaults = (window.getTraitDefaults && window.getTraitDefaults()) || {
912
+ affection: 55, // Lowered to match emotion system defaults
913
  romance: 50,
914
  empathy: 75,
915
  playfulness: 55,
kimi-js/kimi-utils.js CHANGED
@@ -311,24 +311,8 @@ class KimiVideoManager {
311
  this._maxPrefetch = 3;
312
  this._loadTimeout = null;
313
  this.updateVideoCategories();
314
- this.emotionToCategory = {
315
- listening: "listening",
316
- positive: "speakingPositive",
317
- negative: "speakingNegative",
318
- neutral: "neutral",
319
- surprise: "speakingPositive",
320
- laughing: "speakingPositive",
321
- shy: "neutral",
322
- confident: "speakingPositive",
323
- romantic: "speakingPositive",
324
- flirtatious: "speakingPositive",
325
- goodbye: "neutral",
326
- kiss: "speakingPositive",
327
- dancing: "dancing",
328
- speaking: "speakingPositive",
329
- speakingPositive: "speakingPositive",
330
- speakingNegative: "speakingNegative"
331
- };
332
  this.positiveVideos = this.videoCategories.speakingPositive;
333
  this.negativeVideos = this.videoCategories.speakingNegative;
334
  this.neutralVideos = this.videoCategories.neutral;
@@ -995,34 +979,36 @@ class KimiVideoManager {
995
  if (traits && typeof affection === "number") {
996
  let weights = candidateVideos.map(video => {
997
  if (category === "speakingPositive") {
998
- // More positive videos when affection is high, but not extreme
999
- // Also bias within positive towards romance/humor contexts when emotion suggests it
1000
- const base = 1 + (affection / 100) * 0.3;
1001
  let bonus = 0;
1002
  const rom = typeof traits.romance === "number" ? traits.romance : 50;
1003
  const hum = typeof traits.humor === "number" ? traits.humor : 50;
1004
- if (emotion === "romantic") bonus += (rom / 100) * 0.2;
1005
- if (emotion === "laughing") bonus += (hum / 100) * 0.2;
1006
  return base + bonus;
1007
  }
1008
  if (category === "speakingNegative") {
1009
- // More negative/shy videos when affection is low
1010
- return 1 + ((100 - affection) / 100) * 0.4;
1011
  }
1012
  if (category === "neutral") {
1013
- // Neutral videos when affection is moderate (peak at ~50, lower at extremes)
1014
  const distance = Math.abs(50 - affection) / 50; // 0 at 50, 1 at 0 or 100
1015
- return 1 + (1 - Math.min(1, distance)) * 0.2;
 
1016
  }
1017
  if (category === "dancing") {
1018
- // Dancing strongly influenced by playfulness but capped
1019
- return 1 + Math.min(0.6, (traits.playfulness / 100) * 0.7);
 
 
1020
  }
1021
  if (category === "listening") {
1022
- // Listening influenced by empathy and attention
1023
  const empathyWeight = (traits.empathy || 50) / 100;
1024
- // Slightly consider affection too (more patient listening at higher affection)
1025
- return 1 + empathyWeight * 0.2 + (affection / 100) * 0.05;
1026
  }
1027
  return 1;
1028
  });
@@ -1083,9 +1069,29 @@ class KimiVideoManager {
1083
 
1084
  // Ensure determineCategory exists as a class method (used at line ~494 and ~537)
1085
  determineCategory(context, emotion = "neutral", traits = null) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1086
  // Prefer explicit context mapping if provided (e.g., 'listening','dancing')
1087
- if (this.emotionToCategory && this.emotionToCategory[context]) {
1088
- return this.emotionToCategory[context];
1089
  }
1090
  // Normalize generic 'speaking' by emotion polarity
1091
  if (context === "speaking") {
@@ -1094,8 +1100,8 @@ class KimiVideoManager {
1094
  return "neutral";
1095
  }
1096
  // Map by emotion label when possible
1097
- if (this.emotionToCategory && this.emotionToCategory[emotion]) {
1098
- return this.emotionToCategory[emotion];
1099
  }
1100
  return "neutral";
1101
  }
@@ -1241,8 +1247,8 @@ class KimiVideoManager {
1241
  detectedEmotion = emotionAnalysis.reaction;
1242
  }
1243
 
1244
- // Special case: Auto-dancing if playfulness very high
1245
- if (traits && typeof traits.playfulness === "number" && traits.playfulness >= 85) {
1246
  this.switchToContext("dancing", "dancing", null, traits, affection);
1247
  return;
1248
  }
@@ -1286,8 +1292,9 @@ class KimiVideoManager {
1286
 
1287
  // Switch to appropriate context based on dominant emotion
1288
  if (max >= 1 && dominant) {
1289
- // Map emotion to context using our emotion mapping
1290
- const targetCategory = this.emotionToCategory[dominant];
 
1291
  if (targetCategory) {
1292
  this.switchToContext(targetCategory, dominant, null, traits, affection);
1293
  return;
@@ -1312,10 +1319,11 @@ class KimiVideoManager {
1312
  }
1313
  }
1314
 
1315
- // Default to neutral context, with a very subtle positive bias at very high affection
1316
- if (traits && typeof traits.affection === "number" && traits.affection >= 95) {
1317
  const chance = Math.random();
1318
- if (chance < 0.25) {
 
1319
  this.switchToContext("speakingPositive", "positive", null, traits, affection);
1320
  return;
1321
  }
@@ -1850,8 +1858,8 @@ function getMoodCategoryFromPersonality(traits) {
1850
  return window.kimiEmotionSystem.getMoodCategoryFromPersonality(traits);
1851
  }
1852
 
1853
- // Fallback (should not be reached)
1854
- const keys = ["affection", "romance", "empathy", "playfulness", "humor"];
1855
  let sum = 0;
1856
  let count = 0;
1857
  keys.forEach(key => {
@@ -2260,9 +2268,14 @@ class KimiUIStateManager {
2260
  window.KimiDOMUtils.setText("#favorability-text", `${clamped.toFixed(2)}%`);
2261
  window.KimiDOMUtils.get("#favorability-bar").style.width = `${clamped}%`;
2262
  }
2263
- setTranscript(text) {
2264
  this.state.transcript = text;
2265
- window.KimiDOMUtils.setText("#transcript", text);
 
 
 
 
 
2266
  }
2267
  setChatOpen(open) {
2268
  this.state.chatOpen = open;
 
311
  this._maxPrefetch = 3;
312
  this._loadTimeout = null;
313
  this.updateVideoCategories();
314
+ // Use centralized emotion mapping from emotion system
315
+ this.emotionToCategory = null; // Will be fetched from emotion system when needed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
  this.positiveVideos = this.videoCategories.speakingPositive;
317
  this.negativeVideos = this.videoCategories.speakingNegative;
318
  this.neutralVideos = this.videoCategories.neutral;
 
979
  if (traits && typeof affection === "number") {
980
  let weights = candidateVideos.map(video => {
981
  if (category === "speakingPositive") {
982
+ // Positive videos favored by affection, romance, and humor
983
+ const base = 1 + (affection / 100) * 0.4; // Increased from 0.3
 
984
  let bonus = 0;
985
  const rom = typeof traits.romance === "number" ? traits.romance : 50;
986
  const hum = typeof traits.humor === "number" ? traits.humor : 50;
987
+ if (emotion === "romantic") bonus += (rom / 100) * 0.3; // Increased from 0.2
988
+ if (emotion === "laughing") bonus += (hum / 100) * 0.3; // Increased from 0.2
989
  return base + bonus;
990
  }
991
  if (category === "speakingNegative") {
992
+ // Negative videos when affection is low (reduced weight to balance)
993
+ return 1 + ((100 - affection) / 100) * 0.3; // Reduced from 0.4
994
  }
995
  if (category === "neutral") {
996
+ // Neutral videos when affection is moderate, also influenced by intelligence
997
  const distance = Math.abs(50 - affection) / 50; // 0 at 50, 1 at 0 or 100
998
+ const intBonus = ((traits.intelligence || 50) / 100) * 0.1; // Intelligence adds to neutral thoughtfulness
999
+ return 1 + (1 - Math.min(1, distance)) * 0.2 + intBonus;
1000
  }
1001
  if (category === "dancing") {
1002
+ // Dancing strongly influenced by playfulness, romance also adds excitement
1003
+ const playBonus = Math.min(0.6, (traits.playfulness / 100) * 0.7);
1004
+ const romanceBonus = ((traits.romance || 50) / 100) * 0.2; // Romance adds to dance appeal
1005
+ return 1 + playBonus + romanceBonus;
1006
  }
1007
  if (category === "listening") {
1008
+ // Listening influenced by empathy, intelligence, and affection
1009
  const empathyWeight = (traits.empathy || 50) / 100;
1010
+ const intWeight = ((traits.intelligence || 50) / 100) * 0.1; // Intelligence improves listening quality
1011
+ return 1 + empathyWeight * 0.3 + (affection / 100) * 0.1 + intWeight;
1012
  }
1013
  return 1;
1014
  });
 
1069
 
1070
  // Ensure determineCategory exists as a class method (used at line ~494 and ~537)
1071
  determineCategory(context, emotion = "neutral", traits = null) {
1072
+ // Get emotion mapping from centralized emotion system
1073
+ const emotionToCategory = window.kimiEmotionSystem?.emotionToVideoCategory || {
1074
+ listening: "listening",
1075
+ positive: "speakingPositive",
1076
+ negative: "speakingNegative",
1077
+ neutral: "neutral",
1078
+ surprise: "speakingPositive",
1079
+ laughing: "speakingPositive",
1080
+ shy: "neutral",
1081
+ confident: "speakingPositive",
1082
+ romantic: "speakingPositive",
1083
+ flirtatious: "speakingPositive",
1084
+ goodbye: "neutral",
1085
+ kiss: "speakingPositive",
1086
+ dancing: "dancing",
1087
+ speaking: "speakingPositive",
1088
+ speakingPositive: "speakingPositive",
1089
+ speakingNegative: "speakingNegative"
1090
+ };
1091
+
1092
  // Prefer explicit context mapping if provided (e.g., 'listening','dancing')
1093
+ if (emotionToCategory[context]) {
1094
+ return emotionToCategory[context];
1095
  }
1096
  // Normalize generic 'speaking' by emotion polarity
1097
  if (context === "speaking") {
 
1100
  return "neutral";
1101
  }
1102
  // Map by emotion label when possible
1103
+ if (emotionToCategory[emotion]) {
1104
+ return emotionToCategory[emotion];
1105
  }
1106
  return "neutral";
1107
  }
 
1247
  detectedEmotion = emotionAnalysis.reaction;
1248
  }
1249
 
1250
+ // Special case: Auto-dancing if playfulness high (more accessible)
1251
+ if (traits && typeof traits.playfulness === "number" && traits.playfulness >= 75) {
1252
  this.switchToContext("dancing", "dancing", null, traits, affection);
1253
  return;
1254
  }
 
1292
 
1293
  // Switch to appropriate context based on dominant emotion
1294
  if (max >= 1 && dominant) {
1295
+ // Map emotion to context using centralized emotion mapping
1296
+ const emotionToCategory = window.kimiEmotionSystem?.emotionToVideoCategory || {};
1297
+ const targetCategory = emotionToCategory[dominant];
1298
  if (targetCategory) {
1299
  this.switchToContext(targetCategory, dominant, null, traits, affection);
1300
  return;
 
1319
  }
1320
  }
1321
 
1322
+ // Default to neutral context, with a positive bias at high affection (more accessible)
1323
+ if (traits && typeof traits.affection === "number" && traits.affection >= 80) {
1324
  const chance = Math.random();
1325
+ if (chance < 0.35) {
1326
+ // Increased chance from 0.25 to 0.35
1327
  this.switchToContext("speakingPositive", "positive", null, traits, affection);
1328
  return;
1329
  }
 
1858
  return window.kimiEmotionSystem.getMoodCategoryFromPersonality(traits);
1859
  }
1860
 
1861
+ // Fallback (should not be reached) - must match emotion system calculation
1862
+ const keys = ["affection", "romance", "empathy", "playfulness", "humor", "intelligence"];
1863
  let sum = 0;
1864
  let count = 0;
1865
  keys.forEach(key => {
 
2268
  window.KimiDOMUtils.setText("#favorability-text", `${clamped.toFixed(2)}%`);
2269
  window.KimiDOMUtils.get("#favorability-bar").style.width = `${clamped}%`;
2270
  }
2271
+ async setTranscript(text) {
2272
  this.state.transcript = text;
2273
+ // Always use the proper transcript management via VoiceManager
2274
+ if (window.kimiVoiceManager && window.kimiVoiceManager.updateTranscriptVisibility) {
2275
+ await window.kimiVoiceManager.updateTranscriptVisibility(!!text, text);
2276
+ } else {
2277
+ console.warn("VoiceManager not available - transcript display may not work properly");
2278
+ }
2279
  }
2280
  setChatOpen(open) {
2281
  this.state.chatOpen = open;
kimi-js/kimi-voices.js CHANGED
@@ -18,8 +18,9 @@ class KimiVoiceManager {
18
 
19
  // DOM elements
20
  this.micButton = null;
21
- this.transcriptContainer = null;
22
- this.transcriptText = null;
 
23
 
24
  // Callback for voice message analysis
25
  this.onSpeechAnalysis = null;
@@ -72,6 +73,14 @@ class KimiVoiceManager {
72
  return false;
73
  }
74
 
 
 
 
 
 
 
 
 
75
  // Initialize voice synthesis
76
  await this.initVoices();
77
  this.setupVoicesChangedListener();
@@ -289,10 +298,7 @@ class KimiVoiceManager {
289
  console.warn("Unable to speak: empty text or voice not initialized");
290
  return;
291
  }
292
- if (this.transcriptHideTimeout) {
293
- clearTimeout(this.transcriptHideTimeout);
294
- this.transcriptHideTimeout = null;
295
- }
296
  if (this.speechSynthesis.speaking) {
297
  this.speechSynthesis.cancel();
298
  }
@@ -374,12 +380,9 @@ class KimiVoiceManager {
374
 
375
  utterance.onstart = async () => {
376
  this.isSpeaking = true;
377
- const showTranscript = await this.db?.getPreference("showTranscript", true);
378
- if (showTranscript && this.transcriptContainer) {
379
- this.transcriptContainer.classList.add("visible");
380
- } else if (this.transcriptContainer) {
381
- this.transcriptContainer.classList.remove("visible");
382
- }
383
  // Ensure a speaking animation plays (avoid frozen neutral frame during TTS)
384
  try {
385
  if (window.kimiVideo && window.kimiVideo.getCurrentVideoInfo) {
@@ -400,10 +403,10 @@ class KimiVoiceManager {
400
 
401
  utterance.onend = () => {
402
  this.isSpeaking = false;
403
- if (this.transcriptContainer) {
404
- this.transcriptContainer.classList.remove("visible");
405
- }
406
- this.transcriptHideTimeout = null;
407
  if (window.kimiVideo) {
408
  // Do not force neutral if an emotion clip is still playing (speaking/dancing)
409
  try {
@@ -428,10 +431,8 @@ class KimiVoiceManager {
428
 
429
  utterance.onerror = e => {
430
  this.isSpeaking = false;
431
- if (this.transcriptContainer) {
432
- this.transcriptContainer.classList.remove("visible");
433
- }
434
- this.transcriptHideTimeout = null;
435
  };
436
 
437
  this.speechSynthesis.speak(utterance);
@@ -509,38 +510,70 @@ class KimiVoiceManager {
509
  return Math.max(estimatedMilliseconds + bufferTime, 2000);
510
  }
511
 
512
- async showResponseWithPerfectTiming(text) {
513
- if (!this.transcriptContainer || !this.transcriptText) return;
514
- const showTranscript = await this.db?.getPreference("showTranscript", true);
515
- if (!showTranscript) return;
516
- this.transcriptText.textContent = `${this.selectedCharacter}: ${text}`;
517
- this.transcriptContainer.classList.add("visible");
518
  if (this.transcriptHideTimeout) {
519
  clearTimeout(this.transcriptHideTimeout);
520
  this.transcriptHideTimeout = null;
521
  }
522
  }
523
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
524
  showResponse(text) {
525
  this.showResponseWithPerfectTiming(text);
526
  }
527
 
 
528
  async showUserMessage(text, duration = 3000) {
529
- if (!this.transcriptContainer || !this.transcriptText) return;
530
- const showTranscript = await this.db?.getPreference("showTranscript", true);
531
- if (!showTranscript) return;
532
- if (this.transcriptHideTimeout) {
533
- clearTimeout(this.transcriptHideTimeout);
534
- this.transcriptHideTimeout = null;
 
 
535
  }
536
- this.transcriptText.textContent = text;
537
- this.transcriptContainer.classList.add("visible");
538
- this.transcriptHideTimeout = setTimeout(() => {
539
- if (this.transcriptContainer) {
540
- this.transcriptContainer.classList.remove("visible");
541
- }
542
- this.transcriptHideTimeout = null;
543
- }, duration);
544
  }
545
 
546
  // ===== SPEECH RECOGNITION =====
@@ -573,6 +606,7 @@ class KimiVoiceManager {
573
  console.log("🎤 Microphone permission confirmed via onresult");
574
  }
575
 
 
576
  let final_transcript = "";
577
  let interim_transcript = "";
578
  for (let i = event.resultIndex; i < event.results.length; ++i) {
@@ -582,14 +616,11 @@ class KimiVoiceManager {
582
  interim_transcript += event.results[i][0].transcript;
583
  }
584
  }
585
- const showTranscript = await this.db?.getPreference("showTranscript", true);
586
- if (showTranscript && this.transcriptText) {
587
- this.transcriptText.textContent = final_transcript || interim_transcript;
588
- if (this.transcriptContainer && (final_transcript || interim_transcript)) {
589
- this.transcriptContainer.classList.add("visible");
590
- }
591
- } else if (this.transcriptContainer) {
592
- this.transcriptContainer.classList.remove("visible");
593
  }
594
  if (final_transcript && this.onSpeechAnalysis) {
595
  try {
@@ -718,15 +749,14 @@ class KimiVoiceManager {
718
  console.log("🎤 Permission denied - stopping listening");
719
  this.micPermissionGranted = false;
720
  this.stopListening();
721
- if (this.transcriptText) {
722
- this.transcriptText.textContent =
723
- window.kimiI18nManager?.t("mic_permission_denied") ||
724
- "Microphone permission denied. Click again to retry.";
725
- this.transcriptContainer?.classList.add("visible");
726
  setTimeout(() => {
727
- this.transcriptContainer?.classList.remove("visible");
728
  }, 2000);
729
- }
730
  } else {
731
  this.stopListening();
732
  }
@@ -758,9 +788,7 @@ class KimiVoiceManager {
758
  this.isListening = false;
759
  if (this.micButton) this.micButton.classList.remove("is-listening");
760
  if (this.micButton) this.micButton.classList.remove("mic-pulse-active");
761
- if (this.transcriptContainer) {
762
- this.transcriptContainer.classList.remove("visible");
763
- }
764
  };
765
  }
766
 
@@ -782,13 +810,11 @@ class KimiVoiceManager {
782
  else if (this.browser === "opera") key = "sr_not_supported_opera";
783
  else if (this.browser === "safari") key = "sr_not_supported_safari";
784
  const message = window.kimiI18nManager?.t(key) || "Speech recognition is not available in this browser.";
785
- if (this.transcriptText) {
786
- this.transcriptText.textContent = message;
787
- this.transcriptContainer?.classList.add("visible");
788
  setTimeout(() => {
789
- this.transcriptContainer?.classList.remove("visible");
790
  }, 4000);
791
- }
792
  return;
793
  }
794
 
@@ -814,13 +840,11 @@ class KimiVoiceManager {
814
  else if (this.browser === "opera") key = "sr_not_supported_opera";
815
  else if (this.browser === "safari") key = "sr_not_supported_safari";
816
  const message = window.kimiI18nManager?.t(key) || "Speech recognition is not available in this browser.";
817
- if (this.transcriptText) {
818
- this.transcriptText.textContent = message;
819
- this.transcriptContainer?.classList.add("visible");
820
  setTimeout(() => {
821
- this.transcriptContainer?.classList.remove("visible");
822
  }, 4000);
823
- }
824
  return;
825
  }
826
  if (!this.recognition || this.isListening) return;
@@ -828,14 +852,12 @@ class KimiVoiceManager {
828
  // Check microphone API availability
829
  if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
830
  console.warn("MediaDevices API not available");
831
- if (this.transcriptText) {
832
- this.transcriptText.textContent =
833
- window.kimiI18nManager?.t("mic_not_supported") || "Microphone not supported in this browser.";
834
- this.transcriptContainer?.classList.add("visible");
835
  setTimeout(() => {
836
- this.transcriptContainer?.classList.remove("visible");
837
  }, 3000);
838
- }
839
  return;
840
  }
841
 
@@ -857,15 +879,14 @@ class KimiVoiceManager {
857
  return;
858
  } else if (permissionStatus.state === "denied") {
859
  console.log("🎤 Microphone permission denied");
860
- if (this.transcriptText) {
861
- this.transcriptText.textContent =
862
- window.kimiI18nManager?.t("mic_permission_denied") ||
863
- "Microphone permission denied. Please allow access in browser settings.";
864
- this.transcriptContainer?.classList.add("visible");
865
  setTimeout(() => {
866
- this.transcriptContainer?.classList.remove("visible");
867
  }, 4000);
868
- }
869
  return;
870
  }
871
  } catch (error) {
@@ -926,14 +947,13 @@ class KimiVoiceManager {
926
  this.stopListening();
927
 
928
  // Show user-friendly error message
929
- if (this.transcriptText) {
930
- this.transcriptText.textContent =
931
- window.kimiI18nManager?.t("mic_permission_denied") || "Microphone permission denied. Click again to retry.";
932
- this.transcriptContainer?.classList.add("visible");
933
  setTimeout(() => {
934
- this.transcriptContainer?.classList.remove("visible");
935
  }, 3000);
936
- }
937
  }
938
  }
939
 
@@ -971,16 +991,12 @@ class KimiVoiceManager {
971
  }
972
  }
973
 
974
- if (this.transcriptHideTimeout) {
975
- clearTimeout(this.transcriptHideTimeout);
976
- this.transcriptHideTimeout = null;
977
- }
978
 
979
  if (!this.speechSynthesis.speaking) {
980
- this.transcriptHideTimeout = setTimeout(() => {
981
- if (this.transcriptContainer) {
982
- this.transcriptContainer.classList.remove("visible");
983
- }
984
  this.transcriptHideTimeout = null;
985
  }, 2000);
986
  }
@@ -1045,8 +1061,6 @@ class KimiVoiceManager {
1045
  window.kimiI18nManager?.t("test_voice_message_2") || "I am Kimi, your virtual companion!",
1046
  window.kimiI18nManager?.t("test_voice_message_3") || "How are you today, my love?"
1047
  ];
1048
- const randomMessage = testMessages[Math.floor(Math.random() * testMessages.length)];
1049
- await this.speak(randomMessage);
1050
  }
1051
 
1052
  destroy() {
@@ -1056,10 +1070,7 @@ class KimiVoiceManager {
1056
  this.listeningTimeout = null;
1057
  }
1058
 
1059
- if (this.transcriptHideTimeout) {
1060
- clearTimeout(this.transcriptHideTimeout);
1061
- this.transcriptHideTimeout = null;
1062
- }
1063
 
1064
  if (this.recognition) {
1065
  this.recognition.stop();
@@ -1145,9 +1156,7 @@ class KimiVoiceManager {
1145
  console.log("🎤 Interrupting speech to start listening");
1146
  this.speechSynthesis.cancel();
1147
  this.isSpeaking = false;
1148
- if (this.transcriptContainer) {
1149
- this.transcriptContainer.classList.remove("visible");
1150
- }
1151
  }
1152
 
1153
  if (this.isListening) {
 
18
 
19
  // DOM elements
20
  this.micButton = null;
21
+ // Real-time transcript overlay elements (shows live speech transcription and AI responses)
22
+ this.transcriptContainer = null; // Container for transcript overlay
23
+ this.transcriptText = null; // Text element displaying current transcript
24
 
25
  // Callback for voice message analysis
26
  this.onSpeechAnalysis = null;
 
73
  return false;
74
  }
75
 
76
+ // Check transcript elements (non-critical, just warn)
77
+ if (!this.transcriptContainer) {
78
+ console.warn("Transcript container not found in DOM - transcript feature will be disabled");
79
+ }
80
+ if (!this.transcriptText) {
81
+ console.warn("Transcript text element not found in DOM - transcript feature will be disabled");
82
+ }
83
+
84
  // Initialize voice synthesis
85
  await this.initVoices();
86
  this.setupVoicesChangedListener();
 
298
  console.warn("Unable to speak: empty text or voice not initialized");
299
  return;
300
  }
301
+ this.clearTranscriptTimeout();
 
 
 
302
  if (this.speechSynthesis.speaking) {
303
  this.speechSynthesis.cancel();
304
  }
 
380
 
381
  utterance.onstart = async () => {
382
  this.isSpeaking = true;
383
+ // Note: transcript visibility is already handled by showResponseWithPerfectTiming
384
+ // This ensures the transcript stays visible while AI is speaking
385
+
 
 
 
386
  // Ensure a speaking animation plays (avoid frozen neutral frame during TTS)
387
  try {
388
  if (window.kimiVideo && window.kimiVideo.getCurrentVideoInfo) {
 
403
 
404
  utterance.onend = () => {
405
  this.isSpeaking = false;
406
+ // Hide transcript overlay when AI finishes speaking
407
+ this.updateTranscriptVisibility(false);
408
+ // Clear any pending hide timeout
409
+ this.clearTranscriptTimeout();
410
  if (window.kimiVideo) {
411
  // Do not force neutral if an emotion clip is still playing (speaking/dancing)
412
  try {
 
431
 
432
  utterance.onerror = e => {
433
  this.isSpeaking = false;
434
+ this.updateTranscriptVisibility(false);
435
+ this.clearTranscriptTimeout();
 
 
436
  };
437
 
438
  this.speechSynthesis.speak(utterance);
 
510
  return Math.max(estimatedMilliseconds + bufferTime, 2000);
511
  }
512
 
513
+ // ===== REAL-TIME TRANSCRIPT DISPLAY =====
514
+ // Centralized transcript timeout management
515
+ clearTranscriptTimeout() {
 
 
 
516
  if (this.transcriptHideTimeout) {
517
  clearTimeout(this.transcriptHideTimeout);
518
  this.transcriptHideTimeout = null;
519
  }
520
  }
521
 
522
+ // Utility method to safely check transcript preference and control visibility
523
+ async updateTranscriptVisibility(shouldShow, text = null) {
524
+ if (!this.transcriptContainer || !this.transcriptText) return false;
525
+
526
+ const showTranscript = await this.db?.getPreference(
527
+ "showTranscript",
528
+ window.KIMI_CONFIG?.DEFAULTS?.SHOW_TRANSCRIPT ?? true
529
+ );
530
+ if (!showTranscript) {
531
+ // If transcript is disabled, always hide
532
+ this.transcriptContainer.classList.remove("visible");
533
+ return false;
534
+ }
535
+
536
+ if (shouldShow) {
537
+ if (text) {
538
+ // Show with text content
539
+ this.transcriptText.textContent = text;
540
+ this.transcriptContainer.classList.add("visible");
541
+ return true;
542
+ } else {
543
+ // Show but keep existing text (for cases where we just want to maintain visibility)
544
+ this.transcriptContainer.classList.add("visible");
545
+ return true;
546
+ }
547
+ } else {
548
+ // Hide transcript
549
+ this.transcriptContainer.classList.remove("visible");
550
+ return false;
551
+ }
552
+ }
553
+
554
+ // Show AI response text in real-time transcript overlay when AI is speaking
555
+ async showResponseWithPerfectTiming(text) {
556
+ const success = await this.updateTranscriptVisibility(true, `${this.selectedCharacter}: ${text}`);
557
+ if (success) {
558
+ this.clearTranscriptTimeout();
559
+ }
560
+ }
561
+
562
  showResponse(text) {
563
  this.showResponseWithPerfectTiming(text);
564
  }
565
 
566
+ // Show user voice input text in real-time transcript overlay during speech recognition
567
  async showUserMessage(text, duration = 3000) {
568
+ const success = await this.updateTranscriptVisibility(true, text);
569
+ if (success) {
570
+ this.clearTranscriptTimeout();
571
+ // Auto-hide transcript after specified duration
572
+ this.transcriptHideTimeout = setTimeout(async () => {
573
+ await this.updateTranscriptVisibility(false);
574
+ this.transcriptHideTimeout = null;
575
+ }, duration);
576
  }
 
 
 
 
 
 
 
 
577
  }
578
 
579
  // ===== SPEECH RECOGNITION =====
 
606
  console.log("🎤 Microphone permission confirmed via onresult");
607
  }
608
 
609
+ // Process speech recognition results into final and interim transcripts
610
  let final_transcript = "";
611
  let interim_transcript = "";
612
  for (let i = event.resultIndex; i < event.results.length; ++i) {
 
616
  interim_transcript += event.results[i][0].transcript;
617
  }
618
  }
619
+
620
+ // Display real-time speech transcription if enabled
621
+ const transcriptText = final_transcript || interim_transcript;
622
+ if (transcriptText) {
623
+ await this.updateTranscriptVisibility(true, transcriptText);
 
 
 
624
  }
625
  if (final_transcript && this.onSpeechAnalysis) {
626
  try {
 
749
  console.log("🎤 Permission denied - stopping listening");
750
  this.micPermissionGranted = false;
751
  this.stopListening();
752
+ const message =
753
+ window.kimiI18nManager?.t("mic_permission_denied") || "Microphone permission denied. Click again to retry.";
754
+ // Use promise-based approach for async operation
755
+ this.updateTranscriptVisibility(true, message).then(() => {
 
756
  setTimeout(() => {
757
+ this.updateTranscriptVisibility(false);
758
  }, 2000);
759
+ });
760
  } else {
761
  this.stopListening();
762
  }
 
788
  this.isListening = false;
789
  if (this.micButton) this.micButton.classList.remove("is-listening");
790
  if (this.micButton) this.micButton.classList.remove("mic-pulse-active");
791
+ this.updateTranscriptVisibility(false);
 
 
792
  };
793
  }
794
 
 
810
  else if (this.browser === "opera") key = "sr_not_supported_opera";
811
  else if (this.browser === "safari") key = "sr_not_supported_safari";
812
  const message = window.kimiI18nManager?.t(key) || "Speech recognition is not available in this browser.";
813
+ this.updateTranscriptVisibility(true, message).then(() => {
 
 
814
  setTimeout(() => {
815
+ this.updateTranscriptVisibility(false);
816
  }, 4000);
817
+ });
818
  return;
819
  }
820
 
 
840
  else if (this.browser === "opera") key = "sr_not_supported_opera";
841
  else if (this.browser === "safari") key = "sr_not_supported_safari";
842
  const message = window.kimiI18nManager?.t(key) || "Speech recognition is not available in this browser.";
843
+ this.updateTranscriptVisibility(true, message).then(() => {
 
 
844
  setTimeout(() => {
845
+ this.updateTranscriptVisibility(false);
846
  }, 4000);
847
+ });
848
  return;
849
  }
850
  if (!this.recognition || this.isListening) return;
 
852
  // Check microphone API availability
853
  if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
854
  console.warn("MediaDevices API not available");
855
+ const message = window.kimiI18nManager?.t("mic_not_supported") || "Microphone not supported in this browser.";
856
+ this.updateTranscriptVisibility(true, message).then(() => {
 
 
857
  setTimeout(() => {
858
+ this.updateTranscriptVisibility(false);
859
  }, 3000);
860
+ });
861
  return;
862
  }
863
 
 
879
  return;
880
  } else if (permissionStatus.state === "denied") {
881
  console.log("🎤 Microphone permission denied");
882
+ const message =
883
+ window.kimiI18nManager?.t("mic_permission_denied") ||
884
+ "Microphone permission denied. Please allow access in browser settings.";
885
+ this.updateTranscriptVisibility(true, message).then(() => {
 
886
  setTimeout(() => {
887
+ this.updateTranscriptVisibility(false);
888
  }, 4000);
889
+ });
890
  return;
891
  }
892
  } catch (error) {
 
947
  this.stopListening();
948
 
949
  // Show user-friendly error message
950
+ const message =
951
+ window.kimiI18nManager?.t("mic_permission_denied") || "Microphone permission denied. Click again to retry.";
952
+ this.updateTranscriptVisibility(true, message).then(() => {
 
953
  setTimeout(() => {
954
+ this.updateTranscriptVisibility(false);
955
  }, 3000);
956
+ });
957
  }
958
  }
959
 
 
991
  }
992
  }
993
 
994
+ this.clearTranscriptTimeout();
 
 
 
995
 
996
  if (!this.speechSynthesis.speaking) {
997
+ // Hide transcript after delay if AI is not speaking
998
+ this.transcriptHideTimeout = setTimeout(async () => {
999
+ await this.updateTranscriptVisibility(false);
 
1000
  this.transcriptHideTimeout = null;
1001
  }, 2000);
1002
  }
 
1061
  window.kimiI18nManager?.t("test_voice_message_2") || "I am Kimi, your virtual companion!",
1062
  window.kimiI18nManager?.t("test_voice_message_3") || "How are you today, my love?"
1063
  ];
 
 
1064
  }
1065
 
1066
  destroy() {
 
1070
  this.listeningTimeout = null;
1071
  }
1072
 
1073
+ this.clearTranscriptTimeout();
 
 
 
1074
 
1075
  if (this.recognition) {
1076
  this.recognition.stop();
 
1156
  console.log("🎤 Interrupting speech to start listening");
1157
  this.speechSynthesis.cancel();
1158
  this.isSpeaking = false;
1159
+ this.updateTranscriptVisibility(false);
 
 
1160
  }
1161
 
1162
  if (this.isListening) {
kimi-locale/de.json CHANGED
@@ -46,6 +46,8 @@
46
  "advanced_settings": "Erweiterte Einstellungen",
47
  "temperature": "Temperatur (Kreativität)",
48
  "max_tokens": "Maximale Token",
 
 
49
  "available_models": "Verfügbare Openrouter Modelle",
50
  "refresh_list": "Liste Aktualisieren",
51
  "refresh": "Aktualisieren",
@@ -55,6 +57,9 @@
55
  "animations": "Animationen",
56
  "transcript_settings": "Transkript-Einstellungen",
57
  "show_transcript": "Transkript Anzeigen",
 
 
 
58
  "data_management": "Datenverwaltung",
59
  "export_all_data": "Alle Daten Exportieren",
60
  "export": "Exportieren",
@@ -113,6 +118,7 @@
113
  "response_cold_3": "Was willst du?",
114
  "response_cold_4": "Ich bin hier.",
115
  "response_cold_5": "Wie kann ich dir helfen?",
 
116
  "system_prompt_kimi": "Kimi System-Prompt",
117
  "system_prompt_bella": "Bella System-Prompt",
118
  "system_prompt_rosa": "Rosa System-Prompt",
@@ -223,5 +229,6 @@
223
  "memory_category_important": "Wichtige Ereignisse",
224
  "memory_content_placeholder": "z.B.: Ich liebe klassische Musik...",
225
  "memory_management": "Gedächtnisverwaltung",
226
- "add": "Hinzufügen"
 
227
  }
 
46
  "advanced_settings": "Erweiterte Einstellungen",
47
  "temperature": "Temperatur (Kreativität)",
48
  "max_tokens": "Maximale Token",
49
+ "enable_streaming": "Text-Streaming aktivieren",
50
+ "enable_streaming_help": "Zeigt Text an, während er generiert wird, für Echtzeit-Antworten (Standard: aktiviert). Zeigt Text progressiv an, anstatt auf die vollständige Antwort zu warten.",
51
  "available_models": "Verfügbare Openrouter Modelle",
52
  "refresh_list": "Liste Aktualisieren",
53
  "refresh": "Aktualisieren",
 
57
  "animations": "Animationen",
58
  "transcript_settings": "Transkript-Einstellungen",
59
  "show_transcript": "Transkript Anzeigen",
60
+ "show_transcript_note": "Zeigt die Echtzeit-Transkription an, wenn Sie sprechen, um eine Nachricht zu senden, und wenn die KI antwortet.",
61
+ "plugin_status_note": "Derzeit ist nur -Sample Blue Theme- vollständig funktionsfähig. Andere Plugins sind in Entwicklung und ihre Aktivierung hat derzeit keine Wirkung.",
62
+ "memory_system_help": "Das Intelligente Gedächtnis ermöglicht es Ihrem Charakter, sich an Gespräche, Vorlieben und wichtige Details über Sitzungen hinweg zu erinnern. Dies schafft personalisiertere und kohärentere Interaktionen im Laufe der Zeit.",
63
  "data_management": "Datenverwaltung",
64
  "export_all_data": "Alle Daten Exportieren",
65
  "export": "Exportieren",
 
118
  "response_cold_3": "Was willst du?",
119
  "response_cold_4": "Ich bin hier.",
120
  "response_cold_5": "Wie kann ich dir helfen?",
121
+ "system_prompt": "System-Prompt",
122
  "system_prompt_kimi": "Kimi System-Prompt",
123
  "system_prompt_bella": "Bella System-Prompt",
124
  "system_prompt_rosa": "Rosa System-Prompt",
 
229
  "memory_category_important": "Wichtige Ereignisse",
230
  "memory_content_placeholder": "z.B.: Ich liebe klassische Musik...",
231
  "memory_management": "Gedächtnisverwaltung",
232
+ "add": "Hinzufügen",
233
+ "api_key_label": "API Key"
234
  }
kimi-locale/en.json CHANGED
@@ -46,6 +46,8 @@
46
  "advanced_settings": "Advanced Settings",
47
  "temperature": "Temperature (Creativity)",
48
  "max_tokens": "Max Tokens",
 
 
49
  "available_models": "Available Openrouter Models",
50
  "refresh_list": "Refresh List",
51
  "refresh": "Refresh",
@@ -55,6 +57,9 @@
55
  "animations": "Animations",
56
  "transcript_settings": "Transcript Settings",
57
  "show_transcript": "Show Transcript",
 
 
 
58
  "data_management": "Data Management",
59
  "export_all_data": "Export All Data",
60
  "export": "Export",
@@ -113,6 +118,7 @@
113
  "response_cold_3": "What do you want?",
114
  "response_cold_4": "I am here.",
115
  "response_cold_5": "How can I help you?",
 
116
  "system_prompt_kimi": "Kimi System Prompt",
117
  "system_prompt_bella": "Bella System Prompt",
118
  "system_prompt_rosa": "Rosa System Prompt",
@@ -224,5 +230,6 @@
224
  "add": "Add",
225
  "help_providers": "You can use multiple AI providers: OpenRouter, OpenAI, Groq, Together, DeepSeek, Custom OpenAI-compatible, or Local (Ollama). Enter the Base URL and Model ID when required, save your API key per provider, then use ‘Test API Key’.",
226
  "api_key_presence_hint": "Green = API key saved for current provider. Grey = no key saved.",
227
- "api_key_test_hint": "Green = API connectivity verified. Grey = not tested or failed."
 
228
  }
 
46
  "advanced_settings": "Advanced Settings",
47
  "temperature": "Temperature (Creativity)",
48
  "max_tokens": "Max Tokens",
49
+ "enable_streaming": "Enable Text Streaming",
50
+ "enable_streaming_help": "Stream text as it's generated for real-time responses (default: enabled). Shows text progressively instead of waiting for complete response.",
51
  "available_models": "Available Openrouter Models",
52
  "refresh_list": "Refresh List",
53
  "refresh": "Refresh",
 
57
  "animations": "Animations",
58
  "transcript_settings": "Transcript Settings",
59
  "show_transcript": "Show Transcript",
60
+ "show_transcript_note": "Display real-time transcription when you speak to send a message and when the AI responds.",
61
+ "plugin_status_note": "Currently, only -Sample Blue Theme- is fully functional. Other plugins are in development and activating them will have no effect at this time.",
62
+ "memory_system_help": "Intelligent Memory allows your character to remember conversations, preferences, and important details across sessions. This creates more personalized and coherent interactions over time.",
63
  "data_management": "Data Management",
64
  "export_all_data": "Export All Data",
65
  "export": "Export",
 
118
  "response_cold_3": "What do you want?",
119
  "response_cold_4": "I am here.",
120
  "response_cold_5": "How can I help you?",
121
+ "system_prompt": "System Prompt",
122
  "system_prompt_kimi": "Kimi System Prompt",
123
  "system_prompt_bella": "Bella System Prompt",
124
  "system_prompt_rosa": "Rosa System Prompt",
 
230
  "add": "Add",
231
  "help_providers": "You can use multiple AI providers: OpenRouter, OpenAI, Groq, Together, DeepSeek, Custom OpenAI-compatible, or Local (Ollama). Enter the Base URL and Model ID when required, save your API key per provider, then use ‘Test API Key’.",
232
  "api_key_presence_hint": "Green = API key saved for current provider. Grey = no key saved.",
233
+ "api_key_test_hint": "Green = API connectivity verified. Grey = not tested or failed.",
234
+ "api_key_label": "API Key"
235
  }
kimi-locale/es.json CHANGED
@@ -1,5 +1,5 @@
1
  {
2
- "title": "Kimi - Compañera Virtual ??",
3
  "chat_with_kimi": "Chatear con Kimi",
4
  "chat_with_bella": "Chatear con Bella",
5
  "chat_with_rosa": "Chatear con Rosa",
@@ -46,6 +46,8 @@
46
  "advanced_settings": "Configuración Avanzada",
47
  "temperature": "Temperatura (Creatividad)",
48
  "max_tokens": "Máximo de Tokens",
 
 
49
  "available_models": "Modelos Openrouter Disponibles",
50
  "refresh_list": "Actualizar Lista",
51
  "refresh": "Actualizar",
@@ -55,6 +57,9 @@
55
  "animations": "Animaciones",
56
  "transcript_settings": "Configuración de Transcripción",
57
  "show_transcript": "Mostrar Transcripción",
 
 
 
58
  "data_management": "Gestión de Datos",
59
  "export_all_data": "Exportar Todos los Datos",
60
  "export": "Exportar",
@@ -113,6 +118,7 @@
113
  "response_cold_3": "¿Qué quieres?",
114
  "response_cold_4": "Estoy aquí.",
115
  "response_cold_5": "¿Cómo puedo ayudarte?",
 
116
  "system_prompt_kimi": "Prompt del Sistema de Kimi",
117
  "system_prompt_bella": "Prompt del Sistema de Bella",
118
  "system_prompt_rosa": "Prompt del Sistema de Rosa",
@@ -223,5 +229,6 @@
223
  "memory_category_important": "Eventos importantes",
224
  "memory_content_placeholder": "ej.: Me encanta la música clásica...",
225
  "memory_management": "Gestión de Memoria",
226
- "add": "Agregar"
 
227
  }
 
1
  {
2
+ "title": "Kimi - Compañera Virtual 💕",
3
  "chat_with_kimi": "Chatear con Kimi",
4
  "chat_with_bella": "Chatear con Bella",
5
  "chat_with_rosa": "Chatear con Rosa",
 
46
  "advanced_settings": "Configuración Avanzada",
47
  "temperature": "Temperatura (Creatividad)",
48
  "max_tokens": "Máximo de Tokens",
49
+ "enable_streaming": "Activar Streaming de Texto",
50
+ "enable_streaming_help": "Muestra el texto a medida que se genera para respuestas en tiempo real (por defecto: activado). Muestra el texto progresivamente en lugar de esperar la respuesta completa.",
51
  "available_models": "Modelos Openrouter Disponibles",
52
  "refresh_list": "Actualizar Lista",
53
  "refresh": "Actualizar",
 
57
  "animations": "Animaciones",
58
  "transcript_settings": "Configuración de Transcripción",
59
  "show_transcript": "Mostrar Transcripción",
60
+ "show_transcript_note": "Muestra la transcripción en tiempo real cuando hablas para enviar un mensaje y cuando la IA responde.",
61
+ "plugin_status_note": "Actualmente, solo -Sample Blue Theme- es completamente funcional. Otros plugins están en desarrollo y activarlos no tendrá efecto en este momento.",
62
+ "memory_system_help": "La Memoria Inteligente permite a tu personaje recordar conversaciones, preferencias y detalles importantes entre sesiones. Esto crea interacciones más personalizadas y coherentes con el tiempo.",
63
  "data_management": "Gestión de Datos",
64
  "export_all_data": "Exportar Todos los Datos",
65
  "export": "Exportar",
 
118
  "response_cold_3": "¿Qué quieres?",
119
  "response_cold_4": "Estoy aquí.",
120
  "response_cold_5": "¿Cómo puedo ayudarte?",
121
+ "system_prompt": "Prompt del Sistema",
122
  "system_prompt_kimi": "Prompt del Sistema de Kimi",
123
  "system_prompt_bella": "Prompt del Sistema de Bella",
124
  "system_prompt_rosa": "Prompt del Sistema de Rosa",
 
229
  "memory_category_important": "Eventos importantes",
230
  "memory_content_placeholder": "ej.: Me encanta la música clásica...",
231
  "memory_management": "Gestión de Memoria",
232
+ "add": "Agregar",
233
+ "api_key_label": "API Key"
234
  }
kimi-locale/fr.json CHANGED
@@ -46,6 +46,8 @@
46
  "advanced_settings": "Paramètres avancés",
47
  "temperature": "Température (Créativité)",
48
  "max_tokens": "Nombre maximal de tokens",
 
 
49
  "available_models": "Modèles Openrouter disponibles",
50
  "refresh_list": "Rafraîchir la liste",
51
  "refresh": "Rafraîchir",
@@ -55,6 +57,9 @@
55
  "animations": "Animations",
56
  "transcript_settings": "Paramètres de transcription",
57
  "show_transcript": "Afficher la transcription",
 
 
 
58
  "data_management": "Gestion des données",
59
  "export_all_data": "Exporter toutes les données",
60
  "export": "Exporter",
@@ -113,6 +118,7 @@
113
  "response_cold_3": "Que veux-tu ?",
114
  "response_cold_4": "Je suis là.",
115
  "response_cold_5": "Comment puis-je t'aider ?",
 
116
  "system_prompt_kimi": "Prompt système de Kimi",
117
  "system_prompt_bella": "Prompt système de Bella",
118
  "system_prompt_rosa": "Prompt système de Rosa",
@@ -224,5 +230,6 @@
224
  "add": "Ajouter",
225
  "help_providers": "Vous pouvez utiliser plusieurs fournisseurs d’IA : OpenRouter, OpenAI, Groq, Together, DeepSeek, un service OpenAI‑compatible personnalisé, ou Local (Ollama). Renseignez la Base URL et le Model ID si nécessaire, enregistrez votre clé API par fournisseur, puis utilisez ‘Test API Key’.",
226
  "api_key_presence_hint": "Vert = clé API enregistrée pour le fournisseur courant. Gris = aucune clé enregistrée.",
227
- "api_key_test_hint": "Vert = connectivité API vérifiée. Gris = non testé ou échec."
 
228
  }
 
46
  "advanced_settings": "Paramètres avancés",
47
  "temperature": "Température (Créativité)",
48
  "max_tokens": "Nombre maximal de tokens",
49
+ "enable_streaming": "Activer le streaming de texte",
50
+ "enable_streaming_help": "Affiche le texte au fur et à mesure qu'il est généré pour des réponses en temps réel (par défaut : activé). Affiche le texte progressivement au lieu d'attendre la réponse complète.",
51
  "available_models": "Modèles Openrouter disponibles",
52
  "refresh_list": "Rafraîchir la liste",
53
  "refresh": "Rafraîchir",
 
57
  "animations": "Animations",
58
  "transcript_settings": "Paramètres de transcription",
59
  "show_transcript": "Afficher la transcription",
60
+ "show_transcript_note": "Affiche la transcription en temps réel lorsque vous parlez pour envoyer un message et lorsque l'IA répond.",
61
+ "plugin_status_note": "Actuellement, seul -Sample Blue Theme- est entièrement fonctionnel. Les autres plugins sont en développement et les activer n'aura aucun effet pour le moment.",
62
+ "memory_system_help": "La Mémoire Intelligente permet à votre personnage de se souvenir des conversations, préférences et détails importants entre les sessions. Cela crée des interactions plus personnalisées et cohérentes au fil du temps.",
63
  "data_management": "Gestion des données",
64
  "export_all_data": "Exporter toutes les données",
65
  "export": "Exporter",
 
118
  "response_cold_3": "Que veux-tu ?",
119
  "response_cold_4": "Je suis là.",
120
  "response_cold_5": "Comment puis-je t'aider ?",
121
+ "system_prompt": "Prompt système",
122
  "system_prompt_kimi": "Prompt système de Kimi",
123
  "system_prompt_bella": "Prompt système de Bella",
124
  "system_prompt_rosa": "Prompt système de Rosa",
 
230
  "add": "Ajouter",
231
  "help_providers": "Vous pouvez utiliser plusieurs fournisseurs d’IA : OpenRouter, OpenAI, Groq, Together, DeepSeek, un service OpenAI‑compatible personnalisé, ou Local (Ollama). Renseignez la Base URL et le Model ID si nécessaire, enregistrez votre clé API par fournisseur, puis utilisez ‘Test API Key’.",
232
  "api_key_presence_hint": "Vert = clé API enregistrée pour le fournisseur courant. Gris = aucune clé enregistrée.",
233
+ "api_key_test_hint": "Vert = connectivité API vérifiée. Gris = non testé ou échec.",
234
+ "api_key_label": "API Key"
235
  }
kimi-locale/it.json CHANGED
@@ -46,6 +46,8 @@
46
  "advanced_settings": "Impostazioni Avanzate",
47
  "temperature": "Temperatura (Creatività)",
48
  "max_tokens": "Token Massimi",
 
 
49
  "available_models": "Modelli Openrouter Disponibili",
50
  "refresh_list": "Aggiorna Lista",
51
  "refresh": "Aggiorna",
@@ -55,6 +57,9 @@
55
  "animations": "Animazioni",
56
  "transcript_settings": "Impostazioni Trascrizione",
57
  "show_transcript": "Mostra Trascrizione",
 
 
 
58
  "data_management": "Gestione Dati",
59
  "export_all_data": "Esporta Tutti i Dati",
60
  "export": "Esporta",
@@ -113,6 +118,7 @@
113
  "response_cold_3": "Cosa vuoi?",
114
  "response_cold_4": "Sono qui.",
115
  "response_cold_5": "Come posso aiutarti?",
 
116
  "system_prompt_kimi": "Prompt di Sistema di Kimi",
117
  "system_prompt_bella": "Prompt di Sistema di Bella",
118
  "system_prompt_rosa": "Prompt di Sistema di Rosa",
@@ -223,5 +229,6 @@
223
  "memory_category_important": "Eventi importanti",
224
  "memory_content_placeholder": "es.: Amo la musica classica...",
225
  "memory_management": "Gestione della Memoria",
226
- "add": "Aggiungi"
 
227
  }
 
46
  "advanced_settings": "Impostazioni Avanzate",
47
  "temperature": "Temperatura (Creatività)",
48
  "max_tokens": "Token Massimi",
49
+ "enable_streaming": "Abilita Streaming del Testo",
50
+ "enable_streaming_help": "Mostra il testo mentre viene generato per risposte in tempo reale (predefinito: abilitato). Mostra il testo progressivamente invece di aspettare la risposta completa.",
51
  "available_models": "Modelli Openrouter Disponibili",
52
  "refresh_list": "Aggiorna Lista",
53
  "refresh": "Aggiorna",
 
57
  "animations": "Animazioni",
58
  "transcript_settings": "Impostazioni Trascrizione",
59
  "show_transcript": "Mostra Trascrizione",
60
+ "show_transcript_note": "Mostra la trascrizione in tempo reale quando parli per inviare un messaggio e quando l'IA risponde.",
61
+ "plugin_status_note": "Attualmente, solo -Sample Blue Theme- è completamente funzionale. Altri plugin sono in sviluppo e attivarli non avrà effetto al momento.",
62
+ "memory_system_help": "La Memoria Intelligente permette al tuo personaggio di ricordare conversazioni, preferenze e dettagli importanti tra le sessioni. Questo crea interazioni più personalizzate e coerenti nel tempo.",
63
  "data_management": "Gestione Dati",
64
  "export_all_data": "Esporta Tutti i Dati",
65
  "export": "Esporta",
 
118
  "response_cold_3": "Cosa vuoi?",
119
  "response_cold_4": "Sono qui.",
120
  "response_cold_5": "Come posso aiutarti?",
121
+ "system_prompt": "Prompt di Sistema",
122
  "system_prompt_kimi": "Prompt di Sistema di Kimi",
123
  "system_prompt_bella": "Prompt di Sistema di Bella",
124
  "system_prompt_rosa": "Prompt di Sistema di Rosa",
 
229
  "memory_category_important": "Eventi importanti",
230
  "memory_content_placeholder": "es.: Amo la musica classica...",
231
  "memory_management": "Gestione della Memoria",
232
+ "add": "Aggiungi",
233
+ "api_key_label": "API Key"
234
  }
kimi-locale/ja.json CHANGED
@@ -46,6 +46,8 @@
46
  "advanced_settings": "詳細設定",
47
  "temperature": "Temperature(創造性)",
48
  "max_tokens": "最大トークン数",
 
 
49
  "available_models": "利用可能なモデル OpenRouter",
50
  "refresh_list": "リストを更新",
51
  "refresh": "更新",
@@ -55,6 +57,9 @@
55
  "animations": "アニメーション",
56
  "transcript_settings": "転写設定",
57
  "show_transcript": "転写を表示",
 
 
 
58
  "data_management": "データ管理",
59
  "export_all_data": "すべてのデータをエクスポート",
60
  "export": "エクスポート",
@@ -113,6 +118,7 @@
113
  "response_cold_3": "何が欲しいですか?",
114
  "response_cold_4": "私はここにいます。",
115
  "response_cold_5": "どのようにお手伝いできますか?",
 
116
  "system_prompt_kimi": "Kimiシステムプロンプト",
117
  "system_prompt_bella": "Bellaシステムプロンプト",
118
  "system_prompt_rosa": "Rosaシステムプロンプト",
@@ -223,5 +229,6 @@
223
  "memory_category_important": "重要な出来事",
224
  "memory_content_placeholder": "例: クラシック音楽が大好き...",
225
  "memory_management": "メモリ管理",
226
- "add": "追加"
 
227
  }
 
46
  "advanced_settings": "詳細設定",
47
  "temperature": "Temperature(創造性)",
48
  "max_tokens": "最大トークン数",
49
+ "enable_streaming": "テキストストリーミングを有効化",
50
+ "enable_streaming_help": "リアルタイム応答のため、生成されるテキストを表示します(デフォルト:有効)。完全な応答を待つ代わりに、テキストを段階的に表示します。",
51
  "available_models": "利用可能なモデル OpenRouter",
52
  "refresh_list": "リストを更新",
53
  "refresh": "更新",
 
57
  "animations": "アニメーション",
58
  "transcript_settings": "転写設定",
59
  "show_transcript": "転写を表示",
60
+ "show_transcript_note": "メッセージを送信するために話すときと、AIが応答するときのリアルタイム転写を表示します。",
61
+ "plugin_status_note": "現在、-Sample Blue Theme- のみが完全に機能します。他のプラグインは開発中で、それらを有効にしても現時点では効果がありません。",
62
+ "memory_system_help": "インテリジェントメモリにより、キャラクターはセッション間で会話、好み、重要な詳細を記憶できます。これにより、時間の経過とともに、よりパーソナライズされ一貫したインタラクションが生まれます。",
63
  "data_management": "データ管理",
64
  "export_all_data": "すべてのデータをエクスポート",
65
  "export": "エクスポート",
 
118
  "response_cold_3": "何が欲しいですか?",
119
  "response_cold_4": "私はここにいます。",
120
  "response_cold_5": "どのようにお手伝いできますか?",
121
+ "system_prompt": "システムプロンプト",
122
  "system_prompt_kimi": "Kimiシステムプロンプト",
123
  "system_prompt_bella": "Bellaシステムプロンプト",
124
  "system_prompt_rosa": "Rosaシステムプロンプト",
 
229
  "memory_category_important": "重要な出来事",
230
  "memory_content_placeholder": "例: クラシック音楽が大好き...",
231
  "memory_management": "メモリ管理",
232
+ "add": "追加",
233
+ "api_key_label": "API Key"
234
  }
kimi-locale/zh.json CHANGED
@@ -46,6 +46,8 @@
46
  "advanced_settings": "高级设置",
47
  "temperature": "温度(创造性)",
48
  "max_tokens": "最大令牌数",
 
 
49
  "available_models": "可用模型 OpenRouter",
50
  "refresh_list": "刷新列表",
51
  "refresh": "刷新",
@@ -55,6 +57,9 @@
55
  "animations": "动画",
56
  "transcript_settings": "转录设置",
57
  "show_transcript": "显示转录",
 
 
 
58
  "data_management": "数据管理",
59
  "export_all_data": "导出所有数据",
60
  "export": "导出",
@@ -113,6 +118,7 @@
113
  "response_cold_3": "你想要什么?",
114
  "response_cold_4": "我在这里。",
115
  "response_cold_5": "我如何能帮助你?",
 
116
  "system_prompt_kimi": "Kimi系统提示",
117
  "system_prompt_bella": "Bella系统提示",
118
  "system_prompt_rosa": "Rosa系统提示",
@@ -223,5 +229,6 @@
223
  "memory_category_important": "重要事件",
224
  "memory_content_placeholder": "例如:我喜欢古典音乐...",
225
  "memory_management": "记忆管理",
226
- "add": "添加"
 
227
  }
 
46
  "advanced_settings": "高级设置",
47
  "temperature": "温度(创造性)",
48
  "max_tokens": "最大令牌数",
49
+ "enable_streaming": "启用文本流式传输",
50
+ "enable_streaming_help": "实时显示生成的文本以获得实时响应(默认:启用)。逐步显示文本而不是等待完整响应。",
51
  "available_models": "可用模型 OpenRouter",
52
  "refresh_list": "刷新列表",
53
  "refresh": "刷新",
 
57
  "animations": "动画",
58
  "transcript_settings": "转录设置",
59
  "show_transcript": "显示转录",
60
+ "show_transcript_note": "显示当您说话发送消息时和AI回应时的实时转录。",
61
+ "plugin_status_note": "目前只有-Sample Blue Theme-完全可用。其他插件正在开发中,激活它们目前不会有任何效果。",
62
+ "memory_system_help": "智能记忆允许您的角色记住对话、偏好和重要细节跨会话。这创造了随时间推移更个性化和连贯的互动。",
63
  "data_management": "数据管理",
64
  "export_all_data": "导出所有数据",
65
  "export": "导出",
 
118
  "response_cold_3": "你想要什么?",
119
  "response_cold_4": "我在这里。",
120
  "response_cold_5": "我如何能帮助你?",
121
+ "system_prompt": "系统提示",
122
  "system_prompt_kimi": "Kimi系统提示",
123
  "system_prompt_bella": "Bella系统提示",
124
  "system_prompt_rosa": "Rosa系统提示",
 
229
  "memory_category_important": "重要事件",
230
  "memory_content_placeholder": "例如:我喜欢古典音乐...",
231
  "memory_management": "记忆管理",
232
+ "add": "添加",
233
+ "api_key_label": "API Key"
234
  }