VirtualKimi commited on
Commit
680de21
·
verified ·
1 Parent(s): 24277d7

Upload 30 files

Browse files
index.html CHANGED
@@ -57,7 +57,7 @@
57
  },
58
  "dateCreated": "2025-07-16",
59
  "dateModified": "2025-08-30",
60
- "version": "v1.1.3"
61
  }
62
  </script>
63
 
@@ -299,7 +299,7 @@
299
  <div class="config-control">
300
  <div class="slider-container">
301
  <input type="range" class="kimi-slider" id="trait-affection" min="0" max="100"
302
- value="65" />
303
  <span class="slider-value" id="trait-affection-value">65</span>
304
  </div>
305
  </div>
@@ -1072,7 +1072,7 @@
1072
  <h3><i class="fas fa-code"></i> Technical Information</h3>
1073
  <div class="tech-info">
1074
  <p><strong>Created date :</strong> July 16, 2025</p>
1075
- <p><strong>Version :</strong> v1.1.3</p>
1076
  <p><strong>Last update :</strong> August 30, 2025</p>
1077
  <p><strong>Technologies :</strong> HTML5, CSS3, JavaScript ES6+, IndexedDB, Web Speech
1078
  API</p>
@@ -1086,8 +1086,9 @@
1086
  </div>
1087
 
1088
  <script src="dexie.min.js"></script>
1089
- <script type="module" src="kimi-js/kimi-utils.js"></script>
1090
  <script src="kimi-locale/i18n.js" defer></script>
 
 
1091
  <script type="module" src="kimi-js/kimi-main.js"></script>
1092
  <script type="module" src="kimi-js/kimi-config.js"></script>
1093
  <script type="module" src="kimi-js/kimi-error-manager.js"></script>
@@ -1127,7 +1128,7 @@
1127
  "name": "Jean & Kimi"
1128
  },
1129
  "dateCreated": "2025-07-16",
1130
- "version": "v1.1.3"
1131
  }
1132
  }
1133
  </script>
 
57
  },
58
  "dateCreated": "2025-07-16",
59
  "dateModified": "2025-08-30",
60
+ "version": "v1.1.2"
61
  }
62
  </script>
63
 
 
299
  <div class="config-control">
300
  <div class="slider-container">
301
  <input type="range" class="kimi-slider" id="trait-affection" min="0" max="100"
302
+ value="65" title="Adjust affection (independent relationship warmth)" />
303
  <span class="slider-value" id="trait-affection-value">65</span>
304
  </div>
305
  </div>
 
1072
  <h3><i class="fas fa-code"></i> Technical Information</h3>
1073
  <div class="tech-info">
1074
  <p><strong>Created date :</strong> July 16, 2025</p>
1075
+ <p><strong>Version :</strong> v1.1.2</p>
1076
  <p><strong>Last update :</strong> August 30, 2025</p>
1077
  <p><strong>Technologies :</strong> HTML5, CSS3, JavaScript ES6+, IndexedDB, Web Speech
1078
  API</p>
 
1086
  </div>
1087
 
1088
  <script src="dexie.min.js"></script>
 
1089
  <script src="kimi-locale/i18n.js" defer></script>
1090
+ <script type="module" src="kimi-js/kimi-personality-utils.js"></script>
1091
+ <script type="module" src="kimi-js/kimi-utils.js"></script>
1092
  <script type="module" src="kimi-js/kimi-main.js"></script>
1093
  <script type="module" src="kimi-js/kimi-config.js"></script>
1094
  <script type="module" src="kimi-js/kimi-error-manager.js"></script>
 
1128
  "name": "Jean & Kimi"
1129
  },
1130
  "dateCreated": "2025-07-16",
1131
+ "version": "v1.1.2"
1132
  }
1133
  }
1134
  </script>
kimi-js/kimi-constants.js CHANGED
@@ -344,36 +344,11 @@ window.KIMI_PERSONALITY_KEYWORDS = {
344
  negative: ["stupid", "dumb", "foolish", "slow", "naive", "ignorant", "simple"]
345
  },
346
  romance: {
347
- positive: [
348
- "cuddle",
349
- "love",
350
- "romantic",
351
- "kiss",
352
- "tenderness",
353
- "passion",
354
- "charming",
355
- "adorable",
356
- "sweet",
357
- "i love you",
358
- "love you"
359
- ],
360
  negative: ["cold", "distant", "indifferent", "rejection", "loneliness", "breakup", "sad"]
361
  },
