verdiclip 0.2.0__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 (50) hide show
  1. verdiclip-0.2.0/ATTRIBUTION.md +37 -0
  2. verdiclip-0.2.0/LICENSE +21 -0
  3. verdiclip-0.2.0/PKG-INFO +133 -0
  4. verdiclip-0.2.0/README.md +104 -0
  5. verdiclip-0.2.0/pyproject.toml +240 -0
  6. verdiclip-0.2.0/pyproject.toml.orig +133 -0
  7. verdiclip-0.2.0/src/verdiclip/__init__.py +23 -0
  8. verdiclip-0.2.0/src/verdiclip/__main__.py +163 -0
  9. verdiclip-0.2.0/src/verdiclip/capture/__init__.py +3 -0
  10. verdiclip-0.2.0/src/verdiclip/capture/grabber.py +91 -0
  11. verdiclip-0.2.0/src/verdiclip/capture/models.py +98 -0
  12. verdiclip-0.2.0/src/verdiclip/capture/overlay.py +322 -0
  13. verdiclip-0.2.0/src/verdiclip/capture/service.py +216 -0
  14. verdiclip-0.2.0/src/verdiclip/cli.py +154 -0
  15. verdiclip-0.2.0/src/verdiclip/document/__init__.py +3 -0
  16. verdiclip-0.2.0/src/verdiclip/document/annotations.py +584 -0
  17. verdiclip-0.2.0/src/verdiclip/document/codec.py +261 -0
  18. verdiclip-0.2.0/src/verdiclip/document/commands.py +166 -0
  19. verdiclip-0.2.0/src/verdiclip/document/document.py +165 -0
  20. verdiclip-0.2.0/src/verdiclip/document/history.py +106 -0
  21. verdiclip-0.2.0/src/verdiclip/document/style.py +85 -0
  22. verdiclip-0.2.0/src/verdiclip/editor/__init__.py +3 -0
  23. verdiclip-0.2.0/src/verdiclip/editor/canvas.py +556 -0
  24. verdiclip-0.2.0/src/verdiclip/editor/chrome.py +60 -0
  25. verdiclip-0.2.0/src/verdiclip/editor/icons.py +228 -0
  26. verdiclip-0.2.0/src/verdiclip/editor/inline_editors.py +353 -0
  27. verdiclip-0.2.0/src/verdiclip/editor/session.py +267 -0
  28. verdiclip-0.2.0/src/verdiclip/editor/style_bar.py +275 -0
  29. verdiclip-0.2.0/src/verdiclip/editor/style_memory.py +81 -0
  30. verdiclip-0.2.0/src/verdiclip/editor/tools.py +750 -0
  31. verdiclip-0.2.0/src/verdiclip/editor/window.py +638 -0
  32. verdiclip-0.2.0/src/verdiclip/exceptions.py +31 -0
  33. verdiclip-0.2.0/src/verdiclip/geometry.py +167 -0
  34. verdiclip-0.2.0/src/verdiclip/output/__init__.py +3 -0
  35. verdiclip-0.2.0/src/verdiclip/output/delivery.py +153 -0
  36. verdiclip-0.2.0/src/verdiclip/output/naming.py +83 -0
  37. verdiclip-0.2.0/src/verdiclip/platform/__init__.py +1 -0
  38. verdiclip-0.2.0/src/verdiclip/platform/hotkeys.py +293 -0
  39. verdiclip-0.2.0/src/verdiclip/platform/startup.py +116 -0
  40. verdiclip-0.2.0/src/verdiclip/platform/windows.py +225 -0
  41. verdiclip-0.2.0/src/verdiclip/py.typed +0 -0
  42. verdiclip-0.2.0/src/verdiclip/render/__init__.py +3 -0
  43. verdiclip-0.2.0/src/verdiclip/render/renderer.py +355 -0
  44. verdiclip-0.2.0/src/verdiclip/settings.py +300 -0
  45. verdiclip-0.2.0/src/verdiclip/shell/__init__.py +3 -0
  46. verdiclip-0.2.0/src/verdiclip/shell/controller.py +334 -0
  47. verdiclip-0.2.0/src/verdiclip/shell/instance.py +89 -0
  48. verdiclip-0.2.0/src/verdiclip/shell/settings_dialog.py +321 -0
  49. verdiclip-0.2.0/src/verdiclip/shell/theme.py +25 -0
  50. verdiclip-0.2.0/src/verdiclip/shell/tray.py +82 -0
