Skip to content

Commit ed8be8c

Browse files
pdurlejclaude
andcommitted
fix(aiquotas): fire.9.3 - keep AI Quotas item out of Ice's section mgmt
Dogfooding caught it: the AI Quotas status item wasn't visible. Ice was treating its own Fire-owned status item as a managed third-party item and, during the cross-MCP layout demo, pushed it into the Always-Hidden section — macOS persisted a far-left "Preferred Position" (11298), rendering it at x=-9501, off-screen. (This is the exclusion the MVP spec called out as a non-goal/follow-up; now needed.) Fix: - MenuBarItemManager.isValidForCaching excludes Fire-owned non-control items: by window-title prefix "Fire." and as any .ice-namespace item that isn't one of Ice's three control items. Ice no longer caches, classifies, or moves the AI Quotas item. - AIQuotaStatusItemController stamps the status item's window title ("Fire.AIQuotas.Combined") so the title-based exclusion is reliable regardless of how sourcePID resolves on macOS 26. - Self-heal: on creation, if the persisted preferred position is far off-screen (> 8000), drop the autosave key so macOS re-places the item in the visible status area. Fixes already-parked installs automatically; the exclusion prevents recurrence. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d051a2d commit ed8be8c

3 files changed

Lines changed: 40 additions & 4 deletions

File tree

Ice.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@
520520
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
521521
CODE_SIGN_STYLE = Automatic;
522522
COMBINE_HIDPI_IMAGES = YES;
523-
CURRENT_PROJECT_VERSION = 1138;
523+
CURRENT_PROJECT_VERSION = 1139;
524524
DEAD_CODE_STRIPPING = YES;
525525
DEVELOPMENT_ASSET_PATHS = "";
526526
ENABLE_APP_SANDBOX = NO;
@@ -536,7 +536,7 @@
536536
"$(inherited)",
537537
"@executable_path/../Frameworks",
538538
);
539-
MARKETING_VERSION = "0.11.13-fire.9.2";
539+
MARKETING_VERSION = "0.11.13-fire.9.3";
540540
PRODUCT_BUNDLE_IDENTIFIER = com.jordanbaird.Ice;
541541
PRODUCT_NAME = "$(TARGET_NAME)";
542542
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -553,7 +553,7 @@
553553
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
554554
CODE_SIGN_STYLE = Automatic;
555555
COMBINE_HIDPI_IMAGES = YES;
556-
CURRENT_PROJECT_VERSION = 1138;
556+
CURRENT_PROJECT_VERSION = 1139;
557557
DEAD_CODE_STRIPPING = YES;
558558
DEVELOPMENT_ASSET_PATHS = "";
559559
ENABLE_APP_SANDBOX = NO;
@@ -569,7 +569,7 @@
569569
"$(inherited)",
570570
"@executable_path/../Frameworks",
571571
);
572-
MARKETING_VERSION = "0.11.13-fire.9.2";
572+
MARKETING_VERSION = "0.11.13-fire.9.3";
573573
PRODUCT_BUNDLE_IDENTIFIER = com.jordanbaird.Ice;
574574
PRODUCT_NAME = "$(TARGET_NAME)";
575575
SWIFT_EMIT_LOC_STRINGS = YES;

Ice/AIQuotas/AIQuotaStatusItemController.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,39 @@ final class AIQuotaStatusItemController {
2222
/// Lazily creates the status item exactly once.
2323
private func ensureStatusItem() -> NSStatusItem {
2424
if let statusItem { return statusItem }
25+
26+
// Reset a stale autosaved position before creating. If Ice (or a
27+
// prior build that didn't exclude this item) pushed it into a
28+
// hidden section, macOS persisted a far-left "Preferred Position"
29+
// and the item would re-appear off-screen. Clearing the key lets
30+
// macOS place it fresh in the visible status area. Ice's
31+
// isValidForCaching now excludes it, so it won't be pushed again.
32+
Self.resetStaleAutosavePositionIfNeeded()
33+
2534
let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
2635
item.autosaveName = Self.autosaveName
2736
item.button?.setAccessibilityIdentifier("Fire.AIQuotas.StatusItem")
2837
item.button?.toolTip = "AI Quotas"
38+
// Stamp the window title so Ice's item manager can recognize this
39+
// as a Fire-owned, non-managed item (see isValidForCaching).
40+
item.button?.window?.title = Self.autosaveName
2941
statusItem = item
3042
logger.debug("Created AI Quotas status item")
3143
return item
3244
}
3345

46+
/// The macOS-persisted preferred-position values for Ice's three
47+
/// control items cluster well below ~7000; a value far above that
48+
/// means the AI Quotas item was parked off-screen left. If so, drop
49+
/// the key so the item is re-placed in the visible area.
50+
private static func resetStaleAutosavePositionIfNeeded() {
51+
let key = "NSStatusItem Preferred Position \(autosaveName)"
52+
let pos = UserDefaults.standard.object(forKey: key) as? Double
53+
if let pos, pos > 8000 {
54+
UserDefaults.standard.removeObject(forKey: key)
55+
}
56+
}
57+
3458
/// Shows the item (creating it if needed) and updates its title.
3559
func show(title: String, menu: NSMenu) {
3660
let item = ensureStatusItem()

Ice/MenuBar/MenuBarItems/MenuBarItemManager.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,18 @@ extension MenuBarItemManager {
256256
if item.isControlItem, item.tag != .visibleControlItem {
257257
return false
258258
}
259+
// Fire's own non-control menu bar items (e.g. the AI Quotas
260+
// status item) are owned by Ice but must NOT be managed or
261+
// pushed into a hidden section as if they were third-party
262+
// items. Identify defensively: by the window title we stamp
263+
// on it, or as any .ice-namespace item that isn't one of
264+
// Ice's three control items.
265+
if item.tag.title.hasPrefix("Fire.") {
266+
return false
267+
}
268+
if item.tag.namespace == .ice, !item.isControlItem {
269+
return false
270+
}
259271
return true
260272
}
261273

0 commit comments

Comments
 (0)