@opentray/ext-badge-darwin-arm64 0.11.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -4
- package/app/OpenTrayBadgeHelper.app.zip +0 -0
- package/lib/libopentray_ext_badge.dylib +0 -0
- package/package.json +4 -3
- package/app/Info.plist +0 -32
- package/app/main.swift +0 -190
package/README.md
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
# @opentray/ext-badge-darwin-arm64
|
|
2
2
|
|
|
3
|
-
macOS arm64
|
|
3
|
+
macOS arm64 native package for `@opentray/ext-badge`.
|
|
4
4
|
|
|
5
|
-
This package
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
This package stages two Darwin-owned artifacts:
|
|
6
|
+
|
|
7
|
+
- `lib/libopentray_ext_badge.dylib` for the OpenTray dynamic extension ABI
|
|
8
|
+
- `app/OpenTrayBadgeHelper.app.zip` for the Dock-facing helper bundle
|
|
9
|
+
|
|
10
|
+
The helper is intentionally separate from the OpenTray runtime host. It currently projects badge
|
|
11
|
+
text, overlay, attention, and click/quit lifecycle only; progress is not a Dock projection on this
|
|
12
|
+
helper.
|
|
8
13
|
|
|
9
14
|
## Build
|
|
10
15
|
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opentray/ext-badge-darwin-arm64",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "OpenTray badge Dock helper app for macOS arm64.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
"url": "https://github.com/jixoai/opentray"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
|
-
"
|
|
12
|
-
"app"
|
|
11
|
+
"lib/libopentray_ext_badge.dylib",
|
|
12
|
+
"app/OpenTrayBadgeHelper.app.zip",
|
|
13
|
+
"README.md"
|
|
13
14
|
],
|
|
14
15
|
"os": [
|
|
15
16
|
"darwin"
|
package/app/Info.plist
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
-
<plist version="1.0">
|
|
4
|
-
<dict>
|
|
5
|
-
<key>CFBundleDevelopmentRegion</key>
|
|
6
|
-
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
|
7
|
-
<key>CFBundleExecutable</key>
|
|
8
|
-
<string>OpenTrayBadgeHelper</string>
|
|
9
|
-
<key>CFBundleIconFile</key>
|
|
10
|
-
<string>AppIcon</string>
|
|
11
|
-
<key>CFBundleIdentifier</key>
|
|
12
|
-
<string>com.opentray.ext-badge.helper</string>
|
|
13
|
-
<key>CFBundleInfoDictionaryVersion</key>
|
|
14
|
-
<string>6.0</string>
|
|
15
|
-
<key>CFBundleName</key>
|
|
16
|
-
<string>OpenTray Badge Dock Helper</string>
|
|
17
|
-
<key>CFBundleDisplayName</key>
|
|
18
|
-
<string>OpenTray Badge</string>
|
|
19
|
-
<key>CFBundlePackageType</key>
|
|
20
|
-
<string>APPL</string>
|
|
21
|
-
<key>CFBundleShortVersionString</key>
|
|
22
|
-
<string>1.0</string>
|
|
23
|
-
<key>CFBundleVersion</key>
|
|
24
|
-
<string>1</string>
|
|
25
|
-
<key>LSMinimumSystemVersion</key>
|
|
26
|
-
<string>13.0</string>
|
|
27
|
-
<key>NSHighResolutionCapable</key>
|
|
28
|
-
<true/>
|
|
29
|
-
<key>NSPrincipalClass</key>
|
|
30
|
-
<string>NSApplication</string>
|
|
31
|
-
</dict>
|
|
32
|
-
</plist>
|
package/app/main.swift
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import AppKit
|
|
2
|
-
import Foundation
|
|
3
|
-
|
|
4
|
-
final class BadgeDockHelper: NSObject, NSApplicationDelegate {
|
|
5
|
-
private let dockTitle = ProcessInfo.processInfo.environment["OPENTRAY_BADGE_DOCK_TITLE"]
|
|
6
|
-
?? "OpenTray Badge"
|
|
7
|
-
private let dockIconName = ProcessInfo.processInfo.environment["OPENTRAY_BADGE_DOCK_ICON_NAME"]
|
|
8
|
-
?? "bell.badge"
|
|
9
|
-
private let dockIconPath = ProcessInfo.processInfo.environment["OPENTRAY_BADGE_DOCK_ICON_PATH"]
|
|
10
|
-
private let helperLog = argumentValue("--log-path") ?? ProcessInfo.processInfo.environment["OPENTRAY_BADGE_DOCK_LOG"]
|
|
11
|
-
private let clickSignalPath = argumentValue("--click-signal") ?? ProcessInfo.processInfo.environment["OPENTRAY_BADGE_DOCK_CLICK_SIGNAL"]
|
|
12
|
-
private let quitSignalPath = argumentValue("--quit-signal") ?? ProcessInfo.processInfo.environment["OPENTRAY_BADGE_DOCK_QUIT_SIGNAL"]
|
|
13
|
-
private let quitNotifyPath = argumentValue("--quit-notify") ?? ProcessInfo.processInfo.environment["OPENTRAY_BADGE_DOCK_QUIT_NOTIFY"]
|
|
14
|
-
private let stateDir = argumentValue("--state-dir") ?? ProcessInfo.processInfo.environment["OPENTRAY_BADGE_DOCK_STATE_DIR"]
|
|
15
|
-
private let stateBadgePath = argumentValue("--badge-path") ?? ProcessInfo.processInfo.environment["OPENTRAY_BADGE_DOCK_BADGE_PATH"]
|
|
16
|
-
private let stateTitlePath = argumentValue("--title-path") ?? ProcessInfo.processInfo.environment["OPENTRAY_BADGE_DOCK_TITLE_PATH"]
|
|
17
|
-
private let stateIconNamePath = argumentValue("--icon-path") ?? ProcessInfo.processInfo.environment["OPENTRAY_BADGE_DOCK_ICON_NAME_PATH"]
|
|
18
|
-
private let forceMultiOpen = CommandLine.arguments.contains("--force-multi-open")
|
|
19
|
-
private var lifecycleTimer: Timer?
|
|
20
|
-
private var stateTimer: Timer?
|
|
21
|
-
private var cachedDockTitle: String
|
|
22
|
-
private var cachedDockBadge: String?
|
|
23
|
-
private var cachedDockIconName: String
|
|
24
|
-
private var cachedDockIconPath: String?
|
|
25
|
-
private var renderedSignature: String = ""
|
|
26
|
-
|
|
27
|
-
override init() {
|
|
28
|
-
self.cachedDockTitle = argumentValue("--title") ?? ProcessInfo.processInfo.environment["OPENTRAY_BADGE_DOCK_TITLE"] ?? "OpenTray Badge"
|
|
29
|
-
self.cachedDockBadge = argumentValue("--badge") ?? ProcessInfo.processInfo.environment["OPENTRAY_BADGE_DOCK_BADGE"]
|
|
30
|
-
self.cachedDockIconName = argumentValue("--icon-name") ?? ProcessInfo.processInfo.environment["OPENTRAY_BADGE_DOCK_ICON_NAME"] ?? "bell.badge"
|
|
31
|
-
self.cachedDockIconPath = ProcessInfo.processInfo.environment["OPENTRAY_BADGE_DOCK_ICON_PATH"]
|
|
32
|
-
super.init()
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
func applicationDidFinishLaunching(_ notification: Notification) {
|
|
36
|
-
NSApplication.shared.setActivationPolicy(.regular)
|
|
37
|
-
NSApplication.shared.applicationIconImage = placeholderDockIcon()
|
|
38
|
-
applyDockBadge(cachedDockBadge)
|
|
39
|
-
log("launched title=\(cachedDockTitle)")
|
|
40
|
-
startLifecycleWatch()
|
|
41
|
-
startStateWatch()
|
|
42
|
-
NSApp.activate(ignoringOtherApps: true)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
|
|
46
|
-
log("dock-reopen")
|
|
47
|
-
signalDockClick()
|
|
48
|
-
sender.activate(ignoringOtherApps: true)
|
|
49
|
-
return true
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
func applicationWillTerminate(_ notification: Notification) {
|
|
53
|
-
signalDockQuitNotify()
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
private func resolveDockIcon() -> NSImage? {
|
|
57
|
-
if let cachedDockIconPath, !cachedDockIconPath.isEmpty {
|
|
58
|
-
return NSImage(contentsOfFile: cachedDockIconPath)
|
|
59
|
-
}
|
|
60
|
-
return NSImage(systemSymbolName: cachedDockIconName, accessibilityDescription: cachedDockTitle)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
private func applyDockBadge(_ badge: String?) {
|
|
64
|
-
guard let badge, !badge.isEmpty else {
|
|
65
|
-
NSApp.dockTile.badgeLabel = nil
|
|
66
|
-
return
|
|
67
|
-
}
|
|
68
|
-
NSApp.dockTile.badgeLabel = badge
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
private func signalDockClick() {
|
|
72
|
-
guard let clickSignalPath, !clickSignalPath.isEmpty else {
|
|
73
|
-
return
|
|
74
|
-
}
|
|
75
|
-
let payload = "\(Date().timeIntervalSince1970)\n"
|
|
76
|
-
try? payload.write(toFile: clickSignalPath, atomically: true, encoding: .utf8)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
private func startLifecycleWatch() {
|
|
80
|
-
guard let quitSignalPath, !quitSignalPath.isEmpty else {
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
if forceMultiOpen {
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
lifecycleTimer = Timer.scheduledTimer(withTimeInterval: 0.25, repeats: true) { [weak self] timer in
|
|
87
|
-
guard let self else {
|
|
88
|
-
timer.invalidate()
|
|
89
|
-
return
|
|
90
|
-
}
|
|
91
|
-
if FileManager.default.fileExists(atPath: quitSignalPath) {
|
|
92
|
-
timer.invalidate()
|
|
93
|
-
NSApp.terminate(self)
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
private func startStateWatch() {
|
|
99
|
-
guard let stateDir, !stateDir.isEmpty else {
|
|
100
|
-
return
|
|
101
|
-
}
|
|
102
|
-
stateTimer = Timer.scheduledTimer(withTimeInterval: 0.25, repeats: true) { [weak self] _ in
|
|
103
|
-
self?.refreshDockState()
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
private func refreshDockState() {
|
|
108
|
-
var changed = false
|
|
109
|
-
if let stateBadgePath, let badge = readTrimmedFile(stateBadgePath) {
|
|
110
|
-
cachedDockBadge = badge.isEmpty ? nil : badge
|
|
111
|
-
changed = true
|
|
112
|
-
}
|
|
113
|
-
if let stateTitlePath, let title = readTrimmedFile(stateTitlePath), !title.isEmpty {
|
|
114
|
-
cachedDockTitle = title
|
|
115
|
-
changed = true
|
|
116
|
-
}
|
|
117
|
-
if let stateIconNamePath, let iconName = readTrimmedFile(stateIconNamePath), !iconName.isEmpty {
|
|
118
|
-
cachedDockIconName = iconName
|
|
119
|
-
changed = true
|
|
120
|
-
}
|
|
121
|
-
if changed {
|
|
122
|
-
updateDockTile()
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
private func readTrimmedFile(_ path: String) -> String? {
|
|
127
|
-
guard FileManager.default.fileExists(atPath: path) else {
|
|
128
|
-
return nil
|
|
129
|
-
}
|
|
130
|
-
return (try? String(contentsOfFile: path, encoding: .utf8))?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
private func updateDockTile() {
|
|
134
|
-
let signature = [cachedDockTitle, cachedDockBadge ?? "", cachedDockIconName, cachedDockIconPath ?? ""].joined(separator: "|")
|
|
135
|
-
guard signature != renderedSignature else {
|
|
136
|
-
return
|
|
137
|
-
}
|
|
138
|
-
renderedSignature = signature
|
|
139
|
-
applyDockBadge(cachedDockBadge)
|
|
140
|
-
NSApplication.shared.applicationIconImage = resolveDockIcon() ?? placeholderDockIcon()
|
|
141
|
-
NSApplication.shared.dockTile.display()
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
private func placeholderDockIcon() -> NSImage {
|
|
145
|
-
let image = NSImage(size: NSSize(width: 64, height: 64))
|
|
146
|
-
image.lockFocus()
|
|
147
|
-
NSColor.clear.set()
|
|
148
|
-
NSBezierPath(rect: NSRect(x: 0, y: 0, width: 64, height: 64)).fill()
|
|
149
|
-
image.unlockFocus()
|
|
150
|
-
return image
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
private func signalDockQuitNotify() {
|
|
154
|
-
guard let quitNotifyPath, !quitNotifyPath.isEmpty else {
|
|
155
|
-
return
|
|
156
|
-
}
|
|
157
|
-
let payload = "\(Date().timeIntervalSince1970)\n"
|
|
158
|
-
try? payload.write(toFile: quitNotifyPath, atomically: true, encoding: .utf8)
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
private func log(_ message: String) {
|
|
162
|
-
NSLog("OpenTray badge helper: %@", message)
|
|
163
|
-
if let helperLog {
|
|
164
|
-
try? "\(message)\n".appendToFile(atPath: helperLog)
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
private extension String {
|
|
170
|
-
func appendToFile(atPath path: String) throws {
|
|
171
|
-
let url = URL(fileURLWithPath: path)
|
|
172
|
-
let existing = (try? String(contentsOf: url, encoding: .utf8)) ?? ""
|
|
173
|
-
try (existing + self).write(to: url, atomically: true, encoding: .utf8)
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
private func argumentValue(_ name: String) -> String? {
|
|
178
|
-
let arguments = CommandLine.arguments
|
|
179
|
-
for index in 0..<arguments.count {
|
|
180
|
-
if arguments[index] == name, index + 1 < arguments.count {
|
|
181
|
-
return arguments[index + 1]
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
return nil
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
let delegate = BadgeDockHelper()
|
|
188
|
-
NSApplication.shared.delegate = delegate
|
|
189
|
-
NSApplication.shared.setActivationPolicy(.regular)
|
|
190
|
-
NSApplication.shared.run()
|