on 8e40b44 help sign in
CLAUDE.md+2−2
since splitsnapshots
unifiedsplit
@@ -20,8 +20,8 @@ click-to-copy on the headline, and threshold-crossing notifications.
20- `KeychainHelper.swift` — Keychain wrapper (KeychainStore + KeychainHelper + ZaiKeychainHelper)
20- `KeychainHelper.swift` — Keychain wrapper (KeychainStore + KeychainHelper + ZaiKeychainHelper)
21- `VersionFormatter.swift` — Version string formatting
21- `VersionFormatter.swift` — Version string formatting
22
22
23−The status bar title shows `⚡{balance}` (Hyper), `⁕{percent}%` (Claude 5-hour),
23+The status bar title shows `⚡{balance}` (Hyper), `🕐{percent}%` (Claude 5-hour),
24−`⁕{percent}%` (Claude 7-day), `Z{percent}%` (z.ai 5-hour), and `Z{percent}%`
24+`📅{percent}%` (Claude 7-day), `🤖{percent}%` (z.ai 5-hour), and `📆{percent}%`
25(z.ai weekly) — each omitted when absent or at 0%, and entirely absent when the
25(z.ai weekly) — each omitted when absent or at 0%, and entirely absent when the
26service is not configured (no API key or no credentials). An unconfigured service
26service is not configured (no API key or no credentials). An unconfigured service
27produces no placeholder in the menu bar. The three fetches are independent —
27produces no placeholder in the menu bar. The three fetches are independent —
README.md+6−6
@@ -9,8 +9,8 @@ A macOS menu bar application that displays usage limits and credit balances for
9## Features
9## Features
10
10
11- ⚡ **Hyper credits** — balance from `hyper.charm.land`, with sparkline trend, click-to-copy, and low-balance notifications
11- ⚡ **Hyper credits** — balance from `hyper.charm.land`, with sparkline trend, click-to-copy, and low-balance notifications
12−- ⁕ **Claude Code limits** — Pro/Max 5-hour and 7-day usage windows, with countdown to next reset. No setup: if you've signed in with the Claude Code CLI, it just appears
12+- 🤖 **Claude Code limits** — Pro/Max 5-hour and 7-day usage windows, with countdown to next reset. No setup: if you've signed in with the Claude Code CLI, it just appears
13−- Z **z.ai Coding Plan** — 5-hour and weekly token quota percentages, with reset countdowns. Enter your API key in settings
13+- 🤖 **z.ai Coding Plan** — 5-hour and weekly token quota percentages, with reset countdowns. Enter your API key in settings
14- 🔄 **Auto-refresh** — configurable interval (1m / 5m / 15m / 30m), plus refresh on wake from sleep
14- 🔄 **Auto-refresh** — configurable interval (1m / 5m / 15m / 30m), plus refresh on wake from sleep
15- 🖱️ **Right-click menu** — quick access to Refresh and Quit
15- 🖱️ **Right-click menu** — quick access to Refresh and Quit
16- 🔒 **Keychain storage** — API keys stored securely in macOS Keychain
16- 🔒 **Keychain storage** — API keys stored securely in macOS Keychain
@@ -24,10 +24,10 @@ Each service shows an emoji-prefixed segment when configured and above 0%:
24| Service | Emoji | Example |
24| Service | Emoji | Example |
25|---------|-------|---------|
25|---------|-------|---------|
26| Hyper balance | ⚡ | `⚡42` |
26| Hyper balance | ⚡ | `⚡42` |
27−| Claude 5-hour | ⁕ | `⁕62%` |
27+| Claude 5-hour | 🕐 | `🕐62%` |
28−| Claude 7-day | ⁕ | `⁕8%` |
28+| Claude 7-day | 📅 | `📅8%` |
29−| z.ai 5-hour | Z | `Z12%` |
29+| z.ai 5-hour | 🤖 | `🤖12%` |
30−| z.ai weekly | Z | `Z3%` |
30+| z.ai weekly | 📆 | `📆3%` |
31
31
32Unconfigured services don't appear at all — no placeholder, no icon.
32Unconfigured services don't appear at all — no placeholder, no icon.
33
33
UsageMenubar/Info.plist+1−1
@@ -7,7 +7,7 @@
7 <key>CFBundleDisplayName</key>
7 <key>CFBundleDisplayName</key>
8 <string>Usage Menubar</string>
8 <string>Usage Menubar</string>
9 <key>CFBundleIdentifier</key>
9 <key>CFBundleIdentifier</key>
10− <string>soy.iko.usage-menubar</string>
10+ <string>com.ilyakooo0.usage-menubar</string>
11 <key>CFBundleVersion</key>
11 <key>CFBundleVersion</key>
12 <string>1</string>
12 <string>1</string>
13 <key>CFBundleShortVersionString</key>
13 <key>CFBundleShortVersionString</key>
UsageMenubar/KeychainHelper.swift+2−2
@@ -82,7 +82,7 @@ struct KeychainStore {
82
82
83/// Simple wrapper around the macOS Keychain for storing the Hyper API key.
83/// Simple wrapper around the macOS Keychain for storing the Hyper API key.
84enum KeychainHelper {
84enum KeychainHelper {
85− private static let service = "soy.iko.usage-menubar"
85+ private static let service = "com.ilyakooo0.usage-menubar"
86 private static let account = "hyper-api-key"
86 private static let account = "hyper-api-key"
87
87
88 private static let store = KeychainStore(service: service, account: account)
88 private static let store = KeychainStore(service: service, account: account)
@@ -112,7 +112,7 @@ enum KeychainHelper {
112/// Uses the same keychain service as the Hyper key but a different account, so the
112/// Uses the same keychain service as the Hyper key but a different account, so the
113/// two keys coexist without clobbering each other.
113/// two keys coexist without clobbering each other.
114enum ZaiKeychainHelper {
114enum ZaiKeychainHelper {
115− private static let service = "soy.iko.usage-menubar"
115+ private static let service = "com.ilyakooo0.usage-menubar"
116 private static let account = "zai-api-key"
116 private static let account = "zai-api-key"
117
117
118 private static let store = KeychainStore(service: service, account: account)
118 private static let store = KeychainStore(service: service, account: account)
UsageMenubar/MenuView.swift+4−24
@@ -259,7 +259,7 @@ struct MenuView: View {
259 VStack(spacing: 8) {
259 VStack(spacing: 8) {
260 HStack(alignment: .firstTextBaseline, spacing: 8) {
260 HStack(alignment: .firstTextBaseline, spacing: 8) {
261 headlineNumber(
261 headlineNumber(
262− text: "⁕ \\(percent)%",
262+ text: "🅲 \(percent)%",
263 color: Self.usageColor(percent),
263 color: Self.usageColor(percent),
264 trend: viewModel.claudeTrend,
264 trend: viewModel.claudeTrend,
265 copiedConfirmation: viewModel.copiedClaudeConfirmation,
265 copiedConfirmation: viewModel.copiedClaudeConfirmation,
@@ -379,7 +379,7 @@ struct MenuView: View {
379 VStack(spacing: 8) {
379 VStack(spacing: 8) {
380 HStack(alignment: .firstTextBaseline, spacing: 8) {
380 HStack(alignment: .firstTextBaseline, spacing: 8) {
381 headlineNumber(
381 headlineNumber(
382− text: "Z \\(fiveHour)%",
382+ text: "🅉 \(fiveHour)%",
383 color: Self.usageColor(fiveHour),
383 color: Self.usageColor(fiveHour),
384 trend: viewModel.zaiTrend,
384 trend: viewModel.zaiTrend,
385 copiedConfirmation: viewModel.copiedZaiConfirmation,
385 copiedConfirmation: viewModel.copiedZaiConfirmation,
@@ -455,8 +455,7 @@ struct MenuView: View {
455 }
455 }
456
456
457 /// z.ai peak hours: 14:00–18:00 CST (UTC+8), i.e. 06:00–10:00 UTC.
457 /// z.ai peak hours: 14:00–18:00 CST (UTC+8), i.e. 06:00–10:00 UTC.
458− /// Limits are most likely to bind during this window. Displayed in the
458+ /// Limits are most likely to bind during this window.
459− /// user's local timezone so they know when to expect slowdowns.
460 private var zaiPeakHours: some View {
459 private var zaiPeakHours: some View {
461 let inPeak = ViewModel.zaiInPeakHours
460 let inPeak = ViewModel.zaiInPeakHours
462
461
@@ -465,31 +464,12 @@ struct MenuView: View {
465 .fill(inPeak ? Color.orange : Color.secondary.opacity(0.4))
464 .fill(inPeak ? Color.orange : Color.secondary.opacity(0.4))
466 .frame(width: 6, height: 6)
465 .frame(width: 6, height: 6)
467
466
468− Text(inPeak ? "Peak hours now" : "Peak \(Self.zaiPeakHoursLocal)")
467+ Text(inPeak ? "Peak hours now" : "Peak 14:00–18:00 CST")
469 .font(Self.footnoteFont)
468 .font(Self.footnoteFont)
470 .foregroundColor(inPeak ? .orange : .secondary)
469 .foregroundColor(inPeak ? .orange : .secondary)
471 }
470 }
472 }
471 }
473
472
474− /// z.ai peak hours converted to the user's local timezone, e.g. "09:00–13:00".
475− private static var zaiPeakHoursLocal: String {
476− let formatter = DateFormatter()
477− formatter.timeZone = TimeZone.current
478− formatter.dateFormat = "HH:mm"
479−
480− var utc = DateComponents()
481− utc.timeZone = TimeZone(identifier: "UTC")
482− utc.hour = 6
483− var calendar = Calendar(identifier: .gregorian)
484− calendar.timeZone = TimeZone(identifier: "UTC")!
485− let start = calendar.date(from: utc)!
486−
487− utc.hour = 10
488− let end = calendar.date(from: utc)!
489−
490− return "\(formatter.string(from: start))–\(formatter.string(from: end))"
491− }
492−
493 /// Formats the time until a z.ai window resets, e.g. "3h 20m".
473 /// Formats the time until a z.ai window resets, e.g. "3h 20m".
494 private static func zaiResetsIn(from date: Date) -> String {
474 private static func zaiResetsIn(from date: Date) -> String {
495 let interval = date.timeIntervalSince(Date())
475 let interval = date.timeIntervalSince(Date())
UsageMenubar/ViewModel.swift+4−4
@@ -7,7 +7,7 @@ import UserNotifications
7/// Posted when the user changes the refresh interval, so the app delegate can
7/// Posted when the user changes the refresh interval, so the app delegate can
8/// restart its timer with the new period.
8/// restart its timer with the new period.
9extension Notification.Name {
9extension Notification.Name {
10− static let refreshIntervalDidChange = Notification.Name("soy.iko.usage-menubar.refreshIntervalDidChange")
10+ static let refreshIntervalDidChange = Notification.Name("com.ilyakooo0.usage-menubar.refreshIntervalDidChange")
11}
11}
12
12
13/// The direction a metric has moved over the most recent history points.
13/// The direction a metric has moved over the most recent history points.
@@ -243,7 +243,7 @@ final class ViewModel: ObservableObject {
243 /// separated by spaces. An unconfigured service produces no segment.
243 /// separated by spaces. An unconfigured service produces no segment.
244 /// When z.ai is in peak hours, a 🔶 marker is appended to the z.ai segment.
244 /// When z.ai is in peak hours, a 🔶 marker is appended to the z.ai segment.
245 ///
245 ///
246− /// Example: `⚡42 · ⁕62% 8% · Z12% 3%🔶`
246+ /// Example: `⚡42 · 🅲62% 8% · 🅉12% 3%🔶`
247 ///
247 ///
248 /// Takes its values as parameters rather than reading the properties: `@Published`
248 /// Takes its values as parameters rather than reading the properties: `@Published`
249 /// publishes in `willSet`, so a Combine subscriber that called back into the view
249 /// publishes in `willSet`, so a Combine subscriber that called back into the view
@@ -274,7 +274,7 @@ final class ViewModel: ObservableObject {
274 claudeWindows.append("\(sevenDay)%")
274 claudeWindows.append("\(sevenDay)%")
275 }
275 }
276 if !claudeWindows.isEmpty {
276 if !claudeWindows.isEmpty {
277− segments.append("⁕" + claudeWindows.joined(separator: " "))
277+ segments.append("🅲" + claudeWindows.joined(separator: " "))
278 }
278 }
279
279
280 // z.ai — provider emoji + bare percentages
280 // z.ai — provider emoji + bare percentages
@@ -286,7 +286,7 @@ final class ViewModel: ObservableObject {
286 zaiWindows.append("\(zaiWeekly)%")
286 zaiWindows.append("\(zaiWeekly)%")
287 }
287 }
288 if !zaiWindows.isEmpty {
288 if !zaiWindows.isEmpty {
289− var segment = "Z" + zaiWindows.joined(separator: " ")
289+ var segment = "🅉" + zaiWindows.joined(separator: " ")
290 if zaiInPeakHours {
290 if zaiInPeakHours {
291 segment += "🔶"
291 segment += "🔶"
292 }
292 }
UsageMenubarTests/StatusBarTextTests.swift+22−22
@@ -18,7 +18,7 @@ final class StatusBarTextTests: XCTestCase {
18 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil,
18 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil,
19 zaiInPeakHours: false
19 zaiInPeakHours: false
20 )
20 )
21− XCTAssertEqual(title, "⚡42 · \u{2055}62% 8%")
21+ XCTAssertEqual(title, "⚡42 · \u{1F172}62% 8%")
22 }
22 }
23
23
24 func testHyperOnly() {
24 func testHyperOnly() {
@@ -40,7 +40,7 @@ final class StatusBarTextTests: XCTestCase {
40 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil,
40 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil,
41 zaiInPeakHours: false
41 zaiInPeakHours: false
42 )
42 )
43− XCTAssertEqual(title, "\u{2055}62% 8%")
43+ XCTAssertEqual(title, "\u{1F172}62% 8%")
44 }
44 }
45
45
46 func testLoadingWithNothingToShow() {
46 func testLoadingWithNothingToShow() {
@@ -76,7 +76,7 @@ final class StatusBarTextTests: XCTestCase {
76 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil,
76 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil,
77 zaiInPeakHours: false
77 zaiInPeakHours: false
78 )
78 )
79− XCTAssertEqual(title, "\u{2055}62%")
79+ XCTAssertEqual(title, "\u{1F172}62%")
80 }
80 }
81
81
82 func testLoadingKeepsAStaleBalanceOnScreen() {
82 func testLoadingKeepsAStaleBalanceOnScreen() {
@@ -87,7 +87,7 @@ final class StatusBarTextTests: XCTestCase {
87 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil,
87 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil,
88 zaiInPeakHours: false
88 zaiInPeakHours: false
89 )
89 )
90− XCTAssertEqual(title, "⚡42 · \u{2055}62% 8%")
90+ XCTAssertEqual(title, "⚡42 · \u{1F172}62% 8%")
91 }
91 }
92
92
93 func testZeroPercentIsShown() {
93 func testZeroPercentIsShown() {
@@ -98,7 +98,7 @@ final class StatusBarTextTests: XCTestCase {
98 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil,
98 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil,
99 zaiInPeakHours: false
99 zaiInPeakHours: false
100 )
100 )
101− XCTAssertEqual(title, "⚡42 · \u{2055}0% 8%")
101+ XCTAssertEqual(title, "⚡42 · \u{1F172}0% 8%")
102 }
102 }
103
103
104 func testBothClaudeWindowsZero() {
104 func testBothClaudeWindowsZero() {
@@ -109,7 +109,7 @@ final class StatusBarTextTests: XCTestCase {
109 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil,
109 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil,
110 zaiInPeakHours: false
110 zaiInPeakHours: false
111 )
111 )
112− XCTAssertEqual(title, "⚡42 · \u{2055}0% 0%")
112+ XCTAssertEqual(title, "⚡42 · \u{1F172}0% 0%")
113 }
113 }
114
114
115 func testFiveHourOnly() {
115 func testFiveHourOnly() {
@@ -120,7 +120,7 @@ final class StatusBarTextTests: XCTestCase {
120 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil,
120 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil,
121 zaiInPeakHours: false
121 zaiInPeakHours: false
122 )
122 )
123− XCTAssertEqual(title, "\u{2055}62%")
123+ XCTAssertEqual(title, "\u{1F172}62%")
124 }
124 }
125
125
126 func testSevenDayOnly() {
126 func testSevenDayOnly() {
@@ -131,7 +131,7 @@ final class StatusBarTextTests: XCTestCase {
131 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil,
131 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil,
132 zaiInPeakHours: false
132 zaiInPeakHours: false
133 )
133 )
134− XCTAssertEqual(title, "\u{2055}8%")
134+ XCTAssertEqual(title, "\u{1F172}8%")
135 }
135 }
136
136
137 // MARK: - Hyper not configured
137 // MARK: - Hyper not configured
@@ -168,7 +168,7 @@ final class StatusBarTextTests: XCTestCase {
168 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil,
168 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil,
169 zaiInPeakHours: false
169 zaiInPeakHours: false
170 )
170 )
171− XCTAssertEqual(title, "\u{2055}62% 8%")
171+ XCTAssertEqual(title, "\u{1F172}62% 8%")
172 }
172 }
173
173
174 // MARK: - z.ai in Title
174 // MARK: - z.ai in Title
@@ -181,7 +181,7 @@ final class StatusBarTextTests: XCTestCase {
181 zaiFiveHourPercent: 12, zaiWeeklyPercent: nil,
181 zaiFiveHourPercent: 12, zaiWeeklyPercent: nil,
182 zaiInPeakHours: false
182 zaiInPeakHours: false
183 )
183 )
184− XCTAssertEqual(title, "\u{005A}12%")
184+ XCTAssertEqual(title, "\u{1F149}12%")
185 }
185 }
186
186
187 func testZaiWithHyper() {
187 func testZaiWithHyper() {
@@ -192,7 +192,7 @@ final class StatusBarTextTests: XCTestCase {
192 zaiFiveHourPercent: 12, zaiWeeklyPercent: nil,
192 zaiFiveHourPercent: 12, zaiWeeklyPercent: nil,
193 zaiInPeakHours: false
193 zaiInPeakHours: false
194 )
194 )
195− XCTAssertEqual(title, "⚡42 · \u{005A}12%")
195+ XCTAssertEqual(title, "⚡42 · \u{1F149}12%")
196 }
196 }
197
197
198 func testZaiWithClaude() {
198 func testZaiWithClaude() {
@@ -203,7 +203,7 @@ final class StatusBarTextTests: XCTestCase {
203 zaiFiveHourPercent: 12, zaiWeeklyPercent: nil,
203 zaiFiveHourPercent: 12, zaiWeeklyPercent: nil,
204 zaiInPeakHours: false
204 zaiInPeakHours: false
205 )
205 )
206− XCTAssertEqual(title, "\u{2055}62% 8% · \u{005A}12%")
206+ XCTAssertEqual(title, "\u{1F172}62% 8% · \u{1F149}12%")
207 }
207 }
208
208
209 func testAllThreeServices() {
209 func testAllThreeServices() {
@@ -214,7 +214,7 @@ final class StatusBarTextTests: XCTestCase {
214 zaiFiveHourPercent: 12, zaiWeeklyPercent: 3,
214 zaiFiveHourPercent: 12, zaiWeeklyPercent: 3,
215 zaiInPeakHours: false
215 zaiInPeakHours: false
216 )
216 )
217− XCTAssertEqual(title, "⚡42 · \u{2055}62% 8% · \u{005A}12% 3%")
217+ XCTAssertEqual(title, "⚡42 · \u{1F172}62% 8% · \u{1F149}12% 3%")
218 }
218 }
219
219
220 func testZaiZeroIsShown() {
220 func testZaiZeroIsShown() {
@@ -225,7 +225,7 @@ final class StatusBarTextTests: XCTestCase {
225 zaiFiveHourPercent: 0, zaiWeeklyPercent: 0,
225 zaiFiveHourPercent: 0, zaiWeeklyPercent: 0,
226 zaiInPeakHours: false
226 zaiInPeakHours: false
227 )
227 )
228− XCTAssertEqual(title, "⚡42 · \u{005A}0% 0%")
228+ XCTAssertEqual(title, "⚡42 · \u{1F149}0% 0%")
229 }
229 }
230
230
231 func testLoadingKeepsZaiPercentOnScreen() {
231 func testLoadingKeepsZaiPercentOnScreen() {
@@ -236,7 +236,7 @@ final class StatusBarTextTests: XCTestCase {
236 zaiFiveHourPercent: 12, zaiWeeklyPercent: nil,
236 zaiFiveHourPercent: 12, zaiWeeklyPercent: nil,
237 zaiInPeakHours: false
237 zaiInPeakHours: false
238 )
238 )
239− XCTAssertEqual(title, "\u{005A}12%")
239+ XCTAssertEqual(title, "\u{1F149}12%")
240 }
240 }
241
241
242 // MARK: - z.ai Weekly
242 // MARK: - z.ai Weekly
@@ -249,7 +249,7 @@ final class StatusBarTextTests: XCTestCase {
249 zaiFiveHourPercent: nil, zaiWeeklyPercent: 3,
249 zaiFiveHourPercent: nil, zaiWeeklyPercent: 3,
250 zaiInPeakHours: false
250 zaiInPeakHours: false
251 )
251 )
252− XCTAssertEqual(title, "\u{005A}3%")
252+ XCTAssertEqual(title, "\u{1F149}3%")
253 }
253 }
254
254
255 func testZaiWeeklyWithFiveHour() {
255 func testZaiWeeklyWithFiveHour() {
@@ -260,7 +260,7 @@ final class StatusBarTextTests: XCTestCase {
260 zaiFiveHourPercent: 12, zaiWeeklyPercent: 3,
260 zaiFiveHourPercent: 12, zaiWeeklyPercent: 3,
261 zaiInPeakHours: false
261 zaiInPeakHours: false
262 )
262 )
263− XCTAssertEqual(title, "\u{005A}12% 3%")
263+ XCTAssertEqual(title, "\u{1F149}12% 3%")
264 }
264 }
265
265
266 func testZaiWeeklyZeroIsShown() {
266 func testZaiWeeklyZeroIsShown() {
@@ -271,7 +271,7 @@ final class StatusBarTextTests: XCTestCase {
271 zaiFiveHourPercent: 12, zaiWeeklyPercent: 0,
271 zaiFiveHourPercent: 12, zaiWeeklyPercent: 0,
272 zaiInPeakHours: false
272 zaiInPeakHours: false
273 )
273 )
274− XCTAssertEqual(title, "\u{005A}12% 0%")
274+ XCTAssertEqual(title, "\u{1F149}12% 0%")
275 }
275 }
276
276
277 func testLoadingKeepsZaiWeeklyOnScreen() {
277 func testLoadingKeepsZaiWeeklyOnScreen() {
@@ -282,7 +282,7 @@ final class StatusBarTextTests: XCTestCase {
282 zaiFiveHourPercent: nil, zaiWeeklyPercent: 3,
282 zaiFiveHourPercent: nil, zaiWeeklyPercent: 3,
283 zaiInPeakHours: false
283 zaiInPeakHours: false
284 )
284 )
285− XCTAssertEqual(title, "\u{005A}3%")
285+ XCTAssertEqual(title, "\u{1F149}3%")
286 }
286 }
287
287
288 // MARK: - z.ai Peak Hours
288 // MARK: - z.ai Peak Hours
@@ -295,7 +295,7 @@ final class StatusBarTextTests: XCTestCase {
295 zaiFiveHourPercent: 12, zaiWeeklyPercent: 3,
295 zaiFiveHourPercent: 12, zaiWeeklyPercent: 3,
296 zaiInPeakHours: true
296 zaiInPeakHours: true
297 )
297 )
298− XCTAssertEqual(title, "⚡42 · \u{2055}62% 8% · \u{005A}12% 3%\u{1F536}")
298+ XCTAssertEqual(title, "⚡42 · \u{1F172}62% 8% · \u{1F149}12% 3%\u{1F536}")
299 }
299 }
300
300
301 func testPeakHoursWithZaiOnly() {
301 func testPeakHoursWithZaiOnly() {
@@ -306,7 +306,7 @@ final class StatusBarTextTests: XCTestCase {
306 zaiFiveHourPercent: 12, zaiWeeklyPercent: nil,
306 zaiFiveHourPercent: 12, zaiWeeklyPercent: nil,
307 zaiInPeakHours: true
307 zaiInPeakHours: true
308 )
308 )
309− XCTAssertEqual(title, "\u{005A}12%\u{1F536}")
309+ XCTAssertEqual(title, "\u{1F149}12%\u{1F536}")
310 }
310 }
311
311
312 func testPeakHoursIgnoredWhenZaiNotConfigured() {
312 func testPeakHoursIgnoredWhenZaiNotConfigured() {
@@ -318,7 +318,7 @@ final class StatusBarTextTests: XCTestCase {
318 zaiInPeakHours: true
318 zaiInPeakHours: true
319 )
319 )
320 // No z.ai segment, so peak marker has nothing to attach to.
320 // No z.ai segment, so peak marker has nothing to attach to.
321− XCTAssertEqual(title, "⚡42 · \u{2055}62% 8%")
321+ XCTAssertEqual(title, "⚡42 · \u{1F172}62% 8%")
322 }
322 }
323
323
324 // MARK: - Claude Window Percentages
324 // MARK: - Claude Window Percentages
project.yml+4−4
@@ -6,7 +6,7 @@ name: UsageMenubar
6options:
6options:
7 # Reverse-DNS prefix xcodegen uses when it needs to synthesize a bundle id.
7 # Reverse-DNS prefix xcodegen uses when it needs to synthesize a bundle id.
8 # The app target pins its own id explicitly below, so this is just a default.
8 # The app target pins its own id explicitly below, so this is just a default.
9− bundleIdPrefix: soy.iko
9+ bundleIdPrefix: com.ilyakooo0
10 # Oldest macOS we support. SMAppService (launch at login) requires 13.0+;
10 # Oldest macOS we support. SMAppService (launch at login) requires 13.0+;
11 # we target 14.0 (Sonoma). Keep in sync with MACOSX_DEPLOYMENT_TARGET below.
11 # we target 14.0 (Sonoma). Keep in sync with MACOSX_DEPLOYMENT_TARGET below.
12 deploymentTarget:
12 deploymentTarget:
@@ -47,7 +47,7 @@ targets:
47 properties:
47 properties:
48 CFBundleName: UsageMenubar
48 CFBundleName: UsageMenubar
49 CFBundleDisplayName: Usage Menubar
49 CFBundleDisplayName: Usage Menubar
50− CFBundleIdentifier: soy.iko.usage-menubar
50+ CFBundleIdentifier: com.ilyakooo0.usage-menubar
51 # Overwritten by CI — see the note on MARKETING_VERSION above.
51 # Overwritten by CI — see the note on MARKETING_VERSION above.
52 CFBundleVersion: "1"
52 CFBundleVersion: "1"
53 CFBundleShortVersionString: "1.0.0"
53 CFBundleShortVersionString: "1.0.0"
@@ -57,7 +57,7 @@ targets:
57 NSHighResolutionCapable: true
57 NSHighResolutionCapable: true
58 settings:
58 settings:
59 base:
59 base:
60− PRODUCT_BUNDLE_IDENTIFIER: soy.iko.usage-menubar
60+ PRODUCT_BUNDLE_IDENTIFIER: com.ilyakooo0.usage-menubar
61 PRODUCT_NAME: UsageMenubar
61 PRODUCT_NAME: UsageMenubar
62 INFOPLIST_FILE: UsageMenubar/Info.plist
62 INFOPLIST_FILE: UsageMenubar/Info.plist
63 # Grants the outgoing-network-connection entitlement needed to reach
63 # Grants the outgoing-network-connection entitlement needed to reach
@@ -84,7 +84,7 @@ targets:
84 - target: UsageMenubar
84 - target: UsageMenubar
85 settings:
85 settings:
86 base:
86 base:
87− PRODUCT_BUNDLE_IDENTIFIER: soy.iko.usage-menubar.tests
87+ PRODUCT_BUNDLE_IDENTIFIER: com.ilyakooo0.usage-menubar.tests
88 PRODUCT_NAME: UsageMenubarTests
88 PRODUCT_NAME: UsageMenubarTests
89 BUNDLE_LOADER: "$(TEST_HOST)"
89 BUNDLE_LOADER: "$(TEST_HOST)"
90 TEST_HOST: "$(BUILT_PRODUCTS_DIR)/UsageMenubar.app/Contents/MacOS/UsageMenubar"
90 TEST_HOST: "$(BUILT_PRODUCTS_DIR)/UsageMenubar.app/Contents/MacOS/UsageMenubar"