From 562ebca29e38f5ef51ac75a053f7708163cdae66 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Mon, 20 Jul 2026 01:32:53 +0000 Subject: [PATCH 60/71] feat(macos): drag & drop files and text into the terminal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Register the view as a drag destination for file and string pasteboard types. A file drop pastes each path shell-quoted and space-separated (so `program ` works with spaces/metacharacters); a text drop pastes the text verbatim. Both go through the existing bridge_paste path, so a running program or the shell sees the input exactly as a normal paste — no engine or input-path changes, fully isolated from the mouse/selection code. Uses only 10.0-era pasteboard types (NSFilenamesPboardType/NSStringPboardType) and a classic index loop (no fast enumeration), so it compiles on the gcc-4.2 ObjC frontend. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01AeG2jwYMX5gdvvtUnx3PJa --- src/contour_macos/TerminalView.mm | 58 +++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/contour_macos/TerminalView.mm b/src/contour_macos/TerminalView.mm index 49f83b1b..08f2870f 100644 --- a/src/contour_macos/TerminalView.mm +++ b/src/contour_macos/TerminalView.mm @@ -291,6 +291,13 @@ int cbVerifySshHostkey(void* userData, char const* host, int port, char const* f contour_macos::bridge_start([self session]); + // Accept dropped files and text: a file drop pastes its shell-quoted path(s); a text drop pastes + // the text. Both go through the normal paste path, so a program reading stdin (or the shell) sees + // it exactly as if typed/pasted. + [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, + NSStringPboardType, + nil]]; + return self; } @@ -697,4 +704,55 @@ int cbVerifySshHostkey(void* userData, char const* host, int port, char const* f [self setNeedsDisplay:YES]; } +// --- drag & drop (destination) --- + +- (NSDragOperation)draggingEntered:(id)sender +{ + (void) sender; + return NSDragOperationCopy; +} + +- (BOOL)prepareForDragOperation:(id)sender +{ + (void) sender; + return YES; +} + +// Shell-quotes a path by wrapping it in single quotes and escaping any embedded single quote, so a +// path with spaces or metacharacters pastes as one safe argument. +- (NSString*)shellQuote:(NSString*)s +{ + NSString* escaped = [s stringByReplacingOccurrencesOfString:@"'" withString:@"'\\''"]; + return [NSString stringWithFormat:@"'%@'", escaped]; +} + +- (BOOL)performDragOperation:(id)sender +{ + TerminalSession* session = [self session]; + if (!session) + return NO; + NSPasteboard* pb = [sender draggingPasteboard]; + + // Prefer file paths: paste each shell-quoted, space-separated, so `program ` works. + NSArray* files = [pb propertyListForType:NSFilenamesPboardType]; + if ([files count]) + { + NSMutableArray* quoted = [NSMutableArray arrayWithCapacity:[files count]]; + for (NSUInteger i = 0; i < [files count]; ++i) + [quoted addObject:[self shellQuote:(NSString*) [files objectAtIndex:i]]]; + NSString* joined = [quoted componentsJoinedByString:@" "]; + contour_macos::bridge_paste(session, [joined UTF8String]); + return YES; + } + + // Otherwise paste dropped text verbatim. + NSString* text = [pb stringForType:NSStringPboardType]; + if ([text length]) + { + contour_macos::bridge_paste(session, [text UTF8String]); + return YES; + } + return NO; +} + @end