diff --git a/packages/cli/src/ui/utils/clipboardUtils.test.ts b/packages/cli/src/ui/utils/clipboardUtils.test.ts index 025deea516..76eb0bcac3 100644 --- a/packages/cli/src/ui/utils/clipboardUtils.test.ts +++ b/packages/cli/src/ui/utils/clipboardUtils.test.ts @@ -43,6 +43,9 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => { debug: vi.fn(), warn: vi.fn(), }, + Storage: class { + getProjectTempDir = vi.fn(() => '/tmp/global'); + }, }; }); @@ -169,7 +172,7 @@ describe('clipboardUtils', () => { describe('saveClipboardImage (Linux)', () => { const mockTargetDir = '/tmp/target'; - const mockTempDir = path.join(mockTargetDir, '.gemini-clipboard'); + const mockTempDir = path.join('/tmp/global', 'images'); beforeEach(() => { setPlatform('linux'); @@ -240,6 +243,7 @@ describe('clipboardUtils', () => { const result = await promise; + expect(result).toContain(mockTempDir); expect(result).toMatch(/clipboard-\d+\.png$/); expect(spawn).toHaveBeenCalledWith('wl-paste', expect.any(Array)); expect(fs.mkdir).toHaveBeenCalledWith(mockTempDir, { recursive: true }); @@ -310,15 +314,18 @@ describe('clipboardUtils', () => { // Stateless functions continue to use static imports describe('cleanupOldClipboardImages', () => { + const mockTargetDir = '/tmp/target'; it('should not throw errors', async () => { // Should handle missing directories gracefully await expect( - cleanupOldClipboardImages('/path/that/does/not/exist'), + cleanupOldClipboardImages(mockTargetDir), ).resolves.not.toThrow(); }); it('should complete without errors on valid directory', async () => { - await expect(cleanupOldClipboardImages('.')).resolves.not.toThrow(); + await expect( + cleanupOldClipboardImages(mockTargetDir), + ).resolves.not.toThrow(); }); }); diff --git a/packages/cli/src/ui/utils/clipboardUtils.ts b/packages/cli/src/ui/utils/clipboardUtils.ts index 0804a7ef9e..99ead45736 100644 --- a/packages/cli/src/ui/utils/clipboardUtils.ts +++ b/packages/cli/src/ui/utils/clipboardUtils.ts @@ -13,6 +13,7 @@ import { spawnAsync, unescapePath, escapePath, + Storage, } from '@google/gemini-cli-core'; /** @@ -244,19 +245,33 @@ const saveFileWithXclip = async (tempFilePath: string) => { return false; }; +/** + * Gets the directory where clipboard images should be stored for a specific project. + * + * This uses the global temporary directory but creates a project-specific subdirectory + * based on the hash of the project path (via `Storage.getProjectTempDir()`). + * This prevents path conflicts between different projects while keeping the images + * outside of the user's project directory. + * + * @param targetDir The root directory of the current project. + * @returns The absolute path to the images directory. + */ +function getProjectClipboardImagesDir(targetDir: string): string { + const storage = new Storage(targetDir); + const baseDir = storage.getProjectTempDir(); + return path.join(baseDir, 'images'); +} + /** * Saves the image from clipboard to a temporary file (macOS, Windows, and Linux) * @param targetDir The target directory to create temp files within * @returns The path to the saved image file, or null if no image or error */ export async function saveClipboardImage( - targetDir?: string, + targetDir: string, ): Promise { try { - // Create a temporary directory for clipboard images within the target directory - // This avoids security restrictions on paths outside the target directory - const baseDir = targetDir || process.cwd(); - const tempDir = path.join(baseDir, '.gemini-clipboard'); + const tempDir = getProjectClipboardImagesDir(targetDir); await fs.mkdir(tempDir, { recursive: true }); // Generate a unique filename with timestamp @@ -378,11 +393,10 @@ export async function saveClipboardImage( * @param targetDir The target directory where temp files are stored */ export async function cleanupOldClipboardImages( - targetDir?: string, + targetDir: string, ): Promise { try { - const baseDir = targetDir || process.cwd(); - const tempDir = path.join(baseDir, '.gemini-clipboard'); + const tempDir = getProjectClipboardImagesDir(targetDir); const files = await fs.readdir(tempDir); const oneHourAgo = Date.now() - 60 * 60 * 1000; diff --git a/packages/cli/src/ui/utils/clipboardUtils.windows.test.ts b/packages/cli/src/ui/utils/clipboardUtils.windows.test.ts index 714c631640..042702073c 100644 --- a/packages/cli/src/ui/utils/clipboardUtils.windows.test.ts +++ b/packages/cli/src/ui/utils/clipboardUtils.windows.test.ts @@ -16,6 +16,9 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => { return { ...actual, spawnAsync: vi.fn(), + Storage: class { + getProjectTempDir = vi.fn(() => "C:\\User's Files"); + }, }; });