contribution-city 1.3.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 (167) hide show
  1. contribution_city-1.3.0/.github/workflows/tests.yml +170 -0
  2. contribution_city-1.3.0/.gitignore +38 -0
  3. contribution_city-1.3.0/CHANGELOG.md +132 -0
  4. contribution_city-1.3.0/IMPLEMENTATION_SUMMARY.md +122 -0
  5. contribution_city-1.3.0/LICENSE +21 -0
  6. contribution_city-1.3.0/PKG-INFO +382 -0
  7. contribution_city-1.3.0/README.md +349 -0
  8. contribution_city-1.3.0/action.yml +238 -0
  9. contribution_city-1.3.0/docs/gallery/contact-sheet.png +0 -0
  10. contribution_city-1.3.0/docs/gallery/manifest.json +5083 -0
  11. contribution_city-1.3.0/docs/gallery/new-in-1.2.png +0 -0
  12. contribution_city-1.3.0/docs/preview-compact.png +0 -0
  13. contribution_city-1.3.0/docs/preview-navy-cyan.png +0 -0
  14. contribution_city-1.3.0/docs/preview-profile-dark.png +0 -0
  15. contribution_city-1.3.0/docs/preview-profile-light.png +0 -0
  16. contribution_city-1.3.0/docs/visual-testing.md +109 -0
  17. contribution_city-1.3.0/examples/README.md +404 -0
  18. contribution_city-1.3.0/examples/config/all-options.yaml +62 -0
  19. contribution_city-1.3.0/examples/config/heights-and-angles.yaml +16 -0
  20. contribution_city-1.3.0/examples/config/solid.yaml +8 -0
  21. contribution_city-1.3.0/examples/config/transparent.yaml +9 -0
  22. contribution_city-1.3.0/examples/config.example.yaml +38 -0
  23. contribution_city-1.3.0/examples/sample-contributions.json +1831 -0
  24. contribution_city-1.3.0/examples/workflows/basic-city.yml +43 -0
  25. contribution_city-1.3.0/examples/workflows/customized-city.yml +38 -0
  26. contribution_city-1.3.0/examples/workflows/gallery-artifact.yml +44 -0
  27. contribution_city-1.3.0/examples/workflows/generate-profile-city.yml +87 -0
  28. contribution_city-1.3.0/examples/workflows/heights-and-angles.yml +35 -0
  29. contribution_city-1.3.0/examples/workflows/solid-city.yml +43 -0
  30. contribution_city-1.3.0/examples/workflows/transparent-city.yml +53 -0
  31. contribution_city-1.3.0/previews/README.md +33 -0
  32. contribution_city-1.3.0/previews/backgrounds/README.md +12 -0
  33. contribution_city-1.3.0/previews/backgrounds/auto-with-auto-appearance.on-checkerboard.svg +716 -0
  34. contribution_city-1.3.0/previews/backgrounds/auto-with-auto-appearance.svg +711 -0
  35. contribution_city-1.3.0/previews/backgrounds/solid.svg +712 -0
  36. contribution_city-1.3.0/previews/backgrounds/transparent-sky.on-checkerboard.svg +716 -0
  37. contribution_city-1.3.0/previews/backgrounds/transparent-sky.svg +711 -0
  38. contribution_city-1.3.0/previews/backgrounds/transparent.on-checkerboard.svg +661 -0
  39. contribution_city-1.3.0/previews/backgrounds/transparent.svg +656 -0
  40. contribution_city-1.3.0/previews/building-styles/README.md +11 -0
  41. contribution_city-1.3.0/previews/building-styles/detailed.svg +834 -0
  42. contribution_city-1.3.0/previews/building-styles/solid.svg +510 -0
  43. contribution_city-1.3.0/previews/heights/README.md +13 -0
  44. contribution_city-1.3.0/previews/heights/custom-scale-1-5-max-90.svg +892 -0
  45. contribution_city-1.3.0/previews/heights/standard-linear.svg +642 -0
  46. contribution_city-1.3.0/previews/heights/standard-log.svg +824 -0
  47. contribution_city-1.3.0/previews/heights/standard-sqrt.svg +798 -0
  48. contribution_city-1.3.0/previews/heights/standard.svg +712 -0
  49. contribution_city-1.3.0/previews/heights/tall-custom.svg +834 -0
  50. contribution_city-1.3.0/previews/heights/tall-linear.svg +778 -0
  51. contribution_city-1.3.0/previews/heights/tall-log.svg +914 -0
  52. contribution_city-1.3.0/previews/heights/tall-sqrt.svg +906 -0
  53. contribution_city-1.3.0/previews/layouts/README.md +12 -0
  54. contribution_city-1.3.0/previews/layouts/compact.svg +710 -0
  55. contribution_city-1.3.0/previews/layouts/dense-grid.svg +712 -0
  56. contribution_city-1.3.0/previews/layouts/downtown.svg +712 -0
  57. contribution_city-1.3.0/previews/layouts/floating-island.svg +719 -0
  58. contribution_city-1.3.0/previews/layouts/wireframe.svg +578 -0
  59. contribution_city-1.3.0/previews/manifest.json +798 -0
  60. contribution_city-1.3.0/previews/themes/README.md +14 -0
  61. contribution_city-1.3.0/previews/themes/arctic-glass.svg +704 -0
  62. contribution_city-1.3.0/previews/themes/copper-forge.svg +704 -0
  63. contribution_city-1.3.0/previews/themes/cyberpunk.svg +704 -0
  64. contribution_city-1.3.0/previews/themes/emerald-matrix.svg +704 -0
  65. contribution_city-1.3.0/previews/themes/matrix-green.svg +704 -0
  66. contribution_city-1.3.0/previews/themes/monochrome-slate.svg +704 -0
  67. contribution_city-1.3.0/previews/themes/navy-cyan-light.svg +704 -0
  68. contribution_city-1.3.0/previews/themes/navy-cyan.svg +704 -0
  69. contribution_city-1.3.0/previews/themes/neon-harbor.svg +704 -0
  70. contribution_city-1.3.0/previews/themes/profile-light.svg +704 -0
  71. contribution_city-1.3.0/previews/themes/profile.svg +704 -0
  72. contribution_city-1.3.0/previews/themes/solar-amber.svg +704 -0
  73. contribution_city-1.3.0/previews/themes/sunset-grid.svg +704 -0
  74. contribution_city-1.3.0/previews/themes/sunset-neon.svg +704 -0
  75. contribution_city-1.3.0/previews/viewing-angles/README.md +12 -0
  76. contribution_city-1.3.0/previews/viewing-angles/high.svg +682 -0
  77. contribution_city-1.3.0/previews/viewing-angles/low.svg +738 -0
  78. contribution_city-1.3.0/previews/viewing-angles/standard-isometric.svg +712 -0
  79. contribution_city-1.3.0/previews/viewing-angles/top-down.svg +628 -0
  80. contribution_city-1.3.0/pyproject.toml +81 -0
  81. contribution_city-1.3.0/src/contribution_city/__init__.py +15 -0
  82. contribution_city-1.3.0/src/contribution_city/_version.py +3 -0
  83. contribution_city-1.3.0/src/contribution_city/cli.py +475 -0
  84. contribution_city-1.3.0/src/contribution_city/config.py +345 -0
  85. contribution_city-1.3.0/src/contribution_city/github_api.py +267 -0
  86. contribution_city-1.3.0/src/contribution_city/models.py +322 -0
  87. contribution_city-1.3.0/src/contribution_city/previews.py +556 -0
  88. contribution_city-1.3.0/src/contribution_city/renderer/__init__.py +14 -0
  89. contribution_city-1.3.0/src/contribution_city/renderer/geometry.py +98 -0
  90. contribution_city-1.3.0/src/contribution_city/renderer/layouts.py +102 -0
  91. contribution_city-1.3.0/src/contribution_city/renderer/palettes.py +106 -0
  92. contribution_city-1.3.0/src/contribution_city/renderer/presets.py +113 -0
  93. contribution_city-1.3.0/src/contribution_city/renderer/svg.py +849 -0
  94. contribution_city-1.3.0/src/contribution_city/renderer/themes.py +489 -0
  95. contribution_city-1.3.0/src/contribution_city/svgcheck.py +126 -0
  96. contribution_city-1.3.0/src/contribution_city/visual/__init__.py +17 -0
  97. contribution_city-1.3.0/src/contribution_city/visual/baseline.py +167 -0
  98. contribution_city-1.3.0/src/contribution_city/visual/cases.py +449 -0
  99. contribution_city-1.3.0/src/contribution_city/visual/cli.py +234 -0
  100. contribution_city-1.3.0/src/contribution_city/visual/compare.py +179 -0
  101. contribution_city-1.3.0/src/contribution_city/visual/fixtures.py +143 -0
  102. contribution_city-1.3.0/src/contribution_city/visual/gallery.py +178 -0
  103. contribution_city-1.3.0/src/contribution_city/visual/render.py +62 -0
  104. contribution_city-1.3.0/tests/conftest.py +11 -0
  105. contribution_city-1.3.0/tests/data/v110/reference.json +669 -0
  106. contribution_city-1.3.0/tests/snapshots/tiny_default.svg +121 -0
  107. contribution_city-1.3.0/tests/test_action.py +307 -0
  108. contribution_city-1.3.0/tests/test_action_execution.py +327 -0
  109. contribution_city-1.3.0/tests/test_backgrounds.py +146 -0
  110. contribution_city-1.3.0/tests/test_cli.py +176 -0
  111. contribution_city-1.3.0/tests/test_cli_v12.py +198 -0
  112. contribution_city-1.3.0/tests/test_config.py +329 -0
  113. contribution_city-1.3.0/tests/test_docs_and_examples.py +614 -0
  114. contribution_city-1.3.0/tests/test_github_api.py +265 -0
  115. contribution_city-1.3.0/tests/test_models.py +241 -0
  116. contribution_city-1.3.0/tests/test_renderer.py +132 -0
  117. contribution_city-1.3.0/tests/test_renderer_features.py +207 -0
  118. contribution_city-1.3.0/tests/test_svg_quality.py +176 -0
  119. contribution_city-1.3.0/tests/test_themes_layouts.py +324 -0
  120. contribution_city-1.3.0/tests/test_v13_options.py +464 -0
  121. contribution_city-1.3.0/tests/test_visual_regression.py +480 -0
  122. contribution_city-1.3.0/tests/test_workflows.py +94 -0
  123. contribution_city-1.3.0/tests/visual/README.md +9 -0
  124. contribution_city-1.3.0/tests/visual/baselines/background-transparent-sky.png +0 -0
  125. contribution_city-1.3.0/tests/visual/baselines/background-transparent.png +0 -0
  126. contribution_city-1.3.0/tests/visual/baselines/custom-size.png +0 -0
  127. contribution_city-1.3.0/tests/visual/baselines/fit-off.png +0 -0
  128. contribution_city-1.3.0/tests/visual/baselines/fixture-empty.png +0 -0
  129. contribution_city-1.3.0/tests/visual/baselines/fixture-extreme.png +0 -0
  130. contribution_city-1.3.0/tests/visual/baselines/fixture-isolated-peaks.png +0 -0
  131. contribution_city-1.3.0/tests/visual/baselines/fixture-long-streak.png +0 -0
  132. contribution_city-1.3.0/tests/visual/baselines/fixture-minimal.png +0 -0
  133. contribution_city-1.3.0/tests/visual/baselines/fixture-special-chars.png +0 -0
  134. contribution_city-1.3.0/tests/visual/baselines/fixture-uniform.png +0 -0
  135. contribution_city-1.3.0/tests/visual/baselines/height-linear.png +0 -0
  136. contribution_city-1.3.0/tests/visual/baselines/height-log.png +0 -0
  137. contribution_city-1.3.0/tests/visual/baselines/height-sqrt.png +0 -0
  138. contribution_city-1.3.0/tests/visual/baselines/index.json +264 -0
  139. contribution_city-1.3.0/tests/visual/baselines/layout-compact.png +0 -0
  140. contribution_city-1.3.0/tests/visual/baselines/layout-dense-grid.png +0 -0
  141. contribution_city-1.3.0/tests/visual/baselines/layout-downtown.png +0 -0
  142. contribution_city-1.3.0/tests/visual/baselines/layout-floating-island.png +0 -0
  143. contribution_city-1.3.0/tests/visual/baselines/layout-wireframe.png +0 -0
  144. contribution_city-1.3.0/tests/visual/baselines/preset-custom.png +0 -0
  145. contribution_city-1.3.0/tests/visual/baselines/preset-tall.png +0 -0
  146. contribution_city-1.3.0/tests/visual/baselines/range-2024-q4.png +0 -0
  147. contribution_city-1.3.0/tests/visual/baselines/style-solid-tall.png +0 -0
  148. contribution_city-1.3.0/tests/visual/baselines/style-solid.png +0 -0
  149. contribution_city-1.3.0/tests/visual/baselines/theme-arctic-glass.png +0 -0
  150. contribution_city-1.3.0/tests/visual/baselines/theme-copper-forge.png +0 -0
  151. contribution_city-1.3.0/tests/visual/baselines/theme-cyberpunk.png +0 -0
  152. contribution_city-1.3.0/tests/visual/baselines/theme-emerald-matrix.png +0 -0
  153. contribution_city-1.3.0/tests/visual/baselines/theme-matrix-green.png +0 -0
  154. contribution_city-1.3.0/tests/visual/baselines/theme-monochrome-slate.png +0 -0
  155. contribution_city-1.3.0/tests/visual/baselines/theme-navy-cyan-light.png +0 -0
  156. contribution_city-1.3.0/tests/visual/baselines/theme-navy-cyan.png +0 -0
  157. contribution_city-1.3.0/tests/visual/baselines/theme-neon-harbor.png +0 -0
  158. contribution_city-1.3.0/tests/visual/baselines/theme-profile-light.png +0 -0
  159. contribution_city-1.3.0/tests/visual/baselines/theme-profile.png +0 -0
  160. contribution_city-1.3.0/tests/visual/baselines/theme-solar-amber.png +0 -0
  161. contribution_city-1.3.0/tests/visual/baselines/theme-sunset-grid.png +0 -0
  162. contribution_city-1.3.0/tests/visual/baselines/theme-sunset-neon.png +0 -0
  163. contribution_city-1.3.0/tests/visual/baselines/view-high-floating-island.png +0 -0
  164. contribution_city-1.3.0/tests/visual/baselines/view-high.png +0 -0
  165. contribution_city-1.3.0/tests/visual/baselines/view-low.png +0 -0
  166. contribution_city-1.3.0/tests/visual/baselines/view-top-down.png +0 -0
  167. contribution_city-1.3.0/tests/visual/baselines/year-2025.png +0 -0
