Skip to content

Commit 7e89f68

Browse files
committed
Add desktop screenshot skill
1 parent d922229 commit 7e89f68

1 file changed

Lines changed: 116 additions & 0 deletions

File tree

  • .agents/skills/take-desktop-screenshot
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
---
2+
name: take-desktop-screenshot
3+
description: Capture a screenshot of a Compose Desktop app so the agent can verify what the app actually rendered by temporarily wrapping the desktop entry composable with a Compose-only screenshot composable, running the app, then cleaning up the instrumentation.
4+
metadata:
5+
short-description: Capture Compose Desktop screenshots
6+
---
7+
8+
# Take Desktop Screenshot
9+
10+
Use this when asked to take a screenshot of a Compose Desktop app rendered from source, especially when the agent needs to verify the contents of the app visually. This workflow temporarily instruments the desktop entry point, saves a PNG to the Desktop, then removes the instrumentation.
11+
12+
## Workflow
13+
14+
1. Locate the desktop `main()` and the entry composable it launches.
15+
2. Add `TakeScreenshot` in that same desktop entry file.
16+
3. Wrap the entry composable with `TakeScreenshot`.
17+
4. Run the desktop app.
18+
5. Verify the PNG rendered correctly.
19+
6. Clean up: remove the screenshot imports, remove `TakeScreenshot`, and restore the original entry composable call.
20+
21+
## Entry Wrap
22+
23+
Change this:
24+
25+
```kotlin
26+
fun main() = singleWindowApplication(title = "App") {
27+
App()
28+
}
29+
```
30+
31+
To this temporarily:
32+
33+
```kotlin
34+
fun main() = singleWindowApplication(title = "App") {
35+
TakeScreenshot {
36+
App()
37+
}
38+
}
39+
```
40+
41+
## Composable
42+
43+
Paste this into the same desktop entry file as `main()`:
44+
45+
```kotlin
46+
import androidx.compose.foundation.layout.Box
47+
import androidx.compose.foundation.layout.fillMaxSize
48+
import androidx.compose.runtime.Composable
49+
import androidx.compose.runtime.LaunchedEffect
50+
import androidx.compose.ui.Modifier
51+
import androidx.compose.ui.draw.drawWithContent
52+
import androidx.compose.ui.graphics.layer.drawLayer
53+
import androidx.compose.ui.graphics.rememberGraphicsLayer
54+
import androidx.compose.ui.graphics.toAwtImage
55+
import java.io.File
56+
import javax.imageio.ImageIO
57+
import kotlinx.coroutines.delay
58+
59+
@Composable
60+
fun TakeScreenshot(
61+
content: @Composable () -> Unit,
62+
) {
63+
val graphicsLayer = rememberGraphicsLayer()
64+
65+
LaunchedEffect(graphicsLayer) {
66+
delay(2_000)
67+
val image = graphicsLayer.toImageBitmap()
68+
val desktop = File(System.getProperty("user.home"), "Desktop")
69+
var output = File(desktop, "screenshot.png")
70+
var index = 1
71+
72+
while (output.exists()) {
73+
output = File(desktop, "screenshot ($index).png")
74+
index += 1
75+
}
76+
77+
ImageIO.write(image.toAwtImage(), "png", output)
78+
println("Saved Compose screenshot to ${output.absolutePath}")
79+
kotlin.system.exitProcess(0)
80+
}
81+
82+
Box(
83+
modifier = Modifier
84+
.fillMaxSize()
85+
.drawWithContent {
86+
graphicsLayer.record {
87+
this@drawWithContent.drawContent()
88+
}
89+
drawLayer(graphicsLayer)
90+
},
91+
) {
92+
content()
93+
}
94+
}
95+
```
96+
97+
Important: use `graphicsLayer.record { this@drawWithContent.drawContent() }`. Do not use the explicit `density/layoutDirection/size` overload for this workflow; it can capture the layer but miss child content on Desktop.
98+
99+
## Run And Verify
100+
101+
Run the desktop app using the repo's normal command. If it is a Compose Hot Reload demo, prefer the project's configured desktop run command, for example:
102+
103+
```bash
104+
./gradlew :demo-material-impl:hotRunDesktop
105+
```
106+
107+
The app exits itself after saving. Check the newest `~/Desktop/screenshot*.png` with image inspection. If it is blank, keep iterating before cleanup.
108+
109+
## Cleanup
110+
111+
After a good screenshot:
112+
113+
- Restore `main()` to call the entry composable directly.
114+
- Remove `TakeScreenshot`.
115+
- Remove screenshot-only imports.
116+
- Check `git diff` to make sure only intended non-screenshot changes remain.

0 commit comments

Comments
 (0)