sleap-connect 0.0.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sleap_connect-0.0.8/.claude/skills/investigation/SKILL.md +67 -0
- sleap_connect-0.0.8/.claude/skills/qt-testing/SKILL.md +82 -0
- sleap_connect-0.0.8/.claude/skills/qt-testing/scripts/qt_capture.py +131 -0
- sleap_connect-0.0.8/.dockerignore +15 -0
- sleap_connect-0.0.8/.github/workflows/build.yml +56 -0
- sleap_connect-0.0.8/.github/workflows/ci.yml +83 -0
- sleap_connect-0.0.8/.github/workflows/codespell.yml +29 -0
- sleap_connect-0.0.8/.github/workflows/worker_production.yml +68 -0
- sleap_connect-0.0.8/.github/workflows/worker_test.yml +67 -0
- sleap_connect-0.0.8/.gitignore +240 -0
- sleap_connect-0.0.8/.nojekyll +0 -0
- sleap_connect-0.0.8/CHANGELOG.md +29 -0
- sleap_connect-0.0.8/LICENSE +21 -0
- sleap_connect-0.0.8/PKG-INFO +215 -0
- sleap_connect-0.0.8/README.md +170 -0
- sleap_connect-0.0.8/config.example.toml +51 -0
- sleap_connect-0.0.8/dashboard/app.js +4800 -0
- sleap_connect-0.0.8/dashboard/callback.html +249 -0
- sleap_connect-0.0.8/dashboard/config.js +33 -0
- sleap_connect-0.0.8/dashboard/index.html +1693 -0
- sleap_connect-0.0.8/dashboard/sleap-logo.png +0 -0
- sleap_connect-0.0.8/dashboard/styles.css +4615 -0
- sleap_connect-0.0.8/pyproject.toml +115 -0
- sleap_connect-0.0.8/sleap_connect/.devcontainer/devcontainer.json +37 -0
- sleap_connect-0.0.8/sleap_connect/__init__.py +0 -0
- sleap_connect-0.0.8/sleap_connect/api.py +2817 -0
- sleap_connect-0.0.8/sleap_connect/auth/__init__.py +62 -0
- sleap_connect-0.0.8/sleap_connect/auth/credentials.py +413 -0
- sleap_connect-0.0.8/sleap_connect/auth/github.py +120 -0
- sleap_connect-0.0.8/sleap_connect/auth/keypair.py +131 -0
- sleap_connect-0.0.8/sleap_connect/auth/psk.py +114 -0
- sleap_connect-0.0.8/sleap_connect/auth/secret_resolver.py +153 -0
- sleap_connect-0.0.8/sleap_connect/cli.py +3938 -0
- sleap_connect-0.0.8/sleap_connect/client/.devcontainer/devcontainer.json +28 -0
- sleap_connect-0.0.8/sleap_connect/client/.dockerignore +19 -0
- sleap_connect-0.0.8/sleap_connect/client/Dockerfile +48 -0
- sleap_connect-0.0.8/sleap_connect/client/__init__.py +0 -0
- sleap_connect-0.0.8/sleap_connect/client/client.py +624 -0
- sleap_connect-0.0.8/sleap_connect/client/client_class.py +2651 -0
- sleap_connect-0.0.8/sleap_connect/client/client_track_class.py +678 -0
- sleap_connect-0.0.8/sleap_connect/client/directory_browser.py +648 -0
- sleap_connect-0.0.8/sleap_connect/client/file_selector.py +1667 -0
- sleap_connect-0.0.8/sleap_connect/client/file_transfer.py +158 -0
- sleap_connect-0.0.8/sleap_connect/client/fs_viewer_server.py +512 -0
- sleap_connect-0.0.8/sleap_connect/client/pyproject.toml +26 -0
- sleap_connect-0.0.8/sleap_connect/client/static/fs_resolve.html +1626 -0
- sleap_connect-0.0.8/sleap_connect/client/static/fs_viewer.html +1690 -0
- sleap_connect-0.0.8/sleap_connect/config.py +475 -0
- sleap_connect-0.0.8/sleap_connect/encryption/__init__.py +28 -0
- sleap_connect-0.0.8/sleap_connect/encryption/ecdh.py +144 -0
- sleap_connect-0.0.8/sleap_connect/encryption/envelope.py +105 -0
- sleap_connect-0.0.8/sleap_connect/exceptions.py +31 -0
- sleap_connect-0.0.8/sleap_connect/filesystem.py +350 -0
- sleap_connect-0.0.8/sleap_connect/gui/__init__.py +61 -0
- sleap_connect-0.0.8/sleap_connect/gui/presubmission.py +652 -0
- sleap_connect-0.0.8/sleap_connect/gui/runners.py +866 -0
- sleap_connect-0.0.8/sleap_connect/gui/widgets.py +3673 -0
- sleap_connect-0.0.8/sleap_connect/home.py +105 -0
- sleap_connect-0.0.8/sleap_connect/jobs/__init__.py +36 -0
- sleap_connect-0.0.8/sleap_connect/jobs/_exit_code_wrapper.py +75 -0
- sleap_connect-0.0.8/sleap_connect/jobs/builder.py +261 -0
- sleap_connect-0.0.8/sleap_connect/jobs/ports.py +56 -0
- sleap_connect-0.0.8/sleap_connect/jobs/process.py +377 -0
- sleap_connect-0.0.8/sleap_connect/jobs/queue.py +87 -0
- sleap_connect-0.0.8/sleap_connect/jobs/sleap_nn.py +190 -0
- sleap_connect-0.0.8/sleap_connect/jobs/spec.py +278 -0
- sleap_connect-0.0.8/sleap_connect/jobs/store.py +507 -0
- sleap_connect-0.0.8/sleap_connect/jobs/validator.py +456 -0
- sleap_connect-0.0.8/sleap_connect/protocol.py +566 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/__init__.py +0 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/auth.py +177 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/blob_http.py +162 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/blobs.py +211 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/cli.py +1176 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/daemon.py +428 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/envelope.py +286 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/errors.py +70 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/identity.py +99 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/iroh_live.py +144 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/iroh_transport.py +149 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/job_methods.py +1358 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/job_paths.py +222 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/jobs_cli.py +846 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/media.py +151 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/metrics.py +381 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/outputs.py +141 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/pair_code.py +344 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/pairing.py +184 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/runner.py +371 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/server.py +604 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/shared_folders.py +219 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/shell_hook.py +412 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/trust_store.py +243 -0
- sleap_connect-0.0.8/sleap_connect/protocol_v1/worker_info.py +133 -0
- sleap_connect-0.0.8/sleap_connect/rtc_browse.py +508 -0
- sleap_connect-0.0.8/sleap_connect/rtc_client.py +136 -0
- sleap_connect-0.0.8/sleap_connect/rtc_client_track.py +94 -0
- sleap_connect-0.0.8/sleap_connect/rtc_resolve.py +660 -0
- sleap_connect-0.0.8/sleap_connect/rtc_worker.py +79 -0
- sleap_connect-0.0.8/sleap_connect/tui/__init__.py +6 -0
- sleap_connect-0.0.8/sleap_connect/tui/app.py +197 -0
- sleap_connect-0.0.8/sleap_connect/tui/bridge.py +814 -0
- sleap_connect-0.0.8/sleap_connect/tui/screens/__init__.py +13 -0
- sleap_connect-0.0.8/sleap_connect/tui/screens/browser.py +1185 -0
- sleap_connect-0.0.8/sleap_connect/tui/screens/login.py +283 -0
- sleap_connect-0.0.8/sleap_connect/tui/screens/resolve_confirm.py +249 -0
- sleap_connect-0.0.8/sleap_connect/tui/screens/room_select.py +516 -0
- sleap_connect-0.0.8/sleap_connect/tui/screens/secret_input.py +159 -0
- sleap_connect-0.0.8/sleap_connect/tui/widgets/__init__.py +19 -0
- sleap_connect-0.0.8/sleap_connect/tui/widgets/miller.py +656 -0
- sleap_connect-0.0.8/sleap_connect/tui/widgets/slp_panel.py +482 -0
- sleap_connect-0.0.8/sleap_connect/tui/widgets/tree_browser.py +373 -0
- sleap_connect-0.0.8/sleap_connect/tui/widgets/worker_tabs.py +327 -0
- sleap_connect-0.0.8/sleap_connect/worker/.devcontainer/devcontainer.json +28 -0
- sleap_connect-0.0.8/sleap_connect/worker/.dockerignore +19 -0
- sleap_connect-0.0.8/sleap_connect/worker/Dockerfile +13 -0
- sleap_connect-0.0.8/sleap_connect/worker/Dockerfile.test +15 -0
- sleap_connect-0.0.8/sleap_connect/worker/__init__.py +0 -0
- sleap_connect-0.0.8/sleap_connect/worker/admin_controller.py +514 -0
- sleap_connect-0.0.8/sleap_connect/worker/capabilities.py +238 -0
- sleap_connect-0.0.8/sleap_connect/worker/crdt_state.py +394 -0
- sleap_connect-0.0.8/sleap_connect/worker/file_manager.py +1391 -0
- sleap_connect-0.0.8/sleap_connect/worker/job_coordinator.py +248 -0
- sleap_connect-0.0.8/sleap_connect/worker/job_executor.py +1572 -0
- sleap_connect-0.0.8/sleap_connect/worker/mesh_coordinator.py +2469 -0
- sleap_connect-0.0.8/sleap_connect/worker/mesh_messages.py +412 -0
- sleap_connect-0.0.8/sleap_connect/worker/progress_reporter.py +276 -0
- sleap_connect-0.0.8/sleap_connect/worker/pyproject.toml +21 -0
- sleap_connect-0.0.8/sleap_connect/worker/run_training.py +102 -0
- sleap_connect-0.0.8/sleap_connect/worker/state_manager.py +280 -0
- sleap_connect-0.0.8/sleap_connect/worker/worker.py +654 -0
- sleap_connect-0.0.8/sleap_connect/worker/worker_class.py +3697 -0
- sleap_connect-0.0.8/sleap_connect/worker/zmq_check.py +90 -0
- sleap_connect-0.0.8/sleap_rtc/__init__.py +45 -0
- sleap_connect-0.0.8/tests/auth/__init__.py +0 -0
- sleap_connect-0.0.8/tests/auth/test_credentials.py +330 -0
- sleap_connect-0.0.8/tests/auth/test_keypair.py +69 -0
- sleap_connect-0.0.8/tests/auth/test_psk.py +172 -0
- sleap_connect-0.0.8/tests/auth/test_secret_resolver.py +273 -0
- sleap_connect-0.0.8/tests/encryption/__init__.py +0 -0
- sleap_connect-0.0.8/tests/encryption/test_ecdh.py +192 -0
- sleap_connect-0.0.8/tests/encryption/test_envelope.py +155 -0
- sleap_connect-0.0.8/tests/protocol_v1/__init__.py +0 -0
- sleap_connect-0.0.8/tests/protocol_v1/conftest.py +14 -0
- sleap_connect-0.0.8/tests/protocol_v1/pair_code_vectors.json +52 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_auth.py +442 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_blob_http.py +171 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_blobs.py +193 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_cli.py +319 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_daemon.py +357 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_envelope.py +162 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_identity.py +54 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_iroh_blob_stream.py +199 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_iroh_live.py +288 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_iroh_transport.py +292 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_job_methods.py +2156 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_job_paths.py +327 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_jobs_cli.py +424 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_media.py +122 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_metrics.py +241 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_outputs.py +206 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_pair_code.py +287 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_pairing.py +105 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_runner.py +463 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_server.py +418 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_shared_folders.py +183 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_shell_hook.py +230 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_trust_store.py +162 -0
- sleap_connect-0.0.8/tests/protocol_v1/test_worker_info.py +52 -0
- sleap_connect-0.0.8/tests/test_api.py +2484 -0
- sleap_connect-0.0.8/tests/test_auth_github.py +72 -0
- sleap_connect-0.0.8/tests/test_cli.py +512 -0
- sleap_connect-0.0.8/tests/test_cli_config.py +111 -0
- sleap_connect-0.0.8/tests/test_command_builder.py +488 -0
- sleap_connect-0.0.8/tests/test_config_mounts.py +107 -0
- sleap_connect-0.0.8/tests/test_config_path_mappings.py +117 -0
- sleap_connect-0.0.8/tests/test_directory_browser.py +374 -0
- sleap_connect-0.0.8/tests/test_doctor.py +96 -0
- sleap_connect-0.0.8/tests/test_file_manager_fs.py +914 -0
- sleap_connect-0.0.8/tests/test_file_manager_upload.py +226 -0
- sleap_connect-0.0.8/tests/test_file_transfer_upload.py +230 -0
- sleap_connect-0.0.8/tests/test_gui_integration.py +1651 -0
- sleap_connect-0.0.8/tests/test_gui_presubmission.py +816 -0
- sleap_connect-0.0.8/tests/test_gui_runners.py +1477 -0
- sleap_connect-0.0.8/tests/test_home.py +171 -0
- sleap_connect-0.0.8/tests/test_inference_dialog.py +198 -0
- sleap_connect-0.0.8/tests/test_job_executor.py +746 -0
- sleap_connect-0.0.8/tests/test_job_ports.py +36 -0
- sleap_connect-0.0.8/tests/test_job_process.py +543 -0
- sleap_connect-0.0.8/tests/test_job_queue.py +101 -0
- sleap_connect-0.0.8/tests/test_job_spec.py +629 -0
- sleap_connect-0.0.8/tests/test_job_store.py +308 -0
- sleap_connect-0.0.8/tests/test_job_validator.py +605 -0
- sleap_connect-0.0.8/tests/test_p2p_auth.py +419 -0
- sleap_connect-0.0.8/tests/test_persistent_progress_reporter.py +194 -0
- sleap_connect-0.0.8/tests/test_sleap_nn_check.py +168 -0
- sleap_connect-0.0.8/tests/test_slp_path_dialog_autofill.py +195 -0
- sleap_connect-0.0.8/tests/test_slp_path_dialog_save_buttons.py +203 -0
- sleap_connect-0.0.8/tests/test_smoke.py +430 -0
- sleap_connect-0.0.8/tests/test_stop_early_sigint.py +352 -0
- sleap_connect-0.0.8/tests/test_video_path_mapping_prompt.py +149 -0
- sleap_connect-0.0.8/tests/test_worker_config_content.py +521 -0
- sleap_connect-0.0.8/tests/test_worker_fs_routing.py +250 -0
- sleap_connect-0.0.8/tests/test_worker_reconnection.py +854 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: investigation
|
|
3
|
+
description: >
|
|
4
|
+
Scaffolds a structured investigation in scratch/ for empirical research and documentation.
|
|
5
|
+
Use when the user says "start an investigation" or wants to: trace code paths or data flow
|
|
6
|
+
("trace from X to Y", "what touches X", "follow the wiring"), document system architecture
|
|
7
|
+
comprehensively ("document how the system works", "archeology"), investigate bugs
|
|
8
|
+
("figure out why X happens"), explore technical feasibility ("can we do X?"), or explore
|
|
9
|
+
design options ("explore the API", "gather context", "design alternatives").
|
|
10
|
+
Creates dated folder with README. NOT for simple code questions or single-file searches.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Set up an investigation
|
|
14
|
+
|
|
15
|
+
## Instructions
|
|
16
|
+
|
|
17
|
+
1. Create a folder in `{REPO_ROOT}/scratch/` with the format `{YYYY-MM-DD}-{descriptive-name}`.
|
|
18
|
+
2. Create a `README.md` in this folder with: task description, background context, task checklist. Update with findings as you progress.
|
|
19
|
+
3. Create scripts and data files as needed for empirical work.
|
|
20
|
+
4. For complex investigations, split into sub-documents as patterns emerge.
|
|
21
|
+
|
|
22
|
+
## Investigation Patterns
|
|
23
|
+
|
|
24
|
+
These are common patterns, not rigid categories. Most investigations blend multiple patterns.
|
|
25
|
+
|
|
26
|
+
**Tracing** - "trace from X to Y", "what touches X", "follow the wiring"
|
|
27
|
+
- Follow call stack or data flow from a focal component to its connections
|
|
28
|
+
- Can trace forward (X → where does it go?) or backward (what leads to X?)
|
|
29
|
+
- Useful for: assessing impact of changes, understanding coupling
|
|
30
|
+
|
|
31
|
+
**System Architecture Archeology** - "document how the system works", "archeology"
|
|
32
|
+
- Comprehensive documentation of an entire system or flow for reusable reference
|
|
33
|
+
- Start from entry points, trace through all layers, document relationships exhaustively
|
|
34
|
+
- For complex systems, consider numbered sub-documents (01-cli.md, 02-data.md, etc.)
|
|
35
|
+
|
|
36
|
+
**Bug Investigation** - "figure out why X happens", "this is broken"
|
|
37
|
+
- Reproduce → trace root cause → propose fix
|
|
38
|
+
- For cross-repo bugs, consider per-repo task breakdowns
|
|
39
|
+
|
|
40
|
+
**Technical Exploration** - "can we do X?", "is this possible?", "figure out how to"
|
|
41
|
+
- Feasibility testing with proof-of-concept scripts
|
|
42
|
+
- Document what works AND what doesn't
|
|
43
|
+
|
|
44
|
+
**Design Research** - "explore the API", "gather context", "design alternatives"
|
|
45
|
+
- Understand systems and constraints before building
|
|
46
|
+
- Compare alternatives, document trade-offs
|
|
47
|
+
- Include visual artifacts (mockups, screenshots) when relevant
|
|
48
|
+
- For iterative decisions, use numbered "Design Questions" (DQ1, DQ2...) to structure review
|
|
49
|
+
|
|
50
|
+
## Best Practices
|
|
51
|
+
|
|
52
|
+
- Use `uv` with inline dependencies for standalone scripts; for scripts importing local project code, use `python` directly (or `uv run python` if env not activated)
|
|
53
|
+
- Use subagents for parallel exploration to save context
|
|
54
|
+
- Write small scripts to explore APIs interactively
|
|
55
|
+
- Generate figures/diagrams and reference inline in markdown
|
|
56
|
+
- For web servers: `npx serve -p 8080 --cors --no-clipboard &`
|
|
57
|
+
- For screenshots: use Playwright MCP for web, Qt's grab() for GUI
|
|
58
|
+
- For external package API review: clone to `scratch/repos/` for direct source access
|
|
59
|
+
|
|
60
|
+
## Important: Scratch is Gitignored
|
|
61
|
+
|
|
62
|
+
The `scratch/` directory is in `.gitignore` and will NOT be committed.
|
|
63
|
+
|
|
64
|
+
- NEVER delete anything from scratch - it doesn't need cleanup
|
|
65
|
+
- When distilling findings into PRs, include all relevant info inline
|
|
66
|
+
- Copy key findings, code, and data directly into PR descriptions
|
|
67
|
+
- PRs must be self-contained; don't reference scratch files
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: qt-testing
|
|
3
|
+
description: Capture and visually inspect Qt GUI widgets using screenshots. Use when asked to verify GUI rendering, test widget appearance, check layouts, or visually inspect any PySide6/Qt component. Enables Claude to "see" Qt interfaces by capturing offscreen screenshots and analyzing them with vision.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Qt GUI Testing
|
|
7
|
+
|
|
8
|
+
Capture screenshots of Qt widgets for visual inspection without displaying windows on screen.
|
|
9
|
+
|
|
10
|
+
## Quick Start
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
# Capture any widget
|
|
14
|
+
from scripts.qt_capture import capture_widget
|
|
15
|
+
path = capture_widget(my_widget, "description_here")
|
|
16
|
+
# Then read the screenshot with the Read tool
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Core Script
|
|
20
|
+
|
|
21
|
+
Run `scripts/qt_capture.py` or import `capture_widget` from it:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Standalone test
|
|
25
|
+
uv run --with PySide6 python .claude/skills/qt-testing/scripts/qt_capture.py
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Output Location
|
|
29
|
+
|
|
30
|
+
All screenshots save to: `scratch/.qt-screenshots/`
|
|
31
|
+
|
|
32
|
+
Naming: `{YYYY-MM-DD.HH-MM-SS}_{description}.png`
|
|
33
|
+
|
|
34
|
+
## Workflow
|
|
35
|
+
|
|
36
|
+
1. Create/obtain the widget to test
|
|
37
|
+
2. Call `capture_widget(widget, "description")`
|
|
38
|
+
3. Read the saved screenshot with the Read tool
|
|
39
|
+
4. Analyze with vision to verify correctness
|
|
40
|
+
|
|
41
|
+
## Interaction Pattern
|
|
42
|
+
|
|
43
|
+
To interact with widgets (click buttons, etc.):
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
# Find widget at coordinates (from vision analysis)
|
|
47
|
+
target = widget.childAt(x, y)
|
|
48
|
+
|
|
49
|
+
# Trigger it directly (not mouse events)
|
|
50
|
+
if hasattr(target, 'click'):
|
|
51
|
+
target.click()
|
|
52
|
+
QApplication.processEvents()
|
|
53
|
+
|
|
54
|
+
# Capture result
|
|
55
|
+
capture_widget(widget, "after_click")
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Example: Test a Dialog
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
import sys
|
|
62
|
+
from PySide6.QtWidgets import QApplication
|
|
63
|
+
from sleap.gui.learning.dialog import TrainingEditorDialog
|
|
64
|
+
|
|
65
|
+
# Add skill scripts to path
|
|
66
|
+
sys.path.insert(0, ".claude/skills/qt-testing")
|
|
67
|
+
from scripts.qt_capture import capture_widget, init_qt
|
|
68
|
+
|
|
69
|
+
app = init_qt()
|
|
70
|
+
dialog = TrainingEditorDialog()
|
|
71
|
+
path = capture_widget(dialog, "training_dialog")
|
|
72
|
+
dialog.close()
|
|
73
|
+
print(f"Inspect: {path}")
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Key Points
|
|
77
|
+
|
|
78
|
+
- Uses `Qt.WA_DontShowOnScreen` - no window popup
|
|
79
|
+
- Renders identically to on-screen display (verified)
|
|
80
|
+
- Call `processEvents()` after interactions before capture
|
|
81
|
+
- Use `childAt(x, y)` to map vision coordinates to widgets
|
|
82
|
+
- Direct method calls (`.click()`) work; simulated mouse events don't
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Qt widget screenshot capture utility.
|
|
3
|
+
|
|
4
|
+
Captures screenshots of Qt widgets without displaying them on screen.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
from PySide6.QtWidgets import QApplication, QWidget
|
|
13
|
+
from PySide6.QtCore import Qt
|
|
14
|
+
|
|
15
|
+
# Output directory
|
|
16
|
+
OUTPUT_DIR = Path("scratch/.qt-screenshots")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def init_qt() -> QApplication:
|
|
20
|
+
"""Initialize Qt application (required once per process)."""
|
|
21
|
+
app = QApplication.instance()
|
|
22
|
+
if app is None:
|
|
23
|
+
app = QApplication(sys.argv)
|
|
24
|
+
return app
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def timestamp() -> str:
|
|
28
|
+
"""Generate timestamp for filename."""
|
|
29
|
+
return datetime.now().strftime("%Y-%m-%d.%H-%M-%S")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def capture_widget(
|
|
33
|
+
widget: QWidget,
|
|
34
|
+
description: str,
|
|
35
|
+
output_dir: Optional[Path] = None,
|
|
36
|
+
) -> Path:
|
|
37
|
+
"""
|
|
38
|
+
Capture a screenshot of a widget without displaying it on screen.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
widget: The QWidget to capture
|
|
42
|
+
description: Short description for filename (use underscores, no spaces)
|
|
43
|
+
output_dir: Override output directory (default: scratch/.qt-screenshots)
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Path to saved screenshot
|
|
47
|
+
"""
|
|
48
|
+
out = output_dir or OUTPUT_DIR
|
|
49
|
+
out.mkdir(parents=True, exist_ok=True)
|
|
50
|
+
|
|
51
|
+
# Configure for invisible rendering
|
|
52
|
+
widget.setAttribute(Qt.WA_DontShowOnScreen, True)
|
|
53
|
+
widget.show()
|
|
54
|
+
QApplication.processEvents()
|
|
55
|
+
|
|
56
|
+
# Capture
|
|
57
|
+
pixmap = widget.grab()
|
|
58
|
+
if pixmap.isNull():
|
|
59
|
+
raise RuntimeError("Failed to capture widget - pixmap is null")
|
|
60
|
+
|
|
61
|
+
# Save with timestamp
|
|
62
|
+
desc_clean = description.replace(" ", "_").replace("/", "-")
|
|
63
|
+
filename = f"{timestamp()}_{desc_clean}.png"
|
|
64
|
+
filepath = out / filename
|
|
65
|
+
|
|
66
|
+
if not pixmap.save(str(filepath)):
|
|
67
|
+
raise RuntimeError(f"Failed to save screenshot to {filepath}")
|
|
68
|
+
|
|
69
|
+
# Don't close - caller may want to interact further
|
|
70
|
+
widget.hide()
|
|
71
|
+
|
|
72
|
+
return filepath
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def capture_and_click(
|
|
76
|
+
widget: QWidget,
|
|
77
|
+
x: int,
|
|
78
|
+
y: int,
|
|
79
|
+
description: str,
|
|
80
|
+
) -> tuple[Path, Optional[QWidget]]:
|
|
81
|
+
"""
|
|
82
|
+
Click at coordinates and capture the result.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
widget: Parent widget
|
|
86
|
+
x, y: Coordinates to click (in widget coordinate space)
|
|
87
|
+
description: Description for filename
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Tuple of (screenshot path, clicked widget or None)
|
|
91
|
+
"""
|
|
92
|
+
widget.setAttribute(Qt.WA_DontShowOnScreen, True)
|
|
93
|
+
widget.show()
|
|
94
|
+
QApplication.processEvents()
|
|
95
|
+
|
|
96
|
+
# Find and click widget at coordinates
|
|
97
|
+
target = widget.childAt(x, y)
|
|
98
|
+
if target is not None:
|
|
99
|
+
if hasattr(target, "click"):
|
|
100
|
+
target.click()
|
|
101
|
+
elif hasattr(target, "toggle"):
|
|
102
|
+
target.toggle()
|
|
103
|
+
QApplication.processEvents()
|
|
104
|
+
|
|
105
|
+
# Capture result
|
|
106
|
+
path = capture_widget(widget, description)
|
|
107
|
+
return path, target
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# Self-test when run directly
|
|
111
|
+
if __name__ == "__main__":
|
|
112
|
+
from PySide6.QtWidgets import QPushButton, QVBoxLayout, QLabel
|
|
113
|
+
|
|
114
|
+
app = init_qt()
|
|
115
|
+
|
|
116
|
+
# Create test widget
|
|
117
|
+
widget = QWidget()
|
|
118
|
+
widget.setWindowTitle("Qt Capture Test")
|
|
119
|
+
widget.setFixedSize(300, 150)
|
|
120
|
+
|
|
121
|
+
layout = QVBoxLayout(widget)
|
|
122
|
+
layout.addWidget(QLabel("Qt Capture Test Widget"))
|
|
123
|
+
btn = QPushButton("Test Button")
|
|
124
|
+
layout.addWidget(btn)
|
|
125
|
+
|
|
126
|
+
# Capture it
|
|
127
|
+
path = capture_widget(widget, "self_test")
|
|
128
|
+
print(f"Captured: {path}")
|
|
129
|
+
|
|
130
|
+
widget.close()
|
|
131
|
+
print("Self-test complete!")
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: Build and publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
testpypi:
|
|
9
|
+
description: "Publish to TestPyPI instead of PyPI"
|
|
10
|
+
required: false
|
|
11
|
+
default: "false"
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
build:
|
|
15
|
+
name: Build distribution
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Set up uv
|
|
21
|
+
uses: astral-sh/setup-uv@v6
|
|
22
|
+
with:
|
|
23
|
+
python-version: "3.11"
|
|
24
|
+
|
|
25
|
+
- name: Build package
|
|
26
|
+
run: uv build
|
|
27
|
+
|
|
28
|
+
- name: Upload build artifacts
|
|
29
|
+
uses: actions/upload-artifact@v4
|
|
30
|
+
with:
|
|
31
|
+
name: dist
|
|
32
|
+
path: dist/
|
|
33
|
+
|
|
34
|
+
pypi:
|
|
35
|
+
name: Publish to PyPI
|
|
36
|
+
needs: build
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
permissions:
|
|
39
|
+
id-token: write # required for OIDC trusted publishing
|
|
40
|
+
steps:
|
|
41
|
+
- name: Download build artifacts
|
|
42
|
+
uses: actions/download-artifact@v4
|
|
43
|
+
with:
|
|
44
|
+
name: dist
|
|
45
|
+
path: dist/
|
|
46
|
+
|
|
47
|
+
- name: Set up uv
|
|
48
|
+
uses: astral-sh/setup-uv@v6
|
|
49
|
+
|
|
50
|
+
- name: Publish to TestPyPI
|
|
51
|
+
if: ${{ github.event.inputs.testpypi == 'true' }}
|
|
52
|
+
run: uv publish --index testpypi --trusted-publishing always
|
|
53
|
+
|
|
54
|
+
- name: Publish to PyPI
|
|
55
|
+
if: ${{ github.event.inputs.testpypi != 'true' }}
|
|
56
|
+
run: uv publish --trusted-publishing always
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- "sleap_connect/**"
|
|
8
|
+
- "tests/**"
|
|
9
|
+
- ".github/workflows/ci.yml"
|
|
10
|
+
- "pyproject.toml"
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint:
|
|
14
|
+
name: Lint
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up uv
|
|
20
|
+
uses: astral-sh/setup-uv@v6
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.11"
|
|
23
|
+
|
|
24
|
+
- name: Install dev dependencies
|
|
25
|
+
run: uv sync --extra dev
|
|
26
|
+
|
|
27
|
+
- name: Black
|
|
28
|
+
run: uv run black --check sleap_connect/
|
|
29
|
+
|
|
30
|
+
- name: Ruff
|
|
31
|
+
run: uv run ruff check sleap_connect/
|
|
32
|
+
|
|
33
|
+
tests:
|
|
34
|
+
name: Tests (${{ matrix.os }})
|
|
35
|
+
runs-on: ${{ matrix.os }}
|
|
36
|
+
timeout-minutes: 30
|
|
37
|
+
strategy:
|
|
38
|
+
fail-fast: false
|
|
39
|
+
matrix:
|
|
40
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
41
|
+
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v4
|
|
44
|
+
|
|
45
|
+
- name: Set up uv
|
|
46
|
+
uses: astral-sh/setup-uv@v6
|
|
47
|
+
with:
|
|
48
|
+
python-version: "3.11"
|
|
49
|
+
|
|
50
|
+
- name: Install graphics libraries (Ubuntu)
|
|
51
|
+
if: runner.os == 'Linux'
|
|
52
|
+
run: |
|
|
53
|
+
sudo apt-get update
|
|
54
|
+
sudo apt-get install -y \
|
|
55
|
+
libgl1 \
|
|
56
|
+
libegl-mesa0 \
|
|
57
|
+
libgles2 \
|
|
58
|
+
libxcb-xinerama0 \
|
|
59
|
+
libxcb-icccm4 \
|
|
60
|
+
libxcb-image0 \
|
|
61
|
+
libxcb-keysyms1 \
|
|
62
|
+
libxcb-randr0 \
|
|
63
|
+
libxcb-render-util0 \
|
|
64
|
+
libxcb-xkb1 \
|
|
65
|
+
libxkbcommon-x11-0 \
|
|
66
|
+
x11-utils
|
|
67
|
+
|
|
68
|
+
- name: Install dependencies
|
|
69
|
+
run: uv sync --extra dev
|
|
70
|
+
|
|
71
|
+
- name: Run tests (Ubuntu)
|
|
72
|
+
if: runner.os == 'Linux'
|
|
73
|
+
env:
|
|
74
|
+
QT_QPA_PLATFORM: offscreen
|
|
75
|
+
run: uv run pytest tests/ -v --timeout=60
|
|
76
|
+
|
|
77
|
+
- name: Run tests (macOS)
|
|
78
|
+
if: runner.os == 'macOS'
|
|
79
|
+
run: uv run pytest tests/ -v --timeout=60
|
|
80
|
+
|
|
81
|
+
- name: Run tests (Windows)
|
|
82
|
+
if: runner.os == 'Windows'
|
|
83
|
+
run: uv run pytest tests/ -v --timeout=60
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Codespell configuration is within pyproject.toml
|
|
2
|
+
---
|
|
3
|
+
name: Codespell
|
|
4
|
+
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
branches: [main]
|
|
8
|
+
pull_request:
|
|
9
|
+
branches: [main]
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
codespell:
|
|
16
|
+
name: Check for spelling errors
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout
|
|
21
|
+
uses: actions/checkout@v4
|
|
22
|
+
- name: Annotate locations with typos
|
|
23
|
+
uses: codespell-project/codespell-problem-matcher@v1
|
|
24
|
+
- name: Codespell
|
|
25
|
+
uses: codespell-project/actions-codespell@v2
|
|
26
|
+
with:
|
|
27
|
+
skip: ".git*,*.css,openspec"
|
|
28
|
+
ignore_words_list: "doesnt,bistream"
|
|
29
|
+
check_hidden: true
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: Build and Push sleap-rtc-worker (Production Workflow)
|
|
2
|
+
|
|
3
|
+
# Legacy worker image (the room-based `sleap-rtc worker`, installed from PyPI,
|
|
4
|
+
# not built from this repo). Manual only: it used to rebuild on every feature
|
|
5
|
+
# branch push and overwrite the published tags. To be removed with the legacy
|
|
6
|
+
# code (talmolab/sleap-connect#113).
|
|
7
|
+
on:
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout code
|
|
15
|
+
uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Get Git SHA
|
|
18
|
+
id: get_sha
|
|
19
|
+
run: echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
|
20
|
+
|
|
21
|
+
- name: Check disk space (before cleanup)
|
|
22
|
+
run: df -h
|
|
23
|
+
|
|
24
|
+
# Free up ~25-30GB of disk space by removing unused tools
|
|
25
|
+
# Optimized to remove only essential unused components
|
|
26
|
+
- name: Free Disk Space (Ubuntu)
|
|
27
|
+
uses: jlumbroso/free-disk-space@main
|
|
28
|
+
with:
|
|
29
|
+
tool-cache: false
|
|
30
|
+
android: true # ~10GB saved
|
|
31
|
+
dotnet: true # ~5GB saved
|
|
32
|
+
haskell: false # Skip, minimal savings
|
|
33
|
+
large-packages: true # ~8GB saved
|
|
34
|
+
docker-images: true # ~5GB saved, important for Docker builds
|
|
35
|
+
swap-storage: false # Skip, usually not needed
|
|
36
|
+
|
|
37
|
+
- name: Check disk space (after cleanup)
|
|
38
|
+
run: df -h
|
|
39
|
+
|
|
40
|
+
- name: Set up Docker Buildx
|
|
41
|
+
uses: docker/setup-buildx-action@v3
|
|
42
|
+
with:
|
|
43
|
+
driver: docker-container
|
|
44
|
+
|
|
45
|
+
- name: Authenticate to GitHub Container Registry
|
|
46
|
+
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
|
|
47
|
+
|
|
48
|
+
# Build and push with layer caching for faster subsequent builds
|
|
49
|
+
# First build: ~7-12 min, Cached builds: ~2-5 min (50-70% faster)
|
|
50
|
+
- name: Build and push Docker image
|
|
51
|
+
uses: docker/build-push-action@v6
|
|
52
|
+
with:
|
|
53
|
+
context: ./sleap_connect/worker
|
|
54
|
+
file: ./sleap_connect/worker/Dockerfile
|
|
55
|
+
platforms: linux/amd64
|
|
56
|
+
push: true
|
|
57
|
+
# Enable GitHub Actions cache for Docker layers
|
|
58
|
+
cache-from: type=gha
|
|
59
|
+
cache-to: type=gha,mode=max
|
|
60
|
+
tags: |
|
|
61
|
+
ghcr.io/${{ github.repository_owner }}/sleap-rtc-worker:latest
|
|
62
|
+
ghcr.io/${{ github.repository_owner }}/sleap-rtc-worker:linux-amd64
|
|
63
|
+
ghcr.io/${{ github.repository_owner }}/sleap-rtc-worker:linux-amd64-sleap-cuda
|
|
64
|
+
ghcr.io/${{ github.repository_owner }}/sleap-rtc-worker:linux-amd64-${{ steps.get_sha.outputs.sha }}
|
|
65
|
+
|
|
66
|
+
- name: Check disk space (after build)
|
|
67
|
+
if: always()
|
|
68
|
+
run: df -h
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
name: Build and Push sleap-rtc-worker (Test Workflow)
|
|
2
|
+
|
|
3
|
+
# Legacy worker image (the room-based `sleap-rtc worker`, installed from PyPI,
|
|
4
|
+
# not built from this repo). Manual only: it used to rebuild on every feature
|
|
5
|
+
# branch push and overwrite the published tags. To be removed with the legacy
|
|
6
|
+
# code (talmolab/sleap-connect#113).
|
|
7
|
+
on:
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout code
|
|
15
|
+
uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Get Git SHA
|
|
18
|
+
id: get_sha
|
|
19
|
+
run: echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
|
20
|
+
|
|
21
|
+
- name: Check disk space (before cleanup)
|
|
22
|
+
run: df -h
|
|
23
|
+
|
|
24
|
+
# Free up ~25-30GB of disk space by removing unused tools
|
|
25
|
+
# Optimized to remove only essential unused components
|
|
26
|
+
- name: Free Disk Space (Ubuntu)
|
|
27
|
+
uses: jlumbroso/free-disk-space@main
|
|
28
|
+
with:
|
|
29
|
+
tool-cache: false
|
|
30
|
+
android: true # ~10GB saved
|
|
31
|
+
dotnet: true # ~5GB saved
|
|
32
|
+
haskell: false # Skip, minimal savings
|
|
33
|
+
large-packages: true # ~8GB saved
|
|
34
|
+
docker-images: true # ~5GB saved, important for Docker builds
|
|
35
|
+
swap-storage: false # Skip, usually not needed
|
|
36
|
+
|
|
37
|
+
- name: Check disk space (after cleanup)
|
|
38
|
+
run: df -h
|
|
39
|
+
|
|
40
|
+
- name: Set up Docker Buildx
|
|
41
|
+
uses: docker/setup-buildx-action@v3
|
|
42
|
+
with:
|
|
43
|
+
driver: docker-container
|
|
44
|
+
|
|
45
|
+
- name: Authenticate to GitHub Container Registry
|
|
46
|
+
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
|
|
47
|
+
|
|
48
|
+
# Build and push with layer caching for faster subsequent builds
|
|
49
|
+
# First build: ~7-12 min, Cached builds: ~2-5 min (50-70% faster)
|
|
50
|
+
- name: Build and push Docker image
|
|
51
|
+
uses: docker/build-push-action@v6
|
|
52
|
+
with:
|
|
53
|
+
context: .
|
|
54
|
+
file: ./sleap_connect/worker/Dockerfile.test
|
|
55
|
+
platforms: linux/amd64
|
|
56
|
+
push: true
|
|
57
|
+
# Enable GitHub Actions cache for Docker layers
|
|
58
|
+
cache-from: type=gha
|
|
59
|
+
cache-to: type=gha,mode=max
|
|
60
|
+
tags: |
|
|
61
|
+
ghcr.io/${{ github.repository_owner }}/sleap-rtc-worker:linux-amd64-test
|
|
62
|
+
ghcr.io/${{ github.repository_owner }}/sleap-rtc-worker:linux-amd64-sleap-cuda-test
|
|
63
|
+
ghcr.io/${{ github.repository_owner }}/sleap-rtc-worker:linux-amd64-${{ steps.get_sha.outputs.sha }}-test
|
|
64
|
+
|
|
65
|
+
- name: Check disk space (after build)
|
|
66
|
+
if: always()
|
|
67
|
+
run: df -h
|