179 lines
5.5 KiB
JavaScript
179 lines
5.5 KiB
JavaScript
// Filename: frontend/js/pages/chat/SessionManager.js
|
|
|
|
import { nanoid } from 'https://cdn.jsdelivr.net/npm/nanoid/nanoid.js';
|
|
|
|
const LOCAL_STORAGE_KEY = 'gemini_chat_state';
|
|
|
|
/**
|
|
* Manages the state and persistence of chat sessions.
|
|
* This class handles loading from/saving to localStorage,
|
|
* and all operations like creating, switching, and deleting sessions.
|
|
*/
|
|
export class SessionManager {
|
|
constructor() {
|
|
this.state = null;
|
|
}
|
|
|
|
/**
|
|
* Initializes the manager by loading state from localStorage or creating a default state.
|
|
*/
|
|
init() {
|
|
this._loadState();
|
|
}
|
|
|
|
// --- Public API for state access ---
|
|
|
|
getSessions() {
|
|
return this.state.sessions;
|
|
}
|
|
|
|
getCurrentSessionId() {
|
|
return this.state.currentSessionId;
|
|
}
|
|
|
|
getCurrentSession() {
|
|
return this.state.sessions.find(s => s.id === this.state.currentSessionId);
|
|
}
|
|
|
|
// --- Public API for state mutation ---
|
|
|
|
/**
|
|
* Creates a new, empty session and sets it as the current one.
|
|
*/
|
|
createSession() {
|
|
const newSessionId = nanoid();
|
|
const newSession = {
|
|
id: newSessionId,
|
|
name: '新会话',
|
|
systemPrompt: '',
|
|
messages: [],
|
|
modelConfig: { model: 'gemini-2.0-flash-lite' },
|
|
params: { temperature: 0.7 }
|
|
};
|
|
this.state.sessions.unshift(newSession);
|
|
this.state.currentSessionId = newSessionId;
|
|
this._saveState();
|
|
}
|
|
|
|
/**
|
|
* Switches the current session to the one with the given ID.
|
|
* @param {string} sessionId The ID of the session to switch to.
|
|
*/
|
|
switchSession(sessionId) {
|
|
if (this.state.currentSessionId === sessionId) return;
|
|
this.state.currentSessionId = sessionId;
|
|
this._saveState();
|
|
}
|
|
|
|
/**
|
|
* Deletes a session by its ID.
|
|
* @param {string} sessionId The ID of the session to delete.
|
|
*/
|
|
deleteSession(sessionId) {
|
|
this.state.sessions = this.state.sessions.filter(s => s.id !== sessionId);
|
|
|
|
if (this.state.currentSessionId === sessionId) {
|
|
this.state.currentSessionId = this.state.sessions[0]?.id || null;
|
|
if (!this.state.currentSessionId) {
|
|
this._createInitialState(); // Create a new one if all are deleted
|
|
}
|
|
}
|
|
|
|
this._saveState();
|
|
}
|
|
|
|
/**
|
|
* [NEW] Clears all messages from the currently active session.
|
|
*/
|
|
clearCurrentSession() {
|
|
const currentSession = this.getCurrentSession();
|
|
if (currentSession) {
|
|
currentSession.messages = [];
|
|
this._saveState();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a message to the current session and updates the session name if it's the first message.
|
|
* @param {object} message The message object to add.
|
|
* @returns {object} The session that was updated.
|
|
*/
|
|
addMessage(message) {
|
|
const currentSession = this.getCurrentSession();
|
|
if (currentSession) {
|
|
if (currentSession.messages.length === 0 && message.role === 'user') {
|
|
currentSession.name = message.content.substring(0, 30);
|
|
}
|
|
currentSession.messages.push(message);
|
|
this._saveState();
|
|
return currentSession;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
deleteMessage(messageId) {
|
|
const currentSession = this.getCurrentSession();
|
|
if (currentSession) {
|
|
const messageIndex = currentSession.messages.findIndex(m => m.id === messageId);
|
|
if (messageIndex > -1) {
|
|
currentSession.messages.splice(messageIndex, 1);
|
|
this._saveState();
|
|
console.log(`Message ${messageId} deleted.`);
|
|
}
|
|
}
|
|
}
|
|
|
|
truncateMessagesAfter(messageId) {
|
|
const currentSession = this.getCurrentSession();
|
|
if (currentSession) {
|
|
const messageIndex = currentSession.messages.findIndex(m => m.id === messageId);
|
|
// Ensure the message exists and it's not already the last one
|
|
if (messageIndex > -1 && messageIndex < currentSession.messages.length - 1) {
|
|
currentSession.messages.splice(messageIndex + 1);
|
|
this._saveState();
|
|
console.log(`Truncated messages after ${messageId}.`);
|
|
}
|
|
}
|
|
}
|
|
// --- Private persistence methods ---
|
|
|
|
_saveState() {
|
|
try {
|
|
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(this.state));
|
|
} catch (error) {
|
|
console.error("Failed to save session state:", error);
|
|
}
|
|
}
|
|
|
|
_loadState() {
|
|
try {
|
|
const stateString = localStorage.getItem(LOCAL_STORAGE_KEY);
|
|
if (stateString) {
|
|
this.state = JSON.parse(stateString);
|
|
} else {
|
|
this._createInitialState();
|
|
}
|
|
} catch (error) {
|
|
console.error("Failed to load session state, creating initial state:", error);
|
|
this._createInitialState();
|
|
}
|
|
}
|
|
|
|
_createInitialState() {
|
|
const initialSessionId = nanoid();
|
|
this.state = {
|
|
sessions: [{
|
|
id: initialSessionId,
|
|
name: '新会话',
|
|
systemPrompt: '',
|
|
messages: [],
|
|
modelConfig: { model: 'gemini-2.0-flash-lite' },
|
|
params: { temperature: 0.7 }
|
|
}],
|
|
currentSessionId: initialSessionId,
|
|
settings: {}
|
|
};
|
|
this._saveState();
|
|
}
|
|
}
|