星露谷物语 · 塞巴斯蒂安 macOS 桌面宠物
A macOS desktop pet of Sebastian from Stardew Valley — he wanders across your windows, sits on top of them, reacts to keywords, reminds you to drink water, and runs a Pomodoro timer. Built with Python + PyObjC, no Xcode or Swift required.
个人自用项目,使用了星露谷物语的原版像素素材,仅供学习交流,不作商用。 Fan-made project using original Stardew Valley pixel art. For personal & educational use only, not for commercial distribution.
Desktop Physics — Sebastian walks along the top edges of your windows, climbs up their sides, and falls with gravity when a window disappears. Drag him and drop him onto any window edge — he'll grab on.
Keyword Easter Eggs — Click the 🌙 menu bar icon → "说点什么…" to type a keyword. He responds in character:
| Keyword | Reaction |
|---|---|
| 下雨 (rain) | "雨天会让我觉得安心。" |
| 咖啡 (coffee) | "咖啡……行,那个我需要。" |
| 晚安 (goodnight) | curls up to sleep |
| 玛鲁 (Maru) | "……" (silent annoyance) |
| 游戏 (games) | "要一起玩吗。……算了,你大概没空。" |
Full list in config/dialogue.json — add your own keywords there.
Timed Reminders — Four built-in reminders, all spoken in Sebastian's voice:
- 💧 Drink water — every 40 min
- 🚶 Stand up & stretch — every 50 min
- 👀 Look away from screen — every 25 min
- 🌙 Go to sleep — between 1–5 AM, every 30 min
Each can be toggled individually. Intervals have ±3 min jitter so they don't all fire at once.
Pomodoro Timer — 25 min focus + 5 min break (15 min long break after 4 rounds). Live countdown in the menu bar (🍅24:31 / ☕️04:12). During focus, he sits at a keyboard; during break, he gets up and walks around.
Idle Detection — If you're AFK for 5+ minutes, he curls up and sleeps. All reminders go silent (no point talking to an empty chair). When you return, he greets you based on how long you were gone.
Stardew Valley UI — Dialogue bubbles, emotes, and portraits all use the original game's pixel art, rendered with proper nine-patch scaling.
- macOS (tested on macOS 14 Sonoma, Apple Silicon & Intel)
- Python 3.9+ (system Python or Homebrew)
# Install dependencies
python3 -m pip install --user pyobjc-framework-Cocoa pyobjc-framework-Quartz numpy
# Clone and run
git clone https://github.com/SimoneScoop/SebastianPet.git
cd SebastianPet
python3 run.pyThe app appears as a 🌙 icon in your menu bar — no Dock icon. Right-click Sebastian or click the menu bar icon for all controls. Quit from the menu.
./build_app.sh
open build/SebastianPet.appNo Xcode or codesign required. First launch may need: Finder → right-click the app → Open → confirm.
| Action | Effect |
|---|---|
| Drag | Pick him up; drop onto a window edge to perch, or let him fall |
| Double-click | Rest / wake up |
| Right-click | Context menu (same as menu bar) |
| Menu bar 🌙 | Talk / Rest / Pomodoro / Reminders / Opacity / Quit |
Opacity has four levels (100% / 85% / 65% / 40%). Preferences persist in ~/Library/Application Support/SebastianPet/prefs.json.
SebastianPet/
├── run.py # Entry point
├── src/
│ ├── app.py # Main loop, menu bar, event wiring
│ ├── pet_window.py # Transparent floating window + mouse passthrough
│ ├── physics.py # Gravity, edge walking, window tracking
│ ├── animation.py # State machine with priority interrupts
│ ├── sprites.py # Sprite loading, caching, placeholders
│ ├── chroma_key.py # Green screen removal
│ ├── bubble.py # Stardew-style dialogue box (nine-patch)
│ ├── emotes.py # Overhead emote bubbles
│ ├── trigger.py # Keyword input panel
│ ├── dialogue.py # Keyword matching engine
│ ├── reminders.py # Timed reminder scheduler
│ ├── pomodoro.py # Pomodoro timer + hourly chime
│ ├── idle.py # AFK detection (no accessibility permission needed)
│ ├── windows.py # Window enumeration via CGWindowList
│ ├── ui_assets.py # Game UI texture loading & slicing
│ └── prefs.py # User preferences persistence
├── assets/
│ ├── sprites/<state>/frame_*.png # Animation frames (green screen PNG)
│ └── ui/ # Game UI textures (dialog box, emotes, portraits)
├── config/
│ ├── dialogue.json # Keyword → response mappings
│ ├── animation.json # Frame rates, priorities, state config
│ ├── reminders.json # Reminder intervals & dialogue lines
│ └── pomodoro.json # Timer durations & hourly chime lines
├── tools/ # Sprite import & processing scripts
├── tests/ # Unit tests
└── build_app.sh # Package as macOS .app
Mouse Passthrough — The window is transparent and ignores mouse events by default. A 60 Hz timer samples the alpha value under the cursor; interaction is only enabled when hovering over an opaque pixel. No accessibility permissions needed.
Window Physics — CGWindowListCopyWindowInfo enumerates visible windows as obstacles. Sebastian walks along their edges: horizontal edges play idle_wander, vertical edges play climb. Coordinate systems are converted from CGWindowList (top-left origin) to AppKit (bottom-left origin) once, then used consistently.
Animation State Machine — States have priorities (idle < trigger < drag < fall). Higher-priority actions interrupt lower ones. Each state can specify a post_state to transition to when done. Config-driven via animation.json.
Sprite Pipeline — Sprites are green-screen PNGs (#00FF00). Chroma key removal runs at load time with configurable tolerance and edge cleanup. Missing sprites get a purple placeholder with the state name — so you can develop logic before art is ready.
Add keywords: Edit config/dialogue.json. Each entry maps a keyword to a sprite animation, dialogue text, duration, and optional emote.
Adjust reminders: Edit config/reminders.json. Change intervals, add new reminders, or write new dialogue lines.
Swap sprites: Drop new green-screen PNGs into assets/sprites/<state>/. Name frames frame_001.png, frame_002.png, etc. The display scale auto-adjusts based on sprite height.
for t in tests/test_*.py; do python3 "$t"; done- Stardew Valley by ConcernedApe — character design, pixel art sprites, UI textures, and portraits. All game assets belong to ConcernedApe.
- Built with PyObjC for native macOS integration without Xcode.
This is a personal fan project. Game assets are the property of ConcernedApe / Stardew Valley. This project is shared for educational purposes only — please do not redistribute commercially.