VirtualKimi commited on
Commit
b942e65
·
verified ·
1 Parent(s): 7679a56

Upload 33 files

Browse files
CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
  # Virtual Kimi App Changelog
2
 
 
 
 
 
 
 
 
 
 
 
3
  # [1.1.4.1] - 2025-09-03
4
 
5
  ### Bug Fixes
 
1
  # Virtual Kimi App Changelog
2
 
3
+ # [1.1.5] - 2025-09-03
4
+
5
+ ### Bug Fixes
6
+
7
+ - Fixed some issues.
8
+
9
+ ### Changed
10
+
11
+ - Separated the KimiDataManager class and moved logic into the new file `kimi-data-manager.js`.
12
+
13
  # [1.1.4.1] - 2025-09-03
14
 
15
  ### Bug Fixes
index.html CHANGED
@@ -37,29 +37,44 @@
37
  content="Virtual AI companion with evolving personality and advanced voice recognition.">
38
  <meta property="twitter:image" content="kimi-icons/virtualkimi-logo.png">
39
 
40
- <!-- Schema.org markup for Google -->
41
  <script type="application/ld+json">
42
- {
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": {
50
- "@type": "Offer",
51
- "price": "0",
52
- "priceCurrency": "USD"
53
- },
54
- "creator": {
55
- "@type": "Person",
56
- "name": "Jean & Kimi"
57
- },
58
- "dateCreated": "2025-07-16",
59
- "dateModified": "2025-09-03",
60
- "version": "v1.1.4.1"
61
- }
62
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
  <!-- Favicon -->
65
  <link rel="icon" type="image/x-icon" href="favicon.ico">
@@ -1072,7 +1087,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.4.1</p>
1076
  <p><strong>Last update :</strong> September 03, 2025</p>
1077
  <p><strong>Technologies :</strong> HTML5, CSS3, JavaScript ES6+, IndexedDB, Web Speech
1078
  API</p>
@@ -1083,10 +1098,16 @@
1083
  </div>
1084
  </div>
1085
  </div>
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>
@@ -1097,41 +1118,10 @@
1097
  <script type="module" src="kimi-js/kimi-constants.js"></script>
1098
  <script type="module" src="kimi-js/kimi-memory-ui.js"></script>
1099
  <script type="module" src="kimi-js/kimi-appearance.js"></script>
 
1100
  <script type="module" src="kimi-js/kimi-module.js"></script>
1101
  <script type="module" src="kimi-js/kimi-script.js"></script>
1102
  <script type="module" src="kimi-js/kimi-plugin-manager.js"></script>
1103
-
1104
- <!-- Schema.org JSON-LD for better SEO -->
1105
- <script type="application/ld+json">
1106
- {
1107
- "@context": "https://schema.org",
1108
- "@type": "WebPage",
1109
- "name": "Virtual Kimi - Virtual AI Companion",
1110
- "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.",
1111
- "url": "https://virtualkimi.com/virtual-kimi-app/index.html",
1112
- "mainEntity": {
1113
- "@type": "SoftwareApplication",
1114
- "name": "Virtual Kimi",
1115
- "applicationCategory": "AI Companion",
1116
- "operatingSystem": "Web Browser",
1117
- "description": "Virtual Kimi, a virtual AI girlfriend and companion with an evolving personality, multi-provider AI support, voice recognition and immersive interface",
1118
- "features": [
1119
- "Advanced voice recognition",
1120
- "Evolving personality with 6 adjustable traits",
1121
- "Premium LLM integration",
1122
- "5 customizable visual themes",
1123
- "Persistent memory",
1124
- "Intelligent affection system"
1125
- ],
1126
- "author": {
1127
- "@type": "Person",
1128
- "name": "Jean & Kimi"
1129
- },
1130
- "dateCreated": "2025-07-16",
1131
- "version": "v1.1.4.1"
1132
- }
1133
- }
1134
- </script>
1135
  </body>
1136
 
1137
  </html>
 
