From b0b20bd9de8bf4e17f175dcb57c17033a74626c6 Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Fri, 25 Sep 2026 19:11:32 -0700 Subject: [PATCH] oskiewar mac: install writes a Dock tile the Dock can draw The installer added its Dock tile with defaults -array-add, which writes only a URL string; current macOS draws such a tile as "?", and replacing the bundle in place orphaned it. mac-dock-tile.swift adds or rebuilds the tile with a bookmark and bundle id, and repairs any other undrawable tile. Co-Authored-By: Claude Opus 5.5 --- xbox/tools/build-macos-app.sh | 29 ++------------------ xbox/tools/mac-dock-tile.swift | 49 ++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 27 deletions(-) create mode 100644 xbox/tools/mac-dock-tile.swift diff --git a/xbox/tools/build-macos-app.sh b/xbox/tools/build-macos-app.sh index d03584e30d..89ecedbaba 100755 --- a/xbox/tools/build-macos-app.sh +++ b/xbox/tools/build-macos-app.sh @@ -49,33 +49,8 @@ if [ "$install" -eq 1 ]; then # re-register so the Dock redraws the current Oskiewar.icns. touch "$destination" /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -f "$destination" >/dev/null 2>&1 || true - dock_snapshot=$(mktemp "${TMPDIR:-/tmp}/oskiewar-dock.XXXXXX.plist") - dock_found=0 - if defaults export com.apple.dock "$dock_snapshot" >/dev/null 2>&1; then - dock_index=0 - while dock_url=$(/usr/libexec/PlistBuddy -c \ - "Print :persistent-apps:$dock_index:tile-data:file-data:_CFURLString" \ - "$dock_snapshot" 2>/dev/null); do - if [ "$(printf '%s' "$dock_url" | tr '[:upper:]' '[:lower:]')" = \ - "file:///applications/oskiewar.app/" ]; then - /usr/libexec/PlistBuddy -c \ - "Set :persistent-apps:$dock_index:tile-data:file-data:_CFURLString file:///Applications/oskiewar.app/" \ - "$dock_snapshot" - /usr/libexec/PlistBuddy -c \ - "Set :persistent-apps:$dock_index:tile-data:file-label oskiewar" \ - "$dock_snapshot" - defaults import com.apple.dock "$dock_snapshot" >/dev/null - dock_found=1 - break - fi - dock_index=$((dock_index + 1)) - done - fi - rm -f "$dock_snapshot" - if [ "$dock_found" -eq 0 ]; then - defaults write com.apple.dock persistent-apps -array-add \ - '{"tile-data"={"file-data"={"_CFURLString"="file:///Applications/oskiewar.app/";"_CFURLStringType"=15;};"file-label"="oskiewar";};"tile-type"="file-tile";}' - fi + # One proper Dock tile: bookmark and bundle id included, or it draws "?". + swift "$repo_root/xbox/tools/mac-dock-tile.swift" "$destination" || true killall Dock 2>/dev/null || true open "$destination" echo "Installed and opened $destination" diff --git a/xbox/tools/mac-dock-tile.swift b/xbox/tools/mac-dock-tile.swift new file mode 100644 index 0000000000..5902cd6f0e --- /dev/null +++ b/xbox/tools/mac-dock-tile.swift @@ -0,0 +1,49 @@ +// Keep one proper Dock tile for an app: add it if missing, and give every +// tile that lacks one the bookmark and bundle id the Dock needs to draw it. +// +// A tile written with only `_CFURLString` (what `defaults write … -array-add` +// makes) shows as a "?" on current macOS, and replacing the bundle in place +// orphans the old bookmark. Prints what it changed; the caller restarts Dock. +// +// swift mac-dock-tile.swift /Applications/oskiewar.app +import Foundation + +let app = URL(fileURLWithPath: CommandLine.arguments[1]).standardizedFileURL +let domain = "com.apple.dock" as CFString +var apps = (CFPreferencesCopyAppValue("persistent-apps" as CFString, domain) as? [[String: Any]]) ?? [] + +func tile(for url: URL) throws -> [String: Any] { + var data: [String: Any] = [ + "file-data": ["_CFURLString": url.absoluteString, "_CFURLStringType": 15], + "file-label": url.deletingPathExtension().lastPathComponent, + "book": try url.bookmarkData(), + ] + if let bundle = Bundle(url: url)?.bundleIdentifier { data["bundle-identifier"] = bundle } + return ["tile-data": data, "tile-type": "file-tile"] +} + +var changed = false +var found = false +for index in apps.indices { + let data = apps[index]["tile-data"] as? [String: Any] ?? [:] + guard let string = (data["file-data"] as? [String: Any])?["_CFURLString"] as? String, + let url = URL(string: string) else { continue } + let isApp = url.standardizedFileURL.path.lowercased() == app.path.lowercased() + if isApp { found = true } + // Rebuild the target app's tile every time (its bundle was just replaced), + // and any other tile the Dock cannot draw. + if isApp || (data["book"] == nil && FileManager.default.fileExists(atPath: url.path)) { + apps[index] = try tile(for: isApp ? app : url) + changed = true + print("repaired", url.path) + } +} +if !found { + apps.append(try tile(for: app)) + changed = true + print("added", app.path) +} +if changed { + CFPreferencesSetAppValue("persistent-apps" as CFString, apps as CFArray, domain) + CFPreferencesAppSynchronize(domain) +} -- 2.51.2