mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-13 05:12:55 -07:00
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
@@ -43,6 +43,9 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
|||||||
debug: vi.fn(),
|
debug: vi.fn(),
|
||||||
warn: vi.fn(),
|
warn: vi.fn(),
|
||||||
},
|
},
|
||||||
|
Storage: class {
|
||||||
|
getProjectTempDir = vi.fn(() => '/tmp/global');
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -169,7 +172,7 @@ describe('clipboardUtils', () => {
|
|||||||
|
|
||||||
describe('saveClipboardImage (Linux)', () => {
|
describe('saveClipboardImage (Linux)', () => {
|
||||||
const mockTargetDir = '/tmp/target';
|
const mockTargetDir = '/tmp/target';
|
||||||
const mockTempDir = path.join(mockTargetDir, '.gemini-clipboard');
|
const mockTempDir = path.join('/tmp/global', 'images');
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
setPlatform('linux');
|
setPlatform('linux');
|
||||||
@@ -240,6 +243,7 @@ describe('clipboardUtils', () => {
|
|||||||
|
|
||||||
const result = await promise;
|
const result = await promise;
|
||||||
|
|
||||||
|
expect(result).toContain(mockTempDir);
|
||||||
expect(result).toMatch(/clipboard-\d+\.png$/);
|
expect(result).toMatch(/clipboard-\d+\.png$/);
|
||||||
expect(spawn).toHaveBeenCalledWith('wl-paste', expect.any(Array));
|
expect(spawn).toHaveBeenCalledWith('wl-paste', expect.any(Array));
|
||||||
expect(fs.mkdir).toHaveBeenCalledWith(mockTempDir, { recursive: true });
|
expect(fs.mkdir).toHaveBeenCalledWith(mockTempDir, { recursive: true });
|
||||||
@@ -310,15 +314,18 @@ describe('clipboardUtils', () => {
|
|||||||
|
|
||||||
// Stateless functions continue to use static imports
|
// Stateless functions continue to use static imports
|
||||||
describe('cleanupOldClipboardImages', () => {
|
describe('cleanupOldClipboardImages', () => {
|
||||||
|
const mockTargetDir = '/tmp/target';
|
||||||
it('should not throw errors', async () => {
|
it('should not throw errors', async () => {
|
||||||
// Should handle missing directories gracefully
|
// Should handle missing directories gracefully
|
||||||
await expect(
|
await expect(
|
||||||
cleanupOldClipboardImages('/path/that/does/not/exist'),
|
cleanupOldClipboardImages(mockTargetDir),
|
||||||
).resolves.not.toThrow();
|
).resolves.not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should complete without errors on valid directory', async () => {
|
it('should complete without errors on valid directory', async () => {
|
||||||
await expect(cleanupOldClipboardImages('.')).resolves.not.toThrow();
|
await expect(
|
||||||
|
cleanupOldClipboardImages(mockTargetDir),
|
||||||
|
).resolves.not.toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
spawnAsync,
|
spawnAsync,
|
||||||
unescapePath,
|
unescapePath,
|
||||||
escapePath,
|
escapePath,
|
||||||
|
Storage,
|
||||||
} from '@google/gemini-cli-core';
|
} from '@google/gemini-cli-core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -244,19 +245,33 @@ const saveFileWithXclip = async (tempFilePath: string) => {
|
|||||||
return false;
|
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)
|
* Saves the image from clipboard to a temporary file (macOS, Windows, and Linux)
|
||||||
* @param targetDir The target directory to create temp files within
|
* @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
|
* @returns The path to the saved image file, or null if no image or error
|
||||||
*/
|
*/
|
||||||
export async function saveClipboardImage(
|
export async function saveClipboardImage(
|
||||||
targetDir?: string,
|
targetDir: string,
|
||||||
): Promise<string | null> {
|
): Promise<string | null> {
|
||||||
try {
|
try {
|
||||||
// Create a temporary directory for clipboard images within the target directory
|
const tempDir = getProjectClipboardImagesDir(targetDir);
|
||||||
// This avoids security restrictions on paths outside the target directory
|
|
||||||
const baseDir = targetDir || process.cwd();
|
|
||||||
const tempDir = path.join(baseDir, '.gemini-clipboard');
|
|
||||||
await fs.mkdir(tempDir, { recursive: true });
|
await fs.mkdir(tempDir, { recursive: true });
|
||||||
|
|
||||||
// Generate a unique filename with timestamp
|
// Generate a unique filename with timestamp
|
||||||
@@ -378,11 +393,10 @@ export async function saveClipboardImage(
|
|||||||
* @param targetDir The target directory where temp files are stored
|
* @param targetDir The target directory where temp files are stored
|
||||||
*/
|
*/
|
||||||
export async function cleanupOldClipboardImages(
|
export async function cleanupOldClipboardImages(
|
||||||
targetDir?: string,
|
targetDir: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const baseDir = targetDir || process.cwd();
|
const tempDir = getProjectClipboardImagesDir(targetDir);
|
||||||
const tempDir = path.join(baseDir, '.gemini-clipboard');
|
|
||||||
const files = await fs.readdir(tempDir);
|
const files = await fs.readdir(tempDir);
|
||||||
const oneHourAgo = Date.now() - 60 * 60 * 1000;
|
const oneHourAgo = Date.now() - 60 * 60 * 1000;
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
|||||||
return {
|
return {
|
||||||
...actual,
|
...actual,
|
||||||
spawnAsync: vi.fn(),
|
spawnAsync: vi.fn(),
|
||||||
|
Storage: class {
|
||||||
|
getProjectTempDir = vi.fn(() => "C:\\User's Files");
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user