@@ -0,0 +1,170 @@
1
+ name: Tests and Quality Checks
2
+
3
+ permissions:
4
+ contents: read
5
+
6
+ concurrency:
7
+ group: tests-${{ github.ref }}
8
+ cancel-in-progress: true
9
+
10
+ on:
11
+ push:
12
+ branches: [main]
13
+ pull_request:
14
+ branches: [main]
15
+ workflow_dispatch:
16
+
17
+ jobs:
18
+ lint:
19
+ name: Lint & Format Check (Ruff)
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - name: Checkout code
23
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
24
+
25
+ - name: Set up Python
26
+ uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
27
+ with:
28
+ python-version: "3.12"
29
+
30
+ - name: Install Ruff
31
+ run: |
32
+ python -m pip install --upgrade pip
33
+ pip install ".[dev]"
34
+
35
+ - name: Check code formatting and linting
36
+ run: |
37
+ ruff check .
38
+ ruff format --check .
39
+
40
+ test:
41
+ name: Test Suite (Python ${{ matrix.python-version }} on ${{ matrix.os }})
42
+ needs: lint
43
+ runs-on: ${{ matrix.os }}
44
+ strategy:
45
+ fail-fast: false
46
+ matrix:
47
+ os: [ubuntu-latest, windows-latest, macos-latest]
48
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
49
+
50
+ steps:
51
+ - name: Checkout repository
52
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
53
+
54
+ - name: Set up Python ${{ matrix.python-version }}
55
+ uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
56
+ with:
57
+ python-version: ${{ matrix.python-version }}
58
+ cache: "pip"
59
+
60
+ - name: Install dependencies and package
61
+ run: |
62
+ python -m pip install --upgrade pip
63
+ pip install ".[dev]"
64
+
65
+ # Pixel comparisons against the approved baselines run once, in the "visual" job below,
66
+ # so a font or renderer difference on one operating system cannot fail the whole matrix.
67
+ - name: Run test suite with pytest
68
+ run: |
69
+ pytest --tb=short -m "not baseline"
70
+
71
+ - name: Verify CLI executable works
72
+ run: |
73
+ contribution-city generate --input examples/sample-contributions.json --theme navy-cyan --output dist/ci-test.svg
74
+ test -f dist/ci-test.svg || exit 1
75
+ shell: bash
76
+
77
+ - name: Test composite action directly
78
+ if: runner.os == 'Linux' && matrix.python-version == '3.12'
79
+ uses: ./
80
+ with:
81
+ input: examples/sample-contributions.json
82
+ theme: profile
83
+ appearance: auto
84
+ layout: compact
85
+ height-mode: linear
86
+ transparent: "true"
87
+ output: dist/composite-action-test.svg
88
+
89
+ - name: Check generated SVG is well-formed XML
90
+ if: runner.os == 'Linux' && matrix.python-version == '3.12'
91
+ run: python -c "import xml.dom.minidom as m; m.parse('dist/composite-action-test.svg'); print('well-formed')"
92
+ shell: bash
93
+
94
+ - name: Verify composite action output was generated
95
+ if: runner.os == 'Linux' && matrix.python-version == '3.12'
96
+ run: |
97
+ test -f dist/composite-action-test.svg || exit 1
98
+ shell: bash
99
+
100
+ visual:
101
+ name: Visual regression and gallery
102
+ needs: lint
103
+ runs-on: ubuntu-latest
104
+ steps:
105
+ - name: Checkout repository
106
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
107
+
108
+ - name: Set up Python
109
+ uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
110
+ with:
111
+ python-version: "3.12"
112
+ cache: "pip"
113
+
114
+ - name: Install dependencies and package
115
+ run: |
116
+ python -m pip install --upgrade pip
117
+ pip install ".[dev]"
118
+
119
+ - name: Compare renders with the approved baselines
120
+ run: |
121
+ contribution-city visual check --baseline-dir tests/visual/baselines --artifacts-dir visual-artifacts
122
+ pytest --tb=short -m baseline
123
+
124
+ - name: Upload comparison artifacts (only when the comparison fails)
125
+ if: failure()
126
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
127
+ with:
128
+ name: visual-regression-failures
129
+ path: visual-artifacts/
130
+ if-no-files-found: ignore
131
+ retention-days: 7
132
+
133
+ - name: Check that previews/ and the README grids are up to date
134
+ run: contribution-city previews --check --readme README.md
135
+
136
+ - name: Generate the preview gallery
137
+ if: always()
138
+ run: |
139
+ contribution-city gallery --output-dir gallery --fail-on-error
140
+ contribution-city check-svg gallery/svg/*.svg
141
+
142
+ - name: Upload the gallery (contact sheet, manifest, SVGs, thumbnails)
143
+ if: always()
144
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
145
+ with:
146
+ name: contribution-city-gallery
147
+ path: gallery/
148
+ if-no-files-found: ignore
149
+ retention-days: 14
150
+
151
+ build:
152
+ name: Build package
153
+ needs: lint
154
+ runs-on: ubuntu-latest
155
+ steps:
156
+ - name: Checkout repository
157
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
158
+
159
+ - name: Set up Python
160
+ uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
161
+ with:
162
+ python-version: "3.12"
163
+
164
+ - name: Build wheel and check the reported version
165
+ run: |
166
+ python -m pip install --upgrade pip
167
+ pip wheel . --no-deps -w wheelhouse
168
+ pip install wheelhouse/*.whl
169
+ contribution-city --version
170
+ python -c "import contribution_city; print(contribution_city.__version__)"
@@ -0,0 +1,38 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ dist/
8
+ build/
9
+ *.egg-info/
10
+ .eggs/
11
+
12
+ # Virtual environments
13
+ .venv/
14
+ venv/
15
+ ENV/
16
+
17
+ # Testing and coverage
18
+ .pytest_cache/
19
+ .coverage
20
+ htmlcov/
21
+ .ruff_cache/
22
+
23
+ # IDE files
24
+ .vscode/
25
+ .idea/
26
+
27
+ # Local generated output
28
+ output/
29
+ *.svg.tmp
30
+
31
+ # Generated by `contribution-city gallery` and `contribution-city visual check`
32
+ # (docs/gallery/ holds the small curated copies that ARE committed)
33
+ /gallery/
34
+ /visual-artifacts/
35
+ /wheelhouse/
36
+ dist/
37
+
38
+ .venv-v13/
@@ -0,0 +1,132 @@
1
+ # Changelog
2
+
3
+ ## 1.3.0 (2026-11-15)
4
+
5
+ ### Changed - please read
6
+ - **The default building height is now the `standard` preset.** Buildings are proportionate to the
7
+ platform (at most about three tiles high) instead of the towers of 1.0 to 1.2. This changes the
8
+ look of every default render.
9
+ - To get the previous look exactly: `--height-preset tall` (action input `height-preset: tall`,
10
+ YAML `city.height_preset: tall`).
11
+ - **Existing configurations that set `building_scale`, `min_building_height` or
12
+ `max_building_height` without a preset are not reinterpreted:** they select the new `custom`
13
+ preset, which is the 1.2.0 look with their numbers.
14
+ - The preset (overall height profile) and the existing `--height-mode` (power, linear, sqrt, log;
15
+ the curve inside the profile) are independent settings.
16
+ - The Python defaults of `building_scale`, `min_building_height` and `max_building_height` in
17
+ `RenderConfig` / `CitySettings` are now `None` ("use the preset") instead of 1.0 / 6.0 / 125.0. Code
18
+ that passes these values explicitly behaves as before.
19
+ - `examples/config.example.yaml` now shows `height_preset` instead of the three explicit numbers.
20
+ - The example workflows use `background:` instead of the legacy `transparent:` input.
21
+
22
+ ### Added
23
+ - **Height presets:** `standard` (default), `tall` (the 1.0-1.2 look), `custom`
24
+ (`--height-preset`, action input `height-preset`, YAML `city.height_preset`), plus
25
+ `--min-building-height` / `--max-building-height` and validated limits for every height setting
26
+ (scale 0.1-5, minimum 0-40, maximum 8-400, canvas 100-10000 px).
27
+ - **Building styles:** `--building-style detailed|solid`. `solid` draws plain boxes with three
28
+ shades: no windows, no rooftop beacons, no outlines. Works with every theme, layout, preset and view.
29
+ - **Viewing angles:** `--view-angle isometric|low|high|top-down` (30, 20, 42 and 58 degrees of
30
+ camera elevation) and `--view-elevation DEGREES` (10-70). The projection, building heights,
31
+ platform, island and windows follow the camera; the default `isometric` view is byte-identical to
32
+ 1.2.0.
33
+ - **Preview gallery:** `previews/` with about 40 SVGs (themes, layouts, heights, building styles,
34
+ viewing angles, backgrounds), a README index per category, `manifest.json`, and the four preview
35
+ grids in the root README, all generated by the real renderer from deterministic sample data
36
+ (`contribution-city previews`, with `--check` for CI). Transparent previews have
37
+ `*.on-checkerboard.svg` companions; the real files stay transparent.
38
+ - **Examples:** a complete customization guide (`examples/README.md`) and ready-to-copy workflows
39
+ (`basic-city`, `customized-city`, `solid-city`, `heights-and-angles`, `transparent-city`) and
40
+ configurations (`examples/config/`). Tests check that every workflow and every snippet in the guide
41
+ uses only real action inputs with valid values.
42
+ - Action inputs: `height-preset`, `scale` (extended), `min-building-height`, `max-building-height`,
43
+ `building-style`, `view-angle`, `view-elevation`, with early validation of the enumerated values.
44
+ - 70+ new visual cases and 43 approved baselines (was 35).
45
+
46
+ ### Fixed
47
+ - On very small canvases the city could overflow: the fit scale was clamped at 0.5. It now shrinks as
48
+ far as needed (tests: every angle x layout x preset x canvas size stays inside the image).
49
+ - Configuration validation hardening: non-finite and out-of-range tile dimensions are rejected, and
50
+ null, boolean, and invalid non-integer canvas dimensions are rejected with clean ConfigError messages.
51
+
52
+ ### Compatibility
53
+ - For the same options, `--height-preset tall` reproduces 1.2.0 (and 1.1.0) output byte for byte, for
54
+ every theme and for the calendar and compact layouts (tested against references generated from the
55
+ 1.1.0 source). Layout names, theme names, height modes, background behaviour and every CLI option of
56
+ 1.2.0 are unchanged.
57
+
58
+ ## 1.2.0 (2026-10-27)
59
+
60
+ ### Added
61
+ - **Themes:** `neon-harbor`, `cyberpunk`, `emerald-matrix`, `copper-forge`, `arctic-glass`,
62
+ `sunset-grid`. They are plain data in `renderer/palettes.py`; tests check text contrast, level
63
+ separation and window visibility for each.
64
+ - **Layouts:** `dense-grid`, `downtown`, `floating-island`, `wireframe`, from a small layout registry
65
+ (`renderer/layouts.py`). `calendar` and `compact` are unchanged.
66
+ - **Background modes:** `--background solid | transparent | transparent-sky | auto` (config
67
+ `city.background`, action input `background`). It takes precedence over the legacy `transparent`
68
+ flag, which keeps working; contradictory settings warn.
69
+ - **Visual regression suite:** deterministic fixtures, a case registry generated from the theme and
70
+ layout registries, resvg-based PNG rendering, tolerance-based comparison that separates
71
+ antialiasing noise from real changes, approved baselines, failure artifacts (current, baseline,
72
+ diff, side-by-side, JSON) and an explicit `visual approve --yes` command. Baselines are never
73
+ written by tests or CI.
74
+ - **Gallery:** `contribution-city gallery` writes SVGs, PNG thumbnails, a labelled contact sheet and a
75
+ deterministic `manifest.json` (settings, validation status, transparency probe, removed duplicates).
76
+ - **`check-svg` command and `svgcheck` module:** well-formed XML, unique ids, resolving references,
77
+ no external resources or active content, matching viewBox.
78
+ - **`--id-prefix`:** namespaces element ids and scopes the CSS so several SVGs can be inlined in one
79
+ page. Without it the ids are exactly the 1.1.0 ones.
80
+ - **Offline `--year`, `--from`, `--to`** now filter `--input` files as well as live data.
81
+ - Action: inputs `background` and `id-prefix`; early validation of `layout`, `appearance`,
82
+ `background`, `height-mode`; local execution tests of the real script (including hostile values).
83
+ - CI: a `visual` job (baseline comparison, gallery, SVG checks, artifacts) and a `build` job that
84
+ checks the reported version. Example `gallery-artifact.yml` (read-only permissions).
85
+
86
+ ### Changed
87
+ - Default output is byte-identical to 1.1.0 for the same input and options (tested against
88
+ references generated from the 1.1.0 source).
89
+ - CLI help lists themes and layouts from the registries.
90
+ - Window contrast of three palettes added in this cycle was raised after the contrast tests found it
91
+ too low (`neon-harbor`, `emerald-matrix`, `arctic-glass`).
92
+
93
+ ### Fixed
94
+ - The gallery records a case with invalid options as `failed` instead of stopping.
95
+
96
+ ## 1.1.0
97
+
98
+ ### Fixed
99
+ - **SVG escaping:** `custom_title` and the username were written into the SVG unescaped, which could
100
+ produce invalid XML (a broken image) or inject markup. All text is now escaped.
101
+ - **Shell injection in `action.yml`:** inputs were pasted into the bash script. They now reach the
102
+ script only through environment variables, and the token is no longer passed on the command line.
103
+ - **Version and name mismatches:** the version now lives in one place (`_version.py`) and is used by
104
+ the package, the build and the HTTP User-Agent. Docs, example workflow, license and author point
105
+ at `partialHuman/contribution-city`.
106
+ - **README** contained the whole document twice; it now exists once.
107
+ - **`isometric` layout** was only an alias of `calendar`. It was removed from the CLI, the action and
108
+ the config (a helpful error explains that `calendar` is the isometric view).
109
+ - **Streaks** no longer treat missing dates in custom data as consecutive days.
110
+
111
+ ### Added
112
+ - **Auto-fit:** the city is scaled to fill the canvas (`auto_fit`, `--no-fit` to turn it off).
113
+ - **Light and dark:** `appearance: auto` embeds a light palette and follows `prefers-color-scheme`;
114
+ `transparent` removes the background. New themes: `profile`, `profile-light`, `navy-cyan-light`.
115
+ - **Height modes:** `power` (default), `linear` (proportional), `sqrt`, `log`.
116
+ - **Date ranges and several years:** `--year` (repeatable), `--from`, `--to`.
117
+ - **Private contributions:** the count of restricted contributions is read and shown in the header.
118
+ - **Retries** with exponential back-off for network errors, 5xx responses and secondary rate limits.
119
+ - **Accessibility:** root `<title>`, `<desc>` and `role="img"` with totals.
120
+ - **Pixel-art edges** (`crispEdges`) by default (`--smooth` to turn off).
121
+ - `compact` layout leaves a tile between buildings so towers no longer hide each other.
122
+ - Example workflow publishes to an `output` branch (one commit, no history growth) and shows the
123
+ light/dark `<picture>` setup. Third-party actions are pinned to commit SHAs.
124
+
125
+ ### Changed
126
+ - Smaller files: empty tiles are one path with no tooltip, styling uses CSS classes, and windows are
127
+ four paths per building instead of a line each. The sample city went from about 308 KB to about
128
+ 140 KB.
129
+ - Raising the canvas size now enlarges the city (it used to stay at a fixed 18 px tile size).
130
+
131
+ ## 1.0.0
132
+ - Initial release.
@@ -0,0 +1,122 @@
1
+ # Implementation summary: Contribution City 1.3.0
2
+
3
+ Built from the 1.2.0 release archive. Everything below was implemented in the source and checked
4
+ by running the commands listed under [Verification](#verification).
5
+
6
+ ## Read this first
7
+
8
+ - **The reference SVGs and screenshots mentioned in the task did not arrive** (only the 1.2.0 ZIP was
9
+ attached). The *standard* height preset, the solid style and the angle presets were therefore
10
+ designed from the proportions described in the task and from the renderer's own geometry, not
11
+ matched to the images. The values are data in one file
12
+ ([`renderer/presets.py`](src/contribution_city/renderer/presets.py)) if you want to tune them to
13
+ your reference: `STANDARD` (min 3, max 56, growth 3.8, against the old 6 / 125 / 8.5) and
14
+ `VIEW_ANGLES` (30, 20, 42 and 58 degrees).
15
+ - **The default look changes** (standard heights). The old look is `--height-preset tall`.
16
+ Configurations with explicit `building_scale` / `min_building_height` / `max_building_height` are
17
+ not reinterpreted. See [Backward compatibility](#backward-compatibility).
18
+
19
+ ## What was added
20
+
21
+ | Area | New | Where |
22
+ |---|---|---|
23
+ | Heights | Presets `standard` (default), `tall`, `custom`; `--min-building-height`, `--max-building-height`; validated limits | `renderer/presets.py`, `renderer/svg.py`, `config.py`, `cli.py` |
24
+ | Building style | `--building-style detailed\|solid` (solid: no windows, beacons or outlines; three shades) | `renderer/svg.py` |
25
+ | Viewing angle | `--view-angle isometric\|low\|high\|top-down`, `--view-elevation 10-70` | `renderer/svg.py`, `renderer/presets.py` |
26
+ | Gallery | `previews/` (41 SVGs in 6 categories, a README per category, `manifest.json`), four grids in the root README, `contribution-city previews [--check]` | `previews.py`, `previews/`, `README.md` |
27
+ | Examples | `examples/README.md` (19-section guide), 5 new workflows, 4 new configurations | `examples/` |
28
+ | Action | inputs `height-preset`, `min-building-height`, `max-building-height`, `building-style`, `view-angle`, `view-elevation`, early validation | `action.yml` |
29
+ | Tests | 382 new tests (568 in 1.2.0, 950 collected now) | `tests/` |
30
+ | Release | version 1.3.0, changelog section, CI job for gallery freshness | `_version.py`, `CHANGELOG.md`, `.github/workflows/tests.yml` |
31
+
32
+ ### Preset versus mode
33
+
34
+ - **Height preset** = the overall profile (how tall the tallest building may be, and the growth rate).
35
+ - **Height mode** (`power`, `linear`, `sqrt`, `log`, unchanged) = the curve from a day's count to a
36
+ height inside that profile. All four modes work with all presets (tested).
37
+
38
+ ### Defaults, identical in the CLI, YAML and the action
39
+
40
+ | Setting | Default |
41
+ |---|---|
42
+ | height preset | `standard` (it stays unset and becomes `custom` only if scale or min/max are set without a preset) |
43
+ | height mode | `power` |
44
+ | building style | `detailed` |
45
+ | view angle | `isometric` |
46
+ | layout / theme / background | `calendar` / `navy-cyan` / `solid` |
47
+
48
+ A test renders once with the CLI, once with a YAML file and once from Python with nothing set and
49
+ requires identical SVGs; another does the same for every new option.
50
+
51
+ ## Backward compatibility
52
+
53
+ - With `--height-preset tall`, output is **byte-identical to 1.2.0 and 1.1.0** for every original
54
+ theme in the calendar and compact layouts and for the other tested options (23 reference renders
55
+ generated from the original 1.1.0 source). The default `isometric` view is byte-identical to 1.2.0.
56
+ - Explicit legacy height values keep their look: `RenderConfig(building_scale=1.0)` equals `tall`.
57
+ - No theme, layout, height mode, background mode or CLI option was removed or renamed.
58
+ - Python API change: the defaults of `building_scale`, `min_building_height` and
59
+ `max_building_height` in `RenderConfig` / `CitySettings` are `None` ("use the preset") rather than
60
+ 1.0 / 6.0 / 125.0. Code that passes them explicitly is unaffected.
61
+
62
+ ## A bug found and fixed while testing
63
+
64
+ On small canvases the auto-fit scale was clamped at 0.5, so the city could overflow the image. It
65
+ now shrinks as far as needed (regression tests cover every angle x layout x preset x canvas size).
66
+
67
+ ## Verification
68
+
69
+ Run in a clean copy of the final tree (Python 3.12.3, resvg-py 0.5.0, Pillow 13.0.0, Linux):
70
+
71
+ | Command | Result |
72
+ |---|---|
73
+ | `pytest` | **949 passed, 1 skipped** (950 collected). The skip is intentional: one 1.1.0 reference sets `building_scale`, and a separate test covers that case. |
74
+ | `pytest -m baseline` / `-m "not baseline"` | 46 passed / 903 passed, 1 skipped |
75
+ | `ruff check .` | All checks passed |
76
+ | `ruff format --check .` | 53 files already formatted |
77
+ | `pip wheel . --no-deps` | `contribution_city-1.3.0-py3-none-any.whl` built; installed into a fresh virtual environment, `contribution-city --version` prints `1.3.0`, `contribution_city.__version__` is `1.3.0` |
78
+ | CLI smoke tests from the installed wheel | 26 runs covering every preset, mode, style, view angle, background mode and layout: 26 ok, 0 failed. Invalid values print readable errors. |
79
+ | `contribution-city check-svg` on those 26 outputs | all OK |
80
+ | `contribution-city previews --check --readme README.md` | up to date (files and README grids) |
81
+ | `contribution-city visual check` | 43 compared, 43 match |
82
+ | `contribution-city gallery --fail-on-error` | 66 cases: 66 ok, 0 invalid, 0 failed |
83
+ | Link test | every relative link and image in the READMEs, the guide and the preview indexes resolves |
84
+
85
+ ### Not run, and why
86
+
87
+ - **The workflows were not executed on GitHub** (no GitHub access here). What was run: the action's real
88
+ bash script locally with controlled inputs (including hostile values), plus structural tests that
89
+ every `with:` key exists in `action.yml`, values are valid, third-party actions are pinned, permissions
90
+ are minimal and committing workflows cannot trigger themselves.
91
+ - **Image links were checked locally** (the files exist at the relative paths); how GitHub renders them
92
+ was not observed.
93
+ - **No browser test** of `prefers-color-scheme` inside `<img>`; the docs recommend the `<picture>` method
94
+ for GitHub.
95
+ - The pixel baselines were produced and compared on Linux with one renderer version.
96
+ - `@v1` in the examples resolves only after you publish the release and create or move the `v1` tag.
97
+
98
+ ## Known limitations
99
+
100
+ - No oblique (non-symmetric) projection; the four presets change the camera elevation only.
101
+ - `--no-fit` with a low or steep angle can leave the canvas (the default fit cannot).
102
+ - The preview gallery uses dark themes; light-page rendering is covered by the `*-light` themes.
103
+ - `downtown` is not chronological; `wireframe` on a transparent background uses the theme's paper colour
104
+ to hide lines.
105
+ - Some documented GitHub screens (token creation, workflow permissions) may change wording over time.
106
+
107
+ ## Files
108
+
109
+ **Added (72):** `previews/` (49 generated files, of which 41 SVGs), 8 new baseline images,
110
+ `examples/README.md`, `examples/config/{all-options,solid,transparent,heights-and-angles}.yaml`,
111
+ `examples/workflows/{basic-city,customized-city,solid-city,heights-and-angles,transparent-city}.yml`,
112
+ `src/contribution_city/previews.py`, `src/contribution_city/renderer/presets.py`,
113
+ `tests/test_v13_options.py`, `tests/test_docs_and_examples.py`, this file.
114
+
115
+ **Modified (56):** `README.md`, `CHANGELOG.md`, `action.yml`, `.github/workflows/tests.yml`,
116
+ `docs/visual-testing.md`, `docs/gallery/{contact-sheet.png,manifest.json}`, `examples/config.example.yaml`,
117
+ `examples/workflows/generate-profile-city.yml`, `src/contribution_city/{_version,cli,config}.py`,
118
+ `src/contribution_city/renderer/svg.py`, `src/contribution_city/visual/{cases,cli,fixtures}.py`,
119
+ `tests/{snapshots/tiny_default.svg,test_action_execution,test_svg_quality,test_themes_layouts}.py`,
120
+ and 35 re-approved baseline images plus their `index.json` (their default heights changed).
121
+
122
+ **Removed (1):** `docs/gallery/new-in-1.2.png` (a generated image of the previous release).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 partialHuman
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.