mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-15 08:31:14 -07:00
120 lines
3.6 KiB
TypeScript
120 lines
3.6 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import { useState, useCallback } from 'react';
|
|
import type { HistoryItemWithoutId } from '../types.js';
|
|
import * as fs from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
import type {
|
|
Config,
|
|
ConversationRecord,
|
|
ResumedSessionData,
|
|
} from '@google/gemini-cli-core';
|
|
import {
|
|
coreEvents,
|
|
convertSessionToClientHistory,
|
|
} from '@google/gemini-cli-core';
|
|
import type { SessionInfo } from '../../utils/sessionUtils.js';
|
|
import { convertSessionToHistoryFormats } from '../../utils/sessionUtils.js';
|
|
import type { Part } from '@google/genai';
|
|
|
|
export { convertSessionToHistoryFormats };
|
|
|
|
export const useSessionBrowser = (
|
|
config: Config,
|
|
onLoadHistory: (
|
|
uiHistory: HistoryItemWithoutId[],
|
|
clientHistory: Array<{ role: 'user' | 'model'; parts: Part[] }>,
|
|
resumedSessionData: ResumedSessionData,
|
|
) => Promise<void>,
|
|
) => {
|
|
const [isSessionBrowserOpen, setIsSessionBrowserOpen] = useState(false);
|
|
|
|
return {
|
|
isSessionBrowserOpen,
|
|
|
|
openSessionBrowser: useCallback(() => {
|
|
setIsSessionBrowserOpen(true);
|
|
}, []),
|
|
|
|
closeSessionBrowser: useCallback(() => {
|
|
setIsSessionBrowserOpen(false);
|
|
}, []),
|
|
|
|
/**
|
|
* Loads a conversation by ID, and reinitializes the chat recording service with it.
|
|
*/
|
|
handleResumeSession: useCallback(
|
|
async (session: SessionInfo) => {
|
|
try {
|
|
const chatsDir = path.join(
|
|
config.storage.getProjectTempDir(),
|
|
'chats',
|
|
);
|
|
|
|
const fileName = session.fileName;
|
|
|
|
const originalFilePath = path.join(chatsDir, fileName);
|
|
|
|
// Load up the conversation.
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
const conversation: ConversationRecord = JSON.parse(
|
|
await fs.readFile(originalFilePath, 'utf8'),
|
|
);
|
|
|
|
// Use the old session's ID to continue it.
|
|
const existingSessionId = conversation.sessionId;
|
|
config.setSessionId(existingSessionId);
|
|
|
|
const resumedSessionData = {
|
|
conversation,
|
|
filePath: originalFilePath,
|
|
};
|
|
|
|
// We've loaded it; tell the UI about it.
|
|
setIsSessionBrowserOpen(false);
|
|
const historyData = convertSessionToHistoryFormats(
|
|
conversation.messages,
|
|
);
|
|
await onLoadHistory(
|
|
historyData.uiHistory,
|
|
convertSessionToClientHistory(conversation.messages),
|
|
resumedSessionData,
|
|
);
|
|
} catch (error) {
|
|
coreEvents.emitFeedback('error', 'Error resuming session:', error);
|
|
setIsSessionBrowserOpen(false);
|
|
}
|
|
},
|
|
[config, onLoadHistory],
|
|
),
|
|
|
|
/**
|
|
* Deletes a session by ID using the ChatRecordingService.
|
|
*/
|
|
handleDeleteSession: useCallback(
|
|
(session: SessionInfo) => {
|
|
// Note: Chat sessions are stored on disk using a filename derived from
|
|
// the session, e.g. "session-<timestamp>-<sessionIdPrefix>.json".
|
|
// The ChatRecordingService.deleteSession API expects this file basename
|
|
// (without the ".json" extension), not the full session UUID.
|
|
try {
|
|
const chatRecordingService = config
|
|
.getGeminiClient()
|
|
?.getChatRecordingService();
|
|
if (chatRecordingService) {
|
|
chatRecordingService.deleteSession(session.file);
|
|
}
|
|
} catch (error) {
|
|
coreEvents.emitFeedback('error', 'Error deleting session:', error);
|
|
throw error;
|
|
}
|
|
},
|
|
[config],
|
|
),
|
|
};
|
|
};
|