diff --git a/Package.swift b/Package.swift index 1074575..18f7a22 100644 --- a/Package.swift +++ b/Package.swift @@ -33,7 +33,7 @@ let package = Package( name: "ClickItLite", dependencies: [], path: "Sources/ClickIt/Lite", - resources: [] + resources: [.process("Resources")] ), .testTarget( name: "ClickItTests", diff --git a/Sources/ClickIt/Lite/ClickItLiteApp.swift b/Sources/ClickIt/Lite/ClickItLiteApp.swift index feb32d4..90057cf 100644 --- a/Sources/ClickIt/Lite/ClickItLiteApp.swift +++ b/Sources/ClickIt/Lite/ClickItLiteApp.swift @@ -9,6 +9,11 @@ import SwiftUI @main struct ClickItLiteApp: App { + init() { + // Activate custom target cursor when app launches + SimpleCursorManager.shared.activateCustomCursor() + } + var body: some Scene { WindowGroup { SimplifiedMainView() diff --git a/Sources/ClickIt/Lite/Resources/target-64.png b/Sources/ClickIt/Lite/Resources/target-64.png new file mode 100644 index 0000000..6d590cb Binary files /dev/null and b/Sources/ClickIt/Lite/Resources/target-64.png differ diff --git a/Sources/ClickIt/Lite/SimpleClickEngine.swift b/Sources/ClickIt/Lite/SimpleClickEngine.swift index 1913e92..0371390 100644 --- a/Sources/ClickIt/Lite/SimpleClickEngine.swift +++ b/Sources/ClickIt/Lite/SimpleClickEngine.swift @@ -104,6 +104,9 @@ final class SimpleClickEngine { mouseButton = .right } + // Debug logging + print("đŸ–ąī¸ Performing \(type) click at (\(Int(point.x)), \(Int(point.y)))") + // Create and post mouse down event if let mouseDown = CGEvent( mouseEventSource: nil, diff --git a/Sources/ClickIt/Lite/SimpleCursorManager.swift b/Sources/ClickIt/Lite/SimpleCursorManager.swift new file mode 100644 index 0000000..a5d377d --- /dev/null +++ b/Sources/ClickIt/Lite/SimpleCursorManager.swift @@ -0,0 +1,166 @@ +// +// SimpleCursorManager.swift +// ClickIt Lite +// +// Manages custom cursor for ClickIt Lite application. +// + +import AppKit +import SwiftUI + +class SimpleCursorManager { + + // MARK: - Singleton + + static let shared = SimpleCursorManager() + + // MARK: - Properties + + private var customCursor: NSCursor? + private var originalCursor: NSCursor? + private var cursorUpdateTimer: Timer? + private var isCursorActive = false + + // MARK: - Initialization + + private init() { + setupCustomCursor() + } + + // MARK: - Public Methods + + /// Activates the custom target cursor system-wide + func activateCustomCursor() { + guard let customCursor = customCursor else { + print("❌ Custom cursor not available") + showDebugAlert("Cursor Failed", "Custom cursor could not be loaded. Check console for details.") + return + } + + // Prevent double activation + if isCursorActive { + print("â„šī¸ Custom cursor already active") + return + } + + isCursorActive = true + + // Store original cursor for potential restoration + originalCursor = NSCursor.current + + // Set custom cursor immediately + customCursor.set() + + // Keep re-setting the cursor on a timer to ensure it stays active + // This is needed because macOS resets cursor on window focus changes and other events + cursorUpdateTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in + guard let self = self, self.isCursorActive else { return } + self.customCursor?.set() + } + + print("✅ Custom target cursor activated with continuous refresh") + // Optional: Uncomment to show success alert + // showDebugAlert("Cursor Active", "Custom target cursor has been activated") + } + + /// Shows a debug alert (for development/debugging) + private func showDebugAlert(_ title: String, _ message: String) { + DispatchQueue.main.async { + let alert = NSAlert() + alert.messageText = title + alert.informativeText = message + alert.alertStyle = .informational + alert.addButton(withTitle: "OK") + alert.runModal() + } + } + + /// Restores the default system cursor + func restoreDefaultCursor() { + isCursorActive = false + + // Stop the cursor update timer + cursorUpdateTimer?.invalidate() + cursorUpdateTimer = nil + + // Restore arrow cursor + NSCursor.arrow.set() + + print("✅ Default cursor restored") + } + + // MARK: - Private Methods + + /// Finds the correct resource bundle for Swift Package Manager + private func findResourceBundle() -> Bundle { + // Strategy 1: Look for SPM resource bundle (swift run / debug builds) + if let bundleURL = Bundle.main.url(forResource: "ClickIt_ClickItLite", withExtension: "bundle"), + let resourceBundle = Bundle(url: bundleURL) { + print("✅ Found SPM resource bundle at: \(bundleURL.path)") + return resourceBundle + } + + // Strategy 2: Look in Contents/Resources for packaged .app builds + if let resourcePath = Bundle.main.resourcePath, + let bundlePath = Bundle(path: resourcePath + "/ClickIt_ClickItLite.bundle") { + print("✅ Found resource bundle in app Resources: \(resourcePath)/ClickIt_ClickItLite.bundle") + return bundlePath + } + + // Strategy 3: Try Module.bundle (for Xcode builds) + if let bundleURL = Bundle.main.url(forResource: "ClickItLite_ClickItLite", withExtension: "bundle"), + let resourceBundle = Bundle(url: bundleURL) { + print("✅ Found module resource bundle at: \(bundleURL.path)") + return resourceBundle + } + + // Fallback to Bundle.main (resources might be directly in app bundle) + print("âš ī¸ Using Bundle.main as fallback") + return Bundle.main + } + + private func setupCustomCursor() { + // Debug: Print bundle path + print("🔍 Bundle path: \(Bundle.main.bundlePath)") + print("🔍 Resource path: \(Bundle.main.resourcePath ?? "nil")") + + // Find the correct resource bundle for Swift Package Manager + let bundle = findResourceBundle() + print("🔍 Using bundle: \(bundle.bundlePath)") + + // Try to load the target image from resources + guard let imageURL = bundle.url(forResource: "target-64", withExtension: "png") else { + print("❌ Failed to find target-64.png in bundle") + print("🔍 Searched in: \(bundle.bundleURL)") + + // Try to list all resources + if let resourcePath = bundle.resourcePath { + do { + let items = try FileManager.default.contentsOfDirectory(atPath: resourcePath) + print("📁 Available resources in bundle: \(items)") + } catch { + print("❌ Could not list resources: \(error)") + } + } + return + } + + print("✅ Found image at: \(imageURL.path)") + + guard let image = NSImage(contentsOf: imageURL) else { + print("❌ Failed to load NSImage from: \(imageURL.path)") + return + } + + print("✅ NSImage loaded, original size: \(image.size)") + + // Set the cursor size (64x64 pixels) + image.size = NSSize(width: 64, height: 64) + + // Create cursor with hotspot at center (32, 32) + let hotspot = NSPoint(x: 32, y: 32) + customCursor = NSCursor(image: image, hotSpot: hotspot) + + print("✅ Custom cursor created successfully") + } +} diff --git a/build_app_unified.sh b/build_app_unified.sh index b11fb04..af35ed7 100755 --- a/build_app_unified.sh +++ b/build_app_unified.sh @@ -279,11 +279,35 @@ EOF # Make executable chmod +x "$APP_BUNDLE/Contents/MacOS/$APP_NAME" - + + # Copy SPM resource bundles to app bundle + echo "đŸ“Ļ Copying resource bundles..." + # For ClickItLite: ClickIt_ClickItLite.bundle + # For ClickIt: ClickIt_ClickIt.bundle + RESOURCE_BUNDLE_NAME="ClickIt_${SPM_TARGET}.bundle" + + # Find the resource bundle in the build output + for arch in "${ARCH_LIST[@]}"; do + BUILD_PATH=$(swift build -c "$BUILD_MODE" --arch "$arch" --product "$SPM_TARGET" --show-bin-path) + RESOURCE_BUNDLE_PATH="$BUILD_PATH/$RESOURCE_BUNDLE_NAME" + + if [ -d "$RESOURCE_BUNDLE_PATH" ]; then + echo "✅ Found resource bundle at: $RESOURCE_BUNDLE_PATH" + cp -R "$RESOURCE_BUNDLE_PATH" "$APP_BUNDLE/Contents/Resources/" + echo "✅ Copied $RESOURCE_BUNDLE_NAME to app bundle" + break + fi + done + + if [ ! -d "$APP_BUNDLE/Contents/Resources/$RESOURCE_BUNDLE_NAME" ]; then + echo "âš ī¸ Warning: Resource bundle $RESOURCE_BUNDLE_NAME not found" + echo " App may not be able to load custom resources" + fi + # Fix rpath for bundled frameworks (SPM builds) echo "🔧 Adding Frameworks directory to rpath..." install_name_tool -add_rpath "@loader_path/../Frameworks" "$APP_BUNDLE/Contents/MacOS/$APP_NAME" 2>/dev/null || echo " rpath already exists or modification failed" - + echo "✅ SPM build completed successfully!" fi