feat: 桌面启动器改 XiaoheAgent.app 形态——icns 图标、无 .command 后缀,Linux 桌面项同步带图标
This commit is contained in:
@@ -197,24 +197,79 @@ def _print_report(workspace: Path, report: dict) -> None:
|
||||
console.print(f" [dim]- {rel}[/dim]")
|
||||
|
||||
|
||||
_APP_INFO_PLIST = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key><string>XiaoheAgent</string>
|
||||
<key>CFBundleDisplayName</key><string>XiaoheAgent</string>
|
||||
<key>CFBundleIdentifier</key><string>com.xiaohe.agent</string>
|
||||
<key>CFBundleVersion</key><string>1</string>
|
||||
<key>CFBundlePackageType</key><string>APPL</string>
|
||||
<key>CFBundleExecutable</key><string>XiaoheAgent</string>
|
||||
<key>CFBundleIconFile</key><string>XiaoheAgent</string>
|
||||
<key>LSMinimumSystemVersion</key><string>11.0</string>
|
||||
<key>NSHighResolutionCapable</key><true/>
|
||||
</dict>
|
||||
</plist>
|
||||
"""
|
||||
|
||||
_APP_EXECUTABLE = """#!/usr/bin/env bash
|
||||
# XiaoheAgent launcher — opens Terminal running xiaohe.
|
||||
export PATH="$HOME/.local/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"
|
||||
if command -v xiaohe >/dev/null 2>&1; then
|
||||
osascript -e 'tell application "Terminal" to activate' \\
|
||||
-e 'tell application "Terminal" to do script "xiaohe"'
|
||||
else
|
||||
osascript -e 'display dialog "xiaohe command not found — run install.sh first" buttons {"OK"}'
|
||||
fi
|
||||
"""
|
||||
|
||||
|
||||
def _icon_assets() -> tuple[Path | None, Path | None]:
|
||||
"""(icns, png) shipped in the runtime tree, if present."""
|
||||
runtime = get_runtime_root()
|
||||
if runtime is None:
|
||||
return None, None
|
||||
icons = runtime / "assets" / "icon"
|
||||
icns = icons / "XiaoheAgent.icns"
|
||||
png = icons / "xiaohe-icon-512.png"
|
||||
return (icns if icns.is_file() else None), (png if png.is_file() else None)
|
||||
|
||||
|
||||
def _create_launcher() -> None:
|
||||
"""Double-click launcher that opens a terminal running ``xiaohe``."""
|
||||
home = Path.home()
|
||||
icns, png = _icon_assets()
|
||||
if os.uname().sysname == "Darwin": # noqa: PLR2004 — platform check
|
||||
desktop = home / "Desktop"
|
||||
if desktop.is_dir():
|
||||
launcher = desktop / "XiaoheAgent.command"
|
||||
launcher.write_text(
|
||||
"#!/usr/bin/env bash\n"
|
||||
"# XiaoheAgent launcher — double-click opens Terminal running xiaohe.\n"
|
||||
"exec xiaohe\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
launcher.chmod(0o755)
|
||||
console.print(f" [green]launcher:[/green] {launcher}")
|
||||
if not desktop.is_dir():
|
||||
return
|
||||
# Legacy .command launcher is superseded by the .app bundle.
|
||||
legacy = desktop / "XiaoheAgent.command"
|
||||
if legacy.exists():
|
||||
legacy.unlink()
|
||||
app = desktop / "XiaoheAgent.app"
|
||||
macos = app / "Contents" / "MacOS"
|
||||
resources = app / "Contents" / "Resources"
|
||||
macos.mkdir(parents=True, exist_ok=True)
|
||||
resources.mkdir(parents=True, exist_ok=True)
|
||||
(app / "Contents" / "Info.plist").write_text(_APP_INFO_PLIST, encoding="utf-8")
|
||||
executable = macos / "XiaoheAgent"
|
||||
executable.write_text(_APP_EXECUTABLE, encoding="utf-8")
|
||||
executable.chmod(0o755)
|
||||
if icns is not None:
|
||||
shutil.copy2(icns, resources / "XiaoheAgent.icns")
|
||||
console.print(f" [green]launcher:[/green] {app}")
|
||||
else:
|
||||
apps = home / ".local" / "share" / "applications"
|
||||
apps.mkdir(parents=True, exist_ok=True)
|
||||
icon_line = "Icon=utilities-terminal\n"
|
||||
if png is not None:
|
||||
icon_dir = home / ".local" / "share" / "icons" / "hicolor" / "512x512" / "apps"
|
||||
icon_dir.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(png, icon_dir / "xiaohe-agent.png")
|
||||
icon_line = "Icon=xiaohe-agent\n"
|
||||
(apps / "XiaoheAgent.desktop").write_text(
|
||||
"[Desktop Entry]\n"
|
||||
"Type=Application\n"
|
||||
@@ -222,7 +277,7 @@ def _create_launcher() -> None:
|
||||
"Comment=Xiaohe Agent terminal\n"
|
||||
"Exec=xiaohe\n"
|
||||
"Terminal=true\n"
|
||||
"Icon=utilities-terminal\n"
|
||||
f"{icon_line}"
|
||||
"Categories=Utility;\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
@@ -85,3 +85,55 @@ class TestSyncWorkspace:
|
||||
sync_mod.sync_workspace(workspace)
|
||||
report = sync_mod.sync_workspace(workspace, force=True)
|
||||
assert report["added"]
|
||||
|
||||
|
||||
class TestCreateLauncher:
|
||||
@pytest.fixture()
|
||||
def fake_home(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
|
||||
home = tmp_path / "home"
|
||||
(home / "Desktop").mkdir(parents=True)
|
||||
monkeypatch.setattr(Path, "home", staticmethod(lambda: home))
|
||||
return home
|
||||
|
||||
def test_macos_app_bundle_with_icon(
|
||||
self, runtime: Path, fake_home: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
(runtime / "assets" / "icon").mkdir(parents=True)
|
||||
(runtime / "assets" / "icon" / "XiaoheAgent.icns").write_bytes(b"icns")
|
||||
legacy = fake_home / "Desktop" / "XiaoheAgent.command"
|
||||
legacy.write_text("#!/bin/sh\n")
|
||||
monkeypatch.setattr(
|
||||
sync_mod.os, "uname", lambda: type("U", (), {"sysname": "Darwin"})
|
||||
)
|
||||
sync_mod._create_launcher()
|
||||
app = fake_home / "Desktop" / "XiaoheAgent.app"
|
||||
executable = app / "Contents" / "MacOS" / "XiaoheAgent"
|
||||
assert (app / "Contents" / "Info.plist").is_file()
|
||||
assert (app / "Contents" / "Resources" / "XiaoheAgent.icns").is_file()
|
||||
assert executable.stat().st_mode & 0o111
|
||||
assert "xiaohe" in executable.read_text()
|
||||
assert not legacy.exists() # superseded .command removed
|
||||
|
||||
def test_linux_desktop_entry_with_icon(
|
||||
self, runtime: Path, fake_home: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
(runtime / "assets" / "icon").mkdir(parents=True)
|
||||
(runtime / "assets" / "icon" / "xiaohe-icon-512.png").write_bytes(b"png")
|
||||
monkeypatch.setattr(
|
||||
sync_mod.os, "uname", lambda: type("U", (), {"sysname": "Linux"})
|
||||
)
|
||||
sync_mod._create_launcher()
|
||||
entry = (
|
||||
fake_home / ".local" / "share" / "applications" / "XiaoheAgent.desktop"
|
||||
)
|
||||
assert "Icon=xiaohe-agent" in entry.read_text()
|
||||
assert (
|
||||
fake_home
|
||||
/ ".local"
|
||||
/ "share"
|
||||
/ "icons"
|
||||
/ "hicolor"
|
||||
/ "512x512"
|
||||
/ "apps"
|
||||
/ "xiaohe-agent.png"
|
||||
).is_file()
|
||||
|
||||
Reference in New Issue
Block a user