Commit graph

384 commits

Author SHA1 Message Date
徐翔宇
41123d11b9 v2.2.3: Release 2026-04-21 16:59:30 +08:00
徐翔宇
38375b8d12 fix(ux): gear icon on empty state opens settings panel directly
In the "no active sessions" empty state, clicking the gear icon was
calling viewModel.toggleMenu() which rendered NotchMenuView — but
that fallback menu only contains a single SystemSettingsRow, so
users saw a pointless intermediate screen ("返回 | 退出" header +
one "设置 >" row) before reaching the real settings.

Wire the gear straight to SystemSettingsWindow.shared.show() so the
settings panel opens in one click. NotchMenuView can still be
reached via the hamburger icon in the opened-with-sessions header.
2026-04-21 16:57:59 +08:00
徐翔宇
01bf7185f1 v2.2.2: Release 2026-04-21 16:51:40 +08:00
徐翔宇
64232afd5b fix(plugin-install): drop bundleId from dedup set on unload
The loadedBundleIdentifiers set was a dedup guard at loadPlugin's top
(prevents the in-app builtin copy from clashing with a newer user-
installed one in ~/.config/codeisland/plugins/). But neither
unload(id:) nor unloadAll() cleared it, so:

  install  → loadedBundleIdentifiers = {music-player-id}
  uninstall → bundle file deleted, loadedPlugins removed
           → loadedBundleIdentifiers still has {music-player-id}
  reinstall → copyItem to dest OK
           → loadPlugin checks contains(bundleId) → true → silent return
           → UI shows "installed", loadedPlugins empty, plugin invisible

Fix: remove bundle identifier from the set on unload and unloadAll.
Subsequent loadPlugin for the same bundle id now passes the dedup
check and actually registers the plugin.
2026-04-21 16:50:15 +08:00
徐翔宇
0d74b6ed04 v2.2.1: Release 2026-04-21 16:43:10 +08:00
徐翔宇
dca2d788a3 fix(plugin-install): unwrap one level of nested zip
The miomio.chat plugin marketplace re-wraps uploaded plugin bundles
into an outer zip so the user-facing download filename matches the
plugin's marketing name (e.g. "Music Player · 音乐播放器-2.3.0.zip").
That produces a nested structure:

  outer.zip
    └── music-player-v2.3.0.zip   ← our original upload (file, not dir)
          └── music-player.bundle/
                └── Contents/...

installFromURL extracted once with ditto, findBundle recursively
scanned for .bundle, found nothing (because the extracted content is
a .zip file, not a .bundle directory) and aborted with
"No .bundle found in archive".

Fix: after the first extraction, if no .bundle is present AND the
tree contains exactly one .zip, extract that inner zip and search
again. Single-step nested unwrap; does not recurse indefinitely so
we can't get tricked into a zip bomb.
2026-04-21 16:41:50 +08:00
徐翔宇
12228266d5 v2.2.0: Release 2026-04-21 16:27:50 +08:00
徐翔宇
0d847f6a4f fix: notch customization + plugin panel architecture overhaul
notch customization (live edit)
- hardwareNotchWidth: use auxiliaryTopLeftArea/TopRightArea instead of
  safeAreaInsets.left/right. macOS only exposes notch height via
  safeAreaInsets.top; left/right are always zero, so the live-edit
  dashed border was rendering at full screen width on MacBook.
- NotchView.closedNotchSize: stop pinning height to deviceNotchRect on
  hardware-notched machines. Now always reads the user's geo.notchHeight,
  so height arrows in the editor actually resize the island.
- NotchLiveEditOverlay: removed .disabled(hasHardwareNotch) guard on
  height arrows. Software island height is independent of the physical
  camera cutout.
- NotchLiveEditPanel: height 160 -> 220. Old size clipped the
  Save/Cancel row when the user set any non-trivial notch height.

plugin panel
- NativePluginManager: height clamp floor 180 -> 120. Allows plugins
  like the music card to claim a compact panel.
- PluginContentView: replaced the fixed back-button header row with a
  floating chevron chip (ZStack overlay). Plugins now get the full
  panel area and can paint theme color top-to-bottom.