37
  content="Virtual AI companion with evolving personality and advanced voice recognition.">
38
  <meta property="twitter:image" content="kimi-icons/virtualkimi-logo.png">
39
 
40
+ <!-- Schema.org consolidated JSON-LD (WebPage + mainEntity SoftwareApplication) -->
41
  <script type="application/ld+json">
42
+ {
43
+ "@context": "https://schema.org",
44
+ "@type": "WebPage",
45
+ "name": "Virtual Kimi - Virtual AI Companion",
46
+ "description": "Virtual Kimi, your virtual AI girlfriend and companion with an evolving personality, multi-provider AI support, advanced voice recognition and immersive interface.",
47
+ "url": "https://virtualkimi.com/virtual-kimi-app/index.html",
48
+ "mainEntity": {
49
+ "@type": "SoftwareApplication",
50
+ "@id": "https://virtualkimi.com/virtual-kimi-app/#app",
51
+ "name": "Virtual Kimi",
52
+ "description": "Virtual Kimi, your virtual AI girlfriend and companion with an evolving personality, multi-provider AI support, voice recognition and immersive interface",
53
+ "applicationCategory": "AI Companion",
54
+ "operatingSystem": "Web Browser",
55
+ "offers": {
56
+ "@type": "Offer",
57
+ "price": "0",
58
+ "priceCurrency": "USD"
59
+ },
60
+ "creator": {
61
+ "@type": "Person",
62
+ "name": "Jean & Kimi"
63
+ },
64
+ "dateCreated": "2025-07-16",
65
+ "dateModified": "2025-09-03",
66
+ "version": "v1.1.5",
67
+ "features": [
68
+ "Advanced voice recognition",
69
+ "Evolving personality with 6 adjustable traits",
70
+ "Premium LLM integration",
71
+ "5 customizable visual themes",
72
+ "Persistent memory",
73
+ "Intelligent affection system"
74
+ ]
75
+ }
76
+ }
77
+ </script>
78
 
79
  <!-- Favicon -->
80
  <link rel="icon" type="image/x-icon" href="favicon.ico">
 
1087
  <h3><i class="fas fa-code"></i> Technical Information</h3>
1088
  <div class="tech-info">
1089
  <p><strong>Created date :</strong> July 16, 2025</p>
1090
+ <p><strong>Version :</strong> v1.1.5</p>
1091
  <p><strong>Last update :</strong> September 03, 2025</p>
1092
  <p><strong>Technologies :</strong> HTML5, CSS3, JavaScript ES6+, IndexedDB, Web Speech
1093
  API</p>
 
1098
  </div>
1099
  </div>
1100
  </div>
 
1101
 
1102
+ <!-- Script load order (critical):
1103
+ 1. Legacy globals (dexie, i18n) must load before modules needing them.
1104
+ 2. Base utilities (kimi-utils.js) define KimiBaseManager and helpers.
1105
+ 3. Managers that extend base (appearance, data) after utils.
1106
+ 4. Core module wiring (kimi-module.js) after managers so window.* hooks exist.
1107
+ 5. Initialization / orchestration (kimi-script.js) last.
1108
+ Keep this order when adding new managers. -->
1109
  <script src="dexie.min.js"></script>
1110
+ <script src="kimi-locale/i18n.js"></script>
1111
  <script type="module" src="kimi-js/kimi-personality-utils.js"></script>
1112
  <script type="module" src="kimi-js/kimi-utils.js"></script>
1113
  <script type="module" src="kimi-js/kimi-main.js"></script>
 
1118
  <script type="module" src="kimi-js/kimi-constants.js"></script>
1119
  <script type="module" src="kimi-js/kimi-memory-ui.js"></script>
1120
  <script type="module" src="kimi-js/kimi-appearance.js"></script>
1121
+ <script type="module" src="kimi-js/kimi-data-manager.js"></script>
1122
  <script type="module" src="kimi-js/kimi-module.js"></script>
1123
  <script type="module" src="kimi-js/kimi-script.js"></script>
