Skip to content

Add offscreen editor-window capture automation (#114) - #123

Merged
Eli Pinkerton (wallstop) merged 1 commit into
mainfrom
t114/editor-surface-capture
Sep 19, 2026
Merged

Eli Pinkerton (wallstop) merged 1 commit into
mainfrom
t114/editor-surface-capture

Conversation

@wallstop

@wallstop Eli Pinkerton (wallstop) commented Sep 19, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds a Tests-assembly offscreen editor-window capture path so docs imagery (#114) can be regenerated without OS screen capture, which macOS TCC denies to the Unity editor process.

Behavior

  • EditorSurfaceCapture popup-hosts a window, settles its panel layout, renders into an offscreen linear RenderTexture through three repaint/render cycles, and writes a 24-bit PNG; no desktop pixels are ever read, and internal panel methods fail closed if a Unity upgrade renames them.
  • New EditMode tests: the real DataVisualizer window captures to a valid non-blank PNG; a deterministic surface captures twice byte-identically; guards fail closed; -nographics editors skip. Red check: suppressed render passes fail the non-blank assertions (But was: 1), proving the tests detect a broken pipeline.
  • Capture hygiene rules from review: dedicated EditorSurfaceCaptureHostWindow type (a popup-hosted LayoutTestWindow was reused as SplitterWidthPersistenceTests' dock anchor, clamping the pane to 320) and destroy-first CloseWindow (Close() does not reliably unregister popup-hosted windows).

Validation

  • Full EditMode 311/311 (305 prior + 6 new), PlayMode 15/15 on the Unity 6000.4.6f1 host; lint ladder green; npm pack payload unchanged at 172 files (Tests are not shipped).

Risk / Rollback

  • Tests-assembly only; no production, persistence, or shipped-package surface changes. Rollback by reverting the single commit.

Refs #114. Also this run: #121 verified on main and closed on the issue with host evidence (issue #121 comment).


Note

Low Risk
Changes are confined to the Tests assembly with no production or shipped-package surface; the main fragility is reflection on internal UI Toolkit panel APIs, which fails loudly on Unity upgrades.

Overview
Adds Tests-only infrastructure to screenshot Unity EditorWindow UI Toolkit surfaces without OS screen capture (workaround for macOS TCC blocking screencapture on the editor), targeting documentation image regeneration (#114).

EditorSurfaceCapture popup-hosts the window, iterates layout until stable, then drives the panel through reflective ValidateLayout / Repaint / Render into an offscreen linear RenderTexture, encodes a PNG, and restores graphics state and textures on success or failure. It refuses -nographics editors and missing/invalid inputs instead of writing blank images, and reports DistinctColorCount so tests can detect empty renders.

Supporting types include a dedicated EditorSurfaceCaptureHostWindow (avoids reuse with other tests’ window lookups) and DestroyImmediate-based CloseWindow teardown. EditMode tests cover fail-closed guards, byte-identical deterministic captures, and a real DataVisualizer window producing a valid non-blank PNG, with EditorPrefs snapshot/restore around the integration test.

Reviewed by Cursor Bugbot for commit 2c847a6. Bugbot is set up for automated code reviews on this repo. Configure here.

The #114 capture-automation slice needs a Unity-side capture path: macOS
TCC denies `screencapture` to the Unity editor process, so window
rasterization must never read the desktop. EditorSurfaceCapture hosts a
window as a popup, settles its UI Toolkit panel layout, renders the
panel into an offscreen linear RenderTexture through three
repaint/render cycles, and reads back only that target; the panel's
ValidateLayout/Repaint/Render are internal with no public equivalent,
so the reflective invocation fails closed if a Unity upgrade renames
them. Every graphics global is restored and every texture destroyed,
including on failure.

EditMode tests prove the mechanism end to end: the real DataVisualizer
window captures to a valid non-blank PNG; a deterministic surface
captures twice with byte-identical output; guards fail closed on null
window and empty paths; -nographics editors skip. Red check: with the
render passes suppressed both content assertions fail on a blank frame
(Expected: greater than 1, But was: 1), so the tests catch a broken
render pipeline rather than vacuous PNGs.

Two capture-specific rules came out of an interaction with
SplitterWidthPersistenceTests: the capture host must be a dedicated
window type nothing else looks up by name (a popup-hosted
LayoutTestWindow was reused as their dock anchor, clamping the pane to
its 320 minimum), and CloseWindow must DestroyImmediate because
EditorWindow.Close does not reliably unregister popup-hosted windows.
The real-window test also snapshots and restores the window EditorPrefs
keys its lifecycle can touch.

Validation: full EditMode 311/311 (305 prior + 6 new), PlayMode 15/15
on the Unity 6000.4.6f1 host; lint ladder green; npm pack payload
unchanged at 172 files (Tests are not shipped).

Refs #114.
@wallstop
Eli Pinkerton (wallstop) merged commit 7d97494 into main Sep 19, 2026
3 checks passed
@wallstop
Eli Pinkerton (wallstop) deleted the t114/editor-surface-capture branch September 19, 2026 18:57
Eli Pinkerton (wallstop) added a commit that referenced this pull request Oct 10, 2026
DISCLOSURE: LLM-GENERATED TEXT

**Why:** The 300-rendered-character entry cap was guidance only, so
nothing failed when an entry drifted past it.

**What:**

- Add a lint that measures `[Unreleased]` entries as rendered text.
- Exclude issue references and inline link targets from the count.
- Fail closed on a missing or duplicated `[Unreleased]` section.
- Wire the lint into the ladder, pre-commit, and `llm-lint` CI.

Fixes #159

## Type of Change

- [x] New feature (non-breaking change that adds functionality)

## Checklist

- [x] I have added tests that prove my fix is effective or my feature
works
- [x] I have updated the documentation accordingly
- [ ] I have updated the [CHANGELOG](../CHANGELOG.md) for user-facing
changes
- [x] My changes do not introduce breaking changes, or breaking changes
are documented

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Adds CI/pre-commit tooling and tests only; no runtime or release
artifact behavior changes beyond blocking over-long changelog drafts.
> 
> **Overview**
> Turns the existing **300 rendered-character** cap for `CHANGELOG.md`
`[Unreleased]` entries from documentation into an enforced check via new
`scripts/lint-changelog-length.js`.
> 
> The linter measures **rendered** text (joined wraps, stripped
emphasis/code markers) and **does not count** issue references (`#123`,
`(#123)`) or inline link URLs; it only scans `[Unreleased]`, and **fails
closed** if that section is missing or duplicated. It is hooked into
**pre-commit** (on `CHANGELOG.md`), **`llm-lint` CI**, `npm run
lint:changelog-length`, and `lint:llm:full`, with **PowerShell
self-tests** covering boundary cases and exclusions. Agent docs
(`ship-changes`, `.llm/context.md`) now point contributors at `npm run
lint:changelog-length`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
5f3e450. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant