1+ import SwiftUI
2+ import UniformTypeIdentifiers
3+ import Foundation
4+ import AppKit
5+
6+ struct UniversalDropHandler {
7+ static func handleUniversalDrop( providers: [ NSItemProvider ] , contextManager: ContextManager , selectedContextID: UUID ? ) -> Bool {
8+ guard let contextIndex = contextManager. contexts. firstIndex ( where: { $0. id == selectedContextID } ) else {
9+ print ( " No selected context found for drop " )
10+ return false
11+ }
12+ guard !providers. isEmpty else {
13+ print ( " No providers in drop " )
14+ return false
15+ }
16+ var handled = false
17+ for provider in providers {
18+ print ( " Processing provider with types: \( provider. registeredTypeIdentifiers) " )
19+ if provider. hasItemConformingToTypeIdentifier ( UTType . fileURL. identifier) || provider. hasItemConformingToTypeIdentifier ( " public.file-url " ) {
20+ let typeIdentifier = provider. hasItemConformingToTypeIdentifier ( UTType . fileURL. identifier) ? UTType . fileURL. identifier : " public.file-url "
21+ provider. loadItem ( forTypeIdentifier: typeIdentifier, options: nil ) { item, error in
22+ if let error = error {
23+ print ( " Error loading file URL: \( error) " )
24+ return
25+ }
26+ print ( " Loaded item: \( String ( describing: item) ) " )
27+ var url : URL ?
28+ if let urlObject = item as? URL {
29+ url = urlObject
30+ } else if let data = item as? Data {
31+ url = URL ( dataRepresentation: data, relativeTo: nil )
32+ print ( " Converted data to URL: \( String ( describing: url) ) " )
33+ }
34+ if let url = url {
35+ print ( " Processing URL: \( url) " )
36+ DispatchQueue . main. async {
37+ if url. pathExtension == " app " {
38+ if let bundle = Bundle ( url: url) , let bundleId = bundle. bundleIdentifier {
39+ let appItem = AppItem ( name: url. deletingPathExtension ( ) . lastPathComponent, bundleIdentifier: bundleId, windowTitle: nil )
40+ contextManager. addItem ( . application( appItem) , to: contextManager. contexts [ contextIndex] . id)
41+ print ( " Added application: \( appItem. name) " )
42+ }
43+ } else if url. pathExtension == " sh " {
44+ let session = TerminalSession (
45+ workingDirectory: url. deletingLastPathComponent ( ) . path,
46+ command: url. path,
47+ title: url. deletingPathExtension ( ) . lastPathComponent
48+ )
49+ contextManager. addItem ( . terminalSession( session) , to: contextManager. contexts [ contextIndex] . id)
50+ print ( " Added terminal session for script: \( session. title) " )
51+ var bookmark : Data ? = nil
52+ do {
53+ bookmark = try url. bookmarkData ( options: . withSecurityScope, includingResourceValuesForKeys: nil , relativeTo: nil )
54+ } catch {
55+ bookmark = nil
56+ }
57+ let document = DocumentItem (
58+ name: url. deletingPathExtension ( ) . lastPathComponent,
59+ filePath: url. path,
60+ application: " " ,
61+ bookmark: bookmark
62+ )
63+ contextManager. addItem ( . document( document) , to: contextManager. contexts [ contextIndex] . id)
64+ print ( " Added document: \( document. name) " )
65+ } else {
66+ // Fallback: add as document for any other file type
67+ var bookmark : Data ? = nil
68+ do {
69+ bookmark = try url. bookmarkData ( options: . withSecurityScope, includingResourceValuesForKeys: nil , relativeTo: nil )
70+ } catch {
71+ bookmark = nil
72+ }
73+ let document = DocumentItem (
74+ name: url. deletingPathExtension ( ) . lastPathComponent,
75+ filePath: url. path,
76+ application: " " ,
77+ bookmark: bookmark
78+ )
79+ contextManager. addItem ( . document( document) , to: contextManager. contexts [ contextIndex] . id)
80+ print ( " Added document: \( document. name) " )
81+ }
82+ }
83+ }
84+ }
85+ handled = true
86+ } else if provider. hasItemConformingToTypeIdentifier ( " public.shell-script " ) {
87+ provider. loadItem ( forTypeIdentifier: " public.shell-script " , options: nil ) { item, error in
88+ if let error = error {
89+ print ( " Error loading shell script: \( error) " )
90+ return
91+ }
92+ var url : URL ?
93+ if let urlObject = item as? URL {
94+ url = urlObject
95+ } else if let data = item as? Data {
96+ url = URL ( dataRepresentation: data, relativeTo: nil )
97+ print ( " Converted data to URL: \( String ( describing: url) ) " )
98+ }
99+ if let url = url {
100+ DispatchQueue . main. async {
101+ let session = TerminalSession (
102+ workingDirectory: url. deletingLastPathComponent ( ) . path,
103+ command: url. path,
104+ title: url. deletingPathExtension ( ) . lastPathComponent
105+ )
106+ contextManager. addItem ( . terminalSession( session) , to: contextManager. contexts [ contextIndex] . id)
107+ print ( " Added terminal session for script: \( session. title) " )
108+ }
109+ }
110+ }
111+ handled = true
112+ provider. loadItem ( forTypeIdentifier: UTType . url. identifier, options: nil ) { item, _ in
113+ if let data = item as? Data , let url = URL ( dataRepresentation: data, relativeTo: nil ) {
114+ let browserTab = BrowserTab ( title: url. absoluteString, url: url. absoluteString, browser: " default " )
115+ DispatchQueue . main. async {
116+ contextManager. addItem ( . browserTab( browserTab) , to: contextManager. contexts [ contextIndex] . id)
117+ }
118+ } else if let url = item as? URL {
119+ let browserTab = BrowserTab ( title: url. absoluteString, url: url. absoluteString, browser: " default " )
120+ DispatchQueue . main. async {
121+ contextManager. addItem ( . browserTab( browserTab) , to: contextManager. contexts [ contextIndex] . id)
122+ }
123+ }
124+ }
125+ handled = true
126+ let typeIdentifier = provider. hasItemConformingToTypeIdentifier ( UTType . text. identifier) ? UTType . text. identifier : UTType . plainText. identifier
127+ provider. loadItem ( forTypeIdentifier: typeIdentifier, options: nil ) { item, _ in
128+ if let text = item as? String , !text. trimmingCharacters ( in: . whitespacesAndNewlines) . isEmpty {
129+ if let url = URL ( string: text. trimmingCharacters ( in: . whitespacesAndNewlines) ) {
130+ let browserTab = BrowserTab ( title: url. absoluteString, url: url. absoluteString, browser: " default " )
131+ DispatchQueue . main. async {
132+ contextManager. addItem ( . browserTab( browserTab) , to: contextManager. contexts [ contextIndex] . id)
133+ print ( " Added browser tab: \( browserTab. title) " )
134+ }
135+ }
136+ }
137+ }
138+ handled = true
139+ } else if provider. hasItemConformingToTypeIdentifier ( UTType . url. identifier) || provider. hasItemConformingToTypeIdentifier ( " public.url " ) {
140+ let typeIdentifier = provider. hasItemConformingToTypeIdentifier ( UTType . url. identifier) ? UTType . url. identifier : " public.url "
141+ provider. loadItem ( forTypeIdentifier: typeIdentifier, options: nil ) { item, error in
142+ if let error = error {
143+ print ( " Error loading URL: \( error) " )
144+ return
145+ }
146+ var url : URL ?
147+ if let urlObject = item as? URL {
148+ url = urlObject
149+ } else if let data = item as? Data {
150+ url = URL ( dataRepresentation: data, relativeTo: nil )
151+ print ( " Converted data to URL: \( String ( describing: url) ) " )
152+ } else if let str = item as? String , let urlFromString = URL ( string: str) {
153+ url = urlFromString
154+ }
155+ if let url = url {
156+ let browserTab = BrowserTab ( title: url. absoluteString, url: url. absoluteString, browser: " default " )
157+ DispatchQueue . main. async {
158+ contextManager. addItem ( . browserTab( browserTab) , to: contextManager. contexts [ contextIndex] . id)
159+ print ( " Added browser tab: \( browserTab. title) " )
160+ }
161+ }
162+ }
163+ handled = true
164+ }
165+ }
166+ print ( " Drop handling complete. Handled: \( handled) " )
167+ return handled
168+ }
169+ }
0 commit comments