source dump of claude code
at main 121 lines 3.7 kB view raw
1import { mkdir, unlink, writeFile } from 'fs/promises' 2import { tmpdir } from 'os' 3import { join } from 'path' 4import { type AnsiToPngOptions, ansiToPng } from './ansiToPng.js' 5import { execFileNoThrowWithCwd } from './execFileNoThrow.js' 6import { logError } from './log.js' 7import { getPlatform } from './platform.js' 8 9/** 10 * Copies an image (from ANSI text) to the system clipboard. 11 * Supports macOS, Linux (with xclip/xsel), and Windows. 12 * 13 * Pure-TS pipeline: ANSI text → bitmap-font render → PNG encode. No WASM, 14 * no system fonts, so this works in every build (native and JS). 15 */ 16export async function copyAnsiToClipboard( 17 ansiText: string, 18 options?: AnsiToPngOptions, 19): Promise<{ success: boolean; message: string }> { 20 try { 21 const tempDir = join(tmpdir(), 'claude-code-screenshots') 22 await mkdir(tempDir, { recursive: true }) 23 24 const pngPath = join(tempDir, `screenshot-${Date.now()}.png`) 25 const pngBuffer = ansiToPng(ansiText, options) 26 await writeFile(pngPath, pngBuffer) 27 28 const result = await copyPngToClipboard(pngPath) 29 30 try { 31 await unlink(pngPath) 32 } catch { 33 // Ignore cleanup errors 34 } 35 36 return result 37 } catch (error) { 38 logError(error) 39 return { 40 success: false, 41 message: `Failed to copy screenshot: ${error instanceof Error ? error.message : 'Unknown error'}`, 42 } 43 } 44} 45 46async function copyPngToClipboard( 47 pngPath: string, 48): Promise<{ success: boolean; message: string }> { 49 const platform = getPlatform() 50 51 if (platform === 'macos') { 52 // macOS: Use osascript to copy PNG to clipboard 53 // Escape backslashes and double quotes for AppleScript string 54 const escapedPath = pngPath.replace(/\\/g, '\\\\').replace(/"/g, '\\"') 55 const script = `set the clipboard to (read (POSIX file "${escapedPath}") as «class PNGf»)` 56 const result = await execFileNoThrowWithCwd('osascript', ['-e', script], { 57 timeout: 5000, 58 }) 59 60 if (result.code === 0) { 61 return { success: true, message: 'Screenshot copied to clipboard' } 62 } 63 return { 64 success: false, 65 message: `Failed to copy to clipboard: ${result.stderr}`, 66 } 67 } 68 69 if (platform === 'linux') { 70 // Linux: Try xclip first, then xsel 71 const xclipResult = await execFileNoThrowWithCwd( 72 'xclip', 73 ['-selection', 'clipboard', '-t', 'image/png', '-i', pngPath], 74 { timeout: 5000 }, 75 ) 76 77 if (xclipResult.code === 0) { 78 return { success: true, message: 'Screenshot copied to clipboard' } 79 } 80 81 // Try xsel as fallback 82 const xselResult = await execFileNoThrowWithCwd( 83 'xsel', 84 ['--clipboard', '--input', '--type', 'image/png'], 85 { timeout: 5000 }, 86 ) 87 88 if (xselResult.code === 0) { 89 return { success: true, message: 'Screenshot copied to clipboard' } 90 } 91 92 return { 93 success: false, 94 message: 95 'Failed to copy to clipboard. Please install xclip or xsel: sudo apt install xclip', 96 } 97 } 98 99 if (platform === 'windows') { 100 // Windows: Use PowerShell to copy image to clipboard 101 const psScript = `Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Clipboard]::SetImage([System.Drawing.Image]::FromFile('${pngPath.replace(/'/g, "''")}'))` 102 const result = await execFileNoThrowWithCwd( 103 'powershell', 104 ['-NoProfile', '-Command', psScript], 105 { timeout: 5000 }, 106 ) 107 108 if (result.code === 0) { 109 return { success: true, message: 'Screenshot copied to clipboard' } 110 } 111 return { 112 success: false, 113 message: `Failed to copy to clipboard: ${result.stderr}`, 114 } 115 } 116 117 return { 118 success: false, 119 message: `Screenshot to clipboard is not supported on ${platform}`, 120 } 121}