@@ -0,0 +1,37 @@
1
+ # Attribution
2
+
3
+ ## Inspiration
4
+
5
+ **VerdiClip** is an independent, clean-room screenshot and annotation tool for
6
+ Windows. It was created as an original work inspired by the publicly observable
7
+ features and user experience of [Greenshot](https://getgreenshot.org/), a free
8
+ and open-source screenshot tool.
9
+
10
+ VerdiClip is a distinct product with its own name and branding; it is not
11
+ affiliated with, endorsed by, or named after Greenshot.
12
+
13
+ ## Legal Notice
14
+
15
+ - **No code, assets, icons, or other copyrighted materials from Greenshot have
16
+ been used in VerdiClip.** This is a clean-room implementation built from
17
+ scratch using different technologies (Python, Qt) than Greenshot (.NET, C#).
18
+
19
+ - **VerdiClip does not use the Greenshot name or logo in its branding.** The
20
+ Greenshot trademark and logo are the property of the Greenshot development
21
+ team.
22
+
23
+ - **VerdiClip is licensed under the MIT License**, which is legally appropriate
24
+ for an independent implementation that does not derive from GPL-licensed
25
+ source code.
26
+
27
+ ## Greenshot
28
+
29
+ Greenshot is licensed under the
30
+ [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.html).
31
+ For more information about Greenshot, visit:
32
+
33
+ - Website: <https://getgreenshot.org/>
34
+ - Repository: <https://github.com/greenshot/greenshot>
35
+
36
+ We are grateful to the Greenshot team for creating an excellent tool that has
37
+ set the standard for screenshot software on Windows.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mike Hill
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,133 @@
1
+ Metadata-Version: 2.4
2
+ Name: verdiclip
3
+ Version: 0.2.0
4
+ Summary: Fast, faithful screenshot capture and annotation for Windows.
5
+ Keywords: screenshot,annotation,screen-capture,windows
6
+ Author: Mike Hill
7
+ Author-email: Mike Hill <mike@mikejhill.com>
8
+ License-Expression: MIT
9
+ License-File: ATTRIBUTION.md
10
+ License-File: LICENSE
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Win32 (MS Windows)
13
+ Classifier: Intended Audience :: End Users/Desktop
14
+ Classifier: Operating System :: Microsoft :: Windows
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Multimedia :: Graphics :: Editors
20
+ Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture
21
+ Requires-Dist: mss>=10.2.0
22
+ Requires-Dist: pyside6-essentials>=6.11.2
23
+ Requires-Python: >=3.13
24
+ Project-URL: Homepage, https://github.com/mikejhill/verdiclip
25
+ Project-URL: Documentation, https://github.com/mikejhill/verdiclip#readme
26
+ Project-URL: Changelog, https://github.com/mikejhill/verdiclip/blob/main/CHANGELOG.md
27
+ Project-URL: Issues, https://github.com/mikejhill/verdiclip/issues
28
+ Description-Content-Type: text/markdown
29
+
30
+ # VerdiClip
31
+
32
+ [![CI](https://github.com/mikejhill/verdiclip/actions/workflows/ci.yml/badge.svg)](https://github.com/mikejhill/verdiclip/actions/workflows/ci.yml)
33
+ [![PyPI](https://img.shields.io/pypi/v/verdiclip.svg)](https://pypi.org/project/verdiclip/)
34
+ [![Python](https://img.shields.io/pypi/pyversions/verdiclip.svg)](https://pypi.org/project/verdiclip/)
35
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
36
+
37
+ Fast, faithful screenshots for Windows: grab a precise piece of the screen, mark it up, and put it where it needs to go — in seconds.
38
+
39
+ ![VerdiClip editor in the light theme, showing numbered steps, a labeled callout, an arrow, a highlight, and an obfuscated password](docs/images/editor-light.png)
40
+
41
+ VerdiClip is an independent, clean-room project. See [ATTRIBUTION.md](ATTRIBUTION.md) for credits.
42
+
43
+ ## Install
44
+
45
+ Requires Windows 10 or later and Python 3.13+.
46
+
47
+ ```bash
48
+ uv tool install verdiclip
49
+ ```
50
+
51
+ Or with pip: `pip install verdiclip`. Then run `verdiclip` (or `verdiclip-gui` for no console window) and VerdiClip sits in the system tray.
52
+
53
+ ## Capture
54
+
55
+ | Do this | Press |
56
+ | --- | --- |
57
+ | Drag a region, or click a window | `PrtSc` (or left-click the tray icon) |
58
+ | Capture the active window | `Alt+PrtSc` |
59
+ | Capture every monitor | `Ctrl+PrtSc` |
60
+ | Repeat the last capture | `Shift+PrtSc` |
61
+
62
+ ![The capture overlay: the frozen screen is dimmed, the window under the cursor is outlined with its title, and a magnifier shows the exact pixels](docs/images/capture.png)
63
+
64
+ While selecting, the screen is frozen so you get exactly what you saw. A magnifier follows the cursor, arrow keys nudge it by a pixel (`Ctrl` for 10), clicking without dragging captures the window under the cursor, and `Esc` cancels.
65
+
66
+ If Windows or another screenshot tool already owns `PrtSc`, VerdiClip tells you which hotkeys are taken; everything is still available from the tray menu, and you can choose other keys in Settings.
67
+
68
+ ## Annotate
69
+
70
+ | Tools | | Actions | |
71
+ | --- | --- | --- | --- |
72
+ | `V` Select | `T` Text | `Ctrl+Shift+C` Copy image | `Ctrl+Z` / `Ctrl+Y` Undo / redo |
73
+ | `C` Crop | `N` Counter | `Ctrl+S` Save (auto-named) | `Ctrl+C` / `Ctrl+V` Copy / paste marks |
74
+ | `R` Rectangle | `H` Highlight | `Ctrl+Shift+S` Save as | `Delete` Remove selection |
75
+ | `E` Ellipse | `O` Obfuscate | `Ctrl+P` Print | Arrows Nudge (`Ctrl` = 10 px) |
76
+ | `L` Line | `F` Freehand | `Ctrl+wheel` Zoom | `Space`+drag Pan |
77
+ | `A` Arrow | | `Ctrl+0` / `Ctrl+Shift+F` 100% / fit | `Esc` Back out one step |
78
+
79
+ - **Labels in boxes.** After drawing a rectangle or ellipse, just type — the text is centered and wraps inside it. Double-click (or `Enter`/`F2`) to edit later; `Esc` skips.
80
+ - **Shift** draws squares, circles, and 45° lines.
81
+ - **Your styles stick.** Colors, fills, widths, and fonts you pick are remembered per tool for the next screenshot.
82
+ - **Everything is undoable**, including crop, which never throws pixels away.
83
+ - **The quickest path**: draw, `Esc` until nothing is selected, `Enter` — the image is on the clipboard and the editor closes. Nothing you haven't copied, saved, or printed is ever discarded without asking.
84
+
85
+ ![The editor in the dark theme](docs/images/editor-dark.png)
86
+
87
+ ## Settings
88
+
89
+ Open from the tray menu or with `Ctrl+,` in any editor.
90
+
91
+ ![The Settings dialog: after-capture actions can be combined](docs/images/settings.png)
92
+
93
+ - **After capturing** — any combination of: open in the editor, copy to the clipboard, save to the output folder.
94
+ - **Hotkeys** — validated as you type; conflicts are reported.
95
+ - **Output** — folder, file-name pattern (`{date}`, `{time}`, `{title}`, `{counter}`) with a live preview, format, JPEG quality.
96
+ - **Editor** — default color, width, and font.
97
+ - **General** — light, dark, or match-Windows theme; start at sign-in.
98
+
99
+ ## Command line
100
+
101
+ ```bash
102
+ verdiclip capture screen -o shot.png
103
+ verdiclip capture region --region 0,0,1280,720 --clipboard
104
+ verdiclip capture window --delay 3
105
+ verdiclip open picture.png # Opens in the running instance if there is one
106
+ ```
107
+
108
+ ## How it is built
109
+
110
+ Start with [docs/design/philosophy.md](docs/design/philosophy.md): the core purpose, the principles, and the architecture. [docs/design/ux-contract.md](docs/design/ux-contract.md) specifies every interaction; each item has an automated journey test.
111
+
112
+ In short: an edit session is an immutable base image, a non-destructive crop, and a list of immutable annotation values. The only way to change it is a command through the undo history, and one renderer draws both the canvas and the exported image.
113
+
114
+ ## Development
115
+
116
+ Requires Windows 10+ and [uv](https://docs.astral.sh/uv/).
117
+
118
+ ```bash
119
+ uv sync # Create .venv and install dependencies
120
+ uv run verdiclip # Run the tray app
121
+ uv run poe # List tasks
122
+ uv run poe fix # Format and auto-fix lint
123
+ uv run poe check # Format check, lint, strict type check, tests with coverage
124
+ uv run poe ux # UX journey tests only
125
+ uv run poe ux-snapshots # Journeys plus a PNG per step in docs/ux/snapshots
126
+ uv run poe ux-gallery # Key screens in light and dark themes (docs/ux/gallery)
127
+ ```
128
+
129
+ `uv run poe check` is the quality gate: ruff (broad rule set), ty with every rule at error, and pytest with ≥ 90% branch coverage. Regenerate the README screenshots with `uv run python scripts/ux_gallery.py --readme-dir docs/images`. Before a release, also run the manual checklist at the end of the UX contract. See [CONTRIBUTING.md](CONTRIBUTING.md) for commit conventions and releases.
130
+
131
+ ## License
132
+
133
+ [MIT](LICENSE)
@@ -0,0 +1,104 @@
1
+ # VerdiClip
2
+
3
+ [![CI](https://github.com/mikejhill/verdiclip/actions/workflows/ci.yml/badge.svg)](https://github.com/mikejhill/verdiclip/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/verdiclip.svg)](https://pypi.org/project/verdiclip/)
5
+ [![Python](https://img.shields.io/pypi/pyversions/verdiclip.svg)](https://pypi.org/project/verdiclip/)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
+
8
+ Fast, faithful screenshots for Windows: grab a precise piece of the screen, mark it up, and put it where it needs to go — in seconds.
9
+
10
+ ![VerdiClip editor in the light theme, showing numbered steps, a labeled callout, an arrow, a highlight, and an obfuscated password](docs/images/editor-light.png)
11
+
12
+ VerdiClip is an independent, clean-room project. See [ATTRIBUTION.md](ATTRIBUTION.md) for credits.
13
+
14
+ ## Install
15
+
16
+ Requires Windows 10 or later and Python 3.13+.
17
+
18
+ ```bash
19
+ uv tool install verdiclip
20
+ ```
21
+
22
+ Or with pip: `pip install verdiclip`. Then run `verdiclip` (or `verdiclip-gui` for no console window) and VerdiClip sits in the system tray.
23
+
24
+ ## Capture
25
+
26
+ | Do this | Press |
27
+ | --- | --- |
28
+ | Drag a region, or click a window | `PrtSc` (or left-click the tray icon) |
29
+ | Capture the active window | `Alt+PrtSc` |
30
+ | Capture every monitor | `Ctrl+PrtSc` |
31
+ | Repeat the last capture | `Shift+PrtSc` |
32
+
33
+ ![The capture overlay: the frozen screen is dimmed, the window under the cursor is outlined with its title, and a magnifier shows the exact pixels](docs/images/capture.png)
34
+
35
+ While selecting, the screen is frozen so you get exactly what you saw. A magnifier follows the cursor, arrow keys nudge it by a pixel (`Ctrl` for 10), clicking without dragging captures the window under the cursor, and `Esc` cancels.
36
+
37
+ If Windows or another screenshot tool already owns `PrtSc`, VerdiClip tells you which hotkeys are taken; everything is still available from the tray menu, and you can choose other keys in Settings.
38
+
39
+ ## Annotate
40
+
41
+ | Tools | | Actions | |
42
+ | --- | --- | --- | --- |
43
+ | `V` Select | `T` Text | `Ctrl+Shift+C` Copy image | `Ctrl+Z` / `Ctrl+Y` Undo / redo |
44
+ | `C` Crop | `N` Counter | `Ctrl+S` Save (auto-named) | `Ctrl+C` / `Ctrl+V` Copy / paste marks |
45
+ | `R` Rectangle | `H` Highlight | `Ctrl+Shift+S` Save as | `Delete` Remove selection |
46
+ | `E` Ellipse | `O` Obfuscate | `Ctrl+P` Print | Arrows Nudge (`Ctrl` = 10 px) |
47
+ | `L` Line | `F` Freehand | `Ctrl+wheel` Zoom | `Space`+drag Pan |
48
+ | `A` Arrow | | `Ctrl+0` / `Ctrl+Shift+F` 100% / fit | `Esc` Back out one step |
49
+
50
+ - **Labels in boxes.** After drawing a rectangle or ellipse, just type — the text is centered and wraps inside it. Double-click (or `Enter`/`F2`) to edit later; `Esc` skips.
51
+ - **Shift** draws squares, circles, and 45° lines.
52
+ - **Your styles stick.** Colors, fills, widths, and fonts you pick are remembered per tool for the next screenshot.
53
+ - **Everything is undoable**, including crop, which never throws pixels away.
54
+ - **The quickest path**: draw, `Esc` until nothing is selected, `Enter` — the image is on the clipboard and the editor closes. Nothing you haven't copied, saved, or printed is ever discarded without asking.
55
+
56
+ ![The editor in the dark theme](docs/images/editor-dark.png)
57
+
58
+ ## Settings
59
+
60
+ Open from the tray menu or with `Ctrl+,` in any editor.
61
+
62
+ ![The Settings dialog: after-capture actions can be combined](docs/images/settings.png)
63
+
64
+ - **After capturing** — any combination of: open in the editor, copy to the clipboard, save to the output folder.
65
+ - **Hotkeys** — validated as you type; conflicts are reported.
66
+ - **Output** — folder, file-name pattern (`{date}`, `{time}`, `{title}`, `{counter}`) with a live preview, format, JPEG quality.
67
+ - **Editor** — default color, width, and font.
68
+ - **General** — light, dark, or match-Windows theme; start at sign-in.
69
+
70
+ ## Command line
71
+
72
+ ```bash
73
+ verdiclip capture screen -o shot.png
74
+ verdiclip capture region --region 0,0,1280,720 --clipboard
75
+ verdiclip capture window --delay 3
76
+ verdiclip open picture.png # Opens in the running instance if there is one
77
+ ```
78
+
79
+ ## How it is built
80
+
81
+ Start with [docs/design/philosophy.md](docs/design/philosophy.md): the core purpose, the principles, and the architecture. [docs/design/ux-contract.md](docs/design/ux-contract.md) specifies every interaction; each item has an automated journey test.
82
+
83
+ In short: an edit session is an immutable base image, a non-destructive crop, and a list of immutable annotation values. The only way to change it is a command through the undo history, and one renderer draws both the canvas and the exported image.
84
+
85
+ ## Development
86
+
87
+ Requires Windows 10+ and [uv](https://docs.astral.sh/uv/).
88
+
89
+ ```bash
90
+ uv sync # Create .venv and install dependencies
91
+ uv run verdiclip # Run the tray app
92
+ uv run poe # List tasks
93
+ uv run poe fix # Format and auto-fix lint
94
+ uv run poe check # Format check, lint, strict type check, tests with coverage
95
+ uv run poe ux # UX journey tests only
96
+ uv run poe ux-snapshots # Journeys plus a PNG per step in docs/ux/snapshots
97
+ uv run poe ux-gallery # Key screens in light and dark themes (docs/ux/gallery)
98
+ ```
99
+
100
+ `uv run poe check` is the quality gate: ruff (broad rule set), ty with every rule at error, and pytest with ≥ 90% branch coverage. Regenerate the README screenshots with `uv run python scripts/ux_gallery.py --readme-dir docs/images`. Before a release, also run the manual checklist at the end of the UX contract. See [CONTRIBUTING.md](CONTRIBUTING.md) for commit conventions and releases.
101
+
102
+ ## License
103
+
104
+ [MIT](LICENSE)
@@ -0,0 +1,240 @@
1
+ [project]
2
+ name = "verdiclip"
3
+ version = "0.2.0"
4
+ description = "Fast, faithful screenshot capture and annotation for Windows."
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = [
8
+ "LICENSE",
9
+ "ATTRIBUTION.md",
10
+ ]
11
+ requires-python = ">=3.13"
12
+ keywords = [
13
+ "screenshot",
14
+ "annotation",
15
+ "screen-capture",
16
+ "windows",
17
+ ]
18
+ classifiers = [
19
+ "Development Status :: 3 - Alpha",
20
+ "Environment :: Win32 (MS Windows)",
21
+ "Intended Audience :: End Users/Desktop",
22
+ "Operating System :: Microsoft :: Windows",
23
+ "Programming Language :: Python :: 3",
24
+ "Programming Language :: Python :: 3 :: Only",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Programming Language :: Python :: 3.14",
27
+ "Topic :: Multimedia :: Graphics :: Editors",
28
+ "Topic :: Multimedia :: Graphics :: Capture :: Screen Capture",
29
+ ]
30
+ dependencies = [
31
+ "mss>=10.2.0",
32
+ "pyside6-essentials>=6.11.2",
33
+ ]
34
+
35
+ [[project.authors]]
36
+ name = "Mike Hill"
37
+ email = "mike@mikejhill.com"
38
+
39
+ [project.urls]
40
+ Homepage = "https://github.com/mikejhill/verdiclip"
41
+ Documentation = "https://github.com/mikejhill/verdiclip#readme"
42
+ Changelog = "https://github.com/mikejhill/verdiclip/blob/main/CHANGELOG.md"
43
+ Issues = "https://github.com/mikejhill/verdiclip/issues"
44
+
45
+ [project.scripts]
46
+ verdiclip = "verdiclip.__main__:Application.main"
47
+
48
+ [project.gui-scripts]
49
+ verdiclip-gui = "verdiclip.__main__:Application.main"
50
+
51
+ [build-system]
52
+ requires = ["uv_build>=0.11.1,<0.12.0"]
53
+ build-backend = "uv_build"
54
+
55
+ [tool.ruff]
56
+ target-version = "py313"
57
+ line-length = 88
58
+ src = [
59
+ "src",
60
+ "tests",
61
+ ]
62
+
63
+ [tool.ruff.lint]
64
+ select = [
65
+ "A",
66
+ "ANN",
67
+ "ARG",
68
+ "B",
69
+ "BLE",
70
+ "C4",
71
+ "C90",
72
+ "D",
73
+ "DTZ",
74
+ "E",
75
+ "ERA",
76
+ "F",
77
+ "FA",
78
+ "FBT",
79
+ "G",
80
+ "I",
81
+ "LOG",
82
+ "N",
83
+ "PERF",
84
+ "PGH",
85
+ "PIE",
86
+ "PL",
87
+ "PT",
88
+ "PTH",
89
+ "RET",
90
+ "RSE",
91
+ "RUF",
92
+ "S",
93
+ "SIM",
94
+ "T20",
95
+ "TID",
96
+ "TRY",
97
+ "UP",
98
+ "W",
99
+ ]
100
+ ignore = [
101
+ "D107",
102
+ "TRY003",
103
+ ]
104
+
105
+ [tool.ruff.lint.per-file-ignores]
106
+ "tests/**" = [
107
+ "S101",
108
+ "PLR2004",
109
+ ]
110
+
111
+ [tool.ruff.lint.pep8-naming]
112
+ extend-ignore-names = [
113
+ "paintEvent",
114
+ "mousePressEvent",
115
+ "mouseMoveEvent",
116
+ "mouseReleaseEvent",
117
+ "mouseDoubleClickEvent",
118
+ "keyPressEvent",
119
+ "keyReleaseEvent",
120
+ "wheelEvent",
121
+ "resizeEvent",
122
+ "closeEvent",
123
+ "showEvent",
124
+ "focusOutEvent",
125
+ "dragEnterEvent",
126
+ "dropEvent",
127
+ "nativeEventFilter",
128
+ "leaveEvent",
129
+ "enterEvent",
130
+ "sizeHint",
131
+ ]
132
+
133
+ [tool.ruff.lint.pydocstyle]
134
+ convention = "google"
135
+
136
+ [tool.ruff.lint.flake8-tidy-imports]
137
+ ban-relative-imports = "all"
138
+
139
+ [tool.ruff.lint.mccabe]
140
+ max-complexity = 10
141
+
142
+ [tool.ruff.format]
143
+ docstring-code-format = true
144
+
145
+ [tool.ty.environment]
146
+ python-platform = "win32"
147
+
148
+ [tool.ty.rules]
149
+ all = "error"
150
+
151
+ [tool.pytest.ini_options]
152
+ testpaths = ["tests"]
153
+ addopts = [
154
+ "--strict-markers",
155
+ "--strict-config",
156
+ "-ra",
157
+ "--import-mode=importlib",
158
+ ]
159
+ xfail_strict = true
160
+ qt_api = "pyside6"
161
+ markers = [
162
+ "ux: user journey test proving a UX contract item",
163
+ "ux_budget: journey test that enforces a latency budget",
164
+ ]
165
+
166
+ [tool.coverage.run]
167
+ source = ["verdiclip"]
168
+ branch = true
169
+
170
+ [tool.coverage.report]
171
+ fail_under = 90
172
+ show_missing = true
173
+ skip_covered = true
174
+ exclude_also = ["if __name__ == .__main__.:"]
175
+
176
+ [tool.poe.tasks.format]
177
+ cmd = "ruff format ."
178
+ help = "Format code"
179
+
180
+ [tool.poe.tasks.format-check]
181
+ cmd = "ruff format --check ."
182
+ help = "Check formatting"
183
+
184
+ [tool.poe.tasks.lint]
185
+ cmd = "ruff check ."
186
+ help = "Lint code"
187
+
188
+ [tool.poe.tasks.lint-fix]
189
+ cmd = "ruff check . --fix"
190
+ help = "Lint and auto-fix"
191
+
192
+ [tool.poe.tasks.typecheck]
193
+ cmd = "ty check"
194
+ help = "Type check (strict)"
195
+
196
+ [tool.poe.tasks.test]
197
+ cmd = "pytest --cov"
198
+ help = "Run tests with coverage"
199
+
200
+ [tool.poe.tasks.ux]
201
+ cmd = "pytest -m ux tests/ux"
202
+ help = "Run UX journey tests only"
203
+
204
+ [tool.poe.tasks.ux-gallery]
205
+ cmd = "python scripts/ux_gallery.py"
206
+ help = "Render key screens in light and dark themes into docs/ux/gallery for design review"
207
+
208
+ [tool.poe.tasks.ux-snapshots]
209
+ cmd = "pytest -m ux tests/ux --ux-snapshots=docs/ux/snapshots"
210
+ help = "Run UX journeys and save a PNG per step for review"
211
+
212
+ [tool.poe.tasks.lint-workflows]
213
+ shell = "uvx --from actionlint-py actionlint && uvx zizmor --offline .github/workflows"
214
+ help = "Lint GitHub Actions workflows (actionlint + zizmor)"
215
+
216
+ [tool.poe.tasks.fix]
217
+ sequence = [
218
+ "format",
219
+ "lint-fix",
220
+ ]
221
+ help = "Auto-fix formatting and lint"
222
+
223
+ [tool.poe.tasks.check]
224
+ sequence = [
225
+ "format-check",
226
+ "lint",
227
+ "typecheck",
228
+ "test",
229
+ ]
230
+ help = "Run every quality gate"
231
+
232
+ [dependency-groups]
233
+ dev = [
234
+ "poethepoet>=0.48.0",
235
+ "pytest>=9.1.1",
236
+ "pytest-cov>=7.1.0",
237
+ "pytest-qt>=4.5.0",
238
+ "ruff>=0.16.10",
239
+ "ty>=0.0.85",
240
+ ]
@@ -0,0 +1,133 @@
1
+ [project]
2
+ name = "verdiclip"
3
+ version = "0.2.0"
4
+ description = "Fast, faithful screenshot capture and annotation for Windows."
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE", "ATTRIBUTION.md"]
8
+ authors = [{ name = "Mike Hill", email = "mike@mikejhill.com" }]
9
+ requires-python = ">=3.13"
10
+ keywords = ["screenshot", "annotation", "screen-capture", "windows"]
11
+ classifiers = [
12
+ "Development Status :: 3 - Alpha",
13
+ "Environment :: Win32 (MS Windows)",
14
+ "Intended Audience :: End Users/Desktop",
15
+ "Operating System :: Microsoft :: Windows",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3 :: Only",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Programming Language :: Python :: 3.14",
20
+ "Topic :: Multimedia :: Graphics :: Editors",
21
+ "Topic :: Multimedia :: Graphics :: Capture :: Screen Capture",
22
+ ]
23
+ dependencies = [
24
+ "mss>=10.2.0",
25
+ "pyside6-essentials>=6.11.2",
26
+ ]
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/mikejhill/verdiclip"
30
+ Documentation = "https://github.com/mikejhill/verdiclip#readme"
31
+ Changelog = "https://github.com/mikejhill/verdiclip/blob/main/CHANGELOG.md"
32
+ Issues = "https://github.com/mikejhill/verdiclip/issues"
33
+
34
+ [project.scripts]
35
+ verdiclip = "verdiclip.__main__:Application.main"
36
+
37
+ [project.gui-scripts]
38
+ verdiclip-gui = "verdiclip.__main__:Application.main"
39
+
40
+ [build-system]
41
+ requires = ["uv_build>=0.11.1,<0.12.0"]
42
+ build-backend = "uv_build"
43
+
44
+ [tool.ruff]
45
+ target-version = "py313"
46
+ line-length = 88
47
+ src = ["src", "tests"]
48
+
49
+ [tool.ruff.lint]
50
+ select = [
51
+ "A", "ANN", "ARG", "B", "BLE", "C4", "C90", "D", "DTZ", "E", "ERA", "F",
52
+ "FA", "FBT", "G", "I", "LOG", "N", "PERF", "PGH", "PIE", "PL", "PT", "PTH",
53
+ "RET", "RSE", "RUF", "S", "SIM", "T20", "TID", "TRY", "UP", "W",
54
+ ]
55
+ ignore = [
56
+ "D107", # __init__ docstring: the class docstring covers it
57
+ "TRY003", # long messages in raise: context-rich messages are required here
58
+ ]
59
+
60
+ [tool.ruff.lint.per-file-ignores]
61
+ "tests/**" = ["S101", "PLR2004"]
62
+
63
+ [tool.ruff.lint.pep8-naming]
64
+ # Qt virtual overrides must keep Qt's camelCase names.
65
+ extend-ignore-names = [
66
+ "paintEvent", "mousePressEvent", "mouseMoveEvent", "mouseReleaseEvent",
67
+ "mouseDoubleClickEvent", "keyPressEvent", "keyReleaseEvent", "wheelEvent",
68
+ "resizeEvent", "closeEvent", "showEvent", "focusOutEvent", "dragEnterEvent",
69
+ "dropEvent", "nativeEventFilter", "leaveEvent", "enterEvent", "sizeHint",
70
+ ]
71
+
72
+ [tool.ruff.lint.pydocstyle]
73
+ convention = "google"
74
+
75
+ [tool.ruff.lint.flake8-tidy-imports]
76
+ ban-relative-imports = "all"
77
+
78
+ [tool.ruff.lint.mccabe]
79
+ max-complexity = 10
80
+
81
+ [tool.ruff.format]
82
+ docstring-code-format = true
83
+
84
+ [tool.ty.environment]
85
+ # A Windows-only app: type-check against Windows APIs on any host
86
+ python-platform = "win32"
87
+
88
+ [tool.ty.rules]
89
+ all = "error"
90
+
91
+ [tool.pytest.ini_options]
92
+ testpaths = ["tests"]
93
+ addopts = ["--strict-markers", "--strict-config", "-ra", "--import-mode=importlib"]
94
+ xfail_strict = true
95
+ qt_api = "pyside6"
96
+ markers = [
97
+ "ux: user journey test proving a UX contract item",
98
+ "ux_budget: journey test that enforces a latency budget",
99
+ ]
100
+
101
+ [tool.coverage.run]
102
+ source = ["verdiclip"]
103
+ branch = true
104
+
105
+ [tool.coverage.report]
106
+ fail_under = 90
107
+ show_missing = true
108
+ skip_covered = true
109
+ exclude_also = ["if __name__ == .__main__.:"]
110
+
111
+ [tool.poe.tasks]
112
+ format = { cmd = "ruff format .", help = "Format code" }
113
+ format-check = { cmd = "ruff format --check .", help = "Check formatting" }
114
+ lint = { cmd = "ruff check .", help = "Lint code" }
115
+ lint-fix = { cmd = "ruff check . --fix", help = "Lint and auto-fix" }
116
+ typecheck = { cmd = "ty check", help = "Type check (strict)" }
117
+ test = { cmd = "pytest --cov", help = "Run tests with coverage" }
118
+ ux = { cmd = "pytest -m ux tests/ux", help = "Run UX journey tests only" }
119
+ ux-gallery = { cmd = "python scripts/ux_gallery.py", help = "Render key screens in light and dark themes into docs/ux/gallery for design review" }
120
+ ux-snapshots = { cmd = "pytest -m ux tests/ux --ux-snapshots=docs/ux/snapshots", help = "Run UX journeys and save a PNG per step for review" }
121
+ lint-workflows = { shell = "uvx --from actionlint-py actionlint && uvx zizmor --offline .github/workflows", help = "Lint GitHub Actions workflows (actionlint + zizmor)" }
122
+ fix = { sequence = ["format", "lint-fix"], help = "Auto-fix formatting and lint" }
123
+ check = { sequence = ["format-check", "lint", "typecheck", "test"], help = "Run every quality gate" }
124
+
125
+ [dependency-groups]
126
+ dev = [
127
+ "poethepoet>=0.48.0",
128
+ "pytest>=9.1.1",
129
+ "pytest-cov>=7.1.0",
130
+ "pytest-qt>=4.5.0",
131
+ "ruff>=0.16.10",
132
+ "ty>=0.0.85",
133
+ ]