1124
  <script type="module" src="kimi-js/kimi-plugin-manager.js"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1125
  </body>
1126
 
1127
  </html>
kimi-js/kimi-data-manager.js ADDED
@@ -0,0 +1,318 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // KIMI DATA MANAGER (extracted from kimi-module.js)
2
+ // This file contains only the KimiDataManager class and its global exposure.
3
+ // Depends on: KimiBaseManager (defined in kimi-utils.js) and DOM APIs.
4
+
5
+ class KimiDataManager extends KimiBaseManager {
6
+ constructor(database) {
7
+ super();
8
+ this.db = database;
9
+ }
10
+
11
+ async init() {
12
+ this.setupDataControls();
13
+ await this.updateStorageInfo();
14
+ }
15
+
16
+ setupDataControls() {
17
+ const exportButton = document.getElementById("export-data");
18
+ if (exportButton) {
19
+ exportButton.addEventListener("click", () => this.exportAllData());
20
+ }
21
+
22
+ const importButton = document.getElementById("import-data");
23
+ const importFile = document.getElementById("import-file");
24
+ if (importButton && importFile) {
25
+ importButton.addEventListener("click", () => importFile.click());
26
+ importFile.addEventListener("change", e => this.importData(e));
27
+ }
28
+
29
+ const cleanButton = document.getElementById("clean-old-data");
30
+ if (cleanButton) {
31
+ cleanButton.addEventListener("click", async () => {
32
+ if (!this.db) return;
33
+
34
+ const confirmClean = confirm(
35
+ "Delete all conversation messages?\n\n" +
36
+ "This will remove all chat history but keep your preferences and settings.\n\n" +
37
+ "This action cannot be undone."
38
+ );
39
+
40
+ if (!confirmClean) {
41
+ return;
42
+ }
43
+
44
+ try {
45
+ // Clear all conversations directly
46
+ await this.db.db.conversations.clear();
47
+
48
+ // Clear chat UI
49
+ const chatMessages = document.getElementById("chat-messages");
50
+ if (chatMessages) {
51
+ chatMessages.textContent = "";
52
+ }
53
+
54
+ // Reload chat history
55
+ if (typeof window.loadChatHistory === "function") {
56
+ window.loadChatHistory();
57
+ }
58
+
59
+ await this.updateStorageInfo();
60
+ alert("All conversation messages have been deleted successfully!");
61
+ } catch (error) {
62
+ console.error("Error cleaning conversations:", error);
63
+ alert("Error while cleaning conversations. Please try again.");
64
+ }
65
+ });
66
+ }
67
+
68
+ const resetButton = document.getElementById("reset-all-data");
69
+ if (resetButton) {
70
+ resetButton.addEventListener("click", () => this.resetAllData());
71
+ }
72
+ }
73
+
74
+ async exportAllData() {
75
+ if (!this.db) {
76
+ console.error("Database not available");
77
+ return;
78
+ }
79
+
80
+ try {
81
+ const conversations = await this.db.getAllConversations();
82
+ const preferencesObj = await this.db.getAllPreferences();
83
+ // Export preferences as an array of {key,value} so export is directly re-importable
84
+ const preferences = Array.isArray(preferencesObj)
85
+ ? preferencesObj
86
+ : Object.keys(preferencesObj).map(k => ({ key: k, value: preferencesObj[k] }));
87
+ const personalityTraits = await this.db.getAllPersonalityTraits();
88
+ const models = await this.db.getAllLLMModels();
89
+ const memories = await this.db.getAllMemories();
90
+
91
+ const exportData = {
92
+ version: "1.0",
93
+ exportDate: new Date().toISOString(),
94
+ conversations: conversations,
95
+ preferences: preferences,
96
+ personalityTraits: personalityTraits,
97
+ models: models,
98
+ memories: memories,
99
+ metadata: {
100
+ totalConversations: conversations.length,
101
+ totalPreferences: Object.keys(preferences).length,
102
+ totalTraits: Object.keys(personalityTraits).length,
103
+ totalModels: models.length,
104
+ totalMemories: memories.length
105
+ }
106
+ };
107
+
108
+ const dataStr = JSON.stringify(exportData, null, 2);
109
+ const dataBlob = new Blob([dataStr], { type: "application/json" });
110
+
111
+ const url = URL.createObjectURL(dataBlob);
112
+ const a = document.createElement("a");
113
+ a.href = url;
114
+ a.download = `kimi-backup-${new Date().toISOString().split("T")[0]}.json`;
115
+ document.body.appendChild(a);
116
+ a.click();
117
+ document.body.removeChild(a);
118
+ URL.revokeObjectURL(url);
119
+ } catch (error) {
120
+ console.error("Error during export:", error);
121
+ }
122
+ }
123
+
124
+ async importData(event) {
125
+ const file = event.target.files[0];
126
+ if (!file) {
127
+ alert("No file selected.");
128
+ return;
129
+ }
130
+ const reader = new FileReader();
131
+ reader.onload = async e => {
132
+ try {
133
+ const data = JSON.parse(e.target.result);
134
+ try {
135
+ console.log("Import file keys:", Object.keys(data));
136
+ } catch (ex) {}
137
+
138
+ if (data.preferences) {
139
+ try {
140
+ const isArray = Array.isArray(data.preferences);
141
+ const len = isArray ? data.preferences.length : Object.keys(data.preferences).length;
142
+ console.log("Import: preferences type=", isArray ? "array" : "object", "length=", len);
143
+ } catch (ex) {}
144
+ await this.db.setPreferencesBatch(data.preferences);
145
+ } else {
146
+ console.log("Import: no preferences found");
147
+ }
148
+
149
+ if (data.conversations) {
150
+ try {
151
+ console.log(
152
+ "Import: conversations length=",
153
+ Array.isArray(data.conversations) ? data.conversations.length : "not-array"
154
+ );
155
+ } catch (ex) {}
156
+ await this.db.setConversationsBatch(data.conversations);
157
+ } else {
158
+ console.log("Import: no conversations found");
159
+ }
160
+
161
+ if (data.personalityTraits) {
162
+ try {
163
+ console.log("Import: personalityTraits type=", typeof data.personalityTraits);
164
+ } catch (ex) {}
165
+ await this.db.setPersonalityBatch(data.personalityTraits);
166
+ } else {
167
+ console.log("Import: no personalityTraits found");
168
+ }
169
+
170
+ if (data.models) {
171
+ try {
172
+ console.log("Import: models length=", Array.isArray(data.models) ? data.models.length : "not-array");
173
+ } catch (ex) {}
174
+ await this.db.setLLMModelsBatch(data.models);
175
+ } else {
176
+ console.log("Import: no models found");
177
+ }
178
+
179
+ if (data.memories) {
180
+ try {
181
+ console.log(
182
+ "Import: memories length=",
183
+ Array.isArray(data.memories) ? data.memories.length : "not-array"
184
+ );
185
+ } catch (ex) {}
186
+ await this.db.setAllMemories(data.memories);
187
+ } else {
188
+ console.log("Import: no memories found");
189
+ }
190
+
191
+ alert("Import successful!");
192
+ await this.updateStorageInfo();
193
+
194
+ // Reload the page to ensure all UI state is rebuilt from the newly imported DB
195
+ setTimeout(() => {
196
+ location.reload();
197
+ }, 200);
198
+ } catch (err) {
199
+ console.error("Import failed:", err);
200
+ alert("Import failed. Invalid file or format.");
201
+ }
202
+ };
203
+ reader.readAsText(file);
204
+ }
205
+
206
+ async cleanOldData() {
207
+ if (!this.db) {
208
+ console.error("Database not available");
209
+ return;
210
+ }
211
+
212
+ const confirmClean = confirm("Do you want to delete ALL conversations?\n\nThis action is irreversible!");
213
+ if (!confirmClean) {
214
+ return;
215
+ }
216
+
217
+ try {
218
+ // Centralized: use kimi-database.js cleanOldConversations for all deletion logic
219
+ await this.db.cleanOldConversations();
220
+
221
+ if (typeof window.loadChatHistory === "function") {
222
+ window.loadChatHistory();
223
+ }
224
+ const chatMessages = document.getElementById("chat-messages");
225
+ if (chatMessages) {
226
+ chatMessages.textContent = "";
227
+ }
228
+
229
+ await this.updateStorageInfo();
230
+ } catch (error) {
231
+ console.error("Error during cleaning:", error);
232
+ }
233
+ }
234
+
235
+ async resetAllData() {
236
+ if (!this.db) {
237
+ console.error("Database not available");
238
+ return;
239
+ }
240
+
241
+ const confirmReset = confirm(
242
+ "WARNING!\n\n" +
243
+ "Do you REALLY want to delete ALL data?\n\n" +
244
+ "• All conversations\n" +
245
+ "• All preferences\n" +
246
+ "• All configured models\n" +
247
+ "• All personality traits\n\n" +
248
+ "This action is IRREVERSIBLE!"
249
+ );
250
+
251
+ if (!confirmReset) {
252
+ return;
253
+ }
254
+
255
+ try {
256
+ if (this.db.db) {
257
+ this.db.db.close();
258
+ }
259
+
260
+ const deleteRequest = indexedDB.deleteDatabase(this.db.dbName);
261
+
262
+ deleteRequest.onsuccess = () => {
263
+ setTimeout(() => {
264
+ alert("The page will reload to complete the reset.");
265
+ location.reload();
266
+ }, 500);
267
+ };
268
+
269
+ deleteRequest.onerror = () => {
270
+ alert("Error while deleting the database. Please try again.");
271
+ };
272
+ } catch (error) {
273
+ console.error("Error during reset:", error);
274
+ alert("Error during reset. Please try again.");
275
+ }
276
+ }
277
+
278
+ async updateStorageInfo() {
279
+ if (!this.db) return;
280
+
281
+ try {
282
+ // Add a small delay to ensure database operations are complete
283
+ await new Promise(resolve => setTimeout(resolve, 100));
284
+
285
+ const stats = await this.db.getStorageStats();
286
+
287
+ const dbSizeEl = document.getElementById("db-size");
288
+ const storageUsedEl = document.getElementById("storage-used");
289
+
290
+ if (dbSizeEl) {
291
+ dbSizeEl.textContent = this.formatFileSize(stats.totalSize || 0);
292
+ }
293
+
294
+ if (storageUsedEl) {
295
+ const estimate = navigator.storage && navigator.storage.estimate ? await navigator.storage.estimate() : null;
296
+
297
+ if (estimate) {
298
+ storageUsedEl.textContent = this.formatFileSize(estimate.usage || 0);
299
+ } else {
300
+ storageUsedEl.textContent = "N/A";
301
+ }
302
+ }
303
+ } catch (error) {
304
+ console.error("Error while calculating storage:", error);
305
+
306
+ const dbSizeEl = document.getElementById("db-size");
307
+ const storageUsedEl = document.getElementById("storage-used");
308
+
309
+ if (dbSizeEl) dbSizeEl.textContent = "Error";
310
+ if (storageUsedEl) storageUsedEl.textContent = "Error";
311
+ }
312
+ }
313
+ }
314
+
315
+ // Global exposure (legacy pattern). Will be phased out; prefer: import { KimiDataManager } from "./kimi-data-manager.js";
316
+ window.KimiDataManager = KimiDataManager; // DEPRECATED access path (kept for backward compatibility)
317
+
318
+ export { KimiDataManager };
kimi-js/kimi-module.js CHANGED
@@ -1,314 +1,6 @@
1
  // KIMI MODULE SYSTEM
