--- name: docs-screenshots description: "Re-capture or resize VRCQuestTools documentation screenshots (Website/static/img/*.png), produced by the Tools/VRCQuestTools/Debug/Screenshots menu, after a Unity Inspector/EditorWindow field, default value, or layout changes, or when adding a brand-new capture target. Use whenever a component gains/loses/renames a field, a default value changes, an Inspector grows or shrinks, an existing screenshot now shows clipped content, a scrollbar, or excess blank space, or a new component/window needs a doc screenshot." --- # Documentation Screenshot Maintenance Maintains the screenshots embedded in `Website/docs/` (en) and `Website/i18n/ja/docusaurus-plugin-content-docs/current/` (ja), captured by the debug tool at `Assets/VRCQuestTools-DebugUtil/Editor/Screenshots/` and exposed under the Unity menu `Tools/VRCQuestTools/Debug/Screenshots/...`. ## Why this tool exists - **Reproducible**: every window/inspector is captured at a fixed size and position, so screenshots don't vary run to run. - **English UI**: capture always forces `DisplayLanguage.English` for the duration, regardless of the developer's own language setting, and restores it afterward. - **Realistic sample data**: when nothing is selected, the tool creates a temporary GameObject and seeds its fields with plausible, procedurally generated data (materials, textures, a skinned mesh with a blend shape, a PhysBone/PhysBoneCollider/ContactReceiver) instead of leaving fields empty. It never touches a GameObject the developer has hand-selected in the Hierarchy. - **OS-independent capture**: pixels come from Unity's internal GrabPixels rendering (`WindowPixelCapture.cs`, via uLoopMCP's `InternalEditorUtilityBridge`), not real OS screen pixels, so capture is unaffected by Remote Desktop session state or windows overlapping on screen. ## When to use this skill - A component gained, lost, or renamed a serialized field. - A field's default value changed (the sample screenshot may now show a stale or misleading value). - An Inspector or EditorWindow's layout got taller/shorter/wider (a new warning box, an extra foldout section, more/fewer rows in a list, etc.). - An existing screenshot now shows a scrollbar, clipped text/controls, or a noticeably large blank area at the bottom. - A brand-new component or window needs its own documentation screenshot for the first time. ## Key files | File | Role | |---|---| | `ScreenshotMenu.cs` | One `[MenuItem]` per target, each delegating to a private `Capture...(Action onDone)` method that holds the target's file name, size, and `populate`/`configure` lambda in one place. `Capture All` enqueues those same methods directly (never duplicates their parameters), so there is exactly one place to edit per target. | | `ScreenshotSettings.cs` | Fixed `Vector2` size per capture target, plus the shared `CaptureOrigin` and output directory. | | `SampleContentFactory.cs` | Procedural, in-memory-only sample data helpers (`CreateSampleMaterial`, `CreateSampleTexture`, `CreateSampleSkinnedMeshRenderer`, `CreateAdditionalMaterialConvertSettings`). Reuse these before writing a new one. | | `AvatarFixtures.cs` | Instantiates the `SimpleCubeAvatar` test fixture prefab as a temporary avatar (`InstantiateSimpleCubeAvatar`), plus a variant with sample PhysBone/PhysBoneCollider/ContactReceiver children (`InstantiateSimpleCubeAvatarWithDynamics`). | | `ComponentScreenshotCapture.cs` / `WindowScreenshotCapture.cs` | The actual capture plumbing (open, wait a couple of frames, grab pixels, save PNG, clean up). Rarely need touching. | | `CaptureEnvironmentScope.cs` / `AvatarConverterSettingsFoldoutScope.cs` | `IDisposable` save-force-restore scopes: the former forces English + suppresses the update banner globally, the latter forces one Inspector's persisted foldout state open. Follow this pattern if a new target has similar persisted UI state to manage. | ## Procedure 1. **Identify the affected target(s).** Find the target's file name, its `populate`/`configure` lambda in `ScreenshotMenu.cs`, and its size constant in `ScreenshotSettings.cs`. 2. **Update sample data if a field needs it.** Add or adjust the `populate`/`configure` lambda so the new/changed field shows plausible, non-empty data. Two hard rules: - Only mutate inside the temp-GameObject fallback path — never overwrite a GameObject the developer selected themselves (`ComponentScreenshotCapture.Capture`'s `populate` callback already only fires on that path; keep it that way). - Prefer reusing a `SampleContentFactory` helper. If you need a new kind of sample object, follow the existing convention: build it procedurally (no new binary assets in the repo), mark temporary GameObjects `HideFlags.HideInHierarchy | HideFlags.DontSave`, and have the caller destroy everything it created in the capture's `onDone` callback. 3. **Handle collapsed/foldout fields.** If the field lives behind a collapsed foldout, it won't show up in the screenshot just because you populated it — check which of these three patterns the target's Editor class uses, and handle it before capture: - **`SerializedProperty.isExpanded`** (default array/list drawn via `EditorGUILayout.PropertyField`): force it open with `new SerializedObject(component).FindProperty("fieldName").isExpanded = true;` then `ApplyModifiedProperties()` (see `CaptureMaterialSwap` in `ScreenshotMenu.cs` for a working example). - **A local bool field on the Editor class itself** (not a nested `ScriptableSingleton`): check its default value first — if it already defaults to `true`, nothing to do. - **A `ScriptableSingleton`-backed persisted state** (survives across the whole Editor session — this is more common than it looks: e.g. both `AvatarConverterSettingsEditorState` and `MaterialConversionSettingsEditor`'s private nested `EditorState` are `ScriptableSingleton`s, even though the latter's `foldOutAdditionalMaterialSettings` happens to default `true`). This is the developer's own real, currently-open-or-closed Inspector state — never just flip it and leave it. Add a dedicated save/force/restore `IDisposable` scope modeled on `AvatarConverterSettingsFoldoutScope.cs`, and dispose it in the capture's `onDone` so the developer's own UI state is restored afterward. (If the field you need already defaults `true` and you're not forcing anything else on that Editor, you can skip adding a scope — but if the developer ever collapses it during their own work, the next capture will silently show it collapsed until someone notices.) 4. **Compile.** Use the `uloop-compile` skill (force recompile). Expect 0 errors. A couple of pre-existing warnings unrelated to this code are normal — don't chase those, but do make sure no *new* warnings appear. 5. **Capture just the changed target(s) first.** Find the exact menu path in the `[MenuItem(Root + "...")]` attribute for that target in `ScreenshotMenu.cs` — component targets live under a `Components/` submenu (e.g. `"Components/Material Swap"`), windows are directly under the root; note the menu label doesn't always match the output PNG file name (e.g. `"Setup Avatar for Mobile Window"` writes `convert-avatar.png` — check the method body for the actual file name). Trigger it via `uloop-execute-dynamic-code`: ```csharp using UnityEditor; EditorApplication.ExecuteMenuItem("Tools/VRCQuestTools/Debug/Screenshots/