Skip to content

Automate WinUtil title screen generation - #4965

Open
mewclouds wants to merge 5 commits into
ChrisTitusTech:mainfrom
mewclouds:docs/automate-title-screen-generation
Open

Automate WinUtil title screen generation#4965
mewclouds wants to merge 5 commits into
ChrisTitusTech:mainfrom
mewclouds:docs/automate-title-screen-generation

Conversation

@mewclouds

@mewclouds mewclouds commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Type of Change

  • New feature
  • Bug fix
  • Documentation update
  • Refactor
  • UI/UX improvement

Description

This adds a tool and manual GitHub Actions workflow for updating the title screen used in the README and documentation. The same approach can be extended later to keep images of other WinUtil tabs up to date on the Astro website. I can look into that once this is merged and properly tested.

The workflow compiles and opens WinUtil, switches to the Tweaks tab, captures both themes, and creates the final Light and Dark image. If the image changed, it opens or updates a pull request containing only that PNG.

If the workflow fails, it uploads the available image and diagnostic files for troubleshooting. WinUtil is always closed when the run finishes.

This was first built and tested in the title screen generation proof of concept, then moved here and adjusted to use WinUtil's source and existing pull request process. Here's the screenshot that test workflow generated

winutil-light-dark-comparison

The generated image PR could be automatically merged in the future. For now, it is intentionally left for manual review so we can monitor the hosted runs and confirm that the screenshots are reliable.

Issue related to PR

  • N/A

Add local automation for capturing WinUtil in Light and Dark themes
and generating the composite title-screen image.
Add a manual Windows workflow that compiles WinUtil, generates the
Light and Dark composite, and opens an image-only pull request.
@github-actions github-actions Bot added documentation Improvements or additions to documentation new feature New feature or request labels Aug 10, 2026
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added tooling to capture WinUtil in dark and light themes and generate a polished composite title-screen image.
    • Added an on-demand workflow to create or update the title-screen image through a pull request.
    • Added diagnostics and validation to improve capture reliability and troubleshooting.
  • Documentation

    • Added setup, usage, testing, and troubleshooting guidance for title-screen generation.
    • Documented the generated asset workflow and contribution requirements.
  • Tests

    • Added automated checks for theme detection and composite image generation.

Walkthrough

Adds a Windows title-screen toolchain. It captures WinUtil in Dark and Light themes, validates UI state, creates a composite PNG, provides diagnostics and tests, and adds a manual workflow that creates image-only pull requests.

Changes

Title-screen generation

