Makefiles and various changes.
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,7 @@
|
||||
.DS_Store
|
||||
arm64
|
||||
x86_64
|
||||
build
|
||||
Grapp
|
||||
Grapp.app
|
||||
ids
|
||||
|
||||
45
Makefile
Normal file
45
Makefile
Normal 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
|
||||
@@ -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)!)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<key>CFBundleIconName</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.garikme.Grapp</string>
|
||||
<string>com.rednera.Grapp</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
@@ -19,13 +19,13 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.2</string>
|
||||
<string>0.2.0</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.2</string>
|
||||
<string>0.2.0</string>
|
||||
<key>DTPlatformName</key>
|
||||
<string>macosx</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
@@ -40,5 +40,7 @@
|
||||
<true/>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2026 Rednera. All rights reserved.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
19
src/Makefile
19
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
|
||||
|
||||
@@ -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]] = [:]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user