2
-
3
- class KimiDataManager extends KimiBaseManager {
4
- constructor(database) {
5
- super();
6
- this.db = database;
7
- }
8
-
9
- async init() {
10
- this.setupDataControls();
11
- await this.updateStorageInfo();
12
- }
13
-
14
- setupDataControls() {
15
- const exportButton = document.getElementById("export-data");
16
- if (exportButton) {
17
- exportButton.addEventListener("click", () => this.exportAllData());
18
- }
19
-
20
- const importButton = document.getElementById("import-data");
21
- const importFile = document.getElementById("import-file");
22
- if (importButton && importFile) {
23
- importButton.addEventListener("click", () => importFile.click());
24
- importFile.addEventListener("change", e => this.importData(e));
25
- }
26
-
27
- const cleanButton = document.getElementById("clean-old-data");
28
- if (cleanButton) {
29
- cleanButton.addEventListener("click", async () => {
30
- if (!this.db) return;
31
-
32
- const confirmClean = confirm(
33
- "Delete all conversation messages?\n\n" +
34
- "This will remove all chat history but keep your preferences and settings.\n\n" +
35
- "This action cannot be undone."
36
- );
37
-
38
- if (!confirmClean) {
39
- return;
40
- }
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");
48
- if (chatMessages) {
49
- chatMessages.textContent = "";
50
- }
51
-
52
- // Reload chat history
53
- if (typeof window.loadChatHistory === "function") {
54
- window.loadChatHistory();
55
- }
56
-
57
- await this.updateStorageInfo();
58
- alert("All conversation messages have been deleted successfully!");
59
- } catch (error) {
60
- console.error("Error cleaning conversations:", error);
61
- alert("Error while cleaning conversations. Please try again.");
62
- }
63
- });
64
- }
65
-
66
- const resetButton = document.getElementById("reset-all-data");
67
- if (resetButton) {
68
- resetButton.addEventListener("click", () => this.resetAllData());
69
- }
70
- }
71
-
72
- async exportAllData() {
73
- if (!this.db) {
74
- console.error("Database not available");
75
- return;
76
- }
77
-
78
- try {
79
- const conversations = await this.db.getAllConversations();
80
- const preferencesObj = await this.db.getAllPreferences();
81
- // Export preferences as an array of {key,value} so export is directly re-importable
82
- const preferences = Array.isArray(preferencesObj)
83
- ? preferencesObj
84
- : Object.keys(preferencesObj).map(k => ({ key: k, value: preferencesObj[k] }));
85
- const personalityTraits = await this.db.getAllPersonalityTraits();
86
- const models = await this.db.getAllLLMModels();
87
- const memories = await this.db.getAllMemories();
88
-
89
- const exportData = {
90
- version: "1.0",
91
- exportDate: new Date().toISOString(),
92
- conversations: conversations,
93
- preferences: preferences,
94
- personalityTraits: personalityTraits,
95
- models: models,
96
- memories: memories,
97
- metadata: {
98
- totalConversations: conversations.length,
99
- totalPreferences: Object.keys(preferences).length,
100
- totalTraits: Object.keys(personalityTraits).length,
101
- totalModels: models.length,
102
- totalMemories: memories.length
103
- }
104
- };
105
-
106
- const dataStr = JSON.stringify(exportData, null, 2);
107
- const dataBlob = new Blob([dataStr], { type: "application/json" });
108
-
109
- const url = URL.createObjectURL(dataBlob);
110
- const a = document.createElement("a");
111
- a.href = url;
112
- a.download = `kimi-backup-${new Date().toISOString().split("T")[0]}.json`;
113
- document.body.appendChild(a);
114
- a.click();
115
- document.body.removeChild(a);
116
- URL.revokeObjectURL(url);
117
- } catch (error) {
118
- console.error("Error during export:", error);
119
- }
120
- }
121
-
122
- async importData(event) {
123
- const file = event.target.files[0];
124
- if (!file) {
125
- alert("No file selected.");
126
- return;
127
- }
128
- const reader = new FileReader();
129
- reader.onload = async e => {
130
- try {
131
- const data = JSON.parse(e.target.result);
132
- try {
133
- console.log("Import file keys:", Object.keys(data));
134
- } catch (ex) {}
135
-
136
- if (data.preferences) {
137
- try {
138
- const isArray = Array.isArray(data.preferences);
139
- const len = isArray ? data.preferences.length : Object.keys(data.preferences).length;
140
- console.log("Import: preferences type=", isArray ? "array" : "object", "length=", len);
141
- } catch (ex) {}
142
- await this.db.setPreferencesBatch(data.preferences);
143
- } else {
144
- console.log("Import: no preferences found");
145
- }
146
-
147
- if (data.conversations) {
148
- try {
149
- console.log(
150
- "Import: conversations length=",
151
- Array.isArray(data.conversations) ? data.conversations.length : "not-array"
152
- );
153
- } catch (ex) {}
154
- await this.db.setConversationsBatch(data.conversations);
155
- } else {
156
- console.log("Import: no conversations found");
157
- }
158
-
159
- if (data.personalityTraits) {
160
- try {
161
- console.log("Import: personalityTraits type=", typeof data.personalityTraits);
162
- } catch (ex) {}
163
- await this.db.setPersonalityBatch(data.personalityTraits);
164
- } else {
165
- console.log("Import: no personalityTraits found");
166
- }
167
-
168
- if (data.models) {
169
- try {
170
- console.log("Import: models length=", Array.isArray(data.models) ? data.models.length : "not-array");
171
- } catch (ex) {}
172
- await this.db.setLLMModelsBatch(data.models);
173
- } else {
174
- console.log("Import: no models found");
175
- }
176
-
177
- if (data.memories) {
178
- try {
179
- console.log(
180
- "Import: memories length=",
181
- Array.isArray(data.memories) ? data.memories.length : "not-array"
182
- );
183
- } catch (ex) {}
184
- await this.db.setAllMemories(data.memories);
185
- } else {
186
- console.log("Import: no memories found");
187
- }
188
-
189
- alert("Import successful!");
190
- await this.updateStorageInfo();
191
-
192
- // Reload the page to ensure all UI state is rebuilt from the newly imported DB
193
- setTimeout(() => {
194
- location.reload();
195
- }, 200);
196
- } catch (err) {
197
- console.error("Import failed:", err);
198
- alert("Import failed. Invalid file or format.");
199
- }
200
- };
201
- reader.readAsText(file);
202
- }
203
-
204
- async cleanOldData() {
205
- if (!this.db) {
206
- console.error("Database not available");
207
- return;
208
- }
209
-
210
- const confirmClean = confirm("Do you want to delete ALL conversations?\n\nThis action is irreversible!");
211
- if (!confirmClean) {
212
- return;
213
- }
214
-
215
- try {
216
- // Centralized: use kimi-database.js cleanOldConversations for all deletion logic
217
- await this.db.cleanOldConversations();
218
-
219
- if (typeof window.loadChatHistory === "function") {
220
- window.loadChatHistory();
221
- }
222
- const chatMessages = document.getElementById("chat-messages");
223
- if (chatMessages) {
224
- chatMessages.textContent = "";
225
- }
226
-
227
- await this.updateStorageInfo();
228
- } catch (error) {
229
- console.error("Error during cleaning:", error);
230
- }
231
- }
232
-
233
- async resetAllData() {
234
- if (!this.db) {
235
- console.error("Database not available");
236
- return;
237
- }
238
-
239
- const confirmReset = confirm(
240
- "WARNING!\n\n" +
241
- "Do you REALLY want to delete ALL data?\n\n" +
242
- "• All conversations\n" +
243
- "• All preferences\n" +
244
- "• All configured models\n" +
245
- "• All personality traits\n\n" +
246
- "This action is IRREVERSIBLE!"
247
- );
248
-
249
- if (!confirmReset) {
250
- return;
251
- }
252
-
253
- try {
254
- if (this.db.db) {
255
- this.db.db.close();
256
- }
257
-
258
- const deleteRequest = indexedDB.deleteDatabase(this.db.dbName);
259
-
260
- deleteRequest.onsuccess = () => {
261
- setTimeout(() => {
262
- alert("The page will reload to complete the reset.");
263
- location.reload();
264
- }, 500);
265
- };
266
-
267
- deleteRequest.onerror = () => {
268
- alert("Error while deleting the database. Please try again.");
269
- };
270
- } catch (error) {
271
- console.error("Error during reset:", error);
272
- alert("Error during reset. Please try again.");
273
- }
274
- }
275
-
276
- async updateStorageInfo() {
277
- if (!this.db) return;
278
-
279
- try {
280
- // Add a small delay to ensure database operations are complete
281
- await new Promise(resolve => setTimeout(resolve, 100));
282
-
283
- const stats = await this.db.getStorageStats();
284
-
285
- const dbSizeEl = document.getElementById("db-size");
286
- const storageUsedEl = document.getElementById("storage-used");
287
-
288
- if (dbSizeEl) {
289
- dbSizeEl.textContent = this.formatFileSize(stats.totalSize || 0);
290
- }
291
-
292
- if (storageUsedEl) {
293
- const estimate = navigator.storage && navigator.storage.estimate ? await navigator.storage.estimate() : null;
294
-
295
- if (estimate) {
296
- storageUsedEl.textContent = this.formatFileSize(estimate.usage || 0);
297
- } else {
298
- storageUsedEl.textContent = "N/A";
299
- }
300
- }
301
- } catch (error) {
302
- console.error("Error while calculating storage:", error);
303
-
304
- const dbSizeEl = document.getElementById("db-size");
305
- const storageUsedEl = document.getElementById("storage-used");
306
-
307
- if (dbSizeEl) dbSizeEl.textContent = "Error";
308
- if (storageUsedEl) storageUsedEl.textContent = "Error";
309
- }
310
- }
311
- }
312
 
