|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +This file provides guidelines for agentic coding agents working in the RainingKeysPython repository. |
| 4 | + |
| 5 | +## Build & Development Commands |
| 6 | + |
| 7 | +### Running the Application |
| 8 | +```bash |
| 9 | +python main.py |
| 10 | +``` |
| 11 | + |
| 12 | +### Building Executable |
| 13 | +```bash |
| 14 | +python build.py |
| 15 | +``` |
| 16 | +This creates both release and debug builds: |
| 17 | +- `RainingKeysPython.zip` - Release build (no console) |
| 18 | +- `RainingKeysPython-debug.zip` - Debug build (with console) |
| 19 | + |
| 20 | +The build script automatically: |
| 21 | +1. Cleans previous build artifacts |
| 22 | +2. Creates release build with PyInstaller |
| 23 | +3. Creates debug build with console enabled |
| 24 | +4. Copies config.ini to dist directories |
| 25 | +5. Packages both builds into zip files |
| 26 | + |
| 27 | +### Manual PyInstaller Build |
| 28 | +```bash |
| 29 | +pyinstaller RainingKeysPython.spec |
| 30 | +``` |
| 31 | + |
| 32 | +### Installing Dependencies |
| 33 | +```bash |
| 34 | +pip install -r requirements.txt |
| 35 | +``` |
| 36 | + |
| 37 | +## Project Overview |
| 38 | + |
| 39 | +RainingKeysPython is a high-performance, external rhythm game input visualizer built with: |
| 40 | +- **Python 3.10+** |
| 41 | +- **PySide6 (Qt)** - GUI and rendering |
| 42 | +- **pynput** - Global keyboard monitoring |
| 43 | +- **pywin32** - Windows API for transparency/click-through |
| 44 | + |
| 45 | +## Code Style Guidelines |
| 46 | + |
| 47 | +### Imports |
| 48 | +- Import standard library modules first |
| 49 | +- Import third-party modules second |
| 50 | +- Import local modules last |
| 51 | +- Use relative imports within the core package (e.g., `from .configuration import AppConfig`) |
| 52 | +- Group imports by type with blank lines between sections |
| 53 | + |
| 54 | +```python |
| 55 | +import time |
| 56 | +from collections import deque |
| 57 | + |
| 58 | +from PySide6.QtWidgets import QWidget |
| 59 | +from pynput import keyboard |
| 60 | + |
| 61 | +from .configuration import AppConfig |
| 62 | +``` |
| 63 | + |
| 64 | +### Type Hints |
| 65 | +- Use type hints for function signatures, especially for public methods |
| 66 | +- Use dataclasses for configuration objects |
| 67 | +- Type hint complex structures with `typing.Dict`, `typing.List`, etc. |
| 68 | + |
| 69 | +```python |
| 70 | +from dataclasses import dataclass, field |
| 71 | +from typing import Dict, List |
| 72 | + |
| 73 | +@dataclass |
| 74 | +class VisualSettings: |
| 75 | + scroll_speed: int = 800 |
| 76 | + bar_color_str: str = "0,255,255,200" |
| 77 | +``` |
| 78 | + |
| 79 | +### Naming Conventions |
| 80 | +- **Classes**: PascalCase (e.g., `BarPool`, `RainingKeysOverlay`) |
| 81 | +- **Functions/Methods**: snake_case (e.g., `update_canvas`, `handle_input`) |
| 82 | +- **Constants**: UPPER_SNAKE_CASE (e.g., `MAX_BARS`, `FADE_START_Y`) |
| 83 | +- **Private methods**: Leading underscore (e.g., `_init_key_counts`) |
| 84 | +- **Instance variables**: snake_case (e.g., `active_bars`, `config`) |
| 85 | + |
| 86 | +### Class Design |
| 87 | +- Use dataclasses for configuration objects |
| 88 | +- Use `__slots__` for performance-critical classes with many instances |
| 89 | +- Inherit from Qt classes (QWidget, QObject) appropriately |
| 90 | +- Use Qt's Signal/Slot pattern for inter-object communication |
| 91 | + |
| 92 | +```python |
| 93 | +@dataclass |
| 94 | +class AppConfig: |
| 95 | + MAX_BARS: int = 300 |
| 96 | + |
| 97 | +class Bar: |
| 98 | + __slots__ = ['lane_index', 'press_time', 'release_time', 'active'] |
| 99 | +``` |
| 100 | + |
| 101 | +### Error Handling |
| 102 | +- Use try/except with print statements for debugging (not logging module) |
| 103 | +- Provide fallback values for error conditions |
| 104 | +- Check for module availability with ImportError handling |
| 105 | + |
| 106 | +```python |
| 107 | +try: |
| 108 | + import win32gui |
| 109 | + HAS_WIN32 = True |
| 110 | +except ImportError: |
| 111 | + HAS_WIN32 = False |
| 112 | + print("Warning: pywin32 not found.") |
| 113 | +``` |
| 114 | + |
| 115 | +### Qt/PySide6 Specifics |
| 116 | +- Initialize UI in `init_ui()` method |
| 117 | +- Use `super().__init__()` for all custom Qt widgets |
| 118 | +- Connect signals in constructor or init methods |
| 119 | +- Use `blockSignals(True/False)` when programmatically updating UI controls |
| 120 | +- Set window flags for overlay: `FramelessWindowHint | WindowStaysOnTopHint | Tool | WindowTransparentForInput` |
| 121 | + |
| 122 | +### Performance Optimization |
| 123 | +- Use object pooling for frequently created/destroyed objects (see `BarPool` class) |
| 124 | +- Use `deque` for active/inactive object collections |
| 125 | +- Cache geometry calculations that don't change frequently |
| 126 | +- Use `time.perf_counter()` for high-precision timing |
| 127 | + |
| 128 | +### Configuration Management |
| 129 | +- All config flows through `SettingsManager` and `AppConfig` dataclasses |
| 130 | +- Settings are persisted to `config.ini` using ConfigParser |
| 131 | +- Emit `settings_changed` Signal after any config modifications |
| 132 | +- Always save config after changes using `settings.save()` |
| 133 | + |
| 134 | +### Thread Safety |
| 135 | +- Input monitoring runs in separate QThread (`InputMonitor`) |
| 136 | +- Use Qt Signals for thread communication (key_pressed, key_released) |
| 137 | +- Worker objects use `active_keys` set to filter autorepeat events |
| 138 | + |
| 139 | +## Architecture Notes |
| 140 | + |
| 141 | +### Core Components |
| 142 | +- `main.py` - Application entry point, initializes all components |
| 143 | +- `core/configuration.py` - Dataclasses for all config |
| 144 | +- `core/settings_manager.py` - Loads/saves config, emits change signals |
| 145 | +- `core/input_mon.py` - Global keyboard hook via pynput in QThread |
| 146 | +- `core/overlay.py` - Main rendering window, object pooling, paint logic |
| 147 | +- `core/gui.py` - Configuration UI window |
| 148 | +- `core/ui/components.py` - UI widget groups |
| 149 | +- `core/ui/theme.py` - Dark theme constants |
| 150 | + |
| 151 | +### Key Patterns |
| 152 | +1. **Dependency Injection**: Pass `AppConfig` or `SettingsManager` to components |
| 153 | +2. **Signal-Slot**: Use Qt signals for loose coupling between components |
| 154 | +3. **Object Pooling**: Reuse Bar objects to avoid GC pressure |
| 155 | +4. **Configuration Separation**: Data holds values, SettingsManager handles I/O |
| 156 | + |
| 157 | +## Testing |
| 158 | + |
| 159 | +This project currently does not have formal tests configured. When adding tests: |
| 160 | +- Consider pytest as the test framework |
| 161 | +- Mock Qt components for unit tests |
| 162 | +- Test configuration persistence logic |
| 163 | +- Verify object pool behavior under load |
| 164 | +- Test input filtering (autorepeat prevention) |
| 165 | + |
| 166 | +## File Structure |
| 167 | + |
| 168 | +``` |
| 169 | +core/ |
| 170 | +├── __init__.py |
| 171 | +├── configuration.py # Dataclasses for all settings |
| 172 | +├── settings_manager.py # Config persistence, signal emission |
| 173 | +├── input_mon.py # Keyboard hook in QThread |
| 174 | +├── overlay.py # Main rendering window, paint logic |
| 175 | +├── gui.py # Settings UI window |
| 176 | +└── ui/ |
| 177 | + ├── __init__.py |
| 178 | + ├── theme.py # Dark theme constants |
| 179 | + └── components.py # UI widget groups |
| 180 | +``` |
| 181 | + |
| 182 | +## Platform Notes |
| 183 | + |
| 184 | +- **Windows Only**: Uses pywin32 for click-through transparency |
| 185 | +- Python 3.10+ required |
| 186 | +- Requires external overlay compatibility with game window |
0 commit comments