diff --git a/.agents/skills/uloop-screenshot/references/prefab-capture.md b/.agents/skills/uloop-screenshot/references/prefab-capture.md
index 11f348b14..691bfaace 100644
--- a/.agents/skills/uloop-screenshot/references/prefab-capture.md
+++ b/.agents/skills/uloop-screenshot/references/prefab-capture.md
@@ -11,7 +11,7 @@ uloop screenshot --prefab-path Assets/UI/Panel.prefab --prefab-background-color
The first matching rule decides the image (the response `Message` says which one was used):
-1. **UI part** — the root is a RectTransform with no enabled Canvas of its own, or its own Canvas is World Space. The image is sized to the union of all active RectTransforms, so children that overflow the root are included, and nested Canvases inside the part are drawn with it. Sizes are counted in UI units, so a world-space Canvas saved at a small scale keeps its pixel size. A stretched root fills the Game View size. The temporary Canvas that hosts the part sits on the built-in UI layer.
+1. **UI part** — the root is a RectTransform with no enabled Canvas of its own, or its own Canvas is World Space. The image is sized to the union of all active RectTransforms, so children that overflow the root are included, and nested Canvases inside the part are drawn with it. Overflow that an enabled RectMask2D, or a Mask with a Graphic, clips is left out (the mask's rect, moved by RectMask2D padding as uGUI does; softness is ignored). When the root is itself an enabled World Space Canvas with a non-zero size, the image is the Canvas's own size and content outside it is cut off. Sizes are counted in UI units, so a world-space Canvas saved at a small scale keeps its pixel size. A stretched root fills the Game View size. The temporary Canvas that hosts the part sits on the built-in UI layer.
2. **Screen UI** — the root itself has an enabled Screen Space Canvas (Overlay or Camera), or the root is not a RectTransform and contains one. The outermost Canvases are switched to the capture camera and the image is the Game View size, so the layout matches the game. The prefab's own Canvases keep their layers, so a pipeline whose game cameras skip that layer draws nothing and the capture is rejected.
3. **Flat object** — content with no depth (sprites, quads, a lone world-space Canvas) is viewed straight on with an orthographic camera at the Game View size. A lone world-space Canvas is drawn on its own layer, so a pipeline whose game cameras skip that layer draws nothing and the capture is rejected.
4. **3D object** — anything else with an enabled Renderer is viewed from the front-left with a perspective camera at the Game View size, with the whole object in frame.
diff --git a/.claude/skills/uloop-screenshot/references/prefab-capture.md b/.claude/skills/uloop-screenshot/references/prefab-capture.md
index 11f348b14..691bfaace 100644
--- a/.claude/skills/uloop-screenshot/references/prefab-capture.md
+++ b/.claude/skills/uloop-screenshot/references/prefab-capture.md
@@ -11,7 +11,7 @@ uloop screenshot --prefab-path Assets/UI/Panel.prefab --prefab-background-color
The first matching rule decides the image (the response `Message` says which one was used):
-1. **UI part** — the root is a RectTransform with no enabled Canvas of its own, or its own Canvas is World Space. The image is sized to the union of all active RectTransforms, so children that overflow the root are included, and nested Canvases inside the part are drawn with it. Sizes are counted in UI units, so a world-space Canvas saved at a small scale keeps its pixel size. A stretched root fills the Game View size. The temporary Canvas that hosts the part sits on the built-in UI layer.
+1. **UI part** — the root is a RectTransform with no enabled Canvas of its own, or its own Canvas is World Space. The image is sized to the union of all active RectTransforms, so children that overflow the root are included, and nested Canvases inside the part are drawn with it. Overflow that an enabled RectMask2D, or a Mask with a Graphic, clips is left out (the mask's rect, moved by RectMask2D padding as uGUI does; softness is ignored). When the root is itself an enabled World Space Canvas with a non-zero size, the image is the Canvas's own size and content outside it is cut off. Sizes are counted in UI units, so a world-space Canvas saved at a small scale keeps its pixel size. A stretched root fills the Game View size. The temporary Canvas that hosts the part sits on the built-in UI layer.
2. **Screen UI** — the root itself has an enabled Screen Space Canvas (Overlay or Camera), or the root is not a RectTransform and contains one. The outermost Canvases are switched to the capture camera and the image is the Game View size, so the layout matches the game. The prefab's own Canvases keep their layers, so a pipeline whose game cameras skip that layer draws nothing and the capture is rejected.
3. **Flat object** — content with no depth (sprites, quads, a lone world-space Canvas) is viewed straight on with an orthographic camera at the Game View size. A lone world-space Canvas is drawn on its own layer, so a pipeline whose game cameras skip that layer draws nothing and the capture is rejected.
4. **3D object** — anything else with an enabled Renderer is viewed from the front-left with a perspective camera at the Game View size, with the whole object in frame.
diff --git a/Assets/Tests/Editor/PrefabCaptureFramingTests.cs b/Assets/Tests/Editor/PrefabCaptureFramingTests.cs
index 18565466e..6f5cef40d 100644
--- a/Assets/Tests/Editor/PrefabCaptureFramingTests.cs
+++ b/Assets/Tests/Editor/PrefabCaptureFramingTests.cs
@@ -2,6 +2,7 @@
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
+using UnityEngine.UI;
using io.github.hatayama.UnityCliLoop.FirstPartyTools;
using io.github.hatayama.UnityCliLoop.ToolContracts;
@@ -323,6 +324,241 @@ public void Arrange_WithAScaledRootOfAnAwkwardWidth_DoesNotAddAPixel()
Assert.That(frame.ImageSize, Is.EqualTo(new Vector2Int(109, 100)));
}
+ ///
+ /// Verifies a root that is its own world-space Canvas is captured at the Canvas size, not widened by a child that overflows it.
+ ///
+ [Test]
+ public void Arrange_WithAWorldSpaceCanvasRootAndAnOverflowingChild_SizesToTheCanvas()
+ {
+ GameObject root = CreateUiPartWithOverflowingChild(out GameObject _);
+ root.AddComponent