Spaces:
Running
Running
// KIMI DATA MANAGER (extracted from kimi-module.js) | |
// This file contains only the KimiDataManager class and its global exposure. | |
// Depends on: KimiBaseManager (defined in kimi-utils.js) and DOM APIs. | |
class KimiDataManager extends KimiBaseManager { | |
constructor(database) { | |
super(); | |
this.db = database; | |
} | |
async init() { | |
this.setupDataControls(); | |
await this.updateStorageInfo(); | |
} | |
setupDataControls() { | |
const exportButton = document.getElementById("export-data"); | |
if (exportButton) { | |
exportButton.addEventListener("click", () => this.exportAllData()); | |
} | |
const importButton = document.getElementById("import-data"); | |
const importFile = document.getElementById("import-file"); | |
if (importButton && importFile) { | |
importButton.addEventListener("click", () => importFile.click()); | |
importFile.addEventListener("change", e => this.importData(e)); | |
} | |
const cleanButton = document.getElementById("clean-old-data"); | |
if (cleanButton) { | |
cleanButton.addEventListener("click", async () => { | |
if (!this.db) return; | |
const confirmClean = confirm( | |
"Delete all conversation messages?\n\n" + | |
"This will remove all chat history but keep your preferences and settings.\n\n" + | |
"This action cannot be undone." | |
); | |
if (!confirmClean) { | |
return; | |
} | |
try { | |
// Clear all conversations directly | |
await this.db.db.conversations.clear(); | |
// Clear chat UI | |
const chatMessages = document.getElementById("chat-messages"); | |
if (chatMessages) { | |
chatMessages.textContent = ""; | |
} | |
// Reload chat history | |
if (typeof window.loadChatHistory === "function") { | |
window.loadChatHistory(); | |
} | |
await this.updateStorageInfo(); | |
alert("All conversation messages have been deleted successfully!"); | |
} catch (error) { | |
console.error("Error cleaning conversations:", error); | |
alert("Error while cleaning conversations. Please try again."); | |
} | |
}); | |
} | |
const resetButton = document.getElementById("reset-all-data"); | |
if (resetButton) { | |
resetButton.addEventListener("click", () => this.resetAllData()); | |
} | |
} | |
async exportAllData() { | |
if (!this.db) { | |
console.error("Database not available"); | |
return; | |
} | |
try { | |
const conversations = await this.db.getAllConversations(); | |
const preferencesObj = await this.db.getAllPreferences(); | |
// Export preferences as an array of {key,value} so export is directly re-importable | |
const preferences = Array.isArray(preferencesObj) | |
? preferencesObj | |
: Object.keys(preferencesObj).map(k => ({ key: k, value: preferencesObj[k] })); | |
const personalityTraits = await this.db.getAllPersonalityTraits(); | |
const models = await this.db.getAllLLMModels(); | |
const memories = await this.db.getAllMemories(); | |
const exportData = { | |
version: "1.0", | |
exportDate: new Date().toISOString(), | |
conversations: conversations, | |
preferences: preferences, | |
personalityTraits: personalityTraits, | |
models: models, | |
memories: memories, | |
metadata: { | |
totalConversations: conversations.length, | |
totalPreferences: Object.keys(preferences).length, | |
totalTraits: Object.keys(personalityTraits).length, | |
totalModels: models.length, | |
totalMemories: memories.length | |
} | |
}; | |
const dataStr = JSON.stringify(exportData, null, 2); | |
const dataBlob = new Blob([dataStr], { type: "application/json" }); | |
const url = URL.createObjectURL(dataBlob); | |
const a = document.createElement("a"); | |
a.href = url; | |
a.download = `kimi-backup-${new Date().toISOString().split("T")[0]}.json`; | |
document.body.appendChild(a); | |
a.click(); | |
document.body.removeChild(a); | |
URL.revokeObjectURL(url); | |
} catch (error) { | |
console.error("Error during export:", error); | |
} | |
} | |
async importData(event) { | |
const file = event.target.files[0]; | |
if (!file) { | |
alert("No file selected."); | |
return; | |
} | |
const reader = new FileReader(); | |
reader.onload = async e => { | |
try { | |
const data = JSON.parse(e.target.result); | |
try { | |
console.log("Import file keys:", Object.keys(data)); | |
} catch (ex) {} | |
if (data.preferences) { | |
try { | |
const isArray = Array.isArray(data.preferences); | |
const len = isArray ? data.preferences.length : Object.keys(data.preferences).length; | |
console.log("Import: preferences type=", isArray ? "array" : "object", "length=", len); | |
} catch (ex) {} | |
await this.db.setPreferencesBatch(data.preferences); | |
} else { | |
console.log("Import: no preferences found"); | |
} | |
if (data.conversations) { | |
try { | |
console.log( | |
"Import: conversations length=", | |
Array.isArray(data.conversations) ? data.conversations.length : "not-array" | |
); | |
} catch (ex) {} | |
await this.db.setConversationsBatch(data.conversations); | |
} else { | |
console.log("Import: no conversations found"); | |
} | |
if (data.personalityTraits) { | |
try { | |
console.log("Import: personalityTraits type=", typeof data.personalityTraits); | |
} catch (ex) {} | |
await this.db.setPersonalityBatch(data.personalityTraits); | |
} else { | |
console.log("Import: no personalityTraits found"); | |
} | |
if (data.models) { | |
try { | |
console.log("Import: models length=", Array.isArray(data.models) ? data.models.length : "not-array"); | |
} catch (ex) {} | |
await this.db.setLLMModelsBatch(data.models); | |
} else { | |
console.log("Import: no models found"); | |
} | |
if (data.memories) { | |
try { | |
console.log( | |
"Import: memories length=", | |
Array.isArray(data.memories) ? data.memories.length : "not-array" | |
); | |
} catch (ex) {} | |
await this.db.setAllMemories(data.memories); | |
} else { | |
console.log("Import: no memories found"); | |
} | |
alert("Import successful!"); | |
await this.updateStorageInfo(); | |
// Reload the page to ensure all UI state is rebuilt from the newly imported DB | |
setTimeout(() => { | |
location.reload(); | |
}, 200); | |
} catch (err) { | |
console.error("Import failed:", err); | |
alert("Import failed. Invalid file or format."); | |
} | |
}; | |
reader.readAsText(file); | |
} | |
async cleanOldData() { | |
if (!this.db) { | |
console.error("Database not available"); | |
return; | |
} | |
const confirmClean = confirm("Do you want to delete ALL conversations?\n\nThis action is irreversible!"); | |
if (!confirmClean) { | |
return; | |
} | |
try { | |
// Centralized: use kimi-database.js cleanOldConversations for all deletion logic | |
await this.db.cleanOldConversations(); | |
if (typeof window.loadChatHistory === "function") { | |
window.loadChatHistory(); | |
} | |
const chatMessages = document.getElementById("chat-messages"); | |
if (chatMessages) { | |
chatMessages.textContent = ""; | |
} | |
await this.updateStorageInfo(); | |
} catch (error) { | |
console.error("Error during cleaning:", error); | |
} | |
} | |
async resetAllData() { | |
if (!this.db) { | |
console.error("Database not available"); | |
return; | |
} | |
const confirmReset = confirm( | |
"WARNING!\n\n" + | |
"Do you REALLY want to delete ALL data?\n\n" + | |
"β’ All conversations\n" + | |
"β’ All preferences\n" + | |
"β’ All configured models\n" + | |
"β’ All personality traits\n\n" + | |
"This action is IRREVERSIBLE!" | |
); | |
if (!confirmReset) { | |
return; | |
} | |
try { | |
if (this.db.db) { | |
this.db.db.close(); | |
} | |
const deleteRequest = indexedDB.deleteDatabase(this.db.dbName); | |
deleteRequest.onsuccess = () => { | |
setTimeout(() => { | |
alert("The page will reload to complete the reset."); | |
location.reload(); | |
}, 500); | |
}; | |
deleteRequest.onerror = () => { | |
alert("Error while deleting the database. Please try again."); | |
}; | |
} catch (error) { | |
console.error("Error during reset:", error); | |
alert("Error during reset. Please try again."); | |
} | |
} | |
async updateStorageInfo() { | |
if (!this.db) return; | |
try { | |
// Add a small delay to ensure database operations are complete | |
await new Promise(resolve => setTimeout(resolve, 100)); | |
const stats = await this.db.getStorageStats(); | |
const dbSizeEl = document.getElementById("db-size"); | |
const storageUsedEl = document.getElementById("storage-used"); | |
if (dbSizeEl) { | |
dbSizeEl.textContent = this.formatFileSize(stats.totalSize || 0); | |
} | |
if (storageUsedEl) { | |
const estimate = navigator.storage && navigator.storage.estimate ? await navigator.storage.estimate() : null; | |
if (estimate) { | |
storageUsedEl.textContent = this.formatFileSize(estimate.usage || 0); | |
} else { | |
storageUsedEl.textContent = "N/A"; | |
} | |
} | |
} catch (error) { | |
console.error("Error while calculating storage:", error); | |
const dbSizeEl = document.getElementById("db-size"); | |
const storageUsedEl = document.getElementById("storage-used"); | |
if (dbSizeEl) dbSizeEl.textContent = "Error"; | |
if (storageUsedEl) storageUsedEl.textContent = "Error"; | |
} | |
} | |
} | |
// Global exposure (legacy pattern). Will be phased out; prefer: import { KimiDataManager } from "./kimi-data-manager.js"; | |
window.KimiDataManager = KimiDataManager; // DEPRECATED access path (kept for backward compatibility) | |
export { KimiDataManager }; | |