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.
Files changed (204) hide show
  1. sleap_connect-0.0.8/.claude/skills/investigation/SKILL.md +67 -0
  2. sleap_connect-0.0.8/.claude/skills/qt-testing/SKILL.md +82 -0
  3. sleap_connect-0.0.8/.claude/skills/qt-testing/scripts/qt_capture.py +131 -0
  4. sleap_connect-0.0.8/.dockerignore +15 -0
  5. sleap_connect-0.0.8/.github/workflows/build.yml +56 -0
  6. sleap_connect-0.0.8/.github/workflows/ci.yml +83 -0
  7. sleap_connect-0.0.8/.github/workflows/codespell.yml +29 -0
  8. sleap_connect-0.0.8/.github/workflows/worker_production.yml +68 -0
  9. sleap_connect-0.0.8/.github/workflows/worker_test.yml +67 -0
  10. sleap_connect-0.0.8/.gitignore +240 -0
  11. sleap_connect-0.0.8/.nojekyll +0 -0
  12. sleap_connect-0.0.8/CHANGELOG.md +29 -0
  13. sleap_connect-0.0.8/LICENSE +21 -0
  14. sleap_connect-0.0.8/PKG-INFO +215 -0
  15. sleap_connect-0.0.8/README.md +170 -0
  16. sleap_connect-0.0.8/config.example.toml +51 -0
  17. sleap_connect-0.0.8/dashboard/app.js +4800 -0
  18. sleap_connect-0.0.8/dashboard/callback.html +249 -0
  19. sleap_connect-0.0.8/dashboard/config.js +33 -0
  20. sleap_connect-0.0.8/dashboard/index.html +1693 -0
  21. sleap_connect-0.0.8/dashboard/sleap-logo.png +0 -0
  22. sleap_connect-0.0.8/dashboard/styles.css +4615 -0
  23. sleap_connect-0.0.8/pyproject.toml +115 -0
  24. sleap_connect-0.0.8/sleap_connect/.devcontainer/devcontainer.json +37 -0
  25. sleap_connect-0.0.8/sleap_connect/__init__.py +0 -0
  26. sleap_connect-0.0.8/sleap_connect/api.py +2817 -0
  27. sleap_connect-0.0.8/sleap_connect/auth/__init__.py +62 -0
  28. sleap_connect-0.0.8/sleap_connect/auth/credentials.py +413 -0
  29. sleap_connect-0.0.8/sleap_connect/auth/github.py +120 -0
  30. sleap_connect-0.0.8/sleap_connect/auth/keypair.py +131 -0
  31. sleap_connect-0.0.8/sleap_connect/auth/psk.py +114 -0
  32. sleap_connect-0.0.8/sleap_connect/auth/secret_resolver.py +153 -0
  33. sleap_connect-0.0.8/sleap_connect/cli.py +3938 -0
  34. sleap_connect-0.0.8/sleap_connect/client/.devcontainer/devcontainer.json +28 -0
  35. sleap_connect-0.0.8/sleap_connect/client/.dockerignore +19 -0
  36. sleap_connect-0.0.8/sleap_connect/client/Dockerfile +48 -0
  37. sleap_connect-0.0.8/sleap_connect/client/__init__.py +0 -0
  38. sleap_connect-0.0.8/sleap_connect/client/client.py +624 -0
  39. sleap_connect-0.0.8/sleap_connect/client/client_class.py +2651 -0
  40. sleap_connect-0.0.8/sleap_connect/client/client_track_class.py +678 -0
  41. sleap_connect-0.0.8/sleap_connect/client/directory_browser.py +648 -0
  42. sleap_connect-0.0.8/sleap_connect/client/file_selector.py +1667 -0
  43. sleap_connect-0.0.8/sleap_connect/client/file_transfer.py +158 -0
  44. sleap_connect-0.0.8/sleap_connect/client/fs_viewer_server.py +512 -0
  45. sleap_connect-0.0.8/sleap_connect/client/pyproject.toml +26 -0
  46. sleap_connect-0.0.8/sleap_connect/client/static/fs_resolve.html +1626 -0
  47. sleap_connect-0.0.8/sleap_connect/client/static/fs_viewer.html +1690 -0
  48. sleap_connect-0.0.8/sleap_connect/config.py +475 -0
  49. sleap_connect-0.0.8/sleap_connect/encryption/__init__.py +28 -0
  50. sleap_connect-0.0.8/sleap_connect/encryption/ecdh.py +144 -0
  51. sleap_connect-0.0.8/sleap_connect/encryption/envelope.py +105 -0
  52. sleap_connect-0.0.8/sleap_connect/exceptions.py +31 -0
  53. sleap_connect-0.0.8/sleap_connect/filesystem.py +350 -0
  54. sleap_connect-0.0.8/sleap_connect/gui/__init__.py +61 -0
  55. sleap_connect-0.0.8/sleap_connect/gui/presubmission.py +652 -0
  56. sleap_connect-0.0.8/sleap_connect/gui/runners.py +866 -0
  57. sleap_connect-0.0.8/sleap_connect/gui/widgets.py +3673 -0
  58. sleap_connect-0.0.8/sleap_connect/home.py +105 -0
  59. sleap_connect-0.0.8/sleap_connect/jobs/__init__.py +36 -0
  60. sleap_connect-0.0.8/sleap_connect/jobs/_exit_code_wrapper.py +75 -0
  61. sleap_connect-0.0.8/sleap_connect/jobs/builder.py +261 -0
  62. sleap_connect-0.0.8/sleap_connect/jobs/ports.py +56 -0
  63. sleap_connect-0.0.8/sleap_connect/jobs/process.py +377 -0
  64. sleap_connect-0.0.8/sleap_connect/jobs/queue.py +87 -0
  65. sleap_connect-0.0.8/sleap_connect/jobs/sleap_nn.py +190 -0
  66. sleap_connect-0.0.8/sleap_connect/jobs/spec.py +278 -0
  67. sleap_connect-0.0.8/sleap_connect/jobs/store.py +507 -0
  68. sleap_connect-0.0.8/sleap_connect/jobs/validator.py +456 -0
  69. sleap_connect-0.0.8/sleap_connect/protocol.py +566 -0
  70. sleap_connect-0.0.8/sleap_connect/protocol_v1/__init__.py +0 -0
  71. sleap_connect-0.0.8/sleap_connect/protocol_v1/auth.py +177 -0
  72. sleap_connect-0.0.8/sleap_connect/protocol_v1/blob_http.py +162 -0
  73. sleap_connect-0.0.8/sleap_connect/protocol_v1/blobs.py +211 -0
  74. sleap_connect-0.0.8/sleap_connect/protocol_v1/cli.py +1176 -0
  75. sleap_connect-0.0.8/sleap_connect/protocol_v1/daemon.py +428 -0
  76. sleap_connect-0.0.8/sleap_connect/protocol_v1/envelope.py +286 -0
  77. sleap_connect-0.0.8/sleap_connect/protocol_v1/errors.py +70 -0
  78. sleap_connect-0.0.8/sleap_connect/protocol_v1/identity.py +99 -0
  79. sleap_connect-0.0.8/sleap_connect/protocol_v1/iroh_live.py +144 -0
  80. sleap_connect-0.0.8/sleap_connect/protocol_v1/iroh_transport.py +149 -0
  81. sleap_connect-0.0.8/sleap_connect/protocol_v1/job_methods.py +1358 -0
  82. sleap_connect-0.0.8/sleap_connect/protocol_v1/job_paths.py +222 -0
  83. sleap_connect-0.0.8/sleap_connect/protocol_v1/jobs_cli.py +846 -0
  84. sleap_connect-0.0.8/sleap_connect/protocol_v1/media.py +151 -0
  85. sleap_connect-0.0.8/sleap_connect/protocol_v1/metrics.py +381 -0
  86. sleap_connect-0.0.8/sleap_connect/protocol_v1/outputs.py +141 -0
  87. sleap_connect-0.0.8/sleap_connect/protocol_v1/pair_code.py +344 -0
  88. sleap_connect-0.0.8/sleap_connect/protocol_v1/pairing.py +184 -0
  89. sleap_connect-0.0.8/sleap_connect/protocol_v1/runner.py +371 -0
  90. sleap_connect-0.0.8/sleap_connect/protocol_v1/server.py +604 -0
  91. sleap_connect-0.0.8/sleap_connect/protocol_v1/shared_folders.py +219 -0
  92. sleap_connect-0.0.8/sleap_connect/protocol_v1/shell_hook.py +412 -0
  93. sleap_connect-0.0.8/sleap_connect/protocol_v1/trust_store.py +243 -0
  94. sleap_connect-0.0.8/sleap_connect/protocol_v1/worker_info.py +133 -0
  95. sleap_connect-0.0.8/sleap_connect/rtc_browse.py +508 -0
  96. sleap_connect-0.0.8/sleap_connect/rtc_client.py +136 -0
  97. sleap_connect-0.0.8/sleap_connect/rtc_client_track.py +94 -0
  98. sleap_connect-0.0.8/sleap_connect/rtc_resolve.py +660 -0
  99. sleap_connect-0.0.8/sleap_connect/rtc_worker.py +79 -0
  100. sleap_connect-0.0.8/sleap_connect/tui/__init__.py +6 -0
  101. sleap_connect-0.0.8/sleap_connect/tui/app.py +197 -0
  102. sleap_connect-0.0.8/sleap_connect/tui/bridge.py +814 -0
  103. sleap_connect-0.0.8/sleap_connect/tui/screens/__init__.py +13 -0
  104. sleap_connect-0.0.8/sleap_connect/tui/screens/browser.py +1185 -0
  105. sleap_connect-0.0.8/sleap_connect/tui/screens/login.py +283 -0
  106. sleap_connect-0.0.8/sleap_connect/tui/screens/resolve_confirm.py +249 -0
  107. sleap_connect-0.0.8/sleap_connect/tui/screens/room_select.py +516 -0
  108. sleap_connect-0.0.8/sleap_connect/tui/screens/secret_input.py +159 -0
  109. sleap_connect-0.0.8/sleap_connect/tui/widgets/__init__.py +19 -0
  110. sleap_connect-0.0.8/sleap_connect/tui/widgets/miller.py +656 -0
  111. sleap_connect-0.0.8/sleap_connect/tui/widgets/slp_panel.py +482 -0
  112. sleap_connect-0.0.8/sleap_connect/tui/widgets/tree_browser.py +373 -0
  113. sleap_connect-0.0.8/sleap_connect/tui/widgets/worker_tabs.py +327 -0
  114. sleap_connect-0.0.8/sleap_connect/worker/.devcontainer/devcontainer.json +28 -0
  115. sleap_connect-0.0.8/sleap_connect/worker/.dockerignore +19 -0
  116. sleap_connect-0.0.8/sleap_connect/worker/Dockerfile +13 -0
  117. sleap_connect-0.0.8/sleap_connect/worker/Dockerfile.test +15 -0
  118. sleap_connect-0.0.8/sleap_connect/worker/__init__.py +0 -0
  119. sleap_connect-0.0.8/sleap_connect/worker/admin_controller.py +514 -0
  120. sleap_connect-0.0.8/sleap_connect/worker/capabilities.py +238 -0
  121. sleap_connect-0.0.8/sleap_connect/worker/crdt_state.py +394 -0
  122. sleap_connect-0.0.8/sleap_connect/worker/file_manager.py +1391 -0
  123. sleap_connect-0.0.8/sleap_connect/worker/job_coordinator.py +248 -0
  124. sleap_connect-0.0.8/sleap_connect/worker/job_executor.py +1572 -0
  125. sleap_connect-0.0.8/sleap_connect/worker/mesh_coordinator.py +2469 -0
  126. sleap_connect-0.0.8/sleap_connect/worker/mesh_messages.py +412 -0
  127. sleap_connect-0.0.8/sleap_connect/worker/progress_reporter.py +276 -0
  128. sleap_connect-0.0.8/sleap_connect/worker/pyproject.toml +21 -0
  129. sleap_connect-0.0.8/sleap_connect/worker/run_training.py +102 -0
  130. sleap_connect-0.0.8/sleap_connect/worker/state_manager.py +280 -0
  131. sleap_connect-0.0.8/sleap_connect/worker/worker.py +654 -0
  132. sleap_connect-0.0.8/sleap_connect/worker/worker_class.py +3697 -0
  133. sleap_connect-0.0.8/sleap_connect/worker/zmq_check.py +90 -0
  134. sleap_connect-0.0.8/sleap_rtc/__init__.py +45 -0
  135. sleap_connect-0.0.8/tests/auth/__init__.py +0 -0
  136. sleap_connect-0.0.8/tests/auth/test_credentials.py +330 -0
  137. sleap_connect-0.0.8/tests/auth/test_keypair.py +69 -0
  138. sleap_connect-0.0.8/tests/auth/test_psk.py +172 -0
  139. sleap_connect-0.0.8/tests/auth/test_secret_resolver.py +273 -0
  140. sleap_connect-0.0.8/tests/encryption/__init__.py +0 -0
  141. sleap_connect-0.0.8/tests/encryption/test_ecdh.py +192 -0
  142. sleap_connect-0.0.8/tests/encryption/test_envelope.py +155 -0
  143. sleap_connect-0.0.8/tests/protocol_v1/__init__.py +0 -0
  144. sleap_connect-0.0.8/tests/protocol_v1/conftest.py +14 -0
  145. sleap_connect-0.0.8/tests/protocol_v1/pair_code_vectors.json +52 -0
  146. sleap_connect-0.0.8/tests/protocol_v1/test_auth.py +442 -0
  147. sleap_connect-0.0.8/tests/protocol_v1/test_blob_http.py +171 -0
  148. sleap_connect-0.0.8/tests/protocol_v1/test_blobs.py +193 -0
  149. sleap_connect-0.0.8/tests/protocol_v1/test_cli.py +319 -0
  150. sleap_connect-0.0.8/tests/protocol_v1/test_daemon.py +357 -0
  151. sleap_connect-0.0.8/tests/protocol_v1/test_envelope.py +162 -0
  152. sleap_connect-0.0.8/tests/protocol_v1/test_identity.py +54 -0
  153. sleap_connect-0.0.8/tests/protocol_v1/test_iroh_blob_stream.py +199 -0
  154. sleap_connect-0.0.8/tests/protocol_v1/test_iroh_live.py +288 -0
  155. sleap_connect-0.0.8/tests/protocol_v1/test_iroh_transport.py +292 -0
  156. sleap_connect-0.0.8/tests/protocol_v1/test_job_methods.py +2156 -0
  157. sleap_connect-0.0.8/tests/protocol_v1/test_job_paths.py +327 -0
  158. sleap_connect-0.0.8/tests/protocol_v1/test_jobs_cli.py +424 -0
  159. sleap_connect-0.0.8/tests/protocol_v1/test_media.py +122 -0
  160. sleap_connect-0.0.8/tests/protocol_v1/test_metrics.py +241 -0
  161. sleap_connect-0.0.8/tests/protocol_v1/test_outputs.py +206 -0
  162. sleap_connect-0.0.8/tests/protocol_v1/test_pair_code.py +287 -0
  163. sleap_connect-0.0.8/tests/protocol_v1/test_pairing.py +105 -0
  164. sleap_connect-0.0.8/tests/protocol_v1/test_runner.py +463 -0
  165. sleap_connect-0.0.8/tests/protocol_v1/test_server.py +418 -0
  166. sleap_connect-0.0.8/tests/protocol_v1/test_shared_folders.py +183 -0
  167. sleap_connect-0.0.8/tests/protocol_v1/test_shell_hook.py +230 -0
  168. sleap_connect-0.0.8/tests/protocol_v1/test_trust_store.py +162 -0
  169. sleap_connect-0.0.8/tests/protocol_v1/test_worker_info.py +52 -0
  170. sleap_connect-0.0.8/tests/test_api.py +2484 -0
  171. sleap_connect-0.0.8/tests/test_auth_github.py +72 -0
  172. sleap_connect-0.0.8/tests/test_cli.py +512 -0
  173. sleap_connect-0.0.8/tests/test_cli_config.py +111 -0
  174. sleap_connect-0.0.8/tests/test_command_builder.py +488 -0
  175. sleap_connect-0.0.8/tests/test_config_mounts.py +107 -0
  176. sleap_connect-0.0.8/tests/test_config_path_mappings.py +117 -0
  177. sleap_connect-0.0.8/tests/test_directory_browser.py +374 -0
  178. sleap_connect-0.0.8/tests/test_doctor.py +96 -0
  179. sleap_connect-0.0.8/tests/test_file_manager_fs.py +914 -0
  180. sleap_connect-0.0.8/tests/test_file_manager_upload.py +226 -0
  181. sleap_connect-0.0.8/tests/test_file_transfer_upload.py +230 -0
  182. sleap_connect-0.0.8/tests/test_gui_integration.py +1651 -0
  183. sleap_connect-0.0.8/tests/test_gui_presubmission.py +816 -0
  184. sleap_connect-0.0.8/tests/test_gui_runners.py +1477 -0
  185. sleap_connect-0.0.8/tests/test_home.py +171 -0
  186. sleap_connect-0.0.8/tests/test_inference_dialog.py +198 -0
  187. sleap_connect-0.0.8/tests/test_job_executor.py +746 -0
  188. sleap_connect-0.0.8/tests/test_job_ports.py +36 -0
  189. sleap_connect-0.0.8/tests/test_job_process.py +543 -0
  190. sleap_connect-0.0.8/tests/test_job_queue.py +101 -0
  191. sleap_connect-0.0.8/tests/test_job_spec.py +629 -0
  192. sleap_connect-0.0.8/tests/test_job_store.py +308 -0
  193. sleap_connect-0.0.8/tests/test_job_validator.py +605 -0
  194. sleap_connect-0.0.8/tests/test_p2p_auth.py +419 -0
  195. sleap_connect-0.0.8/tests/test_persistent_progress_reporter.py +194 -0
  196. sleap_connect-0.0.8/tests/test_sleap_nn_check.py +168 -0
  197. sleap_connect-0.0.8/tests/test_slp_path_dialog_autofill.py +195 -0
  198. sleap_connect-0.0.8/tests/test_slp_path_dialog_save_buttons.py +203 -0
  199. sleap_connect-0.0.8/tests/test_smoke.py +430 -0
  200. sleap_connect-0.0.8/tests/test_stop_early_sigint.py +352 -0
  201. sleap_connect-0.0.8/tests/test_video_path_mapping_prompt.py +149 -0
  202. sleap_connect-0.0.8/tests/test_worker_config_content.py +521 -0
  203. sleap_connect-0.0.8/tests/test_worker_fs_routing.py +250 -0
  204. 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,15 @@
1
+ .git/
2
+ tests/
3
+ docs/
4
+ scratch/
5
+ *.pyc
6
+ __pycache__/
7
+ *.egg-info/
8
+ .venv/
9
+ .ruff_cache/
10
+ .pytest_cache/
11
+ *.log
12
+ .env
13
+ *.md
14
+ !README.md
15
+ openspec/
@@ -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