- PairPhoneView: absorbed the ~20pt clearance that used to come from
  host chrome by raising its own .padding(.top) from 16 to 44.
- BuiltInPlugins: pair-phone panel height 480 -> 580. Fits header,
  QR, short code, server row, and a couple of linked devices without
  a scrollbar.
2026-04-21 16:25:53 +08:00
徐翔宇
501e2571d8 v2.1.9: Release 2026-04-20 11:26:46 +08:00
徐翔宇
b2f3a3554f feat: System Settings redesign + Notch theme reset + Buddy style picker
Settings window (SystemSettingsView):
- Graphite dark palette (#201f27 sidebar / #1c1c1e detail) replacing the
  lime L-shape, per Claude Design reference bundle. Lime survives only
  as an accent on toggles, icons, and active pills.
- macOS-style titlebar with real traffic lights (red close / yellow
  hide / green decorative) + centered "系统设置".
- Tabs get large H1 + English subtitle ("通用  General preferences").
- New primitives: SectionLabel, SettingsListCard, SettingRow
  (icon tile + label + sublabel + control), InfoRow (pos/neg/hint
  colored dots), IOSToggle (pill slider).
- General tab rewritten: stacked rows with dividers, 3 toggles with
  sublabels, proxy card with ✓/✕/i info rows, language Menu picker,
  accessibility status row.
- Bottom sidebar "返回" → "退出" calling NSApplication.terminate.
- Window resized 720×560 → 960×720 (1.33:1) for reference-mock breathing
  room. Still fixed-size (borderless).
- TextField placeholder: ZStack overlay with solid light gray, since
  SwiftUI TextField.prompt ignores foregroundColor on macOS. Applied to
  both the Anthropic proxy field and the Install-from-URL field.

Notch themes (NotchTheme / NotchCustomization):
- Reset to 7 themes: Classic + Forest / Neon Tokyo / Sunset /
  Retro Arcade / High Contrast / Sakura (ported from Claude Design
  themes.jsx palettes). Dropped: paper, neonLime, cyber, mint, rosegold,
  ocean, aurora, mocha, lavender, cherry.
- Graceful decode: try? c.decode(NotchThemeID) so legacy raw values
  fall back to .classic instead of throwing.
- NotchPalette gains `accent` field. NotchView.statusDotColor and
  badgeColor use accent for .idle, so at-rest notch reflects the theme
  instead of hardcoded 30%-white (invisible on light-bg themes).
- Theme picker replaced Menu dropdown with 2-column grid of preview
  cards, each rendering a mini pill in that theme's own colors. Selected
  card borders/glows in its own accent.

Buddy style (NotchCustomization.BuddyStyle):
- New `buddyStyle: BuddyStyle` field with two cases: .pixelCat, .emoji.
  Evaluated a .neon option via NeonPixelCatView but it degrades to a
  green blob at 16×16; pulled from the picker pending a small-size
  renderer.
- Migration: missing buddyStyle decodes by reading the legacy
  usePixelCat AppStorage bool. Picker writes back to usePixelCat so
  unmigrated call sites (ClaudeInstancesView) stay in sync.
- Old "Pixel Cat Mode" toggle removed from Appearance tab — the new
  segmented picker in the Notch section supersedes it.

Plugin panel size hint for built-ins:
- LoadedPlugin.preferredPanelSize checks an @objc runtime method first,
  then falls back to Info.plist (gated on bundle !== Bundle.main so
  built-ins don't accidentally read the host's plist).
- PairPhonePlugin declares @objc preferredPanelSize() = 340×480, then
  bumped to 440×480 to match the music plugin's width.

Tests:
- NotchThemeTests / NotchCustomizationTests / NotchCustomizationStoreTests
  updated to new theme line-up plus a regression guard for the legacy
  theme ID → .classic fallback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 11:25:15 +08:00
xmqywx
daae3ecba0 docs(pair-panel): remove "官方免费服务器 island.wdao.chat" hint
保留"服务器地址仅保存在本机"一行作为隐私声明,不再推荐具体
服务器地址。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 11:04:43 +08:00
徐翔宇
60c663b561 v2.1.8: Release 2026-04-19 20:40:06 +08:00
徐翔宇
1df8477aaa 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>
2026-04-19 20:38:37 +08:00
徐翔宇
efbec300cc v2.1.7: Release 2026-04-19 02:20:30 +08:00
徐翔宇
9865b5fa69 docs: refresh WeChat group QR + fix filename reference in DEVELOPER-SETUP
- Replace docs/wechat-qr.jpg with current group invite (was expiring)
- DEVELOPER-SETUP pointed at docs/wechat-group-qr.jpg which doesn't
  exist; fix to docs/wechat-qr.jpg and note the 7-day WeChat TTL

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:53:27 +08:00
徐翔宇
db3d154a02 ci: auto-trigger landing redeploy when a GitHub release is published
Companion to the VITE_LATEST_VERSION injection in landing/deploy-landing.yml.
That workflow reads the latest release tag at build time, but it only
runs on pushes to the landing-page branch. This workflow listens for
release events on main (published / edited / released) and dispatches
deploy-landing so the Mac DMG download link gets refreshed without a
manual commit to landing-page.

Flow:
  1. Admin runs release.sh and `gh release create vX.Y.Z ...`
  2. GitHub fires `release: published`
  3. This workflow dispatches deploy-landing.yml on landing-page
  4. deploy-landing fetches the new tag and rebuilds the site

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:44:29 +08:00
徐翔宇
fb275721cf docs: add DEVELOPER-SETUP guide for contributors
Clean, no-secrets walkthrough for getting a dev environment up. Covers
build, directory layout, commit conventions, what not to commit (key
files, .sparkle-keys, build artifacts), and common gotchas.

Complements docs/RELEASE-GUIDE.md which is admin-only and covers the
actual release flow + key management.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:24:04 +08:00
徐翔宇
ecc04f5512 chore: bump build to 23 for v2.1.6 bridge re-release
Marketing version stays at 2.1.6 but build increments to 23 so Sparkle
picks up the re-signed DMG as a newer release. The v2.1.6 tag still
points at build 22 (original release) — intentional, this re-release
is a security patch, not a new feature drop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:22:50 +08:00
徐翔宇
8c8f849bba security: rotate Sparkle EdDSA public key
Old key: 2099yGC8...ih2Q= (private key was accidentally committed to
the repo in a547d434 and must be treated as compromised).

New key: XzmsCyH9tALFSPHRD/P5D5/r7MNV3loKYyOxZMEXblg=
New private key is stored locally at .sparkle-keys/eddsa_private_key
(gitignored) and is distributed out of band to release admins.

The v2.1.6 (build 23) bridge release is signed with the OLD private key
so existing v2.1.5 / v2.1.6 (build 22) users can auto-update one last
time. After that, their Info.plist will contain the new public key and
all future releases (v2.1.7+) are signed with the new private key.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:18:49 +08:00
徐翔宇
c3dd804dd1 security: scrub EdDSA private key from RELEASE-GUIDE
The previous commit (a547d434) committed the Sparkle signing private key
inline in the doc. HEAD no longer exposes it, but the blob is still
reachable via git history (a547d434:docs/RELEASE-GUIDE.md).

Next step is either rewriting history (requires temporarily relaxing
main branch protection) or rotating to a fresh keypair. See incident
discussion outside the repo.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:11:17 +08:00
徐翔宇
38aad3be51 fix: widen instances panel so tool preview + approval buttons don't clip
Active sessions with a tool call preview + inline approval buttons can
render ~130pt tall, but the panel height used perSession=65 + baseHeight=100
which left only ~115pt of ScrollView area after header + bottom padding.
The bottom row ended up under the buddy/stats overlay even though the
ScrollView could technically scroll to it.

Bumps to baseHeight=120 / perSession=100 — gives 1 session 220pt, 2 sessions
320pt, etc. Still capped by expandedMax so large session counts still fit
inside 65% of screen height.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:09:22 +08:00
徐翔宇
a547d43470 fix(release): abort if Sparkle signature is empty + in-repo RELEASE-GUIDE
release.sh previously printed "SKIP Sparkle signing" when the private
key was absent on the release machine, but then still generated and
pushed an appcast with sparkle:edSignature="". This is exactly how
v2.1.6 shipped unsigned — every user saw "此更新未正确签名".

Now: if ED_SIG is empty after the sign_update step, exit 1 with a
diagnostic pointing at docs/RELEASE-GUIDE.md §3 (canonical key).

Also copies the release guide into the repo at docs/RELEASE-GUIDE.md
so new admins don't need the chat-history PDF. Includes the v2.1.6
incident + emergency-resign recipe.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:09:04 +08:00
xmqywx
8485179d1d v2.1.6: Release 2026-04-18 05:23:49 +08:00
xmqywx
a207023dbe fix: force notch to configured size during Live Edit (#30)
`.frame(maxWidth:)` 是 SwiftUI 的上限而非目标值,空闲状态下没有内容
撑起来,刘海就会 shrink-to-content,用户调 ◀▶/▲▼ 宽高箭头时数值
在变、视觉不动,误以为设置没生效。

现在 Live Edit 期间强制把 closed 刘海锁到 closedContentWidth ×
closedNotchSize.height,WYSIWYG;保存退出后恢复跟随内容行为,
避免没开 CC 会话时屏幕顶部一直顶着一条大黑条。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 04:51:00 +08:00
xmqywx
d198f8cbb6 feat: auto-expand toggle + inline Pair iPhone panel (#67, #57)
#67 — Behavior → Auto-Expand on Complete toggle (default ON, preserves
current behavior). Default-ON 用 UserDefaults.object as? Bool ?? true,
避免老用户 key 不存在时被误读成 false。

#57 — Pair iPhone 插件面板改成内联:
- 未配服务器时 "第 1 步 · 配置服务器" 大卡片,带图标+解释段落+
  输入框+保存按钮,让服务器配置步骤明显不可忽略;
- 已配好时显示 QR + 配对码 + 服务器信息一整行(带独立的
  "更换服务器" 按钮,不再是小 pill);
- 顶栏带状态 pill(在线/连接中/错误/未连接);
- 所有字符串走 L10n,中英双语覆盖。

老的 QRPairingWindow 保留未动(Settings → CodeLight tab 仍在用)。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 04:38:00 +08:00
xmqywx
47a6569e0d docs: fix cmux link from cmux.io to cmux.com (#69)
cmux.io 实际会 302 到 mux.coder.com,容易被误以为贴错;cmux.com 是
直连 200 的正确官网。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 04:15:55 +08:00
徐翔宇
5ed5e8083f v2.1.5: Release
- fix: standby indicator left-aligned dot+icon, full user-configured width
- fix: keychain open-access ACL prevents password prompts on every update
2026-04-16 23:13:55 +08:00
徐翔宇
d42e212f56 v2.1.4: Release 2026-04-16 22:54:05 +08:00
徐翔宇
e4a1e61bd5 fix: start isVisible=true to eliminate startup flash on notched Macs
isVisible was initialized to false, causing opacity=0 for 1 render frame
before onAppear fired. On notched Macs this meant the standby content
would flash invisible on launch. Start it true so the notch is visible
from frame 1 — the app always shows standby content regardless of sessions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 22:53:22 +08:00
徐翔宇
ba9d27ee8e v2.1.3: Release 2026-04-16 22:42:52 +08:00
徐翔宇
50ef04fbe3 fix: always show notch for standby state on notched Macs
Two bugs prevented the standby indicator from appearing when no sessions
are active on physical-notch MacBooks:

1. onAppear only set isVisible=true for non-notched devices, so notched
   Macs started invisible and never showed standby at launch.
2. handleStatusChange(.closed) set isVisible=false whenever the notch
   closed with no active sessions, hiding the standby content.

Fix: always set isVisible=true on appear, and never hide on close since
standby content fills the notch when there are no active sessions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 22:40:31 +08:00
徐翔宇
09ee1dd352 v2.1.2: Release 2026-04-16 20:53:33 +08:00
徐翔宇
3865ba9d85 feat: show standby indicator when no active sessions
Display pixel cat icon + "待机中/Standby" text in collapsed notch
when there are no Claude Code sessions, so users know the app is running.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 20:52:34 +08:00
徐翔宇
cb2209a285 security: rotate EdDSA signing key pair
Old key was exposed in git history. New key pair generated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:12:15 +08:00
carey314
79745d764d
Delete RELEASE-GUIDE.md 2026-04-15 19:03:37 +08:00
徐翔宇
904f8dce20 docs: add release guide for multi-admin deployment
Includes Sparkle signing workflow, key setup, troubleshooting,
and step-by-step instructions for new release managers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:00:16 +08:00
徐翔宇
1d0e744b3c feat: add zh-Hans localization for Sparkle update UI
Add zh-Hans.lproj and en.lproj so Sparkle shows Chinese update dialogs
when system language is Chinese.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 18:50:09 +08:00
徐翔宇
619331f3b5 v2.1.3: Release 2026-04-15 18:21:06 +08:00
徐翔宇
499cd736ca fix: use build number for sparkle:version in appcast
Sparkle compares sparkle:version against CFBundleVersion (build number),
not CFBundleShortVersionString. Auto-increment build number on each
release and use it in appcast for correct version comparison.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 18:19:08 +08:00
徐翔宇
47bf2d2019 v2.1.2: Release 2026-04-15 18:15:48 +08:00
徐翔宇
8e576aaa65 fix: correct SUFeedURL to GitHub Pages URL
miomio.chat is the plugin store, not GitHub Pages.
The appcast is hosted at miomioos.github.io/MioIsland/appcast.xml.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 18:13:50 +08:00
徐翔宇
a7c20c6690 v2.1.1: Release 2026-04-15 18:10:30 +08:00
徐翔宇
952a9ebaae style: check for updates button hover uses brand lime color
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 18:09:08 +08:00
徐翔宇
5cfb500ea8 v2.1.0: Release 2026-04-15 17:51:58 +08:00
徐翔宇
2fb5a79bef feat: add Sparkle auto-update with in-app check for updates
- Integrate Sparkle 2.6+ via SPM for EdDSA-signed auto-updates
- Add UpdaterManager wrapper with observable canCheckForUpdates state
- Add "Check for Updates" button in Settings → About tab
- Configure SUFeedURL pointing to miomio.chat/appcast.xml
- Generate and store EdDSA public key in Info.plist
- Add zh-Hans to knownRegions for Sparkle UI localization
- Rewrite release.sh: auto-detect DerivedData, create DMG,
  Sparkle EdDSA signing, appcast.xml generation, and auto-deploy
  appcast to landing-page branch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 17:50:25 +08:00
徐翔宇
c423c83ed6 fix: add Applications symlink to hdiutil DMG fallback
Without create-dmg installed, the DMG only contained the app with no
drag-to-install target. Add an Applications symlink so both paths
produce a usable installer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 16:27:00 +08:00
carey314
8b5d841d2e
Merge pull request #64 from MioMioOS/fix/cmux-probe-and-ui-polish
fix: cmux probe hang + UI polish + quit button
2026-04-15 14:39:05 +08:00
徐翔宇
aee6e71cad docs: update brew command + add WeChat group QR to contact
- Fix brew install command: remove --cask flag, use correct tap path
- Add MioIsland WeChat user group QR code to contact section

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 14:38:10 +08:00
徐翔宇
b7364ee6cd fix: cmux probe hang + UI polish + quit button
Bug fixes:
- fix(#60): probeAutomationPermission passed requestorAddr instead of
  targetAddr to AEDeterminePermissionToAutomateTarget, causing the
  AE permission check to query the wrong app and hang indefinitely,
  freezing the entire settings panel
- fix(#59): discoverClaudeSessionsFromConfig used runShellWithTimeout
  to spawn /bin/ps for pid liveness checks, which fails under certain
  code-signing configurations. Replaced with kill(pid, 0) signal check
  — faster, no subprocess needed, works in all environments

UI improvements:
- Add "Quit Mio Island" button at bottom of Settings → About tab
- Anthropic API Proxy description: improve readability (medium weight,
  gray-white color, wider line spacing), update CodeIsland → MioIsland
- TextField placeholders: change from default black to light gray
  (white 30% opacity) for both proxy URL and plugin install URL fields

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 14:31:26 +08:00
徐翔宇
031a80eaf3 chore: bump MARKETING_VERSION to 2.0.9 2026-04-15 12:04:22 +08:00