362
  affection: {
363
- positive: [
364
- "affection",
365
- "tenderness",
366
- "close",
367
- "warmth",
368
- "kind",
369
- "caring",
370
- "cuddle",
371
- "love",
372
- "adore",
373
- "lovely",
374
- "i love you",
375
- "love you"
376
- ],
377
  negative: [
378
  "mean",
379
  "cold",
@@ -435,21 +410,7 @@ window.KIMI_PERSONALITY_KEYWORDS = {
435
  ]
436
  },
437
  romance: {
438
- positive: [
439
- "câlin",
440
- "amour",
441
- "romantique",
442
- "bisou",
443
- "tendresse",
444
- "passion",
445
- "séduisant",
446
- "charmant",
447
- "adorable",
448
- "je t'aime",
449
- "je taime",
450
- "t'aime",
451
- "taime"
452
- ],
453
  negative: [
454
  "froid",
455
  "froide",
@@ -473,11 +434,6 @@ window.KIMI_PERSONALITY_KEYWORDS = {
473
  "attentionné",
474
  "câlin",
475
  "aimer",
476
- "aime",
477
- "je t'aime",
478
- "je taime",
479
- "t'aime",
480
- "taime",
481
  "adorer",
482
  "adorable"
483
  ],
@@ -565,35 +521,11 @@ window.KIMI_PERSONALITY_KEYWORDS = {
565
  ]
566
  },
567
  romance: {
568
- positive: [
569
- "abrazo",
570
- "amor",
571
- "romántico",
572
- "beso",
573
- "ternura",
574
- "pasión",
575
- "encantador",
576
- "adorable",
577
- "dulce",
578
- "te quiero",
579
- "te amo"
580
- ],
581
  negative: ["frío", "fría", "distante", "indiferente", "rechazo", "soledad", "ruptura", "triste"]
582
  },
583
  affection: {
584
- positive: [
585
- "afecto",
586
- "ternura",
587
- "cerca",
588
- "calidez",
589
- "amable",
590
- "cariño",
591
- "abrazar",
592
- "amor",
593
- "adorar",
594
- "te quiero",
595
- "te amo"
596
- ],
597
  negative: [
598
  "malo",
599
  "mala",
@@ -663,8 +595,7 @@ window.KIMI_PERSONALITY_KEYWORDS = {
663
  "leidenschaft",
664
  "charmant",
665
  "liebenswert",
666
- "süß",
667
- "ich liebe dich"
668
  ],
669
  negative: [
670
  "kalt",
@@ -681,18 +612,7 @@ window.KIMI_PERSONALITY_KEYWORDS = {
681
  ]
682
  },
683
  affection: {
684
- positive: [
685
- "zuneigung",
686
- "zärtlichkeit",
687
- "nah",
688
- "wärme",
689
- "freundlich",
690
- "fürsorglich",
691
- "umarmen",
692
- "liebe",
693
- "anbeten",
694
- "ich liebe dich"
695
- ],
696
  negative: [
697
  "gemein",
698
  "gemeine",
@@ -754,33 +674,11 @@ window.KIMI_PERSONALITY_KEYWORDS = {
754
  negative: ["stupido", "stupida", "sciocco", "sciocca", "lento", "lenta", "ingenuo", "ingenua", "ignorante"]
755
  },
756
  romance: {
757
- positive: [
758
- "abbraccio",
759
- "amore",
760
- "romantico",
761
- "bacio",
762
- "tenerezza",
763
- "passione",
764
- "affascinante",
765
- "adorabile",
766
- "dolce",
767
- "ti amo"
768
- ],
769
  negative: ["freddo", "fredda", "distante", "indifferente", "rifiuto", "solitudine", "rottura", "triste"]
770
  },
771
  affection: {
772
- positive: [
773
- "affetto",
774
- "tenerezza",
775
- "vicino",
776
- "calore",
777
- "gentile",
778
- "premuroso",
779
- "abbraccio",
780
- "amore",
781
- "adorare",
782
- "ti amo"
783
- ],
784
  negative: [
785
  "cattivo",
786
  "cattiva",
@@ -824,7 +722,7 @@ window.KIMI_PERSONALITY_KEYWORDS = {
824
  laughing: ["はは", "笑", "笑う", "面白い", "愉快"],
825
  shy: ["恥ずかしい", "照れる", "赤面", "内気", "遠慮"],
826
  confident: ["自信", "誇り", "確信", "強い", "決意"],
827
- romantic: ["愛", "ロマンチック", "優しい", "抱擁", "キス", "愛しい", "愛してる", "好き"],
828
  flirtatious: ["いちゃつく", "からかう", "誘惑", "魅力", "フリート"],
829
  goodbye: ["さようなら", "バイバイ", "また今度", "チャオ", "またね"],
830
  kiss: ["キス", "抱擁", "チュー"],
@@ -836,7 +734,7 @@ window.KIMI_PERSONALITY_KEYWORDS = {
836
  laughing: ["哈哈", "笑", "大笑", "有趣", "搞笑"],
837
  shy: ["害羞", "尴尬", "脸红", "羞涩", "胆怯"],
838
  confident: ["自信", "骄傲", "确信", "强壮", "坚定"],
839
- romantic: ["爱", "浪漫", "温柔", "拥抱", "吻", "亲爱的", "我爱你"],
840
  flirtatious: ["调情", "挑逗", "诱惑", "魅力", "撒娇"],
841
  goodbye: ["再见", "拜拜", "回头见", "拜", "下次见"],
842
  kiss: ["吻", "亲吻", "拥抱", "亲"],
@@ -910,9 +808,6 @@ window.KIMI_NEGATORS = window.KIMI_NEGATORS || {
910
  window.KIMI_NEGATION_WINDOW = window.KIMI_NEGATION_WINDOW || 3; // tokens to look back for negation
911
  window.KIMI_SMOOTHING_ALPHA = window.KIMI_SMOOTHING_ALPHA || 0.3;
912
  window.KIMI_PERSIST_THRESHOLD = window.KIMI_PERSIST_THRESHOLD || 0.1; // absolute percent (slightly higher to slow small visible jumps)
913
- // Weight applied to counts found in the LLM/Kimi response when updating personality traits.
914
- // You can override at runtime by setting window.KIMI_LLM_RESPONSE_WEIGHT (e.g. 0.2)
915
- window.KIMI_LLM_RESPONSE_WEIGHT = window.KIMI_LLM_RESPONSE_WEIGHT || 0.2;
916
 
917
  // Memory system knobs
918
  window.KIMI_MAX_MEMORIES = window.KIMI_MAX_MEMORIES || 100; // default max memory entries per character
@@ -1098,16 +993,20 @@ window.KIMI_TRAIT_ADJUSTMENT = {
1098
  dancing: 1.05,
1099
  shy: 0.95,
1100
  confident: 1.1,
1101
- flirtatious: 1.2
 
 
 
 
1102
  },
1103
  // Per-trait scaling
1104
  traitGain: {
1105
- affection: 1.15, // Slightly faster affection growth
1106
- romance: 1.2, // Reduced from 1.3 - romance still important but not overpowered
1107
- empathy: 1.1, // Empathy grows steadily
1108
- playfulness: 1.15, // Playful interactions are rewarding
1109
- humor: 1.12, // Humor grows with shared laughter
1110
- intelligence: 1.08 // Intelligence should grow with meaningful conversations
1111
  },
1112
  traitLoss: {
1113
  affection: 0.9,
@@ -1180,7 +1079,7 @@ window.KIMI_CHARACTERS = {
1180
  name: "Kimi",
1181
  summary: "Dreamy, intuitive, captivated by cosmic metaphors",
1182
  traits: {
1183
- // Kimi starts balanced but reserved - cosmic dreamer needs time to open up
1184
  affection: 55, // Starts neutral, grows with interaction
1185
  playfulness: 55,
1186
  intelligence: 75, // Higher intelligence - she's an astrophysicist
@@ -1199,7 +1098,7 @@ window.KIMI_CHARACTERS = {
1199
  name: "Bella",
1200
  summary: "Cheerful, nurturing, sees people as plants needing care",
1201
  traits: {
1202
- // Bella starts more open and caring - nurturing botanist personality
1203
  affection: 60, // Naturally more affectionate
1204
  playfulness: 65, // Cheerful and playful from start
1205
  intelligence: 65, // Smart but not intimidating
@@ -1218,13 +1117,13 @@ window.KIMI_CHARACTERS = {
1218
  name: "Rosa",
1219
  summary: "Chaotic, attention-seeking, thrives on controlled chaos",
1220
  traits: {
1221
- // Rosa starts more distant but playful - chaotic prankster needs to be tamed
1222
  affection: 45, // Lower starting affection - must earn her trust
1223
  playfulness: 80, // Very playful from start - it's her nature
1224
  intelligence: 85, // High intelligence - cunning prankster
1225
  empathy: 55, // Lower empathy initially - focused on chaos
1226
  humor: 75, // High humor - prankster personality
1227
- romance: 50 // Reduced from 60 - chaotic personalities are harder to romance initially
1228
  },
1229
  age: 21,
1230
  birthplace: "Barcelona, Spain",
@@ -1237,7 +1136,7 @@ window.KIMI_CHARACTERS = {
1237
  name: "Stella",
1238
  summary: "Whimsical, artistic, imaginative, playful, transforms chaos into art",
1239
  traits: {
1240
- // Stella starts mysterious and artistic - digital artist with glitchy personality
1241
  affection: 50, // Moderate starting affection - artistic mystery
1242
  playfulness: 70, // Artistic playfulness
1243
  intelligence: 90, // Very high intelligence - digital artist genius
 
344
  negative: ["stupid", "dumb", "foolish", "slow", "naive", "ignorant", "simple"]
345
  },
346
  romance: {
347
+ positive: ["cuddle", "love", "romantic", "kiss", "tenderness", "passion", "charming", "adorable", "sweet"],
 
 
 
 
 
 
 
 
 
 
 
 
348
  negative: ["cold", "distant", "indifferent", "rejection", "loneliness", "breakup", "sad"]
349
  },
350
  affection: {
351
+ positive: ["affection", "tenderness", "close", "warmth", "kind", "caring", "cuddle", "love", "adore", "lovely"],
 
 
 
 
 
 
 
 
 
 
 
 
 
352
  negative: [
353
  "mean",
354
  "cold",
 
410
  ]
411
  },
412
  romance: {
413
+ positive: ["câlin", "amour", "romantique", "bisou", "tendresse", "passion", "séduisant", "charmant", "adorable"],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
  negative: [
415
  "froid",
416
  "froide",
 
434
  "attentionné",
435
  "câlin",
436
  "aimer",
 
 
 
 
 
437
  "adorer",
438
  "adorable"
439
  ],
 
521
  ]
522
  },
523
  romance: {
524
+ positive: ["abrazo", "amor", "romántico", "beso", "ternura", "pasión", "encantador", "adorable", "dulce"],
 
 
 
 
 
 
 
 
 
 
 
 
525
  negative: ["frío", "fría", "distante", "indiferente", "rechazo", "soledad", "ruptura", "triste"]
526
  },
527
  affection: {
528
+ positive: ["afecto", "ternura", "cerca", "calidez", "amable", "cariño", "abrazar", "amor", "adorar"],
 
 
 
 
 
 
 
 
 
 
 
 
529
  negative: [
530
  "malo",
531
  "mala",
 
595
  "leidenschaft",
596
  "charmant",
597
  "liebenswert",
598
+ "süß"
 
599
  ],
600
  negative: [
601
  "kalt",
 
612
  ]
613
  },
614
  affection: {
615
+ positive: ["zuneigung", "zärtlichkeit", "nah", "wärme", "freundlich", "fürsorglich", "umarmen", "liebe", "anbeten"],
 
 
 
 
 
 
 
 
 
 
 
616
  negative: [
617
  "gemein",
618
  "gemeine",
 
674
  negative: ["stupido", "stupida", "sciocco", "sciocca", "lento", "lenta", "ingenuo", "ingenua", "ignorante"]
675
  },
676
  romance: {
677
+ positive: ["abbraccio", "amore", "romantico", "bacio", "tenerezza", "passione", "affascinante", "adorabile", "dolce"],
 
 
 
 
 
 
 
 
 
 
 
678
  negative: ["freddo", "fredda", "distante", "indifferente", "rifiuto", "solitudine", "rottura", "triste"]
679
  },
680
  affection: {
681
+ positive: ["affetto", "tenerezza", "vicino", "calore", "gentile", "premuroso", "abbraccio", "amore", "adorare"],
 
 
 
 
 
 
 
 
 
 
 
682
  negative: [
683
  "cattivo",
684
  "cattiva",
 
722
  laughing: ["はは", "笑", "笑う", "面白い", "愉快"],
723
  shy: ["恥ずかしい", "照れる", "赤面", "内気", "遠慮"],
724
  confident: ["自信", "誇り", "確信", "強い", "決意"],
725
+ romantic: ["愛", "ロマンチック", "優しい", "抱擁", "キス", "愛しい"],
726
  flirtatious: ["いちゃつく", "からかう", "誘惑", "魅力", "フリート"],
727
  goodbye: ["さようなら", "バイバイ", "また今度", "チャオ", "またね"],
728
  kiss: ["キス", "抱擁", "チュー"],
 
734
  laughing: ["哈哈", "笑", "大笑", "有趣", "搞笑"],
735
  shy: ["害羞", "尴尬", "脸红", "羞涩", "胆怯"],
736
  confident: ["自信", "骄傲", "确信", "强壮", "坚定"],
737
+ romantic: ["爱", "浪漫", "温柔", "拥抱", "吻", "亲爱的"],
738
  flirtatious: ["调情", "挑逗", "诱惑", "魅力", "撒娇"],
739
  goodbye: ["再见", "拜拜", "回头见", "拜", "下次见"],
740
  kiss: ["吻", "亲吻", "拥抱", "亲"],
 
808
  window.KIMI_NEGATION_WINDOW = window.KIMI_NEGATION_WINDOW || 3; // tokens to look back for negation
809
  window.KIMI_SMOOTHING_ALPHA = window.KIMI_SMOOTHING_ALPHA || 0.3;
810
  window.KIMI_PERSIST_THRESHOLD = window.KIMI_PERSIST_THRESHOLD || 0.1; // absolute percent (slightly higher to slow small visible jumps)
 
 
 
811
 
812
  // Memory system knobs
813
  window.KIMI_MAX_MEMORIES = window.KIMI_MAX_MEMORIES || 100; // default max memory entries per character
 
993
  dancing: 1.05,
994
  shy: 0.95,
995
  confident: 1.1,
996
+ flirtatious: 1.2,
997
+ surprise: 1.05,
998
+ listening: 1.1,
999
+ kiss: 1.35,
1000
+ goodbye: 0.9
1001
  },
1002
  // Per-trait scaling
1003
  traitGain: {
1004
+ affection: 1.15, // Affection growth multiplier
1005
+ romance: 1.2, // Romance growth multiplier
1006
+ empathy: 1.1, // Empathy growth multiplier
1007
+ playfulness: 1.15, // Playfulness growth multiplier
1008
+ humor: 1.12, // Humor growth multiplier
1009
+ intelligence: 1.08 // Intelligence growth multiplier
1010
  },
1011
  traitLoss: {
1012
  affection: 0.9,
 
1079
  name: "Kimi",
1080
  summary: "Dreamy, intuitive, captivated by cosmic metaphors",
1081
  traits: {
1082
+ // Baseline balanced profile
1083
  affection: 55, // Starts neutral, grows with interaction
1084
  playfulness: 55,
1085
  intelligence: 75, // Higher intelligence - she's an astrophysicist
 
1098
  name: "Bella",
1099
  summary: "Cheerful, nurturing, sees people as plants needing care",
1100
  traits: {
1101
+ // Warm / nurturing baseline profile
1102
  affection: 60, // Naturally more affectionate
1103
  playfulness: 65, // Cheerful and playful from start
1104
  intelligence: 65, // Smart but not intimidating
 
1117
  name: "Rosa",
1118
  summary: "Chaotic, attention-seeking, thrives on controlled chaos",
1119
  traits: {
1120
+ // High playfulness / lower initial affection profile
1121
  affection: 45, // Lower starting affection - must earn her trust
1122
  playfulness: 80, // Very playful from start - it's her nature
1123
  intelligence: 85, // High intelligence - cunning prankster
1124
  empathy: 55, // Lower empathy initially - focused on chaos
1125
  humor: 75, // High humor - prankster personality
1126
+ romance: 50 // Neutral romance starting point
1127
  },
1128
  age: 21,
1129
  birthplace: "Barcelona, Spain",
 
1136
  name: "Stella",
1137
  summary: "Whimsical, artistic, imaginative, playful, transforms chaos into art",
1138
  traits: {
1139
+ // Artistic / high-intellect baseline profile
1140
  affection: 50, // Moderate starting affection - artistic mystery
1141
  playfulness: 70, // Artistic playfulness
1142
  intelligence: 90, // Very high intelligence - digital artist genius
kimi-js/kimi-database.js CHANGED
@@ -98,7 +98,7 @@ class KimiDatabase {
98
  async setConversationsBatch(conversationsArray) {
99
  if (!Array.isArray(conversationsArray)) return;
100
  try {
101
- await this.clearConversations(null);
102
  if (conversationsArray.length) {
103
  await this.db.conversations.bulkPut(conversationsArray);
104
  }
@@ -107,18 +107,6 @@ class KimiDatabase {
107
  }
108
  }
109
 
110
- // Clear conversations helper - centralizes clearing logic
111
- async clearConversations(character = null) {
112
- if (character) {
113
- // Delete only conversations for a given character
114
- const all = await this.db.conversations.where("character").equals(character).toArray();
115
- const ids = all.map(item => item.id);
116
- if (ids.length) return this.db.conversations.bulkDelete(ids);
117
- return Promise.resolve();
118
- }
119
- return this.db.conversations.clear();
120
- }
121
-
122
  async setLLMModelsBatch(modelsArray) {
123
  if (!Array.isArray(modelsArray)) return;
124
  try {
@@ -477,24 +465,6 @@ class KimiDatabase {
477
 
478
  async saveConversation(userText, kimiResponse, favorability, timestamp = new Date(), character = null) {
479
  if (!character) character = await this.getSelectedCharacter();
480
- // If a global cleared timestamp exists and this conversation is older or equal, skip saving
481
- try {
482
- const clearedMap = window.kimiConversationsClearedAt;
483
- if (clearedMap && typeof clearedMap === "object") {
484
- const clearedAtForChar = clearedMap[character];
485
- if (clearedAtForChar) {
486
- const clearedTime = new Date(clearedAtForChar).getTime();
487
- const convTime = new Date(timestamp).getTime();
488
- if (!isNaN(clearedTime) && convTime <= clearedTime) {
489
- // Skip saving to avoid resurrecting cleared messages for this character
490
- return null;
491
- }
492
- }
493
- }
494
- } catch (e) {
495
- // ignore parsing errors and fall through to save
496
- }
497
-
498
  const conversation = {
499
  user: userText,
500
  kimi: kimiResponse,
 
98
  async setConversationsBatch(conversationsArray) {
99
  if (!Array.isArray(conversationsArray)) return;
100
  try {
101
+ await this.db.conversations.clear();
102
  if (conversationsArray.length) {
103
  await this.db.conversations.bulkPut(conversationsArray);
104
  }
 
107
  }
108
  }
109
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  async setLLMModelsBatch(modelsArray) {
111
  if (!Array.isArray(modelsArray)) return;
112
  try {
 
465
 
466
  async saveConversation(userText, kimiResponse, favorability, timestamp = new Date(), character = null) {
467
  if (!character) character = await this.getSelectedCharacter();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
468
  const conversation = {
469
  user: userText,
470
  kimi: kimiResponse,
kimi-js/kimi-emotion-system.js CHANGED
@@ -3,6 +3,22 @@
3
 
4
  class KimiEmotionSystem {
5
  constructor(database = null) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  this.db = database;
7
  this.negativeStreaks = {};
8
 
@@ -45,14 +61,43 @@ class KimiEmotionSystem {
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
52
- humor: 60, // Reduced from 75 - develops sense of humor over time
53
- romance: 50 // Significantly reduced from 95 - romance must be earned!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  };
55
  }
 
56
  // ===== UNIFIED EMOTION ANALYSIS =====
57
  analyzeEmotion(text, lang = "auto") {
58
  if (!text || typeof text !== "string") return this.EMOTIONS.NEUTRAL;
@@ -218,60 +263,51 @@ class KimiEmotionSystem {
218
  return baseDelta * GLOSS * t;
219
  };
220
 
221
- switch (emotion) {
222
- case this.EMOTIONS.POSITIVE:
223
- affection = Math.min(100, adjustUp(affection, scaleGain("affection", 0.4))); // Slightly more affection gain
224
- empathy = Math.min(100, adjustUp(empathy, scaleGain("empathy", 0.2)));
225
- playfulness = Math.min(100, adjustUp(playfulness, scaleGain("playfulness", 0.2)));
226
- humor = Math.min(100, adjustUp(humor, scaleGain("humor", 0.2)));
227
- romance = Math.min(100, adjustUp(romance, scaleGain("romance", 0.1))); // Romance grows very slowly
228
- break;
229
- case this.EMOTIONS.NEGATIVE:
230
- affection = Math.max(0, adjustDown(affection, scaleLoss("affection", 0.6))); // Affection drops faster on negative
231
- empathy = Math.min(100, adjustUp(empathy, scaleGain("empathy", 0.5))); // Empathy still grows (understanding pain)
232
- break;
233
- case this.EMOTIONS.ROMANTIC:
234
- romance = Math.min(100, adjustUp(romance, scaleGain("romance", 0.6))); // Romance should be earned
235
- affection = Math.min(100, adjustUp(affection, scaleGain("affection", 0.5)));
236
- break;
237
- case this.EMOTIONS.LAUGHING:
238
- humor = Math.min(100, adjustUp(humor, scaleGain("humor", 0.8))); // Humor grows with laughter
239
- playfulness = Math.min(100, adjustUp(playfulness, scaleGain("playfulness", 0.4))); // Playfulness connection
240
- affection = Math.min(100, adjustUp(affection, scaleGain("affection", 0.2))); // Small affection boost from shared laughter
241
- break;
242
- case this.EMOTIONS.DANCING:
243
- playfulness = Math.min(100, adjustUp(playfulness, scaleGain("playfulness", 1.2))); // Dancing = maximum playfulness boost
244
- affection = Math.min(100, adjustUp(affection, scaleGain("affection", 0.5))); // Affection from shared activity
245
- break;
246
- case this.EMOTIONS.SHY:
247
- affection = Math.max(0, adjustDown(affection, scaleLoss("affection", 0.1))); // Small affection loss
248
- romance = Math.max(0, adjustDown(romance, scaleLoss("romance", 0.2))); // Shyness reduces romance more
249
- break;
250
- case this.EMOTIONS.CONFIDENT:
251
- affection = Math.min(100, adjustUp(affection, scaleGain("affection", 0.5)));
252
- intelligence = Math.min(100, adjustUp(intelligence, scaleGain("intelligence", 0.1))); // Slight intelligence boost
253
- break;
254
- case this.EMOTIONS.FLIRTATIOUS:
255
- romance = Math.min(100, adjustUp(romance, scaleGain("romance", 0.5)));
256
- playfulness = Math.min(100, adjustUp(playfulness, scaleGain("playfulness", 0.5)));
257
- affection = Math.min(100, adjustUp(affection, scaleGain("affection", 0.2))); // Small affection boost
258
- break;
259
- case this.EMOTIONS.SURPRISE:
260
- intelligence = Math.min(100, adjustUp(intelligence, scaleGain("intelligence", 0.1))); // Surprise stimulates thinking
261
- empathy = Math.min(100, adjustUp(empathy, scaleGain("empathy", 0.1))); // Opens mind to new perspectives
262
- break;
263
- case this.EMOTIONS.KISS:
264
- romance = Math.min(100, adjustUp(romance, scaleGain("romance", 0.8))); // Strong romantic gesture
265
- affection = Math.min(100, adjustUp(affection, scaleGain("affection", 0.6))); // Very affectionate action
266
- break;
267
- case this.EMOTIONS.GOODBYE:
268
- // Slight melancholy but no major trait changes
269
- empathy = Math.min(100, adjustUp(empathy, scaleGain("empathy", 0.05))); // Understanding of parting
270
- break;
271
- case this.EMOTIONS.LISTENING:
272
- intelligence = Math.min(100, adjustUp(intelligence, scaleGain("intelligence", 0.2))); // Active listening shows intelligence
273
- empathy = Math.min(100, adjustUp(empathy, scaleGain("empathy", 0.5))); // Listening builds empathy
274
- break;
275
  }
276
 
277
  // Cross-trait interactions - traits influence each other for more realistic personality development
@@ -305,6 +341,20 @@ class KimiEmotionSystem {
305
  adjustUp
306
  );
307
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
  // Preserve fractional progress to allow gradual visible changes
309
  const to2 = v => Number(Number(v).toFixed(2));
310
  const clamp = v => Math.max(0, Math.min(100, v));
@@ -331,6 +381,40 @@ class KimiEmotionSystem {
331
  return updatedTraits;
332
  }
333
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  // ===== UNIFIED LLM PERSONALITY ANALYSIS =====
335
  async updatePersonalityFromConversation(userMessage, kimiResponse, character = null) {
336
  if (!this.db) return;
@@ -351,44 +435,56 @@ class KimiEmotionSystem {
351
  for (const trait of ["humor", "intelligence", "romance", "affection", "playfulness", "empathy"]) {
352
  const posWords = getPersonalityWords(trait, "positive");
353
  const negWords = getPersonalityWords(trait, "negative");
354
- let value = typeof traits[trait] === "number" && isFinite(traits[trait]) ? traits[trait] : this.TRAIT_DEFAULTS[trait];
355
-
356
- // Count occurrences with proper weighting
357
- let posCount = 0;
358
- let negCount = 0;
359
-
360
- const llmWeight = Number(window.KIMI_LLM_RESPONSE_WEIGHT) || 0.5;
 
 
 
361
  for (const w of posWords) {
362
- posCount += this.countTokenMatches(lowerUser, String(w)) * 1.0;
363
- posCount += this.countTokenMatches(lowerKimi, String(w)) * llmWeight;
364
  }
365
  for (const w of negWords) {
366
- negCount += this.countTokenMatches(lowerUser, String(w)) * 1.0;
367
- negCount += this.countTokenMatches(lowerKimi, String(w)) * llmWeight;
368
  }
369
 
370
- const delta = (posCount - negCount) * 0.8; // softened multiplier to 0.8 for gentler progression
371
 
372
- // Apply streak logic
373
  if (!this.negativeStreaks[trait]) this.negativeStreaks[trait] = 0;
374
-
375
- if (negCount > 0 && posCount === 0) {
376
  this.negativeStreaks[trait]++;
377
- if (this.negativeStreaks[trait] >= 3) {
378
- value = Math.max(0, Math.min(100, value + delta - 1));
379
- } else {
380
- value = Math.max(0, Math.min(100, value + delta));
381
- }
382
- } else if (posCount > 0) {
383
  this.negativeStreaks[trait] = 0;
384
- value = Math.max(0, Math.min(100, value + delta));
385
  }
386
 
387
- if (delta !== 0) {
388
- pendingUpdates[trait] = value;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
  }
390
  }
391
 
 
 
392
  // Flush pending updates in a single batch write to avoid overwrites
393
  if (Object.keys(pendingUpdates).length > 0) {
394
  // Apply smoothing/threshold per trait (read current values)
@@ -669,6 +765,57 @@ class KimiEmotionSystem {
669
  }
670
 
671
  window.KimiEmotionSystem = KimiEmotionSystem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
672
  export default KimiEmotionSystem;
673
 
674
  // ===== BACKWARD COMPATIBILITY LAYER =====
 
3
 
4
  class KimiEmotionSystem {
5
  constructor(database = null) {
6
+ /*
7
+ * Personality Update Pipeline (Refactored)
8
+ * 1. Emotion detected -> base deltas applied via EMOTION_TRAIT_EFFECTS (central map).
9
+ * - Each delta passes through adjustUp / adjustDown with global + per-trait multipliers
10
+ * (window.KIMI_TRAIT_ADJUSTMENT) for consistent scaling.
11
+ * 2. Content keyword analysis (_analyzeTextContent) may override interim trait values (explicit matches).
12
+ * 3. Cross-trait modifiers (_applyCrossTraitModifiers) apply synergy / balancing rules (e.g. high empathy boosts affection, high romance stabilizes affection, intelligence supports empathy/humor).
13
+ * 4. Conversation-based drift (updatePersonalityFromConversation) uses TRAIT_KEYWORD_MODEL:
14
+ * - Counts positive/negative keyword hits (user weighted 1.0, model weighted 0.5).
15
+ * - Computes rawDelta = posHits*posFactor - negHits*negFactor.
16
+ * - Applies sustained negativity amplification after streakPenaltyAfter.
17
+ * - Clamps magnitude to maxStep per trait, then applies directly with bounds [0,100].
18
+ * 5. Persistence: _preparePersistTrait decides threshold & smoothing before batch write.
19
+ * 6. Global personality average (UI) = mean of six core traits (affection included).
20
+ * NOTE: Affection is fully independent (no derived average). All adjustments centralized here to avoid duplication.
21
+ */
22
  this.db = database;
23
  this.negativeStreaks = {};
24
 
 
61
 
62
  // Unified trait defaults - Balanced for progressive experience
63
  this.TRAIT_DEFAULTS = {
64
+ affection: 55, // Baseline neutral affection
65
+ playfulness: 55, // Moderately playful baseline
66
+ intelligence: 70, // Competent baseline intellect
67
+ empathy: 75, // Warm & caring baseline
68
+ humor: 60, // Mild sense of humor baseline
69
+ romance: 50 // Neutral romance baseline (earned over time)
70
+ };
71
+
72
+ // Central emotion -> trait base deltas (pre global multipliers & gainCfg scaling)
73
+ // Positive numbers increase trait, negative decrease.
74
+ // Keep values small; final effect passes through adjustUp/adjustDown and global multipliers.
75
+ this.EMOTION_TRAIT_EFFECTS = {
76
+ positive: { affection: 0.45, empathy: 0.2, playfulness: 0.25, humor: 0.25 },
77
+ negative: { affection: -0.7, empathy: 0.3 },
78
+ romantic: { romance: 0.7, affection: 0.55, empathy: 0.15 },
79
+ flirtatious: { romance: 0.55, playfulness: 0.45, affection: 0.25 },
80
+ laughing: { humor: 0.85, playfulness: 0.5, affection: 0.25 },
81
+ dancing: { playfulness: 1.1, affection: 0.45 },
82
+ surprise: { intelligence: 0.12, empathy: 0.12 },
83
+ shy: { romance: -0.3, affection: -0.12 },
84
+ confident: { intelligence: 0.15, affection: 0.55 },
85
+ listening: { empathy: 0.6, intelligence: 0.25 },
86
+ kiss: { romance: 0.85, affection: 0.7 },
87
+ goodbye: { affection: -0.15, empathy: 0.1 }
88
+ };
89
+
90
+ // Trait keyword scaling model for conversation analysis (per-message delta shaping)
91
+ this.TRAIT_KEYWORD_MODEL = {
92
+ affection: { posFactor: 0.5, negFactor: 0.65, streakPenaltyAfter: 3, maxStep: 2 },
93
+ romance: { posFactor: 0.55, negFactor: 0.75, streakPenaltyAfter: 2, maxStep: 1.8 },
94
+ empathy: { posFactor: 0.4, negFactor: 0.5, streakPenaltyAfter: 3, maxStep: 1.5 },
95
+ playfulness: { posFactor: 0.45, negFactor: 0.4, streakPenaltyAfter: 4, maxStep: 1.4 },
96
+ humor: { posFactor: 0.55, negFactor: 0.45, streakPenaltyAfter: 4, maxStep: 1.6 },
97
+ intelligence: { posFactor: 0.35, negFactor: 0.55, streakPenaltyAfter: 2, maxStep: 1.2 }
98
  };
99
  }
100
+ // (Affection is an independent trait again; previous derived computation removed.)
101
  // ===== UNIFIED EMOTION ANALYSIS =====
102
  analyzeEmotion(text, lang = "auto") {
103
  if (!text || typeof text !== "string") return this.EMOTIONS.NEUTRAL;
 
263
  return baseDelta * GLOSS * t;
264
  };
265
 
266
+ // Apply emotion deltas from centralized map (if defined)
267
+ const map = this.EMOTION_TRAIT_EFFECTS?.[emotion];
268
+ if (map) {
269
+ for (const [traitName, baseDelta] of Object.entries(map)) {
270
+ const delta = baseDelta; // base delta -> will be scaled below
271
+ if (delta === 0) continue;
272
+ switch (traitName) {
273
+ case "affection":
274
+ affection =
275
+ delta > 0
276
+ ? Math.min(100, adjustUp(affection, scaleGain("affection", delta)))
277
+ : Math.max(0, adjustDown(affection, scaleLoss("affection", Math.abs(delta))));
278
+ break;
279
+ case "romance":
280
+ romance =
281
+ delta > 0
282
+ ? Math.min(100, adjustUp(romance, scaleGain("romance", delta)))
283
+ : Math.max(0, adjustDown(romance, scaleLoss("romance", Math.abs(delta))));
284
+ break;
285
+ case "empathy":
286
+ empathy =
287
+ delta > 0
288
+ ? Math.min(100, adjustUp(empathy, scaleGain("empathy", delta)))
289
+ : Math.max(0, adjustDown(empathy, scaleLoss("empathy", Math.abs(delta))));
290
+ break;
291
+ case "playfulness":
292
+ playfulness =
293
+ delta > 0
294
+ ? Math.min(100, adjustUp(playfulness, scaleGain("playfulness", delta)))
295
+ : Math.max(0, adjustDown(playfulness, scaleLoss("playfulness", Math.abs(delta))));
296
+ break;
297
+ case "humor":
298
+ humor =
299
+ delta > 0
300
+ ? Math.min(100, adjustUp(humor, scaleGain("humor", delta)))
301
+ : Math.max(0, adjustDown(humor, scaleLoss("humor", Math.abs(delta))));
302
+ break;
303
+ case "intelligence":
304
+ intelligence =
305
+ delta > 0
306
+ ? Math.min(100, adjustUp(intelligence, scaleGain("intelligence", delta)))
307
+ : Math.max(0, adjustDown(intelligence, scaleLoss("intelligence", Math.abs(delta))));
308
+ break;
309
+ }
310
+ }
 
 
 
 
 
 
 
 
 
311
  }
312
 
313
  // Cross-trait interactions - traits influence each other for more realistic personality development
 
341
  adjustUp
342
  );
343
 
344
+ // Cross-trait modifiers (applied after primary emotion & content changes)
345
+ ({ affection, romance, empathy, playfulness, humor, intelligence } = this._applyCrossTraitModifiers({
346
+ affection,
347
+ romance,
348
+ empathy,
349
+ playfulness,
350
+ humor,
351
+ intelligence,
352
+ adjustUp,
353
+ adjustDown,
354
+ scaleGain,
355
+ scaleLoss
356
+ }));
357
+
358
  // Preserve fractional progress to allow gradual visible changes
359
  const to2 = v => Number(Number(v).toFixed(2));
360
  const clamp = v => Math.max(0, Math.min(100, v));
 
381
  return updatedTraits;
382
  }
383
 
384
+ // Apply cross-trait synergy & balancing rules.
385
+ _applyCrossTraitModifiers(ctx) {
386
+ let { affection, romance, empathy, playfulness, humor, intelligence, adjustUp, adjustDown, scaleGain } = ctx;
387
+ // High empathy soft-boost affection if still lagging
388
+ if (empathy >= 80 && affection < empathy - 8) {
389
+ affection = Math.min(100, adjustUp(affection, scaleGain("affection", 0.08)));
390
+ }
391
+ // High romance amplifies affection gains subtlely
392
+ if (romance >= 80 && affection < romance - 5) {
393
+ affection = Math.min(100, adjustUp(affection, scaleGain("affection", 0.06)));
394
+ }
395
+ // High affection but lower romance triggers slight romance catch-up
396
+ if (affection >= 90 && romance < 70) {
397
+ romance = Math.min(100, adjustUp(romance, scaleGain("romance", 0.05)));
398
+ }
399
+ // Intelligence supports empathy & humor small growth
400
+ if (intelligence >= 85) {
401
+ if (empathy < intelligence - 12) {
402
+ empathy = Math.min(100, adjustUp(empathy, scaleGain("empathy", 0.04)));
403
+ }
404
+ if (humor < 75) {
405
+ humor = Math.min(100, adjustUp(humor, scaleGain("humor", 0.04)));
406
+ }
407
+ }
408
+ // Humor/playfulness mutual reinforcement (retain existing logic but guarded)
409
+ if (humor >= 70 && playfulness < humor - 10) {
410
+ playfulness = Math.min(100, adjustUp(playfulness, scaleGain("playfulness", 0.05)));
411
+ }
412
+ if (playfulness >= 70 && humor < playfulness - 10) {
413
+ humor = Math.min(100, adjustUp(humor, scaleGain("humor", 0.05)));
414
+ }
415
+ return { affection, romance, empathy, playfulness, humor, intelligence };
416
+ }
417
+
418
  // ===== UNIFIED LLM PERSONALITY ANALYSIS =====
419
  async updatePersonalityFromConversation(userMessage, kimiResponse, character = null) {
420
  if (!this.db) return;
 
435
  for (const trait of ["humor", "intelligence", "romance", "affection", "playfulness", "empathy"]) {
436
  const posWords = getPersonalityWords(trait, "positive");
437
  const negWords = getPersonalityWords(trait, "negative");
438
+ let currentVal =
439
+ typeof traits[trait] === "number" && isFinite(traits[trait]) ? traits[trait] : this.TRAIT_DEFAULTS[trait];
440
+ const model = this.TRAIT_KEYWORD_MODEL[trait];
441
+ const posFactor = model.posFactor;
442
+ const negFactor = model.negFactor;
443
+ const maxStep = model.maxStep;
444
+ const streakLimit = model.streakPenaltyAfter;
445
+
446
+ let posScore = 0;
447
+ let negScore = 0;
448
  for (const w of posWords) {
449
+ posScore += this.countTokenMatches(lowerUser, String(w)) * 1.0;
450
+ posScore += this.countTokenMatches(lowerKimi, String(w)) * 0.5;
451
  }
452
  for (const w of negWords) {
453
+ negScore += this.countTokenMatches(lowerUser, String(w)) * 1.0;
454
+ negScore += this.countTokenMatches(lowerKimi, String(w)) * 0.5;
455
  }
456
 
457
+ let rawDelta = posScore * posFactor - negScore * negFactor;
458
 
459
+ // Track negative streaks per trait (only when net negative & no positives)
460
  if (!this.negativeStreaks[trait]) this.negativeStreaks[trait] = 0;
461
+ if (negScore > 0 && posScore === 0) {
 
462
  this.negativeStreaks[trait]++;
463
+ } else if (posScore > 0) {
 
 
 
 
 
464
  this.negativeStreaks[trait] = 0;
 
465
  }
466
 
467
+ if (rawDelta < 0 && this.negativeStreaks[trait] >= streakLimit) {
468
+ rawDelta *= 1.15; // escalate sustained negativity
469
+ }
470
+
471
+ // Clamp magnitude
472
+ if (rawDelta > maxStep) rawDelta = maxStep;
473
+ if (rawDelta < -maxStep) rawDelta = -maxStep;
474
+
475
+ if (rawDelta !== 0) {
476
+ let newVal = currentVal + rawDelta;
477
+ if (rawDelta > 0) {
478
+ newVal = Math.min(100, newVal);
479
+ } else {
480
+ newVal = Math.max(0, newVal);
481
+ }
482
+ pendingUpdates[trait] = newVal;
483
  }
484
  }
485
 
486
+ // Affection stays as independently adjusted by keywords & emotion (no derived override)
487
+
488
  // Flush pending updates in a single batch write to avoid overwrites
489
  if (Object.keys(pendingUpdates).length > 0) {
490
  // Apply smoothing/threshold per trait (read current values)
 
765
  }
766
 
767
  window.KimiEmotionSystem = KimiEmotionSystem;
768
+ // Expose centralized tuning maps for debugging / live adjustments
769
+ Object.defineProperty(window, "KIMI_EMOTION_TRAIT_EFFECTS", {
770
+ get() {
771
+ return window.kimiEmotionSystem ? window.kimiEmotionSystem.EMOTION_TRAIT_EFFECTS : null;
772
+ }
773
+ });
774
+ Object.defineProperty(window, "KIMI_TRAIT_KEYWORD_MODEL", {
775
+ get() {
776
+ return window.kimiEmotionSystem ? window.kimiEmotionSystem.TRAIT_KEYWORD_MODEL : null;
777
+ }
778
+ });
779
+
780
+ // Debug/tuning helpers
781
+ window.setEmotionDelta = function (emotion, trait, value) {
782
+ if (!window.kimiEmotionSystem) return false;
783
+ const map = window.kimiEmotionSystem.EMOTION_TRAIT_EFFECTS;
784
+ if (!map[emotion]) map[emotion] = {};
785
+ map[emotion][trait] = Number(value);
786
+ return true;
787
+ };
788
+ window.resetEmotionDeltas = function () {
789
+ if (!window.kimiEmotionSystem) return false;
790
+ // No stored original snapshot; advise page reload for full reset.
791
+ console.warn("For full reset reload the page (original deltas are not snapshotted).");
792
+ };
793
+ window.setTraitKeywordScaling = function (trait, cfg) {
794
+ if (!window.kimiEmotionSystem) return false;
795
+ const model = window.kimiEmotionSystem.TRAIT_KEYWORD_MODEL;
796
+ if (!model[trait]) return false;
797
+ Object.assign(model[trait], cfg);
798
+ return true;
799
+ };
800
+
801
+ // Force recompute + UI refresh for personality average
802
+ window.refreshPersonalityAverageUI = async function (characterKey = null) {
803
+ try {
804
+ if (window.updateGlobalPersonalityUI) {
805
+ await window.updateGlobalPersonalityUI(characterKey);
806
+ } else if (window.getPersonalityAverage && window.kimiDB) {
807
+ const charKey = characterKey || (await window.kimiDB.getSelectedCharacter());
808
+ const traits = await window.kimiDB.getAllPersonalityTraits(charKey);
809
+ const avg = window.getPersonalityAverage(traits);
810
+ const bar = document.getElementById("favorability-bar");
811
+ const text = document.getElementById("favorability-text");
812
+ if (bar) bar.style.width = `${avg}%`;
813
+ if (text) text.textContent = `${avg.toFixed(2)}%`;
814
+ }
815
+ } catch (err) {
816
+ console.warn("refreshPersonalityAverageUI failed", err);
817
+ }
818
+ };
819
  export default KimiEmotionSystem;
820
 
821
  // ===== BACKWARD COMPATIBILITY LAYER =====
kimi-js/kimi-llm-manager.js CHANGED
@@ -17,7 +17,7 @@ class KimiLLMManager {
17
  type: "openrouter",
18
  contextWindow: 128000,
19
  pricing: { input: 0.05, output: 0.1 },
20
- strengths: ["Multilingual", "Economical", "Fast", "Efficient"]
21
  },
22
  "qwen/qwen3-30b-a3b-instruct-2507": {
23
  name: "Qwen3 30b-a3b instruct 2507",
@@ -25,7 +25,7 @@ class KimiLLMManager {
25
  type: "openrouter",
26
  contextWindow: 131000,
27
  pricing: { input: 0.1, output: 0.3 },
28
- strengths: ["Open Source", "Balanced", "Fast", "Economical"]
29
  },
30
  "nousresearch/hermes-4-70b": {
31
  name: "Nous Hermes 4 70B",
@@ -33,7 +33,7 @@ class KimiLLMManager {
33
  type: "openrouter",
34
  contextWindow: 131000,
35
  pricing: { input: 0.13, output: 0.4 },
36
- strengths: ["Open Source", "Balanced", "Fast", "Economical"]
37
  },
38
  "x-ai/grok-3-mini": {
39
  name: "Grok 3 mini",
@@ -41,7 +41,7 @@ class KimiLLMManager {
41
  type: "openrouter",
42
  contextWindow: 131000,
43
  pricing: { input: 0.3, output: 0.5 },
44
- strengths: ["Multilingual", "Balanced", "Efficient", "Economical"]
45
  },
46
  "cohere/command-r-08-2024": {
47
  name: "Command-R-08-2024",
@@ -49,7 +49,7 @@ class KimiLLMManager {
49
  type: "openrouter",
50
  contextWindow: 128000,
51
  pricing: { input: 0.15, output: 0.6 },
52
- strengths: ["Multilingual", "Economical", "Efficient", "Versatile"]
53
  },
54
  "qwen/qwen3-235b-a22b-2507": {
55
  name: "Qwen3-235b-a22b-2507",
@@ -57,7 +57,7 @@ class KimiLLMManager {
57
  type: "openrouter",
58
  contextWindow: 262000,
59
  pricing: { input: 0.13, output: 0.6 },
60
- strengths: ["Multilingual", "Economical", "Efficient", "Versatile"]
61
  },
62
  "anthropic/claude-3-haiku": {
63
  name: "Claude 3 Haiku",
@@ -65,7 +65,7 @@ class KimiLLMManager {
65
  type: "openrouter",
66
  contextWindow: 200000,
67
  pricing: { input: 0.25, output: 1.25 },
68
- strengths: ["Fast", "Versatile", "Efficient", "Multilingual"]
69
  },
70
  "local/ollama": {
71
  name: "Local Model (Ollama)",
@@ -73,7 +73,7 @@ class KimiLLMManager {
73
  type: "local",
74
  contextWindow: 4096,
75
  pricing: { input: 0, output: 0 },
76
- strengths: ["Private", "Free", "Offline", "Customizable"]
77
  }
78
  };
79
  this.recommendedModelIds = [
 
17
  type: "openrouter",
18
  contextWindow: 128000,
19
  pricing: { input: 0.05, output: 0.1 },
20
+ strengths: ["Multilingual", "Fast", "Efficient", "Economical"]
21
  },
22
  "qwen/qwen3-30b-a3b-instruct-2507": {
23
  name: "Qwen3 30b-a3b instruct 2507",
 
25
  type: "openrouter",
26
  contextWindow: 131000,
27
  pricing: { input: 0.1, output: 0.3 },
28
+ strengths: ["Multilingual", "Fast", "Balanced", "Economical"]
29
  },
30
  "nousresearch/hermes-4-70b": {
31
  name: "Nous Hermes 4 70B",
 
33
  type: "openrouter",
34
  contextWindow: 131000,
35
  pricing: { input: 0.13, output: 0.4 },
36
+ strengths: ["Multilingual", "Fast", "Balanced", "Economical"]
37
  },
38
  "x-ai/grok-3-mini": {
39
  name: "Grok 3 mini",
 
41
  type: "openrouter",
42
  contextWindow: 131000,
43
  pricing: { input: 0.3, output: 0.5 },
44
+ strengths: ["Multilingual", "Fast", "Efficient", "Economical"]
45
  },
46
  "cohere/command-r-08-2024": {
47
  name: "Command-R-08-2024",
 
49
  type: "openrouter",
50
  contextWindow: 128000,
51
  pricing: { input: 0.15, output: 0.6 },
52
+ strengths: ["Multilingual", "Fast", "Versatile", "Economical"]
53
  },
54
  "qwen/qwen3-235b-a22b-2507": {
55
  name: "Qwen3-235b-a22b-2507",
 
57
  type: "openrouter",
58
  contextWindow: 262000,
59
  pricing: { input: 0.13, output: 0.6 },
60
+ strengths: ["Multilingual", "Fast", "Versatile", "Economical"]
61
  },
62
  "anthropic/claude-3-haiku": {
63
  name: "Claude 3 Haiku",
 
65
  type: "openrouter",
66
  contextWindow: 200000,
67
  pricing: { input: 0.25, output: 1.25 },
68
+ strengths: ["Multilingual", "Fast", "Versatile", "Efficient"]
69
  },
70
  "local/ollama": {
71
  name: "Local Model (Ollama)",
 
73
  type: "local",
74
  contextWindow: 4096,
75
  pricing: { input: 0, output: 0 },
76
+ strengths: ["Private", "Offline", "Customizable"]
77
  }
78
  };
79
  this.recommendedModelIds = [
kimi-js/kimi-memory.js CHANGED
@@ -107,21 +107,13 @@ class KimiMemory {
107
  }
108
  }
109
 
 
 
 
 
110
  updateFavorabilityBar() {
111
- try {
112
- const favorabilityBar = document.getElementById("favorability-bar");
113
- const favorabilityText = document.getElementById("favorability-text");
114
- const value = Number(this.affectionTrait) || 0;
115
- const clamped = Math.max(0, Math.min(100, value));
116
-
117
- if (favorabilityBar) {
118
- favorabilityBar.style.width = `${clamped}%`;
119
- }
120
- if (favorabilityText) {
121
- favorabilityText.textContent = `${clamped.toFixed(2)}%`;
122
- }
123
- } catch (error) {
124
- console.error("Error updating favorability bar:", error);
125
  }
126
  }
127
 
 
107
  }
108
  }
109
 
110
+ /**
111
+ * @deprecated Use updateGlobalPersonalityUI().
112
+ * Thin wrapper retained for backward compatibility only.
113
+ */
114
  updateFavorabilityBar() {
115
+ if (window.updateGlobalPersonalityUI) {
116
+ window.updateGlobalPersonalityUI();
 
 
 
 
 
 
 
 
 
 
 
 
117
  }
118
  }
119
 
kimi-js/kimi-module.js CHANGED
@@ -41,7 +41,7 @@ class KimiDataManager extends KimiBaseManager {
41
 
42
  try {
43
  // Clear all conversations directly
44
- await this.db.clearConversations();
45
 
46
  // Clear chat UI
47
  const chatMessages = document.getElementById("chat-messages");
@@ -315,13 +315,61 @@ class KimiDataManager extends KimiBaseManager {
315
  function updateFavorabilityLabel(characterKey) {
316
  const favorabilityLabel = document.getElementById("favorability-label");
317
  if (favorabilityLabel && window.KIMI_CHARACTERS && window.KIMI_CHARACTERS[characterKey]) {
318
- favorabilityLabel.setAttribute("data-i18n", "affection_level_of");
 
 
319
  favorabilityLabel.setAttribute("data-i18n-params", JSON.stringify({ name: window.KIMI_CHARACTERS[characterKey].name }));
320
- favorabilityLabel.textContent = `💖 Affection level of ${window.KIMI_CHARACTERS[characterKey].name}`;
 
 
 
 
 
 
321
  applyTranslations();
322
  }
323
  }
324
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
  async function loadCharacterSection() {
326
  const kimiDB = window.kimiDB;
327
  if (!kimiDB) return;
@@ -822,23 +870,6 @@ async function loadChatHistory() {
822
  chatMessages.removeChild(chatMessages.firstChild);
823
  }
824
 
825
- // Ensure i18n manager has loaded translations to avoid raw keys appearing (e.g., "greeting_high")
826
- if (window.kimiI18nManager && typeof window.kimiI18nManager.applyTranslations === "function") {
827
- // give i18n a short moment to apply if still loading
828
- const start = Date.now();
829
- while (
830
- (window.kimiI18nManager.translations == null || Object.keys(window.kimiI18nManager.translations).length === 0) &&
831
- Date.now() - start < 500
832
- ) {
833
- // small delay
834
- // eslint-disable-next-line no-await-in-loop
835
- await new Promise(r => setTimeout(r, 50));
836
- }
837
- try {
838
- window.kimiI18nManager.applyTranslations();
839
- } catch (e) {}
840
- }
841
-
842
  if (kimiDB) {
843
  try {
844
  const recent = await kimiDB.getRecentConversations(10);
@@ -1562,37 +1593,6 @@ async function sendMessage() {
1562
  message = validation.sanitized || message.trim();
1563
  if (!message) return;
1564
 
1565
- // Force persist current personality sliders to avoid UI/DB desync
1566
- try {
1567
- const kimiDB = window.kimiDB;
1568
- if (kimiDB && typeof kimiDB.setPersonalityBatch === "function") {
1569
- const traitIds = [
1570
- "trait-affection",
1571
- "trait-playfulness",
1572
- "trait-intelligence",
1573
- "trait-empathy",
1574
- "trait-humor",
1575
- "trait-romance"
1576
- ];
1577
- const pending = {};
1578
- traitIds.forEach(id => {
1579
- const el = document.getElementById(id);
1580
- if (el && el.value !== undefined) {
1581
- const trait = id.replace(/^trait-/, "");
1582
- const v = Number(el.value);
1583
- if (isFinite(v)) pending[trait] = v;
1584
- }
1585
- });
1586
- // Only write when there is at least one trait
1587
- if (Object.keys(pending).length > 0) {
1588
- // small timeout to ensure UI changes settled (rare) - use direct write
1589
- await kimiDB.setPersonalityBatch(pending);
1590
- }
1591
- }
1592
- } catch (e) {
1593
- console.warn("Failed to persist personality sliders before send:", e);
1594
- }
1595
-
1596
  addMessageToChat("user", message);
1597
  chatInput.value = "";
1598
  if (waitingIndicator) waitingIndicator.style.display = "inline-block";
@@ -1755,6 +1755,7 @@ function setupSettingsListeners(kimiDB, kimiMemory) {
1755
  const voiceVolumeSlider = document.getElementById("voice-volume");
1756
  const languageSelect = document.getElementById("language-selection");
1757
  const voiceSelect = document.getElementById("voice-selection");
 
1758
  const traitSliders = [
1759
  "trait-affection",
1760
  "trait-playfulness",
@@ -1901,7 +1902,7 @@ function setupSettingsListeners(kimiDB, kimiMemory) {
1901
  personalityBatchTimeout = setTimeout(async () => {
1902
  if (kimiDB && Object.keys(pendingTraitChanges).length > 0) {
1903
  try {
1904
- // Use batch operation for all pending changes
1905
  await kimiDB.setPersonalityBatch(pendingTraitChanges);
1906
 
1907
  // Side-effects handled by central 'personality:updated' listener.
@@ -2254,7 +2255,10 @@ async function syncPersonalityTraits(characterName = null) {
2254
  // Update memory cache
2255
  if (window.kimiMemory && updatedTraits.affection) {
2256
  window.kimiMemory.affectionTrait = updatedTraits.affection;
2257
- if (window.kimiMemory.updateFavorabilityBar) {
 
 
 
2258
  window.kimiMemory.updateFavorabilityBar();
2259
  }
2260
  }
 
41
 
42
  try {
43
  // Clear all conversations directly
44
+ await this.db.db.conversations.clear();
45
 
46
  // Clear chat UI
47
  const chatMessages = document.getElementById("chat-messages");
 
315
  function updateFavorabilityLabel(characterKey) {
316
  const favorabilityLabel = document.getElementById("favorability-label");
317
  if (favorabilityLabel && window.KIMI_CHARACTERS && window.KIMI_CHARACTERS[characterKey]) {
318
+ // New semantics: show overall personality average (independent display)
319
+ favorabilityLabel.removeAttribute("for"); // decouple from any specific slider
320
+ favorabilityLabel.setAttribute("data-i18n", "personality_average_of");
321
  favorabilityLabel.setAttribute("data-i18n-params", JSON.stringify({ name: window.KIMI_CHARACTERS[characterKey].name }));
322
+ favorabilityLabel.textContent = `💖 Personality average of ${window.KIMI_CHARACTERS[characterKey].name}`;
323
+ if (!favorabilityLabel.getAttribute("title")) {
324
+ favorabilityLabel.setAttribute(
325
+ "title",
326
+ "Average of (Affection + Playfulness + Intelligence + Empathy + Humor + Romance) / 6"
327
+ );
328
+ }
329
  applyTranslations();
330
  }
331
  }
332
 
333
+ // Delegated personality average computation (single source of truth in KimiEmotionSystem)
334
+ function computePersonalityAverage(traits) {
335
+ if (window.kimiEmotionSystem && typeof window.kimiEmotionSystem.calculatePersonalityAverage === "function") {
336
+ return Number(window.kimiEmotionSystem.calculatePersonalityAverage(traits).toFixed(2));
337
+ }
338
+ // Fallback minimal (should rarely occur before emotion system init)
339
+ const keys = ["affection", "playfulness", "intelligence", "empathy", "humor", "romance"];
340
+ let sum = 0,
341
+ count = 0;
342
+ for (const k of keys) {
343
+ const v = traits && traits[k];
344
+ if (typeof v === "number" && isFinite(v)) {
345
+ sum += Math.max(0, Math.min(100, v));
346
+ count++;
347
+ }
348
+ }
349
+ return count ? Number((sum / count).toFixed(2)) : 0;
350
+ }
351
+
352
+ // Update UI elements (bar + percentage text + label) based on overall personality average
353
+ async function updateGlobalPersonalityUI(characterKey = null) {
354
+ try {
355
+ const db = window.kimiDB;
356
+ if (!db) return;
357
+ const character = characterKey || (await db.getSelectedCharacter());
358
+ const traits = await db.getAllPersonalityTraits(character);
359
+ const avg = computePersonalityAverage(traits);
360
+ // Reuse existing favorability bar elements for global average
361
+ const bar = document.getElementById("favorability-bar");
362
+ const text = document.getElementById("favorability-text");
363
+ if (bar) bar.style.width = `${avg}%`;
364
+ if (text) text.textContent = `${avg.toFixed(2)}%`;
365
+ // Update label content if character provided
366
+ updateFavorabilityLabel(character);
367
+ } catch (e) {
368
+ console.warn("Failed to update global personality UI", e);
369
+ }
370
+ }
371
+ window.updateGlobalPersonalityUI = updateGlobalPersonalityUI;
372
+
373
  async function loadCharacterSection() {
374
  const kimiDB = window.kimiDB;
375
  if (!kimiDB) return;
 
870
  chatMessages.removeChild(chatMessages.firstChild);
871
  }
872
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
873
  if (kimiDB) {
874
  try {
875
  const recent = await kimiDB.getRecentConversations(10);
 
1593
  message = validation.sanitized || message.trim();
1594
  if (!message) return;
1595
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1596
  addMessageToChat("user", message);
1597
  chatInput.value = "";
1598
  if (waitingIndicator) waitingIndicator.style.display = "inline-block";
 
1755
  const voiceVolumeSlider = document.getElementById("voice-volume");
1756
  const languageSelect = document.getElementById("language-selection");
1757
  const voiceSelect = document.getElementById("voice-selection");
1758
+ // Affection restored as editable trait.
1759
  const traitSliders = [
1760
  "trait-affection",
1761
  "trait-playfulness",
 
1902
  personalityBatchTimeout = setTimeout(async () => {
1903
  if (kimiDB && Object.keys(pendingTraitChanges).length > 0) {
1904
  try {
1905
+ // Use batch operation for all pending changes (affection included)
1906
  await kimiDB.setPersonalityBatch(pendingTraitChanges);
1907
 
1908
  // Side-effects handled by central 'personality:updated' listener.
 
2255
  // Update memory cache
2256
  if (window.kimiMemory && updatedTraits.affection) {
2257
  window.kimiMemory.affectionTrait = updatedTraits.affection;
2258
+ if (window.updateGlobalPersonalityUI) {
2259
+ window.updateGlobalPersonalityUI();
2260
+ } else if (window.kimiMemory.updateFavorabilityBar) {
2261
+ // Fallback (will internally compute average now)
2262
  window.kimiMemory.updateFavorabilityBar();
2263
  }
2264
  }
kimi-js/kimi-personality-utils.js ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Central personality utilities (single source of truth wrappers)
2
+ // All calculations route through KimiEmotionSystem if available.
3
+ (function () {
4
+ function calcAverage(traits) {
5
+ if (window.kimiEmotionSystem && typeof window.kimiEmotionSystem.calculatePersonalityAverage === "function") {
6
+ return window.kimiEmotionSystem.calculatePersonalityAverage(traits || {});
7
+ }
8
+ const keys = ["affection", "playfulness", "intelligence", "empathy", "humor", "romance"];
9
+ let sum = 0,
10
+ c = 0;
11
+ for (const k of keys) {
12
+ const v = traits && traits[k];
13
+ if (typeof v === "number" && isFinite(v)) {
14
+ sum += Math.max(0, Math.min(100, v));
15
+ c++;
16
+ }
17
+ }
18
+ return c ? Number((sum / c).toFixed(2)) : 0;
19
+ }
20
+ /**
21
+ * @deprecated Call updateGlobalPersonalityUI() directly.
22
+ */
23
+ async function refreshUI(characterKey = null) {
24
+ if (window.updateGlobalPersonalityUI) {
25
+ return window.updateGlobalPersonalityUI(characterKey);
26
+ }
27
+ }
28
+ window.KimiPersonalityUtils = { calcAverage, refreshUI };
29
+ })();
kimi-js/kimi-script.js CHANGED
@@ -31,12 +31,13 @@ document.addEventListener("DOMContentLoaded", async function () {
31
  const selectedCharacter = await kimiDB.getPreference("selectedCharacter", "kimi");
32
  const favorabilityLabel = window.KimiDOMUtils.get("#favorability-label");
33
  if (favorabilityLabel && window.KIMI_CHARACTERS && window.KIMI_CHARACTERS[selectedCharacter]) {
34
- favorabilityLabel.setAttribute("data-i18n", "affection_level_of");
 
35
  favorabilityLabel.setAttribute(
36
  "data-i18n-params",
37
  JSON.stringify({ name: window.KIMI_CHARACTERS[selectedCharacter].name })
38
  );
39
- favorabilityLabel.textContent = `💖 Affection level of ${window.KIMI_CHARACTERS[selectedCharacter].name}`;
40
  }
41
  const chatHeaderName = window.KimiDOMUtils.get(".chat-header span[data-i18n]");
42
  if (chatHeaderName && window.KIMI_CHARACTERS && window.KIMI_CHARACTERS[selectedCharacter]) {
@@ -58,24 +59,6 @@ document.addEventListener("DOMContentLoaded", async function () {
58
  await kimiMemory.init();
59
  window.kimiMemory = kimiMemory;
60
 
61
- // Setup BroadcastChannel to announce conversation clears across tabs (very small, optional)
62
- try {
63
- if (typeof BroadcastChannel !== "undefined") {
64
- window.kimiBroadcast = new BroadcastChannel("kimi-db-events");
65
- window.kimiBroadcast.addEventListener("message", ev => {
66
- try {
67
- const data = ev.data || {};
68
- if (data && data.type === "conversations:cleared" && data.character) {
69
- window.kimiConversationsClearedAt = window.kimiConversationsClearedAt || {};
70
- window.kimiConversationsClearedAt[data.character] = data.timestamp || new Date().toISOString();
71
- }
72
- } catch (e) {
73
- // ignore
74
- }
75
- });
76
- }
77
- } catch (e) {}
78
-
79
  // Expose globally (already set before init)
80
 
81
  // Load available models now that LLM is ready
@@ -86,6 +69,12 @@ document.addEventListener("DOMContentLoaded", async function () {
86
  isSystemReady = true;
87
  window.isSystemReady = true;
88
  // API config UI will be initialized after ApiUi is defined
 
 
 
 
 
 
89
  if (window.refreshAllSliders) {
90
  try {
91
  await window.refreshAllSliders();
@@ -427,82 +416,67 @@ document.addEventListener("DOMContentLoaded", async function () {
427
  async function attachCharacterSection() {
428
  let saveCharacterBtn = window.KimiDOMUtils.get("#save-character-btn");
429
  if (saveCharacterBtn) {
430
- // Prevent attaching the same listener multiple times
431
- if (saveCharacterBtn.dataset.kimiSaveListenerAttached === "1") {
432
- // already attached
433
- } else {
434
- saveCharacterBtn.dataset.kimiSaveListenerAttached = "1";
435
- saveCharacterBtn.addEventListener("click", async e => {
436
- const settingsPanel = window.KimiDOMUtils.get(".settings-panel");
437
- let scrollTop = settingsPanel ? settingsPanel.scrollTop : null;
438
- const characterGrid = window.KimiDOMUtils.get("#character-grid");
439
- const selectedCard = characterGrid ? characterGrid.querySelector(".character-card.selected") : null;
440
- if (!selectedCard) return;
441
- const charKey = selectedCard.dataset.character;
442
- // Character save should not toggle the API key saved indicator.
443
- const promptInput = window.KimiDOMUtils.get(`#prompt-${charKey}`);
444
- const prompt = promptInput ? promptInput.value : "";
445
-
446
- await window.kimiDB.setSelectedCharacter(charKey);
447
- await window.kimiDB.setSystemPromptForCharacter(charKey, prompt);
448
- // Ensure memory system uses the correct character
449
- if (window.kimiMemorySystem) {
450
- window.kimiMemorySystem.selectedCharacter = charKey;
451
- }
452
- if (window.kimiVideo && window.kimiVideo.setCharacter) {
453
- window.kimiVideo.setCharacter(charKey);
454
- if (window.kimiVideo.switchToContext) {
455
- window.kimiVideo.switchToContext("neutral");
456
- }
457
- }
458
- if (window.voiceManager && window.voiceManager.updateSelectedCharacter) {
459
- await window.voiceManager.updateSelectedCharacter();
460
  }
 
 
 
 
461
 
462
- await window.loadCharacterSection();
463
- if (settingsPanel && scrollTop !== null) {
464
- requestAnimationFrame(() => {
465
- settingsPanel.scrollTop = scrollTop;
466
- });
467
- }
468
- // Refresh memory tab after character selection
469
- if (window.kimiMemoryUI && typeof window.kimiMemoryUI.updateMemoryStats === "function") {
470
- await window.kimiMemoryUI.updateMemoryStats();
471
- }
472
- // Refresh chat history immediately so the chat-messages container shows the selected character's history
473
- if (typeof window.loadChatHistory === "function") {
474
- try {
475
- await window.loadChatHistory();
476
- } catch (e) {
477
- console.warn("Failed to refresh chat history after character change:", e);
478
- }
 
 
 
 
 
 
 
 
 
 
479
  }
480
- saveCharacterBtn.setAttribute("data-i18n", "saved");
481
- saveCharacterBtn.classList.add("success");
482
- saveCharacterBtn.disabled = true;
483
-
484
- setTimeout(() => {
485
- saveCharacterBtn.setAttribute("data-i18n", "save");
486
- saveCharacterBtn.classList.remove("success");
487
- saveCharacterBtn.disabled = false;
488
- }, 900);
489
- // Also reload the page shortly after restoring the button so the user sees feedback
490
- setTimeout(() => {
491
- try {
492
- location.reload();
493
- } catch (e) {
494
- console.warn("Failed to reload after save feedback:", e);
495
- }
496
- }, 1100);
497
- });
498
- }
499
  }
500
  let settingsButton2 = window.KimiDOMUtils.get("#settings-button");
501
  if (settingsButton2) {
502
- if (!settingsButton2.dataset.kimiSettingsListenerAttached) {
503
- settingsButton2.dataset.kimiSettingsListenerAttached = "1";
504
- settingsButton2.addEventListener("click", window.loadCharacterSection);
505
- }
506
  }
507
  }
508
  await attachCharacterSection();
@@ -944,29 +918,7 @@ document.addEventListener("DOMContentLoaded", async function () {
944
  }
945
  if (window.kimiDB && window.kimiDB.db) {
946
  try {
947
- const selectedCharacter = await window.kimiDB.getSelectedCharacter();
948
- // Mark cleared timestamp per-character to prevent pending async saves from re-adding old messages
949
- window.kimiConversationsClearedAt = window.kimiConversationsClearedAt || {};
950
- const ts = new Date().toISOString();
951
- window.kimiConversationsClearedAt[selectedCharacter] = ts;
952
- await window.kimiDB.clearConversations(selectedCharacter);
953
- // Broadcast the clear to other tabs so they set their cleared markers too
954
- try {
955
- if (window.kimiBroadcast && typeof window.kimiBroadcast.postMessage === "function") {
956
- window.kimiBroadcast.postMessage({
957
- type: "conversations:cleared",
958
- character: selectedCharacter,
959
- timestamp: ts
960
- });
961
- }
962
- } catch (e) {}
963
- // Clear the cleared marker for this character shortly after to allow new conversations again
964
- setTimeout(() => {
965
- try {
966
- if (window.kimiConversationsClearedAt)
967
- delete window.kimiConversationsClearedAt[selectedCharacter];
968
- } catch (e) {}
969
- }, 5000);
970
  } catch (error) {
971
  console.error("Error deleting conversations:", error);
972
  }
@@ -1016,7 +968,7 @@ document.addEventListener("DOMContentLoaded", async function () {
1016
  if (personalityPayload) {
1017
  const { character, traits } = personalityPayload;
1018
  const defaults = (window.getTraitDefaults && window.getTraitDefaults()) || {
1019
- affection: 55, // Lowered to match emotion system defaults
1020
  romance: 50,
1021
  empathy: 75,
1022
  playfulness: 55,
@@ -1292,6 +1244,11 @@ document.addEventListener("DOMContentLoaded", async function () {
1292
  } catch {
1293
  } finally {
1294
  lastTraits = {};
 
 
 
 
 
1295
  }
1296
  }, 120); // small debounce
1297
  });
 
31
  const selectedCharacter = await kimiDB.getPreference("selectedCharacter", "kimi");
32
  const favorabilityLabel = window.KimiDOMUtils.get("#favorability-label");
33
  if (favorabilityLabel && window.KIMI_CHARACTERS && window.KIMI_CHARACTERS[selectedCharacter]) {
34
+ favorabilityLabel.removeAttribute("for");
35
+ favorabilityLabel.setAttribute("data-i18n", "personality_average_of");
36
  favorabilityLabel.setAttribute(
37
  "data-i18n-params",
38
  JSON.stringify({ name: window.KIMI_CHARACTERS[selectedCharacter].name })
39
  );
40
+ favorabilityLabel.textContent = `💖 Personality average of ${window.KIMI_CHARACTERS[selectedCharacter].name}`;
41
  }
42
  const chatHeaderName = window.KimiDOMUtils.get(".chat-header span[data-i18n]");
43
  if (chatHeaderName && window.KIMI_CHARACTERS && window.KIMI_CHARACTERS[selectedCharacter]) {
 
59
  await kimiMemory.init();
60
  window.kimiMemory = kimiMemory;
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  // Expose globally (already set before init)
63
 
64
  // Load available models now that LLM is ready
 
69
  isSystemReady = true;
70
  window.isSystemReady = true;
71
  // API config UI will be initialized after ApiUi is defined
72
+ // Update global personality average UI once initial traits are loaded
73
+ if (window.updateGlobalPersonalityUI) {
74
+ try {
75
+ await window.updateGlobalPersonalityUI(selectedCharacter);
76
+ } catch {}
77
+ }
78
  if (window.refreshAllSliders) {
79
  try {
80
  await window.refreshAllSliders();
 
416
  async function attachCharacterSection() {
417
  let saveCharacterBtn = window.KimiDOMUtils.get("#save-character-btn");
418
  if (saveCharacterBtn) {
419
+ saveCharacterBtn.addEventListener("click", async e => {
420
+ const settingsPanel = window.KimiDOMUtils.get(".settings-panel");
421
+ let scrollTop = settingsPanel ? settingsPanel.scrollTop : null;
422
+ const characterGrid = window.KimiDOMUtils.get("#character-grid");
423
+ const selectedCard = characterGrid ? characterGrid.querySelector(".character-card.selected") : null;
424
+ if (!selectedCard) return;
425
+ const charKey = selectedCard.dataset.character;
426
+ // Character save should not toggle the API key saved indicator.
427
+ const promptInput = window.KimiDOMUtils.get(`#prompt-${charKey}`);
428
+ const prompt = promptInput ? promptInput.value : "";
429
+
430
+ await window.kimiDB.setSelectedCharacter(charKey);
431
+ await window.kimiDB.setSystemPromptForCharacter(charKey, prompt);
432
+ // Ensure memory system uses the correct character
433
+ if (window.kimiMemorySystem) {
434
+ window.kimiMemorySystem.selectedCharacter = charKey;
435
+ }
436
+ if (window.kimiVideo && window.kimiVideo.setCharacter) {
437
+ window.kimiVideo.setCharacter(charKey);
438
+ if (window.kimiVideo.switchToContext) {
439
+ window.kimiVideo.switchToContext("neutral");
 
 
 
 
 
 
 
 
 
440
  }
441
+ }
442
+ if (window.voiceManager && window.voiceManager.updateSelectedCharacter) {
443
+ await window.voiceManager.updateSelectedCharacter();
444
+ }
445
 
446
+ await window.loadCharacterSection();
447
+ if (settingsPanel && scrollTop !== null) {
448
+ requestAnimationFrame(() => {
449
+ settingsPanel.scrollTop = scrollTop;
450
+ });
451
+ }
452
+ // Refresh memory tab after character selection
453
+ if (window.kimiMemoryUI && typeof window.kimiMemoryUI.updateMemoryStats === "function") {
454
+ await window.kimiMemoryUI.updateMemoryStats();
455
+ }
456
+ saveCharacterBtn.setAttribute("data-i18n", "saved");
457
+ saveCharacterBtn.classList.add("success");
458
+ saveCharacterBtn.disabled = true;
459
+
460
+ setTimeout(() => {
461
+ saveCharacterBtn.setAttribute("data-i18n", "save");
462
+ saveCharacterBtn.classList.remove("success");
463
+ saveCharacterBtn.disabled = false;
464
+ }, 1000);
465
+
466
+ // Force full UI refresh to ensure all character-specific modules reinitialize.
467
+ // Full page refresh to reinitialize all character-dependent modules.
468
+ setTimeout(() => {
469
+ try {
470
+ window.location.reload();
471
+ } catch (e) {
472
+ console.warn("Page reload failed", e);
473
  }
474
+ }, 1200); // slightly after button reset to allow visual feedback
475
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  }
477
  let settingsButton2 = window.KimiDOMUtils.get("#settings-button");
478
  if (settingsButton2) {
479
+ settingsButton2.addEventListener("click", window.loadCharacterSection);
 
 
 
480
  }
481
  }
482
  await attachCharacterSection();
 
918
  }
919
  if (window.kimiDB && window.kimiDB.db) {
920
  try {
921
+ await window.kimiDB.db.conversations.clear();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
922
  } catch (error) {
923
  console.error("Error deleting conversations:", error);
924
  }
 
968
  if (personalityPayload) {
969
  const { character, traits } = personalityPayload;
970
  const defaults = (window.getTraitDefaults && window.getTraitDefaults()) || {
971
+ affection: 55, // Baseline affection default
972
  romance: 50,
973
  empathy: 75,
974
  playfulness: 55,
 
1244
  } catch {
1245
  } finally {
1246
  lastTraits = {};
1247
+ if (window.updateGlobalPersonalityUI) {
1248
+ try {
1249
+ await window.updateGlobalPersonalityUI();
1250
+ } catch {}
1251
+ }
1252
  }
1253
  }, 120); // small debounce
1254
  });
kimi-js/kimi-utils.js CHANGED
@@ -1017,17 +1017,17 @@ class KimiVideoManager {
1017
  let weights = candidateVideos.map(video => {
1018
  if (category === "speakingPositive") {
1019
  // Positive videos favored by affection, romance, and humor
1020
- const base = 1 + (affection / 100) * 0.4; // Increased from 0.3
1021
  let bonus = 0;
1022
  const rom = typeof traits.romance === "number" ? traits.romance : 50;
1023
  const hum = typeof traits.humor === "number" ? traits.humor : 50;
1024
- if (emotion === "romantic") bonus += (rom / 100) * 0.3; // Increased from 0.2
1025
- if (emotion === "laughing") bonus += (hum / 100) * 0.3; // Increased from 0.2
1026
  return base + bonus;
1027
  }
1028
  if (category === "speakingNegative") {
1029
  // Negative videos when affection is low (reduced weight to balance)
1030
- return 1 + ((100 - affection) / 100) * 0.3; // Reduced from 0.4
1031
  }
1032
  if (category === "neutral") {
1033
  // Neutral videos when affection is moderate, also influenced by intelligence
@@ -2295,13 +2295,23 @@ class KimiUIStateManager {
2295
  this.state.activeTab = tabName;
2296
  if (this.tabManager) this.tabManager.activateTab(tabName);
2297
  }
2298
- setFavorability(value) {
 
 
 
 
2299
  const v = Number(value) || 0;
2300
  const clamped = Math.max(0, Math.min(100, v));
2301
  this.state.favorability = clamped;
2302
  window.KimiDOMUtils.setText("#favorability-text", `${clamped.toFixed(2)}%`);
2303
  window.KimiDOMUtils.get("#favorability-bar").style.width = `${clamped}%`;
2304
  }
 
 
 
 
 
 
2305
  async setTranscript(text) {
2306
  this.state.transcript = text;
2307
  // Always use the proper transcript management via VoiceManager
 
1017
  let weights = candidateVideos.map(video => {
1018
  if (category === "speakingPositive") {
1019
  // Positive videos favored by affection, romance, and humor
1020
+ const base = 1 + (affection / 100) * 0.4; // Affection influence factor
1021
  let bonus = 0;
1022
  const rom = typeof traits.romance === "number" ? traits.romance : 50;
1023
  const hum = typeof traits.humor === "number" ? traits.humor : 50;
1024
+ if (emotion === "romantic") bonus += (rom / 100) * 0.3; // Romance context bonus
1025
+ if (emotion === "laughing") bonus += (hum / 100) * 0.3; // Humor context bonus
1026
  return base + bonus;
1027
  }
1028
  if (category === "speakingNegative") {
1029
  // Negative videos when affection is low (reduced weight to balance)
1030
+ return 1 + ((100 - affection) / 100) * 0.3; // Low-affection influence factor
1031
  }
1032
  if (category === "neutral") {
1033
  // Neutral videos when affection is moderate, also influenced by intelligence
 
2295
  this.state.activeTab = tabName;
2296
  if (this.tabManager) this.tabManager.activateTab(tabName);
2297
  }
2298
+ /**
2299
+ * @deprecated Prefer calling updateGlobalPersonalityUI() after updating traits.
2300
+ * This direct setter will be removed in a future cleanup.
2301
+ */
2302
+ setPersonalityAverage(value) {
2303
  const v = Number(value) || 0;
2304
  const clamped = Math.max(0, Math.min(100, v));
2305
  this.state.favorability = clamped;
2306
  window.KimiDOMUtils.setText("#favorability-text", `${clamped.toFixed(2)}%`);
2307
  window.KimiDOMUtils.get("#favorability-bar").style.width = `${clamped}%`;
2308
  }
2309
+ /**
2310
+ * @deprecated Use setPersonalityAverage() (itself deprecated) or updateGlobalPersonalityUI().
2311
+ */
2312
+ setFavorability(value) {
2313
+ this.setPersonalityAverage(value);
2314
+ }
2315
  async setTranscript(text) {
2316
  this.state.transcript = text;
2317
  // Always use the proper transcript management via VoiceManager
kimi-js/kimi-voices.js CHANGED
@@ -208,16 +208,16 @@ class KimiVoiceManager {
208
  return;
209
  }
210
 
211
- // Only get language from DB if not already set
212
  if (!this.selectedLanguage) {
213
  const selectedLanguage = await this.db?.getPreference("selectedLanguage", "en");
214
- // Normalize legacy formats (en-US, en_US, us:en -> en) using shared util
215
  this.selectedLanguage = window.KimiLanguageUtils.normalizeLanguageCode(selectedLanguage || "en") || "en";
216
  }
 
217
 
218
  const savedVoice = await this.db?.getPreference("selectedVoice", "auto");
219
 
220
- const filteredVoices = this.getVoicesForLanguage(this.selectedLanguage);
221
 
222
  if (savedVoice && savedVoice !== "auto") {
223
  // Only search within language-compatible voices
@@ -225,7 +225,7 @@ class KimiVoiceManager {
225
  if (foundVoice) {
226
  this.currentVoice = foundVoice;
227
  console.log(
228
- `🎤 Voice restored from cache: "${foundVoice.name}" (${foundVoice.lang}) for language "${this.selectedLanguage}"`
229
  );
230
  this.updateVoiceSelector();
231
  this._initializingVoices = false;
@@ -233,7 +233,7 @@ class KimiVoiceManager {
233
  } else {
234
  // Saved voice not compatible with current language, fall back to auto-selection
235
  console.log(
236
- `🎤 Saved voice "${savedVoice}" not compatible with language "${this.selectedLanguage}", using auto-selection`
237
  );
238
  await this.db?.setPreference("selectedVoice", "auto");
239
  }
@@ -333,7 +333,7 @@ class KimiVoiceManager {
333
  // Debug: Show what voices are available and why they don't match
334
  if (filteredVoices.length > 0 && filteredVoices.length <= 5) {
335
  console.log(
336
- `🎤 Available voices for ${this.selectedLanguage}:`,
337
  filteredVoices.map(v => ({
338
  name: v.name,
339
  lang: v.lang,
@@ -362,7 +362,7 @@ class KimiVoiceManager {
362
  } else {
363
  // Log successful voice selection with language info
364
  console.log(
365
- `🎤 Voice loaded: "${this.currentVoice.name}" (${this.currentVoice.lang}) for language "${this.selectedLanguage}"`
366
  );
367
  }
368
 
@@ -861,16 +861,26 @@ class KimiVoiceManager {
861
  }
862
 
863
  // ===== SPEECH RECOGNITION =====
864
- setupSpeechRecognition() {
865
  if (!this.SpeechRecognition) {
866
  // Do not show a UI message during initial load; only log.
867
  console.log("Your browser does not support speech recognition.");
868
  return;
869
  }
 
870
  this.recognition = new this.SpeechRecognition();
871
  this.recognition.continuous = true;
872
- const langCode = this.getLanguageCode(this.selectedLanguage || "en");
873
- this.recognition.lang = langCode;
 
 
 
 
 
 
 
 
 
874
  this.recognition.interimResults = true;
875
 
876
  // Add onstart handler to confirm permission
@@ -1048,9 +1058,9 @@ class KimiVoiceManager {
1048
  return;
1049
  }
1050
 
1051
- // If permission was previously granted, start directly
1052
  if (this.micPermissionGranted) {
1053
- console.log("🎤 Using previously granted microphone permission");
1054
  this.startRecognitionDirectly();
1055
  return;
1056
  }
@@ -1223,19 +1233,30 @@ class KimiVoiceManager {
1223
  } // Helper to modulate emotion based on personality traits
1224
  _modulateEmotionByPersonality(emotion) {
1225
  try {
 
1226
  let avg = 50;
1227
- if (this.memory && typeof this.memory.affectionTrait === "number") {
1228
- avg = this.memory.affectionTrait;
1229
- }
1230
-
1231
- // Low affection makes emotions more subdued
1232
- if (avg <= 20 && emotion !== "neutral") {
1233
- return "shy";
1234
- }
1235
- if (avg <= 40 && emotion === "positive") {
1236
- return "shy";
 
 
 
 
 
 
1237
  }
1238
 
 
 
 
 
1239
  return emotion;
1240
  } catch (e) {
1241
  return emotion;
@@ -1362,10 +1383,56 @@ class KimiVoiceManager {
1362
  console.warn(`🎤 No voice found for language "${newLang}"`);
1363
  }
1364
 
 
 
 
 
 
 
 
 
 
 
 
1365
  if (this.recognition) {
1366
- const langCode = this.getLanguageCode(newLang);
1367
- this.recognition.lang = langCode;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1368
  }
 
 
 
1369
  }
1370
 
1371
  async updateSelectedCharacter() {
 
208
  return;
209
  }
210
 
211
+ // Resolve effective selectedLanguage if missing
212
  if (!this.selectedLanguage) {
213
  const selectedLanguage = await this.db?.getPreference("selectedLanguage", "en");
 
214
  this.selectedLanguage = window.KimiLanguageUtils.normalizeLanguageCode(selectedLanguage || "en") || "en";
215
  }
216
+ const effectiveLang = await this.getEffectiveLanguage(this.selectedLanguage);
217
 
218
  const savedVoice = await this.db?.getPreference("selectedVoice", "auto");
219
 
220
+ const filteredVoices = this.getVoicesForLanguage(effectiveLang);
221
 
222
  if (savedVoice && savedVoice !== "auto") {
223
  // Only search within language-compatible voices
 
225
  if (foundVoice) {
226
  this.currentVoice = foundVoice;
227
  console.log(
228
+ `🎤 Voice restored from cache: "${foundVoice.name}" (${foundVoice.lang}) for language "${effectiveLang}"`
229
  );
230
  this.updateVoiceSelector();
231
  this._initializingVoices = false;
 
233
  } else {
234
  // Saved voice not compatible with current language, fall back to auto-selection
235
  console.log(
236
+ `🎤 Saved voice "${savedVoice}" not compatible with language "${effectiveLang}", using auto-selection`
237
  );
238
  await this.db?.setPreference("selectedVoice", "auto");
239
  }
 
333
  // Debug: Show what voices are available and why they don't match
334
  if (filteredVoices.length > 0 && filteredVoices.length <= 5) {
335
  console.log(
336
+ `🎤 Available voices for ${effectiveLang}:`,
337
  filteredVoices.map(v => ({
338
  name: v.name,
339
  lang: v.lang,
 
362
  } else {
363
  // Log successful voice selection with language info
364
  console.log(
365
+ `🎤 Voice loaded: "${this.currentVoice.name}" (${this.currentVoice.lang}) for language "${effectiveLang}"`
366
  );
367
  }
368
 
 
861
  }
862
 
863
  // ===== SPEECH RECOGNITION =====
864
+ async setupSpeechRecognition() {
865
  if (!this.SpeechRecognition) {
866
  // Do not show a UI message during initial load; only log.
867
  console.log("Your browser does not support speech recognition.");
868
  return;
869
  }
870
+ // Always create a fresh instance (some browsers cache language at construction time)
871
  this.recognition = new this.SpeechRecognition();
872
  this.recognition.continuous = true;
873
+
874
+ // Resolve effective language (block invalid 'auto')
875
+ const normalized = await this.getEffectiveLanguage(this.selectedLanguage);
876
+ const langCode = this.getLanguageCode(normalized || "en");
877
+ try {
878
+ this.recognition.lang = langCode;
879
+ } catch (e) {
880
+ console.warn("Could not set recognition.lang, fallback en-US", e);
881
+ this.recognition.lang = "en-US";
882
+ }
883
+ console.log(`🎤 SpeechRecognition initialized (lang=${this.recognition.lang})`);
884
  this.recognition.interimResults = true;
885
 
886
  // Add onstart handler to confirm permission
 
1058
  return;
1059
  }
1060
 
1061
+ // If microphone permission already granted, start directly
1062
  if (this.micPermissionGranted) {
1063
+ console.log("🎤 Microphone permission already granted");
1064
  this.startRecognitionDirectly();
1065
  return;
1066
  }
 
1233
  } // Helper to modulate emotion based on personality traits
1234
  _modulateEmotionByPersonality(emotion) {
1235
  try {
1236
+ // Obtain full traits if possible for a more robust modulation
1237
  let avg = 50;
1238
+ if (window.kimiEmotionSystem && window.kimiEmotionSystem.db) {
1239
+ // Attempt synchronous-like cache via memory first
1240
+ const traits = {
1241
+ affection: this.memory?.affectionTrait,
1242
+ playfulness: this.memory?.playfulnessTrait,
1243
+ intelligence: this.memory?.intelligenceTrait,
1244
+ empathy: this.memory?.empathyTrait,
1245
+ humor: this.memory?.humorTrait,
1246
+ romance: this.memory?.romanceTrait
1247
+ };
1248
+ // If at least affection present, compute average using emotion system helper
1249
+ if (typeof traits.affection === "number") {
1250
+ avg = window.kimiEmotionSystem.calculatePersonalityAverage(traits);
1251
+ }
1252
+ } else if (this.memory && typeof this.memory.affectionTrait === "number") {
1253
+ avg = this.memory.affectionTrait; // fallback
1254
  }
1255
 
1256
+ // Weighted interpretation: very low affection still softens positive expression
1257
+ // If overall avg low, dampen by shifting to 'shy'.
1258
+ if (avg <= 20 && emotion !== "neutral") return "shy";
1259
+ if (avg <= 40 && emotion === "positive") return "shy";
1260
  return emotion;
1261
  } catch (e) {
1262
  return emotion;
 
1383
  console.warn(`🎤 No voice found for language "${newLang}"`);
1384
  }
1385
 
1386
+ // Update recognition language safely (recreate instance to avoid stale internal state)
1387
+ this._refreshRecognitionLanguage(newLang);
1388
+ }
1389
+
1390
+ /**
1391
+ * Recreate speech recognition instance with a new language.
1392
+ * Some browsers (notably Chrome) may ignore lang changes mid-session; recreating ensures consistency.
1393
+ */
1394
+ async _refreshRecognitionLanguage(newLang) {
1395
+ if (!this.SpeechRecognition) return;
1396
+ const wasListening = this.isListening;
1397
  if (this.recognition) {
1398
+ try {
1399
+ if (this.isListening) this.recognition.stop();
1400
+ } catch {}
1401
+ this.recognition.onresult = null;
1402
+ this.recognition.onstart = null;
1403
+ this.recognition.onend = null;
1404
+ this.recognition.onerror = null;
1405
+ this.recognition = null;
1406
+ }
1407
+ this.selectedLanguage = newLang;
1408
+ await this.setupSpeechRecognition();
1409
+ console.log(`🎤 Recognition language refreshed -> ${this.recognition?.lang}`);
1410
+ // Restart listening if it was active
1411
+ if (wasListening) {
1412
+ // Small delay to allow new instance to settle
1413
+ setTimeout(() => {
1414
+ this.startListening();
1415
+ }, 150);
1416
+ }
1417
+ }
1418
+
1419
+ // Return a normalized concrete language code (primary subtag) never 'auto'
1420
+ async getEffectiveLanguage(raw) {
1421
+ let base = raw || this.selectedLanguage || "en";
1422
+ if (base === "auto") {
1423
+ try {
1424
+ if (window.KimiLanguageUtils?.getLanguage) {
1425
+ base = await window.KimiLanguageUtils.getLanguage();
1426
+ } else {
1427
+ base = navigator.language?.split("-")[0] || "en";
1428
+ }
1429
+ } catch {
1430
+ base = "en";
1431
+ }
1432
  }
1433
+ return window.KimiLanguageUtils?.normalizeLanguageCode
1434
+ ? window.KimiLanguageUtils.normalizeLanguageCode(base)
1435
+ : base || "en";
1436
  }
1437
 
1438
  async updateSelectedCharacter() {
kimi-locale/de.json CHANGED
@@ -95,6 +95,7 @@
95
  "start_listening": "Zuhören Beginnen",
96
  "kimi_affection_level": "💖 Kimis Zuneigungsgrad",
97
  "affection_level_of": "💖 Zuneigungsgrad von {name}",
 
98
  "greeting_low": "Hallo.",
99
  "greeting_mid": "Hallo. Wie kann ich dir helfen?",
100
  "greeting_high": "Hallo mein Liebling! 💕",
 
95
  "start_listening": "Zuhören Beginnen",
96
  "kimi_affection_level": "💖 Kimis Zuneigungsgrad",
97
  "affection_level_of": "💖 Zuneigungsgrad von {name}",
98
+ "personality_average_of": "💖 Persönlichkeitsdurchschnitt von {name}",
99
  "greeting_low": "Hallo.",
100
  "greeting_mid": "Hallo. Wie kann ich dir helfen?",
101
  "greeting_high": "Hallo mein Liebling! 💕",
kimi-locale/en.json CHANGED
@@ -95,6 +95,7 @@
95
  "start_listening": "Start Listening",
96
  "kimi_affection_level": "💖 Kimi's Affection Level",
97
  "affection_level_of": "💖 Affection level of {name}",
 
98
  "greeting_low": "Hello.",
99
  "greeting_mid": "Hi. How can I help you?",
100
  "greeting_high": "Hello my love! 💕",
 
95
  "start_listening": "Start Listening",
96
  "kimi_affection_level": "💖 Kimi's Affection Level",
97
  "affection_level_of": "💖 Affection level of {name}",
98
+ "personality_average_of": "💖 Personality average of {name}",
99
  "greeting_low": "Hello.",
100
  "greeting_mid": "Hi. How can I help you?",
101
  "greeting_high": "Hello my love! 💕",
kimi-locale/es.json CHANGED
@@ -95,6 +95,7 @@
95
  "start_listening": "Comenzar a Escuchar",
96
  "kimi_affection_level": "💖 Nivel de Afecto de Kimi",
97
  "affection_level_of": "💖 Nivel de afecto de {name}",
 
98
  "greeting_low": "Hola.",
99
  "greeting_mid": "Hola. ¿Cómo puedo ayudarte?",
100
  "greeting_high": "¡Hola mi amor! 💕",
 
95
  "start_listening": "Comenzar a Escuchar",
96
  "kimi_affection_level": "💖 Nivel de Afecto de Kimi",
97
  "affection_level_of": "💖 Nivel de afecto de {name}",
98
+ "personality_average_of": "💖 Promedio de personalidad de {name}",
99
  "greeting_low": "Hola.",
100
  "greeting_mid": "Hola. ¿Cómo puedo ayudarte?",
101
  "greeting_high": "¡Hola mi amor! 💕",
kimi-locale/fr.json CHANGED
@@ -95,6 +95,7 @@
95
  "start_listening": "Commencer à écouter",
96
  "kimi_affection_level": "💖 Niveau d'affection de Kimi",
97
  "affection_level_of": "💖 Niveau d'affection de {name}",
 
98
  "greeting_low": "Bonjour.",
99
  "greeting_mid": "Bonjour. Comment puis-je vous aider ?",
100
  "greeting_high": "Salut mon amour ! 💕",
 
95
  "start_listening": "Commencer à écouter",
96
  "kimi_affection_level": "💖 Niveau d'affection de Kimi",
97
  "affection_level_of": "💖 Niveau d'affection de {name}",
98
+ "personality_average_of": "💖 Moyenne personnalité de {name}",
99
  "greeting_low": "Bonjour.",
100
  "greeting_mid": "Bonjour. Comment puis-je vous aider ?",
101
  "greeting_high": "Salut mon amour ! 💕",
kimi-locale/it.json CHANGED
@@ -95,6 +95,7 @@
95
  "start_listening": "Inizia ad Ascoltare",
96
  "kimi_affection_level": "💖 Livello di Affetto di Kimi",
97
  "affection_level_of": "💖 Livello di affetto di {name}",
 
98
  "greeting_low": "Ciao.",
99
  "greeting_mid": "Ciao. Come posso aiutarti?",
100
  "greeting_high": "Ciao amore mio! 💕",
 
95
  "start_listening": "Inizia ad Ascoltare",
96
  "kimi_affection_level": "💖 Livello di Affetto di Kimi",
97
  "affection_level_of": "💖 Livello di affetto di {name}",
98
+ "personality_average_of": "💖 Media personalità di {name}",
99
  "greeting_low": "Ciao.",
100
  "greeting_mid": "Ciao. Come posso aiutarti?",
101
  "greeting_high": "Ciao amore mio! 💕",
kimi-locale/ja.json CHANGED
@@ -95,6 +95,7 @@
95
  "start_listening": "聞き始める",
96
  "kimi_affection_level": "💖 Kimiの愛情レベル",
97
  "affection_level_of": "💖 {name}の愛情レベル",
 
98
  "greeting_low": "こんにちは。",
99
  "greeting_mid": "こんにちは。どのようにお手伝いできますか?",
100
  "greeting_high": "こんにちは、愛しい人! 💕",
 
95
  "start_listening": "聞き始める",
96
  "kimi_affection_level": "💖 Kimiの愛情レベル",
97
  "affection_level_of": "💖 {name}の愛情レベル",
98
+ "personality_average_of": "💖 {name}のパーソナリティ平均",
99
  "greeting_low": "こんにちは。",
100
  "greeting_mid": "こんにちは。どのようにお手伝いできますか?",
101
  "greeting_high": "こんにちは、愛しい人! 💕",
kimi-locale/zh.json CHANGED
@@ -95,6 +95,7 @@
95
  "start_listening": "开始聆听",
96
  "kimi_affection_level": "💖 Kimi的喜爱程度",
97
  "affection_level_of": "💖 {name}的喜爱程度",
 
98
  "greeting_low": "你好。",
99
  "greeting_mid": "你好。我可以帮助您什么?",
100
  "greeting_high": "你好,我的爱! 💕",
 
95
  "start_listening": "开始聆听",
96
  "kimi_affection_level": "💖 Kimi的喜爱程度",
97
  "affection_level_of": "💖 {name}的喜爱程度",
98
+ "personality_average_of": "💖 {name}的人格平均值",
99
  "greeting_low": "你好。",
100
  "greeting_mid": "你好。我可以帮助您什么?",
101
  "greeting_high": "你好,我的爱! 💕",