313
  // Fonctions utilitaires et logique (référencent window.*)
314
 
@@ -2052,8 +1744,7 @@ function setupSettingsListeners(kimiDB, kimiMemory) {
2052
  }
2053
  }
2054
 
2055
- // Exposer globalement
2056
- window.KimiDataManager = KimiDataManager;
2057
  window.updateFavorabilityLabel = updateFavorabilityLabel;
2058
  window.loadCharacterSection = loadCharacterSection;
2059
  window.getBasicResponse = getBasicResponse;
 
1
  // KIMI MODULE SYSTEM
2
+ // KimiDataManager has been extracted to kimi-data-manager.js
3
+ // (kept global via window.KimiDataManager)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
  // Fonctions utilitaires et logique (référencent window.*)
6
 
 
1744
  }
1745
  }
1746
 
1747
+ // Exposer globalement (KimiDataManager already exposed in kimi-data-manager.js)
 
1748
  window.updateFavorabilityLabel = updateFavorabilityLabel;
1749
  window.loadCharacterSection = loadCharacterSection;
1750
  window.getBasicResponse = getBasicResponse;
kimi-js/kimi-script.js CHANGED
@@ -3,6 +3,7 @@ import KimiLLMManager from "./kimi-llm-manager.js";
3
  import KimiEmotionSystem from "./kimi-emotion-system.js";
4
  import KimiMemorySystem from "./kimi-memory-system.js";
5
  import KimiMemory from "./kimi-memory.js";
 
6
 
7
  document.addEventListener("DOMContentLoaded", async function () {
8
  const DEFAULT_SYSTEM_PROMPT = window.DEFAULT_SYSTEM_PROMPT;
 
3
  import KimiEmotionSystem from "./kimi-emotion-system.js";
4
  import KimiMemorySystem from "./kimi-memory-system.js";
5
  import KimiMemory from "./kimi-memory.js";
6
+ import { KimiDataManager } from "./kimi-data-manager.js"; // Explicit import (phasing out window.KimiDataManager)
7
 
8
  document.addEventListener("DOMContentLoaded", async function () {
9
  const DEFAULT_SYSTEM_PROMPT = window.DEFAULT_SYSTEM_PROMPT;