Layer / File(s) Summary
Project configuration and operating rules
.github/CODEOWNERS, .gitignore, AGENTS.md, SPEC.md, tools/title-screen/*
Defines the Python 3.13 project, dependencies, usage rules, generated-asset workflow, ownership, and tests.
Window capture and image compositing
tools/title-screen/capture_winutil.py, tools/title-screen/create_composite.py, tools/title-screen/test_create_composite.py
Captures WinUtil through Win32/GDI APIs and combines validated Dark and Light captures into a framed diagonal composite.
Theme automation and diagnostics
tools/title-screen/automate_title_screen.py, tools/title-screen/inspect_winutil.py, tools/title-screen/test_automate_title_screen.py
Automates theme selection and capture, validates brightness, generates the composite, and records UI Automation diagnostics.
Manual GitHub Actions workflow
.github/workflows/generate-title-screen.yaml
Configures a Windows runner, launches WinUtil, runs generation, creates or updates an image-only pull request, uploads failure diagnostics, and cleans up the process.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GitHubActions
  participant WinUtil
  participant automate_title_screen
  participant capture_winutil
  participant create_composite
  participant PullRequest
  GitHubActions->>WinUtil: compile and launch from main
  GitHubActions->>automate_title_screen: run generation
  automate_title_screen->>WinUtil: select Tweaks and themes
  automate_title_screen->>capture_winutil: capture Dark and Light states
  capture_winutil-->>automate_title_screen: return PNG captures
  automate_title_screen->>create_composite: create composite PNG
  GitHubActions->>PullRequest: commit generated image and open or update PR
Loading

Suggested labels: skip-changelog

Suggested reviewers: christitustech

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: automating WinUtil title-screen generation.
Description check ✅ Passed The description accurately explains the new title-screen tool, workflow, image generation, pull request behavior, and failure diagnostics.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mewclouds
mewclouds marked this pull request as ready for review August 10, 2026 22:25

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (10)
.github/workflows/generate-title-screen.yaml (2)

10-12: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

cancel-in-progress: true can cancel a run during pull request creation.

A second manual dispatch cancels the first run. If the cancellation lands between the branch push and the pull request creation, the title-screen-update branch remains without a pull request. The next run recovers because create-pull-request reuses the branch, so the impact is limited to a stray branch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/generate-title-screen.yaml around lines 10 - 12, Update
the workflow concurrency configuration for winutil-title-screen by disabling
cancel-in-progress so an active run can complete branch pushing and pull request
creation. Preserve the existing concurrency group.

39-52: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Set $ErrorActionPreference to Stop in the nested PowerShell command.

A missing Set-DisplayResolution cmdlet can produce exit code 0, so $LASTEXITCODE does not report the failure. The later GetSystemMetrics check still detects a resolution mismatch; this change provides the correct failure signal and error context.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/generate-title-screen.yaml around lines 39 - 52, Update
the nested PowerShell command in the “Set display resolution” step to set
$ErrorActionPreference to "Stop" before invoking Set-DisplayResolution, ensuring
missing-cmdlet or other PowerShell errors terminate the command with failure
details while preserving the existing $LASTEXITCODE check.
tools/title-screen/test_create_composite.py (2)

13-41: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add a case for the mismatched-dimension guard.

create_composite raises ValueError when the Dark and Light sizes differ. That branch has no coverage.

💚 Proposed additional test
     `@staticmethod`
     def _create_capture(path: Path, content_color: tuple[int, int, int, int]):
         image = Image.new("RGBA", (70, 46), (0, 0, 0, 255))
         draw = ImageDraw.Draw(image)
         draw.rectangle((3, 3, 66, 42), fill=content_color)
         image.save(path, "PNG")
+
+    def test_rejects_mismatched_dimensions(self):
+        with tempfile.TemporaryDirectory(prefix="winutil-composite-test-") as temp_dir:
+            work_dir = Path(temp_dir)
+            dark_path = work_dir / "dark.png"
+            light_path = work_dir / "light.png"
+            self._create_capture(dark_path, (32, 34, 36, 255))
+            Image.new("RGBA", (60, 40), (235, 237, 239, 255)).save(light_path, "PNG")
+
+            with self.assertRaises(ValueError):
+                create_composite(dark_path, light_path, work_dir / "out.png")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/title-screen/test_create_composite.py` around lines 13 - 41, Add a test
method alongside test_creates_only_requested_composite that creates Dark and
Light captures with different dimensions, asserts create_composite raises
ValueError, and verifies the mismatched-dimension guard is covered without
requiring an output file.

9-9: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Both test modules use flat imports and only run from tools/title-screen. Neither file belongs to a package, and neither adds the module directory to sys.path. Test discovery from the repository root fails with ModuleNotFoundError. Declare the test path in tools/title-screen/pyproject.toml, or document the required working directory in tools/title-screen/README.md.

  • tools/title-screen/test_create_composite.py#L9-L9: make from create_composite import BORDER_COLOR, create_composite resolvable from the repository root.
  • tools/title-screen/test_automate_title_screen.py#L7-L7: make from automate_title_screen import is_dark_mode resolvable from the repository root. This import also loads pywinauto, so the module fails to import on non-Windows machines even though the test itself needs no GUI. Consider importing is_dark_mode from a Windows-free helper module.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/title-screen/test_create_composite.py` at line 9, Make both
title-screen test imports work when tests run from the repository root: update
tools/title-screen/pyproject.toml or document the required working directory in
tools/title-screen/README.md for test_create_composite.py line 9, and apply the
same resolution to test_automate_title_screen.py line 7. Also move or reuse
is_dark_mode from automate_title_screen through a Windows-free helper so the
test does not import pywinauto on non-Windows systems.
tools/title-screen/test_automate_title_screen.py (1)

11-16: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add coverage for set_theme input validation and capture_theme mismatch detection.

set_theme rejects unsupported theme names, and capture_theme raises when the capture does not match expected_dark. Neither needs a GUI: set_theme fails before connecting, and capture_theme accepts an injected capture function only if you refactor it. Testing the ValueError path is the cheap win.

💚 Proposed additional test
         self.assertTrue(is_dark_mode(dark_image))
         self.assertFalse(is_dark_mode(light_image))
+
+    def test_rejects_unsupported_theme_name(self):
+        with self.assertRaises(ValueError):
+            set_theme(0, "Sepia")

Import set_theme alongside is_dark_mode for this test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/title-screen/test_automate_title_screen.py` around lines 11 - 16,
Extend test_classifies_dark_and_light_captures to import set_theme and assert
that unsupported theme names raise ValueError, without requiring GUI setup.
Prioritize this validation-path coverage; capture_theme mismatch coverage is
optional only if its capture dependency is refactored to be injectable.
tools/title-screen/create_composite.py (1)

35-89: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider getbbox() on a thresholded mask instead of per-pixel Python scans.

The four generator scans read pixels one at a time through Image.load(). For a 1920x1080 capture with a wide shadow, this runs many Python-level iterations. Pillow can do the same work in C:

♻️ Alternative bounds detection
-    width, height = image.size
-    pixels = image.load()
-
-    left = next(...)
+    width, height = image.size
+    content_mask = image.convert("L").point(
+        lambda value: 255 if value > BRIGHTNESS_THRESHOLD else 0
+    )
+    bbox = content_mask.getbbox()
+    if bbox is None:
+        left, top, right, bottom = 0, 0, width - 1, height - 1
+    else:
+        left, top, right, bottom = bbox[0], bbox[1], bbox[2] - 1, bbox[3] - 1

Note one behavior difference: the luminance conversion weights channels, while _contains_content tests each channel separately. If per-channel detection matters for colored edge pixels, keep the current logic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/title-screen/create_composite.py` around lines 35 - 89, Optimize
_find_content_bounds by creating a thresholded Pillow mask and using its
getbbox() to detect content bounds, replacing the per-pixel Image.load()
generator scans while preserving the existing fallback, margin warning, and
returned coordinate format. Verify that luminance-based thresholding does not
lose colored edge pixels; if _contains_content’s per-channel behavior is
required, retain the current detection logic instead.
tools/title-screen/capture_winutil.py (2)

30-36: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Narrow the DPI-awareness fallback and record the failure reason.

Both handlers catch Exception and the inner handler discards it. Ruff flags this as BLE001 and S110. SetProcessDpiAwarenessContext and SetProcessDpiAwareness fail with OSError, and a missing export raises AttributeError. A silent failure here produces virtualized coordinates and a mis-sized capture, so the reason should stay visible.

♻️ Proposed narrowing
-try:
-    ctypes.windll.user32.SetProcessDpiAwarenessContext(-4)
-except Exception:
-    try:
-        ctypes.windll.shcore.SetProcessDpiAwareness(2)
-    except Exception:
-        pass
+try:
+    ctypes.windll.user32.SetProcessDpiAwarenessContext(-4)
+except (AttributeError, OSError):
+    try:
+        ctypes.windll.shcore.SetProcessDpiAwareness(2)
+    except (AttributeError, OSError) as exc:
+        print(f"Warning: could not enable per-monitor DPI awareness: {exc}")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/title-screen/capture_winutil.py` around lines 30 - 36, Update the
DPI-awareness setup around SetProcessDpiAwarenessContext and
SetProcessDpiAwareness to catch only OSError and AttributeError, and retain the
failure reason by logging or otherwise reporting the caught exceptions instead
of silently passing. Preserve the fallback from the context API to the shcore
API while ensuring failures from both attempts remain visible.

Source: Linters/SAST tools


344-358: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Preserve the PrintWindow error codes.

Load user32 with ctypes.WinDLL("user32", use_last_error=True) and replace both kernel32.GetLastError() calls with ctypes.get_last_error() immediately after each failed call. Otherwise, Python or ctypes activity can overwrite the live Windows error state and make the diagnostic codes unreliable. Capture behavior is unchanged.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/title-screen/capture_winutil.py` around lines 344 - 358, Load user32
with ctypes.WinDLL("user32", use_last_error=True), then update both failed-call
branches around PrintWindow to capture errors via ctypes.get_last_error()
immediately after each failed invocation instead of kernel32.GetLastError().
Preserve the existing retry and RuntimeError behavior.
tools/title-screen/inspect_winutil.py (1)

28-45: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Mark the intentional blind excepts so Ruff stays quiet.

A diagnostics dumper must survive stale UIA elements, so catching Exception here is correct. Ruff still reports BLE001 on lines 37, 43, 60, and 123. Add # noqa: BLE001 with a short reason, or configure a per-file ignore in the project Ruff settings.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/title-screen/inspect_winutil.py` around lines 28 - 45, Mark the
intentional broad Exception handlers in inspect_winutil.py with Ruff BLE001
suppression and a brief reason, covering the handlers at the element inspection,
child enumeration, and other reported locations (lines 37, 43, 60, and 123).
Alternatively, add a narrowly scoped per-file BLE001 ignore in the Ruff
configuration for this diagnostics dumper.

Source: Linters/SAST tools

tools/title-screen/automate_title_screen.py (1)

40-56: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Guard the center-band crop for short captures.

If height is below 20, height // 2 - 10 is negative. Pillow then pads the crop with black pixels and biases the result toward Dark. A minimum-size check keeps the classification honest.

🛡️ Proposed guard
     width, height = image.size
+    if width < 4 or height < 20:
+        raise ValueError(f"Capture is too small to classify: {width}x{height}")
     sample = image.crop(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/title-screen/automate_title_screen.py` around lines 40 - 56, Update
is_dark_mode to guard against captures with height below the 20-pixel
center-band requirement before cropping, returning the appropriate non-dark
classification for undersized images; preserve the existing crop and
brightness-threshold logic for valid heights.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/generate-title-screen.yaml:
- Around line 27-30: Update the actions/checkout step in the workflow to set
persist-credentials to false, preventing the default job token from being stored
in the repository’s Git configuration while preserving the existing main branch
checkout.
- Around line 6-8: Reduce the workflow-level permissions block to only contents:
read, removing pull-requests: write and contents: write because the pull request
step authenticates with secrets.AUTO_MERGE rather than GITHUB_TOKEN.
- Line 28: Update the actions/checkout step to reference its full immutable
commit SHA instead of the mutable v7 tag, preserving the existing checkout
behavior and permissions.

In `@tools/title-screen/automate_title_screen.py`:
- Around line 205-232: Update the capture flow around capture_theme so
get_window_capture_size(hwnd) is called immediately before both the dark and
light captures, passing the freshly read dimensions to each capture. Preserve
the existing theme-selection and composite behavior; the dimensions returned by
maximize_window may remain only for startup logging or be removed if no longer
needed.

In `@tools/title-screen/pyproject.toml`:
- Around line 1-12: Update the repository operating instructions in AGENTS.md to
acknowledge the Python/uv project under tools/title-screen instead of describing
the rest of the repository as exclusively PowerShell. Document the tool’s
supported uv/Python commands and verification path so agents run its checks when
modifying the title-screen project.

In `@tools/title-screen/README.md`:
- Around line 1-75: Shorten the tools/title-screen README to a brief high-level
entry point describing the tool’s purpose and linking to a hand-written
documentation page under docs. Move the requirements, generation procedure,
automation details, tests, and troubleshooting guidance into that docs page, or
document an intentional tool-local README exception in AGENTS.md if retaining
the detailed README.

---

Nitpick comments:
In @.github/workflows/generate-title-screen.yaml:
- Around line 10-12: Update the workflow concurrency configuration for
winutil-title-screen by disabling cancel-in-progress so an active run can
complete branch pushing and pull request creation. Preserve the existing
concurrency group.
- Around line 39-52: Update the nested PowerShell command in the “Set display
resolution” step to set $ErrorActionPreference to "Stop" before invoking
Set-DisplayResolution, ensuring missing-cmdlet or other PowerShell errors
terminate the command with failure details while preserving the existing
$LASTEXITCODE check.

In `@tools/title-screen/automate_title_screen.py`:
- Around line 40-56: Update is_dark_mode to guard against captures with height
below the 20-pixel center-band requirement before cropping, returning the
appropriate non-dark classification for undersized images; preserve the existing
crop and brightness-threshold logic for valid heights.

In `@tools/title-screen/capture_winutil.py`:
- Around line 30-36: Update the DPI-awareness setup around
SetProcessDpiAwarenessContext and SetProcessDpiAwareness to catch only OSError
and AttributeError, and retain the failure reason by logging or otherwise
reporting the caught exceptions instead of silently passing. Preserve the
fallback from the context API to the shcore API while ensuring failures from
both attempts remain visible.
- Around line 344-358: Load user32 with ctypes.WinDLL("user32",
use_last_error=True), then update both failed-call branches around PrintWindow
to capture errors via ctypes.get_last_error() immediately after each failed
invocation instead of kernel32.GetLastError(). Preserve the existing retry and
RuntimeError behavior.

In `@tools/title-screen/create_composite.py`:
- Around line 35-89: Optimize _find_content_bounds by creating a thresholded
Pillow mask and using its getbbox() to detect content bounds, replacing the
per-pixel Image.load() generator scans while preserving the existing fallback,
margin warning, and returned coordinate format. Verify that luminance-based
thresholding does not lose colored edge pixels; if _contains_content’s
per-channel behavior is required, retain the current detection logic instead.

In `@tools/title-screen/inspect_winutil.py`:
- Around line 28-45: Mark the intentional broad Exception handlers in
inspect_winutil.py with Ruff BLE001 suppression and a brief reason, covering the
handlers at the element inspection, child enumeration, and other reported
locations (lines 37, 43, 60, and 123). Alternatively, add a narrowly scoped
per-file BLE001 ignore in the Ruff configuration for this diagnostics dumper.

In `@tools/title-screen/test_automate_title_screen.py`:
- Around line 11-16: Extend test_classifies_dark_and_light_captures to import
set_theme and assert that unsupported theme names raise ValueError, without
requiring GUI setup. Prioritize this validation-path coverage; capture_theme
mismatch coverage is optional only if its capture dependency is refactored to be
injectable.

In `@tools/title-screen/test_create_composite.py`:
- Around line 13-41: Add a test method alongside
test_creates_only_requested_composite that creates Dark and Light captures with
different dimensions, asserts create_composite raises ValueError, and verifies
the mismatched-dimension guard is covered without requiring an output file.
- Line 9: Make both title-screen test imports work when tests run from the
repository root: update tools/title-screen/pyproject.toml or document the
required working directory in tools/title-screen/README.md for
test_create_composite.py line 9, and apply the same resolution to
test_automate_title_screen.py line 7. Also move or reuse is_dark_mode from
automate_title_screen through a Windows-free helper so the test does not import
pywinauto on non-Windows systems.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 53f6a1ab-e52a-4aa9-a0c0-7bb7abb86afb

📥 Commits

Reviewing files that changed from the base of the PR and between ea5de27 and 3b4598c.

⛔ Files ignored due to path filters (1)
  • tools/title-screen/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • .github/CODEOWNERS
  • .github/workflows/generate-title-screen.yaml
  • .gitignore
  • AGENTS.md
  • SPEC.md
  • tools/title-screen/.python-version
  • tools/title-screen/README.md
  • tools/title-screen/automate_title_screen.py
  • tools/title-screen/capture_winutil.py
  • tools/title-screen/create_composite.py
  • tools/title-screen/inspect_winutil.py
  • tools/title-screen/pyproject.toml
  • tools/title-screen/test_automate_title_screen.py
  • tools/title-screen/test_create_composite.py

Comment thread .github/workflows/generate-title-screen.yaml Outdated
Comment thread .github/workflows/generate-title-screen.yaml
Comment thread .github/workflows/generate-title-screen.yaml
Comment thread tools/title-screen/automate_title_screen.py
Comment thread tools/title-screen/pyproject.toml
Comment thread tools/title-screen/README.md

@seanh1995 seanh1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i love this

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3b4598c6c8

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread tools/title-screen/capture_winutil.py
@mewclouds

Copy link
Copy Markdown
Contributor Author

@coderabbitai

Thanks for the additional suggestions. I reviewed the collapsed nitpicks and am not planning to address the following items in this PR:

  • cancel-in-progress: true matches the proven PoC. The fixed update branch also allows a later run to recover if a run is canceled. Two concurrent runs wouldn't happen.
  • The display step verifies the final resolution with GetSystemMetrics, so failure to reach 1920x1080 is already reported.
  • The tests are intentionally run from the documented tools/title-screen uv project. This is a Windows-only tool, so making its imports platform-independent or runnable from the repository root is not a requirement.
  • Additional tests for simple internal validation guards would not materially improve confidence in the Windows UI Automation and capture behavior.
  • The current content-bound scan runs once per generated image and preserves per-channel detection. The proposed optimization is unnecessary here and changes that behavior.
  • The DPI fallback and PrintWindow error suggestions only refine diagnostics. The current capture validates its dimensions, pixels, return values, and error paths.
  • Ruff is not configured for this project, so adding Ruff-specific suppressions would document a tool that is not being used.
  • WinUtil captures cannot realistically be small enough to trigger the proposed 20-pixel classification edge case.
  • Additional docstrings are not needed for clear internal helpers. The repository guidance asks us to avoid comments and documentation that only repeat what the code already says.

Please disregard these items for this PR.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@mewclouds Acknowledged. Your rationale is sufficient for each item.

I will disregard the collapsed nitpicks for this PR. The current scope and validation approach are acceptable.

ʕ⁠´⁠•⁠ᴥ⁠•⁠`⁠ʔ


✏️ Learnings added
Learnt from: mewclouds
URL: https://github.com/ChrisTitusTech/winutil/pull/4965

Timestamp: 2026-08-10T22:50:46.896Z
Learning: For the Windows-only `tools/title-screen` project, tests run from its documented uv project directory. Repository-root execution and platform-independent imports are not required.

Learnt from: mewclouds
URL: https://github.com/ChrisTitusTech/winutil/pull/4965

Timestamp: 2026-08-10T22:50:46.896Z
Learning: The `tools/title-screen` project does not use Ruff. Do not request Ruff-specific suppressions or configuration for this project.

Learnt from: mewclouds
URL: https://github.com/ChrisTitusTech/winutil/pull/4965

Timestamp: 2026-08-10T22:50:46.896Z
Learning: In this repository, avoid docstrings or comments that only restate clear internal helper behavior.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

Tested image is correct, SHA-256 hash is identical to the previously verified output.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
tools/title-screen/capture_winutil.py (2)

164-191: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Deselect the bitmap before calling GetDIBits. GetDIBits requires the bitmap to be unselected from every DC. Restore previous_bitmap before line 374; the current code restores it only after GetDIBits returns.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/title-screen/capture_winutil.py` around lines 164 - 191, Update the
bitmap capture flow around GetDIBits to restore previous_bitmap to the device
context before calling GetDIBits, ensuring the captured bitmap is unselected
first. Keep the existing cleanup behavior after GetDIBits while avoiding any use
of the bitmap after it has been restored.

Source: Coding guidelines


343-390: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Deselect bitmap before calling GetDIBits.

bitmap remains selected into memory_dc when GetDIBits runs. Win32 requires the bitmap to be deselected first. Restore previous_bitmap before GetDIBits, and track the selection state so cleanup does not restore it twice. Otherwise, GetDIBits can return fewer scan lines and capture_window raises instead of generating the title-screen captures.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/title-screen/capture_winutil.py` around lines 343 - 390, Update the
bitmap-selection flow around capture_window so previous_bitmap is restored
before calling GetDIBits, ensuring bitmap is deselected when pixel data is read.
Track whether the bitmap has already been restored, and make the finally cleanup
restore it only when still selected, avoiding a second restoration while
preserving cleanup on earlier failures.

Source: MCP tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tools/title-screen/capture_winutil.py`:
- Around line 164-191: Update the bitmap capture flow around GetDIBits to
restore previous_bitmap to the device context before calling GetDIBits, ensuring
the captured bitmap is unselected first. Keep the existing cleanup behavior
after GetDIBits while avoiding any use of the bitmap after it has been restored.
- Around line 343-390: Update the bitmap-selection flow around capture_window so
previous_bitmap is restored before calling GetDIBits, ensuring bitmap is
deselected when pixel data is read. Track whether the bitmap has already been
restored, and make the finally cleanup restore it only when still selected,
avoiding a second restoration while preserving cleanup on earlier failures.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bf030d1d-4808-4ef1-94ff-ae895a1af0c2

📥 Commits

Reviewing files that changed from the base of the PR and between 3b4598c and 61d3db4.

📒 Files selected for processing (4)
  • .github/workflows/generate-title-screen.yaml
  • AGENTS.md
  • tools/title-screen/capture_winutil.py
  • tools/title-screen/create_composite.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • AGENTS.md
  • .github/workflows/generate-title-screen.yaml
  • tools/title-screen/create_composite.py

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 61d3db4522

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread tools/title-screen/capture_winutil.py
@mewclouds

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: 47a1a6c6a5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation new feature New feature or request skip-changelog

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants