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.
- verdiclip-0.2.0/ATTRIBUTION.md +37 -0
- verdiclip-0.2.0/LICENSE +21 -0
- verdiclip-0.2.0/PKG-INFO +133 -0
- verdiclip-0.2.0/README.md +104 -0
- verdiclip-0.2.0/pyproject.toml +240 -0
- verdiclip-0.2.0/pyproject.toml.orig +133 -0
- verdiclip-0.2.0/src/verdiclip/__init__.py +23 -0
- verdiclip-0.2.0/src/verdiclip/__main__.py +163 -0
- verdiclip-0.2.0/src/verdiclip/capture/__init__.py +3 -0
- verdiclip-0.2.0/src/verdiclip/capture/grabber.py +91 -0
- verdiclip-0.2.0/src/verdiclip/capture/models.py +98 -0
- verdiclip-0.2.0/src/verdiclip/capture/overlay.py +322 -0
- verdiclip-0.2.0/src/verdiclip/capture/service.py +216 -0
- verdiclip-0.2.0/src/verdiclip/cli.py +154 -0
- verdiclip-0.2.0/src/verdiclip/document/__init__.py +3 -0
- verdiclip-0.2.0/src/verdiclip/document/annotations.py +584 -0
- verdiclip-0.2.0/src/verdiclip/document/codec.py +261 -0
- verdiclip-0.2.0/src/verdiclip/document/commands.py +166 -0
- verdiclip-0.2.0/src/verdiclip/document/document.py +165 -0
- verdiclip-0.2.0/src/verdiclip/document/history.py +106 -0
- verdiclip-0.2.0/src/verdiclip/document/style.py +85 -0
- verdiclip-0.2.0/src/verdiclip/editor/__init__.py +3 -0
- verdiclip-0.2.0/src/verdiclip/editor/canvas.py +556 -0
- verdiclip-0.2.0/src/verdiclip/editor/chrome.py +60 -0
- verdiclip-0.2.0/src/verdiclip/editor/icons.py +228 -0
- verdiclip-0.2.0/src/verdiclip/editor/inline_editors.py +353 -0
- verdiclip-0.2.0/src/verdiclip/editor/session.py +267 -0
- verdiclip-0.2.0/src/verdiclip/editor/style_bar.py +275 -0
- verdiclip-0.2.0/src/verdiclip/editor/style_memory.py +81 -0
- verdiclip-0.2.0/src/verdiclip/editor/tools.py +750 -0
- verdiclip-0.2.0/src/verdiclip/editor/window.py +638 -0
- verdiclip-0.2.0/src/verdiclip/exceptions.py +31 -0
- verdiclip-0.2.0/src/verdiclip/geometry.py +167 -0
- verdiclip-0.2.0/src/verdiclip/output/__init__.py +3 -0
- verdiclip-0.2.0/src/verdiclip/output/delivery.py +153 -0
- verdiclip-0.2.0/src/verdiclip/output/naming.py +83 -0
- verdiclip-0.2.0/src/verdiclip/platform/__init__.py +1 -0
- verdiclip-0.2.0/src/verdiclip/platform/hotkeys.py +293 -0
- verdiclip-0.2.0/src/verdiclip/platform/startup.py +116 -0
- verdiclip-0.2.0/src/verdiclip/platform/windows.py +225 -0
- verdiclip-0.2.0/src/verdiclip/py.typed +0 -0
- verdiclip-0.2.0/src/verdiclip/render/__init__.py +3 -0
- verdiclip-0.2.0/src/verdiclip/render/renderer.py +355 -0
- verdiclip-0.2.0/src/verdiclip/settings.py +300 -0
- verdiclip-0.2.0/src/verdiclip/shell/__init__.py +3 -0
- verdiclip-0.2.0/src/verdiclip/shell/controller.py +334 -0
- verdiclip-0.2.0/src/verdiclip/shell/instance.py +89 -0
- verdiclip-0.2.0/src/verdiclip/shell/settings_dialog.py +321 -0
- verdiclip-0.2.0/src/verdiclip/shell/theme.py +25 -0
- 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.
|
verdiclip-0.2.0/LICENSE
ADDED
|
@@ -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.
|
verdiclip-0.2.0/PKG-INFO
ADDED
|
@@ -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
|
+
[](https://github.com/mikejhill/verdiclip/actions/workflows/ci.yml)
|
|
33
|
+
[](https://pypi.org/project/verdiclip/)
|
|
34
|
+
[](https://pypi.org/project/verdiclip/)
|
|
35
|
+
[](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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
86
|
+
|
|
87
|
+
## Settings
|
|
88
|
+
|
|
89
|
+
Open from the tray menu or with `Ctrl+,` in any editor.
|
|
90
|
+
|
|
91
|
+

|
|
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
|
+
[](https://github.com/mikejhill/verdiclip/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/verdiclip/)
|
|
5
|
+
[](https://pypi.org/project/verdiclip/)
|
|
6
|
+
[](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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
57
|
+
|
|
58
|
+
## Settings
|
|
59
|
+
|
|
60
|
+
Open from the tray menu or with `Ctrl+,` in any editor.
|
|
61
|
+
|
|
62
|
+

|
|
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
|
+
]
|