on master help sign in
feat: use provider emoji ๐Ÿ…ฒ Claude, ๐Ÿ…‰ z.ai in menu bar
abde53aiko
UsageMenubar/MenuView.swift+2โˆ’2
@@ -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: "๐Ÿ•‘ \(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,
UsageMenubar/ViewModel.swift+12โˆ’12
@@ -238,11 +238,11 @@ final class ViewModel: ObservableObject {
238 }
238 }
239
239
240 /// The text to show in the menu bar. Each configured provider contributes a
240 /// The text to show in the menu bar. Each configured provider contributes a
241โˆ’ /// segment; segments are separated by ` ยท ` (middle dot). Within a segment,
241+ /// segment prefixed with a provider emoji; segments are separated by ` ยท `
242+ /// (middle dot). Within a segment, windows are listed as bare percentages
242โˆ’ /// windows are separated by a space. An unconfigured service produces no
243+ /// separated by spaces. An unconfigured service produces no segment.
243โˆ’ /// segment โ€” no placeholder, no icon.
244 ///
244 ///
245โˆ’ /// Example: `โšก42 ยท ๐Ÿ•62% ๐Ÿ“…8% ยท ๐Ÿ•‘12% ๐Ÿ“†3%`
245+ /// Example: `โšก42 ยท ๐Ÿ…ฒ62% 8% ยท ๐Ÿ…‰12% 3%`
246 ///
246 ///
247 /// Takes its values as parameters rather than reading the properties: `@Published`
247 /// Takes its values as parameters rather than reading the properties: `@Published`
248 /// publishes in `willSet`, so a Combine subscriber that called back into the view
248 /// publishes in `willSet`, so a Combine subscriber that called back into the view
@@ -263,28 +263,28 @@ final class ViewModel: ObservableObject {
263 segments.append("โšก\(formatBalance(balance))")
263 segments.append("โšก\(formatBalance(balance))")
264 }
264 }
265
265
266โˆ’ // Claude โ€” windows joined by space within the segment
266+ // Claude โ€” provider emoji + bare percentages
267 var claudeWindows: [String] = []
267 var claudeWindows: [String] = []
268 if let fiveHour = claudeFiveHourPercent, fiveHour > 0 {
268 if let fiveHour = claudeFiveHourPercent, fiveHour > 0 {
269โˆ’ claudeWindows.append("๐Ÿ•\(fiveHour)%")
269+ claudeWindows.append("\(fiveHour)%")
270 }
270 }
271 if let sevenDay = claudeSevenDayPercent, sevenDay > 0 {
271 if let sevenDay = claudeSevenDayPercent, sevenDay > 0 {
272โˆ’ claudeWindows.append("๐Ÿ“…\(sevenDay)%")
272+ claudeWindows.append("\(sevenDay)%")
273 }
273 }
274 if !claudeWindows.isEmpty {
274 if !claudeWindows.isEmpty {
275โˆ’ segments.append(claudeWindows.joined(separator: " "))
275+ segments.append("๐Ÿ…ฒ" + claudeWindows.joined(separator: " "))
276 }
276 }
277
277
278โˆ’ // z.ai โ€” windows joined by space within the segment
278+ // z.ai โ€” provider emoji + bare percentages
279 var zaiWindows: [String] = []
279 var zaiWindows: [String] = []
280 if let zaiFiveHour = zaiFiveHourPercent, zaiFiveHour > 0 {
280 if let zaiFiveHour = zaiFiveHourPercent, zaiFiveHour > 0 {
281โˆ’ zaiWindows.append("๐Ÿ•‘\(zaiFiveHour)%")
281+ zaiWindows.append("\(zaiFiveHour)%")
282 }
282 }
283 if let zaiWeekly = zaiWeeklyPercent, zaiWeekly > 0 {
283 if let zaiWeekly = zaiWeeklyPercent, zaiWeekly > 0 {
284โˆ’ zaiWindows.append("๐Ÿ“†\(zaiWeekly)%")
284+ zaiWindows.append("\(zaiWeekly)%")
285 }
285 }
286 if !zaiWindows.isEmpty {
286 if !zaiWindows.isEmpty {
287โˆ’ segments.append(zaiWindows.joined(separator: " "))
287+ segments.append("๐Ÿ…‰" + zaiWindows.joined(separator: " "))
288 }
288 }
289
289
290 if segments.isEmpty {
290 if segments.isEmpty {
UsageMenubarTests/StatusBarTextTests.swift+17โˆ’17
@@ -17,7 +17,7 @@ final class StatusBarTextTests: XCTestCase {
17 claudeFiveHourPercent: 62, claudeSevenDayPercent: 8,
17 claudeFiveHourPercent: 62, claudeSevenDayPercent: 8,
18 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil
18 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil
19 )
19 )
20โˆ’ XCTAssertEqual(title, "โšก42 ยท ๐Ÿ•62% ๐Ÿ“…8%")
20+ XCTAssertEqual(title, "โšก42 ยท \u{1F172}62% 8%")
21 }
21 }
22
22
23 func testHyperOnly() {
23 func testHyperOnly() {
@@ -37,7 +37,7 @@ final class StatusBarTextTests: XCTestCase {
37 claudeFiveHourPercent: 62, claudeSevenDayPercent: 8,
37 claudeFiveHourPercent: 62, claudeSevenDayPercent: 8,
38 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil
38 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil
39 )
39 )
40โˆ’ XCTAssertEqual(title, "๐Ÿ•62% ๐Ÿ“…8%")
40+ XCTAssertEqual(title, "\u{1F172}62% 8%")
41 }
41 }
42
42
43 func testLoadingWithNothingToShow() {
43 func testLoadingWithNothingToShow() {
@@ -70,7 +70,7 @@ final class StatusBarTextTests: XCTestCase {
70 claudeFiveHourPercent: 62, claudeSevenDayPercent: nil,
70 claudeFiveHourPercent: 62, claudeSevenDayPercent: nil,
71 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil
71 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil
72 )
72 )
73โˆ’ XCTAssertEqual(title, "๐Ÿ•62%")
73+ XCTAssertEqual(title, "\u{1F172}62%")
74 }
74 }
75
75
76 func testLoadingKeepsAStaleBalanceOnScreen() {
76 func testLoadingKeepsAStaleBalanceOnScreen() {
@@ -80,7 +80,7 @@ final class StatusBarTextTests: XCTestCase {
80 claudeFiveHourPercent: 62, claudeSevenDayPercent: 8,
80 claudeFiveHourPercent: 62, claudeSevenDayPercent: 8,
81 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil
81 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil
82 )
82 )
83โˆ’ XCTAssertEqual(title, "โšก42 ยท ๐Ÿ•62% ๐Ÿ“…8%")
83+ XCTAssertEqual(title, "โšก42 ยท \u{1F172}62% 8%")
84 }
84 }
85
85
86 func testZeroPercentIsShownRatherThanOmitted() {
86 func testZeroPercentIsShownRatherThanOmitted() {
@@ -90,7 +90,7 @@ final class StatusBarTextTests: XCTestCase {
90 claudeFiveHourPercent: 0, claudeSevenDayPercent: 8,
90 claudeFiveHourPercent: 0, claudeSevenDayPercent: 8,
91 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil
91 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil
92 )
92 )
93โˆ’ XCTAssertEqual(title, "โšก42 ยท ๐Ÿ“…8%")
93+ XCTAssertEqual(title, "โšก42 ยท \u{1F172}8%")
94 }
94 }
95
95
96 func testBothClaudeWindowsZero() {
96 func testBothClaudeWindowsZero() {
@@ -110,7 +110,7 @@ final class StatusBarTextTests: XCTestCase {
110 claudeFiveHourPercent: 62, claudeSevenDayPercent: nil,
110 claudeFiveHourPercent: 62, claudeSevenDayPercent: nil,
111 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil
111 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil
112 )
112 )
113โˆ’ XCTAssertEqual(title, "๐Ÿ•62%")
113+ XCTAssertEqual(title, "\u{1F172}62%")
114 }
114 }
115
115
116 func testSevenDayOnly() {
116 func testSevenDayOnly() {
@@ -120,7 +120,7 @@ final class StatusBarTextTests: XCTestCase {
120 claudeFiveHourPercent: nil, claudeSevenDayPercent: 8,
120 claudeFiveHourPercent: nil, claudeSevenDayPercent: 8,
121 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil
121 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil
122 )
122 )
123โˆ’ XCTAssertEqual(title, "๐Ÿ“…8%")
123+ XCTAssertEqual(title, "\u{1F172}8%")
124 }
124 }
125
125
126 // MARK: - Hyper not configured
126 // MARK: - Hyper not configured
@@ -154,7 +154,7 @@ final class StatusBarTextTests: XCTestCase {
154 claudeFiveHourPercent: 62, claudeSevenDayPercent: 8,
154 claudeFiveHourPercent: 62, claudeSevenDayPercent: 8,
155 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil
155 zaiFiveHourPercent: nil, zaiWeeklyPercent: nil
156 )
156 )
157โˆ’ XCTAssertEqual(title, "๐Ÿ•62% ๐Ÿ“…8%")
157+ XCTAssertEqual(title, "\u{1F172}62% 8%")
158 }
158 }
159
159
160 // MARK: - z.ai in Title
160 // MARK: - z.ai in Title
@@ -166,7 +166,7 @@ final class StatusBarTextTests: XCTestCase {
166 claudeFiveHourPercent: nil, claudeSevenDayPercent: nil,
166 claudeFiveHourPercent: nil, claudeSevenDayPercent: nil,
167 zaiFiveHourPercent: 12, zaiWeeklyPercent: nil
167 zaiFiveHourPercent: 12, zaiWeeklyPercent: nil
168 )
168 )
169โˆ’ XCTAssertEqual(title, "๐Ÿ•‘12%")
169+ XCTAssertEqual(title, "\u{1F169}12%")
170 }
170 }
171
171
172 func testZaiWithHyper() {
172 func testZaiWithHyper() {
@@ -176,7 +176,7 @@ final class StatusBarTextTests: XCTestCase {
176 claudeFiveHourPercent: nil, claudeSevenDayPercent: nil,
176 claudeFiveHourPercent: nil, claudeSevenDayPercent: nil,
177 zaiFiveHourPercent: 12, zaiWeeklyPercent: nil
177 zaiFiveHourPercent: 12, zaiWeeklyPercent: nil
178 )
178 )
179โˆ’ XCTAssertEqual(title, "โšก42 ยท ๐Ÿ•‘12%")
179+ XCTAssertEqual(title, "โšก42 ยท \u{1F169}12%")
180 }
180 }
181
181
182 func testZaiWithClaude() {
182 func testZaiWithClaude() {
@@ -186,7 +186,7 @@ final class StatusBarTextTests: XCTestCase {
186 claudeFiveHourPercent: 62, claudeSevenDayPercent: 8,
186 claudeFiveHourPercent: 62, claudeSevenDayPercent: 8,
187 zaiFiveHourPercent: 12, zaiWeeklyPercent: nil
187 zaiFiveHourPercent: 12, zaiWeeklyPercent: nil
188 )
188 )
189โˆ’ XCTAssertEqual(title, "๐Ÿ•62% ๐Ÿ“…8% ยท ๐Ÿ•‘12%")
189+ XCTAssertEqual(title, "\u{1F172}62% 8% ยท \u{1F169}12%")
190 }
190 }
191
191
192 func testAllThreeServices() {
192 func testAllThreeServices() {
@@ -196,7 +196,7 @@ final class StatusBarTextTests: XCTestCase {
196 claudeFiveHourPercent: 62, claudeSevenDayPercent: 8,
196 claudeFiveHourPercent: 62, claudeSevenDayPercent: 8,
197 zaiFiveHourPercent: 12, zaiWeeklyPercent: 3
197 zaiFiveHourPercent: 12, zaiWeeklyPercent: 3
198 )
198 )
199โˆ’ XCTAssertEqual(title, "โšก42 ยท ๐Ÿ•62% ๐Ÿ“…8% ยท ๐Ÿ•‘12% ๐Ÿ“†3%")
199+ XCTAssertEqual(title, "โšก42 ยท \u{1F172}62% 8% ยท \u{1F169}12% 3%")
200 }
200 }
201
201
202 func testZaiZeroIsOmitted() {
202 func testZaiZeroIsOmitted() {
@@ -216,7 +216,7 @@ final class StatusBarTextTests: XCTestCase {
216 claudeFiveHourPercent: nil, claudeSevenDayPercent: nil,
216 claudeFiveHourPercent: nil, claudeSevenDayPercent: nil,
217 zaiFiveHourPercent: 12, zaiWeeklyPercent: nil
217 zaiFiveHourPercent: 12, zaiWeeklyPercent: nil
218 )
218 )
219โˆ’ XCTAssertEqual(title, "๐Ÿ•‘12%")
219+ XCTAssertEqual(title, "\u{1F169}12%")
220 }
220 }
221
221
222 // MARK: - z.ai Weekly
222 // MARK: - z.ai Weekly
@@ -228,7 +228,7 @@ final class StatusBarTextTests: XCTestCase {
228 claudeFiveHourPercent: nil, claudeSevenDayPercent: nil,
228 claudeFiveHourPercent: nil, claudeSevenDayPercent: nil,
229 zaiFiveHourPercent: nil, zaiWeeklyPercent: 3
229 zaiFiveHourPercent: nil, zaiWeeklyPercent: 3
230 )
230 )
231โˆ’ XCTAssertEqual(title, "๐Ÿ“†3%")
231+ XCTAssertEqual(title, "\u{1F169}3%")
232 }
232 }
233
233
234 func testZaiWeeklyWithFiveHour() {
234 func testZaiWeeklyWithFiveHour() {
@@ -238,7 +238,7 @@ final class StatusBarTextTests: XCTestCase {
238 claudeFiveHourPercent: nil, claudeSevenDayPercent: nil,
238 claudeFiveHourPercent: nil, claudeSevenDayPercent: nil,
239 zaiFiveHourPercent: 12, zaiWeeklyPercent: 3
239 zaiFiveHourPercent: 12, zaiWeeklyPercent: 3
240 )
240 )
241โˆ’ XCTAssertEqual(title, "๐Ÿ•‘12% ๐Ÿ“†3%")
241+ XCTAssertEqual(title, "\u{1F169}12% 3%")
242 }
242 }
243
243
244 func testZaiWeeklyZeroIsOmitted() {
244 func testZaiWeeklyZeroIsOmitted() {
@@ -248,7 +248,7 @@ final class StatusBarTextTests: XCTestCase {
248 claudeFiveHourPercent: nil, claudeSevenDayPercent: nil,
248 claudeFiveHourPercent: nil, claudeSevenDayPercent: nil,
249 zaiFiveHourPercent: 12, zaiWeeklyPercent: 0
249 zaiFiveHourPercent: 12, zaiWeeklyPercent: 0
250 )
250 )
251โˆ’ XCTAssertEqual(title, "๐Ÿ•‘12%")
251+ XCTAssertEqual(title, "\u{1F169}12%")
252 }
252 }
253
253
254 func testLoadingKeepsZaiWeeklyOnScreen() {
254 func testLoadingKeepsZaiWeeklyOnScreen() {
@@ -258,7 +258,7 @@ final class StatusBarTextTests: XCTestCase {
258 claudeFiveHourPercent: nil, claudeSevenDayPercent: nil,
258 claudeFiveHourPercent: nil, claudeSevenDayPercent: nil,
259 zaiFiveHourPercent: nil, zaiWeeklyPercent: 3
259 zaiFiveHourPercent: nil, zaiWeeklyPercent: 3
260 )
260 )
261โˆ’ XCTAssertEqual(title, "๐Ÿ“†3%")
261+ XCTAssertEqual(title, "\u{1F169}3%")
262 }
262 }
263
263
264 // MARK: - Claude Window Percentages
264 // MARK: - Claude Window Percentages