quickthumb 0.1.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.
- quickthumb-0.1.0/.claude/agents/tdd-implementer.md +41 -0
- quickthumb-0.1.0/.claude/agents/tdd-test-writer.md +50 -0
- quickthumb-0.1.0/.claude/settings.json +34 -0
- quickthumb-0.1.0/.github/workflows/publish.yaml +74 -0
- quickthumb-0.1.0/.gitignore +162 -0
- quickthumb-0.1.0/.vscode/extensions.json +3 -0
- quickthumb-0.1.0/.vscode/settings.json +13 -0
- quickthumb-0.1.0/CLAUDE.md +107 -0
- quickthumb-0.1.0/DESIGN.md +400 -0
- quickthumb-0.1.0/LICENSE +21 -0
- quickthumb-0.1.0/PKG-INFO +293 -0
- quickthumb-0.1.0/PROGRESS.md +66 -0
- quickthumb-0.1.0/README.md +280 -0
- quickthumb-0.1.0/examples/README.md +18 -0
- quickthumb-0.1.0/examples/assets/c-g-JgDUVGAXsso-unsplash.jpg +0 -0
- quickthumb-0.1.0/examples/youtube_thumbnail_01.png +0 -0
- quickthumb-0.1.0/examples/youtube_thumbnail_01.py +49 -0
- quickthumb-0.1.0/pyproject.toml +81 -0
- quickthumb-0.1.0/quickthumb/__init__.py +32 -0
- quickthumb-0.1.0/quickthumb/canvas.py +1183 -0
- quickthumb-0.1.0/quickthumb/errors.py +10 -0
- quickthumb-0.1.0/quickthumb/models.py +374 -0
- quickthumb-0.1.0/tests/__init__.py +1 -0
- quickthumb-0.1.0/tests/conftest.py +41 -0
- quickthumb-0.1.0/tests/fixtures/sample_image.jpg +0 -0
- quickthumb-0.1.0/tests/snapshots/arial_bold.png +0 -0
- quickthumb-0.1.0/tests/snapshots/arial_bold_italic.png +0 -0
- quickthumb-0.1.0/tests/snapshots/arial_italic.png +0 -0
- quickthumb-0.1.0/tests/snapshots/blend_mode_darken.png +0 -0
- quickthumb-0.1.0/tests/snapshots/blend_mode_lighten.png +0 -0
- quickthumb-0.1.0/tests/snapshots/blend_mode_multiply.png +0 -0
- quickthumb-0.1.0/tests/snapshots/blend_mode_normal.png +0 -0
- quickthumb-0.1.0/tests/snapshots/blend_mode_overlay.png +0 -0
- quickthumb-0.1.0/tests/snapshots/blend_mode_screen.png +0 -0
- quickthumb-0.1.0/tests/snapshots/bold_italic_with_named_font.png +0 -0
- quickthumb-0.1.0/tests/snapshots/bold_with_named_font.png +0 -0
- quickthumb-0.1.0/tests/snapshots/font_name_works.png +0 -0
- quickthumb-0.1.0/tests/snapshots/image_background_basic.png +0 -0
- quickthumb-0.1.0/tests/snapshots/image_background_with_opacity.png +0 -0
- quickthumb-0.1.0/tests/snapshots/image_brightness_decrease.png +0 -0
- quickthumb-0.1.0/tests/snapshots/image_brightness_increase.png +0 -0
- quickthumb-0.1.0/tests/snapshots/image_fit_contain.png +0 -0
- quickthumb-0.1.0/tests/snapshots/image_fit_cover.png +0 -0
- quickthumb-0.1.0/tests/snapshots/image_fit_fill.png +0 -0
- quickthumb-0.1.0/tests/snapshots/italic_with_named_font.png +0 -0
- quickthumb-0.1.0/tests/snapshots/jpeg_format.jpg +0 -0
- quickthumb-0.1.0/tests/snapshots/linear_gradient_brightness_decrease.png +0 -0
- quickthumb-0.1.0/tests/snapshots/linear_gradient_brightness_increase.png +0 -0
- quickthumb-0.1.0/tests/snapshots/linear_gradient_diagonal.png +0 -0
- quickthumb-0.1.0/tests/snapshots/linear_gradient_horizontal.png +0 -0
- quickthumb-0.1.0/tests/snapshots/linear_gradient_with_opacity.png +0 -0
- quickthumb-0.1.0/tests/snapshots/outline_basic.png +0 -0
- quickthumb-0.1.0/tests/snapshots/outline_with_offset.png +0 -0
- quickthumb-0.1.0/tests/snapshots/percentage_with_alignment.png +0 -0
- quickthumb-0.1.0/tests/snapshots/radial_gradient_brightness_decrease.png +0 -0
- quickthumb-0.1.0/tests/snapshots/radial_gradient_brightness_increase.png +0 -0
- quickthumb-0.1.0/tests/snapshots/radial_gradient_centered.png +0 -0
- quickthumb-0.1.0/tests/snapshots/radial_gradient_with_opacity.png +0 -0
- quickthumb-0.1.0/tests/snapshots/rich_text_advanced_styles.png +0 -0
- quickthumb-0.1.0/tests/snapshots/rich_text_alignment.png +0 -0
- quickthumb-0.1.0/tests/snapshots/rich_text_different_colors.png +0 -0
- quickthumb-0.1.0/tests/snapshots/rich_text_mixed_fonts.png +0 -0
- quickthumb-0.1.0/tests/snapshots/rich_text_mixed_styles.png +0 -0
- quickthumb-0.1.0/tests/snapshots/rich_text_with_effects.png +0 -0
- quickthumb-0.1.0/tests/snapshots/solid_background.png +0 -0
- quickthumb-0.1.0/tests/snapshots/solid_color_brightness_decrease.png +0 -0
- quickthumb-0.1.0/tests/snapshots/solid_color_brightness_increase.png +0 -0
- quickthumb-0.1.0/tests/snapshots/text_alignment.png +0 -0
- quickthumb-0.1.0/tests/snapshots/text_bold_and_italic.png +0 -0
- quickthumb-0.1.0/tests/snapshots/text_no_wrapping.png +0 -0
- quickthumb-0.1.0/tests/snapshots/text_rendering.png +0 -0
- quickthumb-0.1.0/tests/snapshots/text_with_basic_shadow.png +0 -0
- quickthumb-0.1.0/tests/snapshots/text_with_blurred_shadow.png +0 -0
- quickthumb-0.1.0/tests/snapshots/text_with_glow.png +0 -0
- quickthumb-0.1.0/tests/snapshots/text_with_letter_spacing.png +0 -0
- quickthumb-0.1.0/tests/snapshots/text_with_line_height.png +0 -0
- quickthumb-0.1.0/tests/snapshots/text_with_multiple_glows.png +0 -0
- quickthumb-0.1.0/tests/snapshots/text_with_stroke.png +0 -0
- quickthumb-0.1.0/tests/snapshots/text_wrapping_center_aligned.png +0 -0
- quickthumb-0.1.0/tests/snapshots/text_wrapping_center_aligned_percentage.png +0 -0
- quickthumb-0.1.0/tests/snapshots/text_wrapping_left_aligned.png +0 -0
- quickthumb-0.1.0/tests/snapshots/text_wrapping_right_aligned.png +0 -0
- quickthumb-0.1.0/tests/snapshots/times_new_roman_bold.png +0 -0
- quickthumb-0.1.0/tests/snapshots/times_new_roman_bold_italic.png +0 -0
- quickthumb-0.1.0/tests/snapshots/times_new_roman_italic.png +0 -0
- quickthumb-0.1.0/tests/snapshots/webp_format.webp +0 -0
- quickthumb-0.1.0/tests/test_background_layers.py +384 -0
- quickthumb-0.1.0/tests/test_canvas.py +164 -0
- quickthumb-0.1.0/tests/test_outline_layers.py +153 -0
- quickthumb-0.1.0/tests/test_rendering.py +1162 -0
- quickthumb-0.1.0/tests/test_text_effects.py +410 -0
- quickthumb-0.1.0/tests/test_text_layers.py +508 -0
- quickthumb-0.1.0/uv.lock +626 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tdd-implementer
|
|
3
|
+
description: "Use this agent when you have existing failing tests and need to generate clean, self-explanatory production code that rigorously follows the TDD 'Green' and 'Refactor' phases."
|
|
4
|
+
model: sonnet
|
|
5
|
+
color: blue
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are a Senior Software Engineer specializing in **TDD (Test-Driven Development)**. Your goal is to implement business logic to pass existing failing tests (Green Phase) and then optimize the code (Refactor Phase).
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
You must write the production code to make the tests pass, adhering strictly to the operational constraints defined below.
|
|
13
|
+
|
|
14
|
+
## Operational Constraints
|
|
15
|
+
|
|
16
|
+
1. **Self-Explanatory Code:**
|
|
17
|
+
- **DO NOT** write comments explaining "what" or "how".
|
|
18
|
+
- Code must be readable solely through descriptive variable names, function names, and clear logic flow.
|
|
19
|
+
- _Exception:_ Docstrings for public API documentation are allowed if required by the language standard, but keep them minimal.
|
|
20
|
+
2. **Zero-Noise Error Handling:**
|
|
21
|
+
- **DO NOT** add `try-catch` blocks solely for logging purposes.
|
|
22
|
+
3. **TDD Workflow (Green -> Refactor):**
|
|
23
|
+
- **Step 1 (Green):** Write the minimum code necessary to pass the test.
|
|
24
|
+
- **Step 2 (Refactor):** Optimize stricture, eliminate duplication, and improve readability without changing behavior.
|
|
25
|
+
4. **Black-Box Testing (Encapsulation)**:
|
|
26
|
+
- DO NOT access or assert against private properties or methods in tests.
|
|
27
|
+
- Tests must verify behavior strictly through the public API/Interface.
|
|
28
|
+
5. **Implementation Volume Control:**
|
|
29
|
+
- **BEFORE** generating code, estimate the required lines of implementation code (excluding test code volume).
|
|
30
|
+
- If the implementation change requires **>300 lines** (and is NOT a simple repetitive task), **STOP** and **ASK** me to confirm.
|
|
31
|
+
|
|
32
|
+
Before presenting code, verify:
|
|
33
|
+
|
|
34
|
+
- [ ] All tests pass
|
|
35
|
+
- [ ] No explanatory comments exist (except minimal docstrings if needed)
|
|
36
|
+
- [ ] No try-catch blocks for logging only
|
|
37
|
+
- [ ] All names are self-explanatory
|
|
38
|
+
- [ ] Code follows TDD Green-Refactor cycle
|
|
39
|
+
- [ ] User was consulted if >300 lines (non-repetitive)
|
|
40
|
+
- [ ] No premature optimization
|
|
41
|
+
- [ ] Code is as simple as possible, but no simpler
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tdd-test-writer
|
|
3
|
+
description: "Use this agent when you need to translate business requirements into structured, failing test cases to rigorously initiate the 'Red' phase of Test-Driven Development."
|
|
4
|
+
model: sonnet
|
|
5
|
+
color: red
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Role
|
|
9
|
+
|
|
10
|
+
You are an expert Software QA Engineer and TDD (Test-Driven Development) Specialist. Your goal is to convert business requirements into high-quality, failing test cases (The "Red" phase of TDD).
|
|
11
|
+
|
|
12
|
+
## Instructions
|
|
13
|
+
|
|
14
|
+
1. **Analyze Requirements:** deeply understand the provided requirements to identify happy paths, edge cases, and potential error states.
|
|
15
|
+
2. **Define Test Scope:** Create "Meaningful Tests" only. Avoid redundant or trivial tests.
|
|
16
|
+
3. **Draft Test Cases:** Write the test code following the constraints below.
|
|
17
|
+
|
|
18
|
+
## Filtering Criteria
|
|
19
|
+
|
|
20
|
+
### 1. ❌ IGNORE / DO NOT TEST
|
|
21
|
+
* **Language Syntax:** Do not test if the programming language works (e.g., method chaining returning `self`, simple type instantiation).
|
|
22
|
+
* **Third-Party Libraries:** Do not test if standard libraries (JSON, HTTP clients, ORMs) work. Assume they work.
|
|
23
|
+
* **Trivial Data Access:** Do not test simple Getters/Setters unless they contain transformation logic.
|
|
24
|
+
* **Redundant Checks:** If a "Happy Path" test already verifies a result, do not create a separate test just to check a sub-property of that same result.
|
|
25
|
+
* **Parameter Variations:** Do not create multiple tests for different parameter values when one test suffices to verify the feature works.
|
|
26
|
+
* **Feature Combinations:** Do not test combinations of already-tested features unless there's specific integration logic.
|
|
27
|
+
* **Implementation Detail:** Do not test internal optimizations, private methods, or internal state
|
|
28
|
+
|
|
29
|
+
### 2. ✅ MUST TEST
|
|
30
|
+
* **Business Invariants:** Rules that must always be true (e.g., "Balance cannot be negative", "Username must be unique").
|
|
31
|
+
* **State Transitions:** Verifying allowed and disallowed status changes (e.g., "Cannot refund a cancelled order").
|
|
32
|
+
* **Boundary Analysis:** Edge cases at the limits of logic (e.g., 0, Max Int, Empty Lists, Leap Years).
|
|
33
|
+
* **Error Handling:** Custom exceptions and fallback logic defined by the business requirements.
|
|
34
|
+
* **Distinct Feature Parameters:** If a parameter changes the behavior significantly, test one variation to prove the parameter works.
|
|
35
|
+
|
|
36
|
+
## Constraints & Standards
|
|
37
|
+
|
|
38
|
+
1. **TDD Red Phase Only:**
|
|
39
|
+
- All tests MUST fail initially.
|
|
40
|
+
- **DO NOT** implement the business logic or functional code.
|
|
41
|
+
- Inside the test function, simply call a failure method.
|
|
42
|
+
2. **Structure (Given-When-Then):**
|
|
43
|
+
- You must explicitly comment the sections within each test function:
|
|
44
|
+
- `Given`: Setup initial state/mocks.
|
|
45
|
+
- `When`: Execute the action.
|
|
46
|
+
- `Then`: Assert the expected result.
|
|
47
|
+
3. **Documentation:**
|
|
48
|
+
- Every test function must have a clear **Docstring** summarizing the test scenario and expected outcome.
|
|
49
|
+
4. **Naming Convention:**
|
|
50
|
+
- Use descriptive names reflecting the behavior (e.g., `test_should_return_error_when_input_is_negative`).
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PostToolUse": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "Write|Edit",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "jq -r '.tool_input.file_path | select(endswith(\".py\"))' | xargs -r uv tool run ruff format >&2 || { [ $? -eq 1 ] && exit 2; }"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"type": "command",
|
|
13
|
+
"command": "jq -r '.tool_input.file_path | select(endswith(\".py\"))' | xargs -r uv tool run ruff check --fix >&2 || { [ $? -eq 1 ] && exit 2; }"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"type": "command",
|
|
17
|
+
"command": "jq -r '.tool_input.file_path | select(endswith(\".py\"))' | xargs -r uv tool run ty check >&2 || { [ $? -eq 1 ] && exit 2; }"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"Notification": [
|
|
23
|
+
{
|
|
24
|
+
"matcher": "",
|
|
25
|
+
"hooks": [
|
|
26
|
+
{
|
|
27
|
+
"type": "command",
|
|
28
|
+
"command": "terminal-notifier -title 'Claude Code' -message 'Waiting for your input' -sound Glass"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
name: Automated Release Process
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: write
|
|
13
|
+
id-token: write
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout repository
|
|
16
|
+
uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Install uv
|
|
19
|
+
uses: astral-sh/setup-uv@v5
|
|
20
|
+
with:
|
|
21
|
+
enable-cache: true
|
|
22
|
+
python-version: "3.10"
|
|
23
|
+
|
|
24
|
+
- name: Test
|
|
25
|
+
run: |
|
|
26
|
+
uv run pytest tests/ --ignore=tests/test_rendering.py
|
|
27
|
+
|
|
28
|
+
- name: Determine Version Change
|
|
29
|
+
id: version_check
|
|
30
|
+
run: |
|
|
31
|
+
VERSION="v$(uv version --short)"
|
|
32
|
+
echo "Current version: $VERSION"
|
|
33
|
+
|
|
34
|
+
LATEST_RELEASE=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
|
35
|
+
https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r '.tag_name')
|
|
36
|
+
echo "Latest release version: $LATEST_RELEASE"
|
|
37
|
+
|
|
38
|
+
if [ "$VERSION" != "$LATEST_RELEASE" ]; then
|
|
39
|
+
echo "version_changed=true" >> $GITHUB_OUTPUT
|
|
40
|
+
echo "new_version=$VERSION" >> $GITHUB_OUTPUT
|
|
41
|
+
else
|
|
42
|
+
echo "version_changed=false" >> $GITHUB_OUTPUT
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
- name: Create Release
|
|
46
|
+
if: steps.version_check.outputs.version_changed == 'true'
|
|
47
|
+
uses: softprops/action-gh-release@v2
|
|
48
|
+
with:
|
|
49
|
+
tag_name: ${{ steps.version_check.outputs.new_version }}
|
|
50
|
+
generate_release_notes: True
|
|
51
|
+
|
|
52
|
+
- name: mint API token
|
|
53
|
+
id: mint-token
|
|
54
|
+
run: |
|
|
55
|
+
# retrieve the ambient OIDC token
|
|
56
|
+
resp=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
|
|
57
|
+
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=pypi")
|
|
58
|
+
oidc_token=$(jq -r '.value' <<< "${resp}")
|
|
59
|
+
|
|
60
|
+
# exchange the OIDC token for an API token
|
|
61
|
+
resp=$(curl -X POST https://pypi.org/_/oidc/mint-token -d "{\"token\": \"${oidc_token}\"}")
|
|
62
|
+
api_token=$(jq -r '.token' <<< "${resp}")
|
|
63
|
+
|
|
64
|
+
# mask the newly minted API token, so that we don't accidentally leak it
|
|
65
|
+
echo "::add-mask::${api_token}"
|
|
66
|
+
|
|
67
|
+
# see the next step in the workflow for an example of using this step output
|
|
68
|
+
echo "api-token=${api_token}" >> "${GITHUB_OUTPUT}"
|
|
69
|
+
|
|
70
|
+
- name: Build and publish to PyPI
|
|
71
|
+
if: steps.version_check.outputs.version_changed == 'true'
|
|
72
|
+
run: |
|
|
73
|
+
uv build
|
|
74
|
+
uv publish --token ${{ steps.mint-token.outputs.api-token }}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py,cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# poetry
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
102
|
+
#poetry.lock
|
|
103
|
+
|
|
104
|
+
# pdm
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
106
|
+
#pdm.lock
|
|
107
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
108
|
+
# in version control.
|
|
109
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
110
|
+
.pdm.toml
|
|
111
|
+
.pdm-python
|
|
112
|
+
.pdm-build/
|
|
113
|
+
|
|
114
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
115
|
+
__pypackages__/
|
|
116
|
+
|
|
117
|
+
# Celery stuff
|
|
118
|
+
celerybeat-schedule
|
|
119
|
+
celerybeat.pid
|
|
120
|
+
|
|
121
|
+
# SageMath parsed files
|
|
122
|
+
*.sage.py
|
|
123
|
+
|
|
124
|
+
# Environments
|
|
125
|
+
.env
|
|
126
|
+
.venv
|
|
127
|
+
env/
|
|
128
|
+
venv/
|
|
129
|
+
ENV/
|
|
130
|
+
env.bak/
|
|
131
|
+
venv.bak/
|
|
132
|
+
|
|
133
|
+
# Spyder project settings
|
|
134
|
+
.spyderproject
|
|
135
|
+
.spyproject
|
|
136
|
+
|
|
137
|
+
# Rope project settings
|
|
138
|
+
.ropeproject
|
|
139
|
+
|
|
140
|
+
# mkdocs documentation
|
|
141
|
+
/site
|
|
142
|
+
|
|
143
|
+
# mypy
|
|
144
|
+
.mypy_cache/
|
|
145
|
+
.dmypy.json
|
|
146
|
+
dmypy.json
|
|
147
|
+
|
|
148
|
+
# Pyre type checker
|
|
149
|
+
.pyre/
|
|
150
|
+
|
|
151
|
+
# pytype static type analyzer
|
|
152
|
+
.pytype/
|
|
153
|
+
|
|
154
|
+
# Cython debug symbols
|
|
155
|
+
cython_debug/
|
|
156
|
+
|
|
157
|
+
# PyCharm
|
|
158
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
159
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
160
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
161
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
162
|
+
#.idea/
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"[python]": {
|
|
3
|
+
"editor.codeActionsOnSave": {
|
|
4
|
+
"source.fixAll": "explicit",
|
|
5
|
+
"source.organizeImports": "explicit"
|
|
6
|
+
},
|
|
7
|
+
"editor.defaultFormatter": "charliermarsh.ruff"
|
|
8
|
+
},
|
|
9
|
+
"ty.importStrategy": "fromEnvironment",
|
|
10
|
+
"ruff.importStrategy": "fromEnvironment",
|
|
11
|
+
"python.testing.unittestEnabled": false,
|
|
12
|
+
"python.testing.pytestEnabled": true
|
|
13
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
QuickThumb is a Python library for programmatic thumbnail generation. See [README.md](README.md) for features and examples, [DESIGN.md](DESIGN.md) for API specifications and design principles.
|
|
8
|
+
|
|
9
|
+
## Project Structure
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
.
|
|
13
|
+
├── CLAUDE.md
|
|
14
|
+
├── DESIGN.md
|
|
15
|
+
├── LICENSE
|
|
16
|
+
├── README.md
|
|
17
|
+
├── examples
|
|
18
|
+
│ ├── README.md
|
|
19
|
+
│ └── youtube_thumbnail.py
|
|
20
|
+
├── pyproject.toml
|
|
21
|
+
├── quickthumb
|
|
22
|
+
│ ├── __init__.py
|
|
23
|
+
│ ├── canvas.py
|
|
24
|
+
│ ├── errors.py
|
|
25
|
+
│ └── models.py
|
|
26
|
+
├── tests
|
|
27
|
+
│ ├── __init__.py
|
|
28
|
+
│ ├── test_background_layers.py
|
|
29
|
+
│ ├── test_canvas.py
|
|
30
|
+
│ └── test_text_layers.py
|
|
31
|
+
└── uv.lock
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Development Commands
|
|
35
|
+
|
|
36
|
+
### Setup
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Install dependencies
|
|
40
|
+
uv sync
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Testing
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Run all tests
|
|
47
|
+
uv run pytest tests/ -v
|
|
48
|
+
|
|
49
|
+
# Run single test file
|
|
50
|
+
uv run pytest tests/test_canvas_creation.py -v
|
|
51
|
+
|
|
52
|
+
# Run specific test
|
|
53
|
+
uv run pytest tests/test_canvas_creation.py::TestCanvasCreation::test_should_create_canvas_with_explicit_dimensions -v
|
|
54
|
+
|
|
55
|
+
# Run with coverage report
|
|
56
|
+
uv run pytest tests/ --cov=quickthumb --cov-report=html
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Code Quality
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Type checking
|
|
63
|
+
uv run ty check quickthumb/
|
|
64
|
+
|
|
65
|
+
# Linting
|
|
66
|
+
uv run ruff check quickthumb/
|
|
67
|
+
|
|
68
|
+
# Auto-fix linting issues
|
|
69
|
+
uv run ruff check --fix quickthumb/
|
|
70
|
+
|
|
71
|
+
# Format code
|
|
72
|
+
uv run ruff format quickthumb/
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Architecture
|
|
76
|
+
|
|
77
|
+
### Module Organization
|
|
78
|
+
|
|
79
|
+
- **`models.py`**: Pydantic models for validation and serialization
|
|
80
|
+
- `LinearGradient`: Gradient configuration with type, angle, stops
|
|
81
|
+
- `BackgroundLayer`: Background layer with color/gradient/image
|
|
82
|
+
- `TextLayer`: Text layer with content, styling, positioning
|
|
83
|
+
- `CanvasModel`: Canvas serialization model (width, height, layers)
|
|
84
|
+
- **`canvas.py`**: Canvas class with method chaining API
|
|
85
|
+
- **`errors.py`**: Custom exceptions (`ValidationError`, `QuickthumbError`)
|
|
86
|
+
|
|
87
|
+
### Layer Stack Model
|
|
88
|
+
|
|
89
|
+
Layers are appended to `Canvas._layers` in call order and rendered sequentially:
|
|
90
|
+
|
|
91
|
+
1. Background layers (stackable with blend modes)
|
|
92
|
+
2. Text layers
|
|
93
|
+
3. Decoration layers
|
|
94
|
+
|
|
95
|
+
### Dual API
|
|
96
|
+
|
|
97
|
+
Both Python method chaining and JSON configuration map to the same internal `Canvas._layers` structure.
|
|
98
|
+
|
|
99
|
+
## Testing Philosophy
|
|
100
|
+
|
|
101
|
+
Follows **strict TDD** as defined in `.claude/agents/tdd-implementer.md`:
|
|
102
|
+
|
|
103
|
+
- Self-explanatory code (no "what"/"how" comments)
|
|
104
|
+
- Black-box testing (public API only)
|
|
105
|
+
- Green → Refactor cycle
|
|
106
|
+
|
|
107
|
+
Test naming: `test_should_{expected_behavior}_{optional_context}`
|