From 540480f2db35a961591ed277f33533a6c675937b Mon Sep 17 00:00:00 2001 From: igor Date: Thu, 8 Jan 2026 01:01:13 -0800 Subject: [PATCH] Makefiles and various changes. --- .gitignore | 2 + Makefile | 45 ++++++++++++ src/AboutViewController.swift | 129 ++++++++++++++++------------------ src/AppDelegate.swift | 47 +++++++++++++ src/Info.plist | 8 ++- src/Makefile | 19 +++-- src/PathManager.swift | 6 +- 7 files changed, 171 insertions(+), 85 deletions(-) create mode 100644 Makefile diff --git a/.gitignore b/.gitignore index e3282f7..5ac5cfb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ .DS_Store arm64 x86_64 +build Grapp Grapp.app +ids diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1f48d33 --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +APPLE_ID := $(shell cat ./ids/APPLE_ID) +TEAM_ID := $(shell cat ./ids/TEAM_ID) +APP_SPECIFIC_PASSWORD := $(shell cat ./ids/APP_SPECIFIC_PASSWORD) + +exe = Grapp + +$(exe).app: + $(MAKE) -C src FLAGS=-O CFLAGS=-O3 default + +container: + ditto -c -k --keepParent ./src/$(exe).app ./build/$(exe).zip + +notarize: + xcrun notarytool submit ./build/$(exe).zip \ + --apple-id "$(APPLE_ID)" --team-id "$(TEAM_ID)" \ + --password "$(APP_SPECIFIC_PASSWORD)" --wait + +staple: + cd build && \ + ditto -xk $(exe).zip . && \ + rm $(exe).zip && \ + xcrun stapler staple $(exe).app + +zip: + cd build && \ + ditto -c -k --keepParent $(exe).app $(exe).zip + +dmg: + cp background.png build && cd build && \ + create-dmg --volname "$(exe) Installer" --window-size 600 400 \ + --background "background.png" --icon "$(exe).app" 200 170 \ + --app-drop-link 400 170 --icon-size 100 "$(exe).dmg" "$(exe)" + rm build/background.png + +all: $(exe).app container notarize staple zip dmg + +status: + @echo 'xcrun notarytool log "" --apple-id "$(APPLE_ID)" --team-id "$(TEAM_ID)" --password "$(APP_SPECIFIC_PASSWORD)" developer_log.json' + +clean: + rm -rf build + +clean-all: clean + mkdir build + @$(MAKE) -C src clean-all diff --git a/src/AboutViewController.swift b/src/AboutViewController.swift index 15629ef..90ff324 100644 --- a/src/AboutViewController.swift +++ b/src/AboutViewController.swift @@ -2,21 +2,14 @@ import AppKit // TODO: Change to appropriate links. fileprivate enum AboutLinks { - // static let website = "https://cmdbar.app" + static let website = "https://cmdbar.rednera.com" // static let documentation = "https://cmdbar.app/documentation" - // static let privacy = "https://cmdbar.app/#privacy-policy" - static let author = "https://kolokolnikov.org" + static let privacy = "https://cmdbar.rednera.com/#privacy-policy" + static let author = "https://kolokolnikov.dev" } enum Strings { - static let copyright = "Copyright © 2024\nGarikMI. All rights reserved." - static let evaluationTitle = "License - Evaluation" - static let evaluationMessage = "You are currently using evaluation license. CmdBar will quit after 20 minutes. If you already own a license, enter it below or purchase a license." - static let activate = "Activate" - static let proTitle = "License - Activated" - static let proMessage = "Thank you for purchasing CmdBar! Enjoy!" - static let deactivate = "Deactivate" - static let activating = "Activating..." + static let copyright = "Copyright © 2026 Rednera.\nAll rights reserved." } class AboutViewController: NSViewController, NSTextFieldDelegate { @@ -83,26 +76,26 @@ class AboutViewController: NSViewController, NSTextFieldDelegate { return textField }() - private var authorButton: NSButton = { - let button = NSButton() - button.title = "Author" - button.sizeToFit() - button.bezelStyle = .rounded - button.action = #selector(author) - button.translatesAutoresizingMaskIntoConstraints = false - return button - }() - - // private var privacyButton: NSButton = { + // private var authorButton: NSButton = { // let button = NSButton() - // button.title = "Privacy Policy" + // button.title = "Author" // button.sizeToFit() // button.bezelStyle = .rounded - // button.action = #selector(privacy) + // button.action = #selector(author) // button.translatesAutoresizingMaskIntoConstraints = false // return button // }() + private var privacyButton: NSButton = { + let button = NSButton() + button.title = "Privacy Policy" + button.sizeToFit() + button.bezelStyle = .rounded + button.action = #selector(privacy) + button.translatesAutoresizingMaskIntoConstraints = false + return button + }() + // private var documentationButton: NSButton = { // let button = NSButton() // button.title = "Docs" @@ -112,16 +105,16 @@ class AboutViewController: NSViewController, NSTextFieldDelegate { // button.translatesAutoresizingMaskIntoConstraints = false // return button // }() - // - // private var websiteButton: NSButton = { - // let button = NSButton() - // button.title = "CmdBar.app" - // button.sizeToFit() - // button.bezelStyle = .rounded - // button.action = #selector(website) - // button.translatesAutoresizingMaskIntoConstraints = false - // return button - // }() + + private var websiteButton: NSButton = { + let button = NSButton() + button.title = "Grapp" + button.sizeToFit() + button.bezelStyle = .rounded + button.action = #selector(website) + button.translatesAutoresizingMaskIntoConstraints = false + return button + }() private var buttonsContainer: NSLayoutGuide = { let container = NSLayoutGuide() @@ -139,10 +132,10 @@ class AboutViewController: NSViewController, NSTextFieldDelegate { // Buttons view.addLayoutGuide(buttonsContainer) - // view.addSubview(privacyButton) + view.addSubview(privacyButton) // view.addSubview(documentationButton) - // view.addSubview(websiteButton) - view.addSubview(authorButton) + view.addSubview(websiteButton) + // view.addSubview(authorButton) setupConstraints() } @@ -208,51 +201,51 @@ class AboutViewController: NSViewController, NSTextFieldDelegate { buttonsContainer.centerXAnchor .constraint(equalTo: view.centerXAnchor), - authorButton.topAnchor - .constraint(equalTo: buttonsContainer.topAnchor), - authorButton.bottomAnchor - .constraint(equalTo: buttonsContainer.bottomAnchor), - authorButton.leadingAnchor - .constraint(equalTo: buttonsContainer.leadingAnchor), - authorButton.trailingAnchor - .constraint(equalTo: buttonsContainer.trailingAnchor), - - // privacyButton.topAnchor + // authorButton.topAnchor // .constraint(equalTo: buttonsContainer.topAnchor), - // privacyButton.bottomAnchor + // authorButton.bottomAnchor // .constraint(equalTo: buttonsContainer.bottomAnchor), - // privacyButton.leadingAnchor + // authorButton.leadingAnchor // .constraint(equalTo: buttonsContainer.leadingAnchor), - // + // authorButton.trailingAnchor + // .constraint(equalTo: buttonsContainer.trailingAnchor), + + privacyButton.topAnchor + .constraint(equalTo: buttonsContainer.topAnchor), + privacyButton.bottomAnchor + .constraint(equalTo: buttonsContainer.bottomAnchor), + privacyButton.leadingAnchor + .constraint(equalTo: buttonsContainer.leadingAnchor), + // documentationButton.firstBaselineAnchor // .constraint(equalTo: privacyButton.firstBaselineAnchor), // documentationButton.leadingAnchor // .constraint(equalTo: privacyButton.trailingAnchor, // constant: ViewConstants.spacing10), - // - // websiteButton.firstBaselineAnchor - // .constraint(equalTo: privacyButton.firstBaselineAnchor), - // websiteButton.leadingAnchor - // .constraint(equalTo: documentationButton.trailingAnchor, - // constant: ViewConstants.spacing10), - // websiteButton.trailingAnchor - // .constraint(equalTo: buttonsContainer.trailingAnchor), + + websiteButton.firstBaselineAnchor + .constraint(equalTo: privacyButton.firstBaselineAnchor), + websiteButton.leadingAnchor + .constraint(equalTo: privacyButton.trailingAnchor, + constant: ViewConstants.spacing10), + websiteButton.trailingAnchor + .constraint(equalTo: buttonsContainer.trailingAnchor), ]) } - @objc private func author() { - NSWorkspace.shared.open(URL(string: AboutLinks.author)!) + // @objc private func author() { + // NSWorkspace.shared.open(URL(string: AboutLinks.author)!) + // } + + @objc private func privacy() { + NSWorkspace.shared.open(URL(string: AboutLinks.privacy)!) } - // @objc private func privacy() { - // NSWorkspace.shared.open(URL(string: AboutLinks.privacy)!) - // } - // // @objc private func documentation() { // NSWorkspace.shared.open(URL(string: AboutLinks.documentation)!) // } - // - // @objc private func website() { - // NSWorkspace.shared.open(URL(string: AboutLinks.website)!) - // } + + @objc private func website() { + NSWorkspace.shared.open(URL(string: AboutLinks.website)!) + } } diff --git a/src/AppDelegate.swift b/src/AppDelegate.swift index 82dcfff..3893bac 100644 --- a/src/AppDelegate.swift +++ b/src/AppDelegate.swift @@ -2,6 +2,41 @@ import Cocoa import Carbon import ServiceManagement +class ClipboardMonitor { + private var timer: Timer? + private var lastChangeCount: Int = NSPasteboard.general.changeCount + + func startMonitoring(interval: TimeInterval = 0.5) { + timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { [weak self] _ in + print("CALLED") + self?.checkForChanges() + } + } + + func stopMonitoring() { + timer?.invalidate() + timer = nil + } + + private func checkForChanges() { + print("CHECKING") + let pasteboard = NSPasteboard.general + if pasteboard.changeCount != lastChangeCount { + lastChangeCount = pasteboard.changeCount + handlePasteboardChange(pasteboard) + } + } + + private func handlePasteboardChange(_ pasteboard: NSPasteboard) { + if let copiedString = pasteboard.string(forType: .string) { + print("Clipboard changed: \(copiedString)") + } else { + print("Clipboard changed (non-string content)") + } + } +} + + class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { let fileManager = FileManager.default @@ -10,7 +45,19 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { MenulessWindow(viewController: SettingsViewController()) let aboutWindow = MenulessWindow(viewController: AboutViewController()) + var monitor: ClipboardMonitor? + func applicationDidFinishLaunching(_ notification: Notification) { + + +monitor = ClipboardMonitor() +monitor?.startMonitoring() + + + + + + settingsWindow.title = "Settings" aboutWindow.level = .statusBar diff --git a/src/Info.plist b/src/Info.plist index 26bb547..ec531bc 100644 --- a/src/Info.plist +++ b/src/Info.plist @@ -11,7 +11,7 @@ CFBundleIconName AppIcon CFBundleIdentifier - com.garikme.Grapp + com.rednera.Grapp CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -19,13 +19,13 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.2 + 0.2.0 CFBundleSupportedPlatforms MacOSX CFBundleVersion - 0.2 + 0.2.0 DTPlatformName macosx DTPlatformVersion @@ -40,5 +40,7 @@ NSPrincipalClass NSApplication + NSHumanReadableCopyright + Copyright © 2026 Rednera. All rights reserved. diff --git a/src/Makefile b/src/Makefile index 8408377..de0be60 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,3 +1,6 @@ +APPLE_DEVELOPMENT := $(shell cat ../ids/APPLE_DEVELOPMENT) +APPLE_DEVELOPER_ID_APPLICATION := $(shell cat ../ids/APPLE_DEVELOPER_ID_APPLICATION) + FLAGS = -g CFLAGS = -g #-O @@ -23,12 +26,6 @@ FRAMEWORKS = -framework AppKit -framework ServiceManagement default: $(EXEC).app -# HACK: Target is getting touched because timestamps of the generated -# object file don't change unless there's an actual change in the -# outputted object code. This results in this target running every -# single time. I'm not sure whether that's the exact reason, but -# I can't imagine why timestamps wouldn't change. When clang -# generates same exact executable, timestamps do change. ./arm64/%.o: %.swift swift -frontend -c \ -target arm64-apple-macos$(MACOS_VERSION) $(FLAGS) \ @@ -86,11 +83,11 @@ $(EXEC).app: $(EXEC) mkdir -p $@/Contents/Resources/ && \ cp Info.plist $@/Contents/ && \ cp resources/AppIcon.icns $@/Contents/Resources/ && \ - cp $(EXEC) $@/Contents/MacOS/ - # $(if $(DEBUG), codesign --entitlements Grapp.entitlements \ - # -s ${APPLE_DEVELOPMENT} -f --timestamp -o runtime $(EXEC).app, \ - # codesign -s ${APPLE_DEVELOPER_ID_APPLICATION} -f --timestamp \ - # -o runtime $(EXEC).app) + cp $(EXEC) $@/Contents/MacOS/ && \ + $(if $(DEBUG), codesign --entitlements Grapp.entitlements \ + -s ${APPLE_DEVELOPMENT} -f --timestamp -o runtime $(EXEC).app, \ + codesign -s ${APPLE_DEVELOPER_ID_APPLICATION} -f --timestamp \ + -o runtime $(EXEC).app) clean: rm -rf $(EXEC) $(EXEC).app arm64 x86_64 diff --git a/src/PathManager.swift b/src/PathManager.swift index 8c5ea4b..44ba077 100644 --- a/src/PathManager.swift +++ b/src/PathManager.swift @@ -19,9 +19,9 @@ final class PathManager { "/Applications", "/System/Applications", "/System/Applications/Utilities", - "/System/Library/CoreServices", - "/Applications/Xcode.app/Contents/Applications", - "/System/Library/CoreServices/Applications" + "/System/Library/CoreServices", // TODO: NOTE: Remove this one? + "/System/Library/CoreServices/Applications", + "/Applications/Xcode.app/Contents/Applications" ] private(set) var paths: [String: [Program]] = [:]