feat: plugin panel size hint via Info.plist + backfill NSAppleEvents key

Two changes prep for v2.1.8:

1. Plugin size hint API

   Plugins can now declare preferred expanded panel dimensions via two
   optional Info.plist keys:

     MioPluginPreferredWidth   (Number, 280..1200)
     MioPluginPreferredHeight  (Number, 180..900)

   When both are present, the host caps the expanded panel to the
   requested size (clamped to 95% of screen dimensions so a plugin
   can't escape the display). Missing or out-of-range values fall
   back to the default ~620×780.

   - NativePluginManager.LoadedPlugin grows a `preferredPanelSize: CGSize?`
     computed property reading Info.plist at lookup time.
   - NotchViewModel.openedSize for .plugin(let pluginId) consults
     NativePluginManager.shared.plugin(id:)?.preferredPanelSize before
     falling back to the old default.

   Motivation: mio-plugin-music ships as a compact card. At the default
   620×780 the card floats in ~500pt of dead vertical space.

2. Info.plist: NSAppleEventsUsageDescription

   The v2.1.7 binary shipped with this key (release.sh picked it up from
   disk at build time), but the v2.1.7 source commit only included the
   pbxproj bump. Backfilling the source so git matches what's running
   in /Applications.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
徐翔宇 2026-04-19 20:38:37 +08:00
parent efbec300cc
commit 1df8477aaa
3 changed files with 47 additions and 2 deletions

View file

@ -126,11 +126,25 @@ class NotchViewModel: ObservableObject {
width: min(screenRect.width * 0.4, 480),
height: 320
)
case .plugin:
return CGSize(
case .plugin(let pluginId):
// Default budget for plugins that don't declare a preferred size
// (48% of screen width capped at 620, 78% of height capped at 780).
let defaultSize = CGSize(
width: min(screenRect.width * 0.48, 620),
height: min(screenRect.height * 0.78, 780)
)
// Let a plugin request a smaller (or slightly larger) panel via
// Info.plist: MioPluginPreferredWidth / MioPluginPreferredHeight.
// The hint is already sanity-clamped by NativePluginManager; we
// still clip to the actual display so a plugin can't escape the
// user's screen.
if let hint = NativePluginManager.shared.plugin(id: pluginId)?.preferredPanelSize {
return CGSize(
width: min(hint.width, screenRect.width * 0.95),
height: min(hint.height, screenRect.height * 0.95)
)
}
return defaultSize
case .instances:
let baseHeight: CGFloat = 120
let perSession: CGFloat = 100

View file

@ -24,5 +24,8 @@
<key>SUEnableAutomaticChecks</key>
<true/>
<key>NSAppleEventsUsageDescription</key>
<string>Mio Island reads Now Playing info from music apps (Spotify, Apple Music, Google Chrome) via AppleScript to show the current track in your notch. Mio Island 通过 AppleScript 读取 Spotify、Apple Music、Chrome 等音乐播放器的当前曲目信息,在刘海区域显示。</string>
</dict>
</plist>

View file

@ -76,6 +76,34 @@ final class NativePluginManager: ObservableObject {
let result = instance.perform(sel, with: slot, with: context)
return result?.takeUnretainedValue() as? NSView
}
/// Optional panel size hint from the plugin's Info.plist:
/// MioPluginPreferredWidth (Number, 280..1200)
/// MioPluginPreferredHeight (Number, 180..900)
/// Both must be present to take effect. Returns nil when either is
/// missing or out of bounds, letting the host fall back to its
/// default `(min(screenW*0.48, 620), min(screenH*0.78, 780))`.
var preferredPanelSize: CGSize? {
guard
let info = bundle.infoDictionary,
let rawW = (info["MioPluginPreferredWidth"] as? NSNumber)?.doubleValue,
let rawH = (info["MioPluginPreferredHeight"] as? NSNumber)?.doubleValue
else {
return nil
}
// Sanity clamp: reject absurdly small (< mini notch size) or
// absurdly large (bigger than full-HD) hints.
guard rawW >= 280, rawW <= 1200, rawH >= 180, rawH <= 900 else {
return nil
}
return CGSize(width: rawW, height: rawH)
}
}
/// Look up a loaded plugin by id. Used by NotchViewModel to ask for a
/// panel size hint before allocating the expanded area.
func plugin(id: String) -> LoadedPlugin? {
loadedPlugins.first(where: { $0.id == id })
}
private var pluginsDir: URL {