Makefiles and various changes.

This commit is contained in:
2026-01-08 01:01:13 -08:00
parent 42fe921dbc
commit 540480f2db
7 changed files with 171 additions and 85 deletions

2
.gitignore vendored
View File

@@ -1,5 +1,7 @@
.DS_Store .DS_Store
arm64 arm64
x86_64 x86_64
build
Grapp Grapp
Grapp.app Grapp.app
ids

45
Makefile Normal file
View File

@@ -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

View File

@@ -2,21 +2,14 @@ import AppKit
// TODO: Change to appropriate links. // TODO: Change to appropriate links.
fileprivate enum AboutLinks { 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 documentation = "https://cmdbar.app/documentation"
// static let privacy = "https://cmdbar.app/#privacy-policy" static let privacy = "https://cmdbar.rednera.com/#privacy-policy"
static let author = "https://kolokolnikov.org" static let author = "https://kolokolnikov.dev"
} }
enum Strings { enum Strings {
static let copyright = "Copyright © 2024\nGarikMI. All rights reserved." static let copyright = "Copyright © 2026 Rednera.\nAll 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..."
} }
class AboutViewController: NSViewController, NSTextFieldDelegate { class AboutViewController: NSViewController, NSTextFieldDelegate {
@@ -83,26 +76,26 @@ class AboutViewController: NSViewController, NSTextFieldDelegate {
return textField return textField
}() }()
private var authorButton: NSButton = { // 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 = {
// let button = NSButton() // let button = NSButton()
// button.title = "Privacy Policy" // button.title = "Author"
// button.sizeToFit() // button.sizeToFit()
// button.bezelStyle = .rounded // button.bezelStyle = .rounded
// button.action = #selector(privacy) // button.action = #selector(author)
// button.translatesAutoresizingMaskIntoConstraints = false // button.translatesAutoresizingMaskIntoConstraints = false
// return button // 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 = { // private var documentationButton: NSButton = {
// let button = NSButton() // let button = NSButton()
// button.title = "Docs" // button.title = "Docs"
@@ -112,16 +105,16 @@ class AboutViewController: NSViewController, NSTextFieldDelegate {
// button.translatesAutoresizingMaskIntoConstraints = false // button.translatesAutoresizingMaskIntoConstraints = false
// return button // return button
// }() // }()
//
// private var websiteButton: NSButton = { private var websiteButton: NSButton = {
// let button = NSButton() let button = NSButton()
// button.title = "CmdBar.app" button.title = "Grapp"
// button.sizeToFit() button.sizeToFit()
// button.bezelStyle = .rounded button.bezelStyle = .rounded
// button.action = #selector(website) button.action = #selector(website)
// button.translatesAutoresizingMaskIntoConstraints = false button.translatesAutoresizingMaskIntoConstraints = false
// return button return button
// }() }()
private var buttonsContainer: NSLayoutGuide = { private var buttonsContainer: NSLayoutGuide = {
let container = NSLayoutGuide() let container = NSLayoutGuide()
@@ -139,10 +132,10 @@ class AboutViewController: NSViewController, NSTextFieldDelegate {
// Buttons // Buttons
view.addLayoutGuide(buttonsContainer) view.addLayoutGuide(buttonsContainer)
// view.addSubview(privacyButton) view.addSubview(privacyButton)
// view.addSubview(documentationButton) // view.addSubview(documentationButton)
// view.addSubview(websiteButton) view.addSubview(websiteButton)
view.addSubview(authorButton) // view.addSubview(authorButton)
setupConstraints() setupConstraints()
} }
@@ -208,51 +201,51 @@ class AboutViewController: NSViewController, NSTextFieldDelegate {
buttonsContainer.centerXAnchor buttonsContainer.centerXAnchor
.constraint(equalTo: view.centerXAnchor), .constraint(equalTo: view.centerXAnchor),
authorButton.topAnchor // 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
// .constraint(equalTo: buttonsContainer.topAnchor), // .constraint(equalTo: buttonsContainer.topAnchor),
// privacyButton.bottomAnchor // authorButton.bottomAnchor
// .constraint(equalTo: buttonsContainer.bottomAnchor), // .constraint(equalTo: buttonsContainer.bottomAnchor),
// privacyButton.leadingAnchor // authorButton.leadingAnchor
// .constraint(equalTo: buttonsContainer.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 // documentationButton.firstBaselineAnchor
// .constraint(equalTo: privacyButton.firstBaselineAnchor), // .constraint(equalTo: privacyButton.firstBaselineAnchor),
// documentationButton.leadingAnchor // documentationButton.leadingAnchor
// .constraint(equalTo: privacyButton.trailingAnchor, // .constraint(equalTo: privacyButton.trailingAnchor,
// constant: ViewConstants.spacing10), // constant: ViewConstants.spacing10),
//
// websiteButton.firstBaselineAnchor websiteButton.firstBaselineAnchor
// .constraint(equalTo: privacyButton.firstBaselineAnchor), .constraint(equalTo: privacyButton.firstBaselineAnchor),
// websiteButton.leadingAnchor websiteButton.leadingAnchor
// .constraint(equalTo: documentationButton.trailingAnchor, .constraint(equalTo: privacyButton.trailingAnchor,
// constant: ViewConstants.spacing10), constant: ViewConstants.spacing10),
// websiteButton.trailingAnchor websiteButton.trailingAnchor
// .constraint(equalTo: buttonsContainer.trailingAnchor), .constraint(equalTo: buttonsContainer.trailingAnchor),
]) ])
} }
@objc private func author() { // @objc private func author() {
NSWorkspace.shared.open(URL(string: AboutLinks.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() { // @objc private func documentation() {
// NSWorkspace.shared.open(URL(string: AboutLinks.documentation)!) // NSWorkspace.shared.open(URL(string: AboutLinks.documentation)!)
// } // }
//
// @objc private func website() { @objc private func website() {
// NSWorkspace.shared.open(URL(string: AboutLinks.website)!) NSWorkspace.shared.open(URL(string: AboutLinks.website)!)
// } }
} }

View File

@@ -2,6 +2,41 @@ import Cocoa
import Carbon import Carbon
import ServiceManagement 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 { class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
let fileManager = FileManager.default let fileManager = FileManager.default
@@ -10,7 +45,19 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
MenulessWindow(viewController: SettingsViewController()) MenulessWindow(viewController: SettingsViewController())
let aboutWindow = MenulessWindow(viewController: AboutViewController()) let aboutWindow = MenulessWindow(viewController: AboutViewController())
var monitor: ClipboardMonitor?
func applicationDidFinishLaunching(_ notification: Notification) { func applicationDidFinishLaunching(_ notification: Notification) {
monitor = ClipboardMonitor()
monitor?.startMonitoring()
settingsWindow.title = "Settings" settingsWindow.title = "Settings"
aboutWindow.level = .statusBar aboutWindow.level = .statusBar

View File

@@ -11,7 +11,7 @@
<key>CFBundleIconName</key> <key>CFBundleIconName</key>
<string>AppIcon</string> <string>AppIcon</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>com.garikme.Grapp</string> <string>com.rednera.Grapp</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
@@ -19,13 +19,13 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>0.2</string> <string>0.2.0</string>
<key>CFBundleSupportedPlatforms</key> <key>CFBundleSupportedPlatforms</key>
<array> <array>
<string>MacOSX</string> <string>MacOSX</string>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>0.2</string> <string>0.2.0</string>
<key>DTPlatformName</key> <key>DTPlatformName</key>
<string>macosx</string> <string>macosx</string>
<key>DTPlatformVersion</key> <key>DTPlatformVersion</key>
@@ -40,5 +40,7 @@
<true/> <true/>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>
<string>NSApplication</string> <string>NSApplication</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2026 Rednera. All rights reserved.</string>
</dict> </dict>
</plist> </plist>

View File

@@ -1,3 +1,6 @@
APPLE_DEVELOPMENT := $(shell cat ../ids/APPLE_DEVELOPMENT)
APPLE_DEVELOPER_ID_APPLICATION := $(shell cat ../ids/APPLE_DEVELOPER_ID_APPLICATION)
FLAGS = -g FLAGS = -g
CFLAGS = -g CFLAGS = -g
#-O #-O
@@ -23,12 +26,6 @@ FRAMEWORKS = -framework AppKit -framework ServiceManagement
default: $(EXEC).app 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 ./arm64/%.o: %.swift
swift -frontend -c \ swift -frontend -c \
-target arm64-apple-macos$(MACOS_VERSION) $(FLAGS) \ -target arm64-apple-macos$(MACOS_VERSION) $(FLAGS) \
@@ -86,11 +83,11 @@ $(EXEC).app: $(EXEC)
mkdir -p $@/Contents/Resources/ && \ mkdir -p $@/Contents/Resources/ && \
cp Info.plist $@/Contents/ && \ cp Info.plist $@/Contents/ && \
cp resources/AppIcon.icns $@/Contents/Resources/ && \ cp resources/AppIcon.icns $@/Contents/Resources/ && \
cp $(EXEC) $@/Contents/MacOS/ cp $(EXEC) $@/Contents/MacOS/ && \
# $(if $(DEBUG), codesign --entitlements Grapp.entitlements \ $(if $(DEBUG), codesign --entitlements Grapp.entitlements \
# -s ${APPLE_DEVELOPMENT} -f --timestamp -o runtime $(EXEC).app, \ -s ${APPLE_DEVELOPMENT} -f --timestamp -o runtime $(EXEC).app, \
# codesign -s ${APPLE_DEVELOPER_ID_APPLICATION} -f --timestamp \ codesign -s ${APPLE_DEVELOPER_ID_APPLICATION} -f --timestamp \
# -o runtime $(EXEC).app) -o runtime $(EXEC).app)
clean: clean:
rm -rf $(EXEC) $(EXEC).app arm64 x86_64 rm -rf $(EXEC) $(EXEC).app arm64 x86_64

View File

@@ -19,9 +19,9 @@ final class PathManager {
"/Applications", "/Applications",
"/System/Applications", "/System/Applications",
"/System/Applications/Utilities", "/System/Applications/Utilities",
"/System/Library/CoreServices", "/System/Library/CoreServices", // TODO: NOTE: Remove this one?
"/Applications/Xcode.app/Contents/Applications", "/System/Library/CoreServices/Applications",
"/System/Library/CoreServices/Applications" "/Applications/Xcode.app/Contents/Applications"
] ]
private(set) var paths: [String: [Program]] = [:] private(set) var paths: [String: [Program]] = [:]