
Renders and inspects @Preview composables on-device or headless, offering pixel-accurate screenshots, interactive inspector, rich hierarchy/semantics extraction, accessibility checks, and AI agent integration.
Render and inspect Jetpack Compose @Preview composables outside the IDE. One command builds, deploys to a device/emulator, and opens an interactive inspector with full hierarchy, semantics, typography, colors, and pixel-accurate screenshots.
@PreviewLightDark, custom multi-preview annotations, @PreviewParameter
adb on PATH// app/build.gradle.kts
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.plugin.compose")
id("dev.mikepenz.composebuddy") // auto-configures everything
}The plugin automatically:
buddyDebug build typebuddyPreviewList and buddyPreviewDeploy Gradle tasks./gradlew :compose-buddy-cli:installDist# Build, install on device, and open the interactive inspector
compose-buddy device --project /path/to/project --module :app
# List available previews
compose-buddy device --project /path/to/project --module :app --list
# Launch a specific preview
compose-buddy device --project /path/to/project --module :app \
--preview "com.example.PreviewsKt.MyPreview"
# Capture a single frame as PNG + JSON
compose-buddy device --project /path/to/project --module :app \
--output ./out --preview "com.example.PreviewsKt.MyPreview"That's it. One command handles the entire flow: Gradle build → APK install → ADB port forward → activity launch → WebSocket connect → inspector UI.
For environments without a device/emulator:
# Render all @Preview composables as PNG images
compose-buddy render --project /path/to/project --module :app --output ./previews
# Use the desktop renderer (no Android SDK needed)
compose-buddy render --project /path/to/project --module :app \
--renderer desktop --output ./previewsEach render produces PNG images and a JSON manifest:
previews/
├── manifest.json
├── com_example_ui_ButtonPreview.png
├── com_example_ui_CardPreview.png
├── com_example_ui_ThemePreview_Light.png ← multi-preview variant
├── com_example_ui_ThemePreview_Dark.png
├── com_example_ui_GreetingPreview_0.png ← @PreviewParameter index 0
├── com_example_ui_GreetingPreview_1.png
└── ...
Render @Preview composables as PNG images.
Project options:
| Flag | Description | Default |
|---|---|---|
--project |
Gradle project root | . |
--module |
Gradle module (e.g., :app) |
auto-discover |
--preview |
FQN filter (supports * glob) |
all |
--output |
Output directory | build/compose-buddy |
--build |
Build the project before rendering | false |
--renderer |
Renderer backend: android, android-direct, desktop, auto
|
auto |
@Preview parameter overrides:
| Flag | Description | Default |
|---|---|---|
--widthDp |
Override width in dp | from @Preview
|
--heightDp |
Override height in dp | from @Preview
|
--density |
Override density in dpi (e.g., 420, 480, 560) | from @Preview
|
--locale |
Override locale (BCP 47, e.g., en, ja) |
from @Preview
|
--fontScale |
Override font scale (e.g., 1.0, 1.5, 2.0) | from @Preview
|
--dark-mode |
Enable dark mode (sets uiMode to UI_MODE_NIGHT_YES) |
false |
--uiMode |
Override uiMode bitmask (e.g., 0x21) |
from @Preview
|
--device |
Device ID or spec (e.g., id:pixel_5, spec:width=1080px,height=1920px,dpi=420) |
from @Preview
|
--showBackground |
Show background behind composable | from @Preview
|
--backgroundColor |
Background ARGB color (e.g., 0xFFFFFFFF) |
from @Preview
|
--showSystemUi |
Show system UI decorations | false |
--apiLevel |
Target SDK API level | from @Preview
|
Output options:
| Flag | Description | Default |
|---|---|---|
--format |
Output format: json, human, or agent
|
auto-detect |
--agent |
Condensed JSON for AI agent validation | false |
--hierarchy |
Include hierarchy JSON in output | false |
--semantics |
Semantic fields: all, default, or comma-separated (e.g., text,role,onClick) |
default |
--max-params |
Max @PreviewParameter values to render |
10 |
Inspect layout hierarchy of @Preview composables. Can export hierarchy JSON or launch an interactive inspector window.
| Flag | Description | Default |
|---|---|---|
--project |
Gradle project root | . |
--module |
Gradle module | auto-discover |
--preview |
FQN filter | all |
--ui |
Launch interactive inspector window | false |
--semantics |
Include semantics | true |
--modifiers |
Include modifier chain | false |
--source-locations |
Include source file/line | false |
--format |
Output format: json or human
|
json |
--renderer |
Renderer backend | auto |
--max-frames |
Frame history limit for inspector UI | 100 |
--width |
Override width in dp | from @Preview
|
--height |
Override height in dp | from @Preview
|
--locale |
Override locale | from @Preview
|
--font-scale |
Override font scale | from @Preview
|
--dark-mode |
Enable dark mode | false |
Run accessibility and design spec analysis on @Preview composables.
| Flag | Description | Default |
|---|---|---|
--project |
Gradle project root | . |
--module |
Gradle module | auto-discover |
--preview |
FQN filter | all |
--output |
Output directory | build/compose-buddy |
--checks |
Checks to run: content-description, touch-target, contrast, all
|
all |
--design-tokens |
Path to design tokens JSON file | none |
--format |
Output format: json or human
|
auto-detect |
--renderer |
Renderer backend | auto |
--widthDp |
Override width | from @Preview
|
--heightDp |
Override height | from @Preview
|
--density |
Override density | from @Preview
|
--fontScale |
Override font scale | from @Preview
|
--dark-mode |
Enable dark mode | false |
Returns exit code 1 if any findings are reported, 0 if clean.
Start an MCP server for AI agent access.
| Flag | Description | Default |
|---|---|---|
--project |
Gradle project root | . |
--module |
Gradle module | auto-discover |
--transport |
MCP transport protocol | stdio |
Install compose-buddy skills into AI coding agents.
| Flag | Description | Default |
|---|---|---|
--system, -s |
Target system: claude, gemini, copilot, cursor, windsurf, cline, junie, or all
|
all |
--project |
Project directory to install into | . |
--global |
Install globally (user-level) instead of project-level | false |
--list |
List available skills without installing | false |
Available skills: compose-buddy-render, compose-buddy-a11y, compose-ui-loop.
Launch the inspector with the --ui flag:
compose-buddy inspect --project /path/to/project --module :app --uiThis opens a Compose Desktop window (1280x800) with four panels:
Hierarchical view of all composable nodes. Each node shows:
T = Text, L = Layout, E = Element, B = Box, C = Column, R = Row, I = Image/Icon, Btn = Button, S = Screen)Click a node to select it. Hover to highlight.
Rendered preview image with interactive overlays:
Detailed properties for the selected component:
Frame history scrubber for navigating through render history (up to 100 frames). Each re-render adds a new frame.
The recommended way to use Compose Buddy. Renders @Preview composables on a real Android device or emulator for pixel-accurate output with full Android runtime APIs — no classpath issues, no layoutlib quirks.
compose-buddy device --project . --module :app
This single command:
buddyDebug APK via Gradle@Preview composables# Build, install, and open inspector with first available preview
compose-buddy device --project . --module :app
# List available previews
compose-buddy device --project . --module :app --list
# Launch a specific preview
compose-buddy device --project . --module :app --preview "com.example.PreviewsKt.MyPreview"
# Capture a single frame as PNG + JSON to a directory
compose-buddy device --project . --module :app --output ./out \
--preview "com.example.PreviewsKt.MyPreview"
# Stream JSON frames to stdout
compose-buddy device --project . --module :app --format json
# Skip build (reuse existing APK) for faster iteration
compose-buddy device --project . --module :app --skip-build
# Low-level: connect to an already-running preview (no build/install)
compose-buddy device connect --port 7890 --format inspector| Flag | Description | Default |
|---|---|---|
--project |
Gradle project root | . |
--module |
Gradle module (e.g., :app) |
required |
--preview |
Preview FQN to launch | first available |
--port |
WebSocket port | 7890 |
--device |
ADB device serial | auto-detect |
--format |
Output: inspector, json, png
|
inspector |
--output |
Capture one frame to directory, then exit | stream |
--skip-build |
Skip Gradle build | false |
--skip-install |
Skip build and install | false |
--list |
List available previews and exit | false |
Each frame includes a pixel-accurate screenshot plus a rich hierarchy tree with:
| Data | Source |
|---|---|
| Screenshot | PixelCopy (API 26+) or drawToBitmap fallback |
| Semantics | Full dynamic iteration of all SemanticsPropertyKey entries |
| Typography | fontSize, fontWeight, fontFamily, lineHeight, letterSpacing (via slot table) |
| Colors | backgroundColor, foregroundColor, contentColor (via slot table + pixel sampling) |
| Layout | bounds, boundsInParent, offsetFromParent, size (all in dp) |
| Shapes | RoundedCornerShape, CircleShape, etc. (via slot table) |
| Source | Source file path + line number (via slot table) |
| Accessibility | role, contentDescription, testTag, onClick, disabled, focused, etc. |
compose-buddy device --project . --module :app
│
├─ ./gradlew :app:assembleBuddyDebug
├─ adb install -r app-buddyDebug.apk
├─ ./gradlew :app:buddyPreviewList → selects preview FQN
├─ adb forward tcp:7890 tcp:7890
├─ adb shell am start ... BuddyPreviewActivity -e preview <fqn>
│
▼
BuddyPreviewActivity (on device)
├─ Looks up preview in KSP-generated BuddyPreviewRegistry
├─ Hosts the composable via setContent {}
├─ Starts WebSocket server on port 7890
└─ On "rerender" command: captures screenshot + semantics + slot table
│
WebSocket (BuddyFrame JSON)
│
▼
Host CLI / Inspector UI
└─ FrameMapper → RenderResult + HierarchyNode → Inspector / JSON / PNG
The MCP tools renderPreview and inspectHierarchy accept a source: "device" parameter:
{"tool": "renderPreview", "arguments": {"previewFqn": "...", "source": "device"}}| Device (recommended) | Layoutlib (offline) | Desktop (KMP) | |
|---|---|---|---|
| Command | compose-buddy device |
compose-buddy render --renderer android |
compose-buddy render --renderer desktop |
| Rendering | Real Android runtime | Simulated via layoutlib | Skia (ImageComposeScene) |
| Accuracy | Pixel-identical to production | High (minor differences) | Approximate |
| API support | Full (camera, network, sensors) | Limited | Desktop APIs only |
| Hierarchy depth | Full (semantics + slot table) | Full (semantics + slot table) | Basic |
| Requires | Device/emulator + ADB | Android SDK (ANDROID_HOME) |
Nothing |
| Best for | Development, interactive inspection | CI pipelines, headless environments | KMP Desktop projects |
Each rendered preview includes a layout hierarchy. All dimensions are in dp — convert to pixels: px = dp * densityDpi / 160.
| Field | Description |
|---|---|
name |
Node type: Text, Button, Layout, Element, Clickable, Container
|
sourceFile |
Source file path (e.g., com/example/ui/Card.kt) |
sourceLine |
Line number of the @Preview function |
bounds |
Absolute position in dp {left, top, right, bottom}
|
boundsInParent |
Position relative to parent |
size |
Size in dp {width, height}
|
offsetFromParent |
Distance from parent edges in dp (inferred padding) |
semantics |
Semantic properties map |
children |
Child nodes |
Default set (always included):
| Property | Description |
|---|---|
text |
Text content from Text() composables |
contentDescription |
Accessibility description |
role |
Semantic role: Button, Checkbox, Switch, etc. |
onClick |
Whether element is clickable |
testTag |
Test tag for UI testing |
Color properties (from pixel sampling):
| Property | Description |
|---|---|
backgroundColor |
Dominant color at element edges |
foregroundColor |
Color at element center (if different from background) |
dominantColor |
Center color for leaf nodes |
Use --semantics all to include all available properties, or --semantics text,role,onClick for a custom set.
compose-buddy render --project ./my-app --module :app --output ./out --format json{
"version": "1.0.0",
"densityDpi": 420,
"availableSemantics": ["text", "contentDescription", "role", "onClick", "backgroundColor"],
"results": [{
"previewName": "com.example.ui.CardPreview",
"imagePath": "/out/com_example_ui_CardPreview.png",
"imageWidth": 562,
"imageHeight": 1000,
"hierarchy": {
"name": "Layout",
"sourceFile": "com/example/ui/Card.kt",
"sourceLine": 42,
"size": {"width": 135.0, "height": 48.0},
"offsetFromParent": {"left": 16.0, "top": 16.0, "right": 228.0, "bottom": 16.0},
"semantics": {"backgroundColor": "#FFE6E0E9"},
"children": [
{
"name": "Text",
"size": {"width": 86.0, "height": 24.0},
"semantics": {"text": "Hello World"}
}
]
}
}]
}For condensed AI agent output, use --agent:
compose-buddy render --project ./my-app --module :app --output ./out --agentCLI (compose-buddy-cli)
→ PreviewDiscovery (bytecode scanning for @Preview)
→ Renderer selection (android | android-direct | desktop | auto)
├── Android path:
│ → LayoutlibRenderer → ForkedRenderer → child JVM
│ → RenderWorker → Bridge.init() → PaparazziSdk
│ → ComposeView.setContent { previewFunction() }
│ → Snapshot → PNG + HierarchyNode tree
└── Desktop path:
→ DesktopRenderer → DesktopForkedRenderer → child JVM
→ DesktopRenderWorker → ImageComposeScene
→ Reflection-based composable invocation
→ Skia rendering → PNG + HierarchyNode tree
Key design decisions:
@Preview without loading classes (avoids Android framework deps)@Preview$Container on annotation classes~/.compose-buddy/cache/
| Project Type | Status |
|---|---|
| Android app/library | Supported |
| KMP with Android target | Supported |
| Multi-module builds | Supported (all module classes resolved) |
| Compose Desktop (KMP) | Supported (desktop renderer) |
Copyright 2026 Mike Penz
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Render and inspect Jetpack Compose @Preview composables outside the IDE. One command builds, deploys to a device/emulator, and opens an interactive inspector with full hierarchy, semantics, typography, colors, and pixel-accurate screenshots.
@PreviewLightDark, custom multi-preview annotations, @PreviewParameter
adb on PATH// app/build.gradle.kts
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.plugin.compose")
id("dev.mikepenz.composebuddy") // auto-configures everything
}The plugin automatically:
buddyDebug build typebuddyPreviewList and buddyPreviewDeploy Gradle tasks./gradlew :compose-buddy-cli:installDist# Build, install on device, and open the interactive inspector
compose-buddy device --project /path/to/project --module :app
# List available previews
compose-buddy device --project /path/to/project --module :app --list
# Launch a specific preview
compose-buddy device --project /path/to/project --module :app \
--preview "com.example.PreviewsKt.MyPreview"
# Capture a single frame as PNG + JSON
compose-buddy device --project /path/to/project --module :app \
--output ./out --preview "com.example.PreviewsKt.MyPreview"That's it. One command handles the entire flow: Gradle build → APK install → ADB port forward → activity launch → WebSocket connect → inspector UI.
For environments without a device/emulator:
# Render all @Preview composables as PNG images
compose-buddy render --project /path/to/project --module :app --output ./previews
# Use the desktop renderer (no Android SDK needed)
compose-buddy render --project /path/to/project --module :app \
--renderer desktop --output ./previewsEach render produces PNG images and a JSON manifest:
previews/
├── manifest.json
├── com_example_ui_ButtonPreview.png
├── com_example_ui_CardPreview.png
├── com_example_ui_ThemePreview_Light.png ← multi-preview variant
├── com_example_ui_ThemePreview_Dark.png
├── com_example_ui_GreetingPreview_0.png ← @PreviewParameter index 0
├── com_example_ui_GreetingPreview_1.png
└── ...
Render @Preview composables as PNG images.
Project options:
| Flag | Description | Default |
|---|---|---|
--project |
Gradle project root | . |
--module |
Gradle module (e.g., :app) |
auto-discover |
--preview |
FQN filter (supports * glob) |
all |
--output |
Output directory | build/compose-buddy |
--build |
Build the project before rendering | false |
--renderer |
Renderer backend: android, android-direct, desktop, auto
|
auto |
@Preview parameter overrides:
| Flag | Description | Default |
|---|---|---|
--widthDp |
Override width in dp | from @Preview
|
--heightDp |
Override height in dp | from @Preview
|
--density |
Override density in dpi (e.g., 420, 480, 560) | from @Preview
|
--locale |
Override locale (BCP 47, e.g., en, ja) |
from @Preview
|
--fontScale |
Override font scale (e.g., 1.0, 1.5, 2.0) | from @Preview
|
--dark-mode |
Enable dark mode (sets uiMode to UI_MODE_NIGHT_YES) |
false |
--uiMode |
Override uiMode bitmask (e.g., 0x21) |
from @Preview
|
--device |
Device ID or spec (e.g., id:pixel_5, spec:width=1080px,height=1920px,dpi=420) |
from @Preview
|
--showBackground |
Show background behind composable | from @Preview
|
--backgroundColor |
Background ARGB color (e.g., 0xFFFFFFFF) |
from @Preview
|
--showSystemUi |
Show system UI decorations | false |
--apiLevel |
Target SDK API level | from @Preview
|
Output options:
| Flag | Description | Default |
|---|---|---|
--format |
Output format: json, human, or agent
|
auto-detect |
--agent |
Condensed JSON for AI agent validation | false |
--hierarchy |
Include hierarchy JSON in output | false |
--semantics |
Semantic fields: all, default, or comma-separated (e.g., text,role,onClick) |
default |
--max-params |
Max @PreviewParameter values to render |
10 |
Inspect layout hierarchy of @Preview composables. Can export hierarchy JSON or launch an interactive inspector window.
| Flag | Description | Default |
|---|---|---|
--project |
Gradle project root | . |
--module |
Gradle module | auto-discover |
--preview |
FQN filter | all |
--ui |
Launch interactive inspector window | false |
--semantics |
Include semantics | true |
--modifiers |
Include modifier chain | false |
--source-locations |
Include source file/line | false |
--format |
Output format: json or human
|
json |
--renderer |
Renderer backend | auto |
--max-frames |
Frame history limit for inspector UI | 100 |
--width |
Override width in dp | from @Preview
|
--height |
Override height in dp | from @Preview
|
--locale |
Override locale | from @Preview
|
--font-scale |
Override font scale | from @Preview
|
--dark-mode |
Enable dark mode | false |
Run accessibility and design spec analysis on @Preview composables.
| Flag | Description | Default |
|---|---|---|
--project |
Gradle project root | . |
--module |
Gradle module | auto-discover |
--preview |
FQN filter | all |
--output |
Output directory | build/compose-buddy |
--checks |
Checks to run: content-description, touch-target, contrast, all
|
all |
--design-tokens |
Path to design tokens JSON file | none |
--format |
Output format: json or human
|
auto-detect |
--renderer |
Renderer backend | auto |
--widthDp |
Override width | from @Preview
|
--heightDp |
Override height | from @Preview
|
--density |
Override density | from @Preview
|
--fontScale |
Override font scale | from @Preview
|
--dark-mode |
Enable dark mode | false |
Returns exit code 1 if any findings are reported, 0 if clean.
Start an MCP server for AI agent access.
| Flag | Description | Default |
|---|---|---|
--project |
Gradle project root | . |
--module |
Gradle module | auto-discover |
--transport |
MCP transport protocol | stdio |
Install compose-buddy skills into AI coding agents.
| Flag | Description | Default |
|---|---|---|
--system, -s |
Target system: claude, gemini, copilot, cursor, windsurf, cline, junie, or all
|
all |
--project |
Project directory to install into | . |
--global |
Install globally (user-level) instead of project-level | false |
--list |
List available skills without installing | false |
Available skills: compose-buddy-render, compose-buddy-a11y, compose-ui-loop.
Launch the inspector with the --ui flag:
compose-buddy inspect --project /path/to/project --module :app --uiThis opens a Compose Desktop window (1280x800) with four panels:
Hierarchical view of all composable nodes. Each node shows:
T = Text, L = Layout, E = Element, B = Box, C = Column, R = Row, I = Image/Icon, Btn = Button, S = Screen)Click a node to select it. Hover to highlight.
Rendered preview image with interactive overlays:
Detailed properties for the selected component:
Frame history scrubber for navigating through render history (up to 100 frames). Each re-render adds a new frame.
The recommended way to use Compose Buddy. Renders @Preview composables on a real Android device or emulator for pixel-accurate output with full Android runtime APIs — no classpath issues, no layoutlib quirks.
compose-buddy device --project . --module :app
This single command:
buddyDebug APK via Gradle@Preview composables# Build, install, and open inspector with first available preview
compose-buddy device --project . --module :app
# List available previews
compose-buddy device --project . --module :app --list
# Launch a specific preview
compose-buddy device --project . --module :app --preview "com.example.PreviewsKt.MyPreview"
# Capture a single frame as PNG + JSON to a directory
compose-buddy device --project . --module :app --output ./out \
--preview "com.example.PreviewsKt.MyPreview"
# Stream JSON frames to stdout
compose-buddy device --project . --module :app --format json
# Skip build (reuse existing APK) for faster iteration
compose-buddy device --project . --module :app --skip-build
# Low-level: connect to an already-running preview (no build/install)
compose-buddy device connect --port 7890 --format inspector| Flag | Description | Default |
|---|---|---|
--project |
Gradle project root | . |
--module |
Gradle module (e.g., :app) |
required |
--preview |
Preview FQN to launch | first available |
--port |
WebSocket port | 7890 |
--device |
ADB device serial | auto-detect |
--format |
Output: inspector, json, png
|
inspector |
--output |
Capture one frame to directory, then exit | stream |
--skip-build |
Skip Gradle build | false |
--skip-install |
Skip build and install | false |
--list |
List available previews and exit | false |
Each frame includes a pixel-accurate screenshot plus a rich hierarchy tree with:
| Data | Source |
|---|---|
| Screenshot | PixelCopy (API 26+) or drawToBitmap fallback |
| Semantics | Full dynamic iteration of all SemanticsPropertyKey entries |
| Typography | fontSize, fontWeight, fontFamily, lineHeight, letterSpacing (via slot table) |
| Colors | backgroundColor, foregroundColor, contentColor (via slot table + pixel sampling) |
| Layout | bounds, boundsInParent, offsetFromParent, size (all in dp) |
| Shapes | RoundedCornerShape, CircleShape, etc. (via slot table) |
| Source | Source file path + line number (via slot table) |
| Accessibility | role, contentDescription, testTag, onClick, disabled, focused, etc. |
compose-buddy device --project . --module :app
│
├─ ./gradlew :app:assembleBuddyDebug
├─ adb install -r app-buddyDebug.apk
├─ ./gradlew :app:buddyPreviewList → selects preview FQN
├─ adb forward tcp:7890 tcp:7890
├─ adb shell am start ... BuddyPreviewActivity -e preview <fqn>
│
▼
BuddyPreviewActivity (on device)
├─ Looks up preview in KSP-generated BuddyPreviewRegistry
├─ Hosts the composable via setContent {}
├─ Starts WebSocket server on port 7890
└─ On "rerender" command: captures screenshot + semantics + slot table
│
WebSocket (BuddyFrame JSON)
│
▼
Host CLI / Inspector UI
└─ FrameMapper → RenderResult + HierarchyNode → Inspector / JSON / PNG
The MCP tools renderPreview and inspectHierarchy accept a source: "device" parameter:
{"tool": "renderPreview", "arguments": {"previewFqn": "...", "source": "device"}}| Device (recommended) | Layoutlib (offline) | Desktop (KMP) | |
|---|---|---|---|
| Command | compose-buddy device |
compose-buddy render --renderer android |
compose-buddy render --renderer desktop |
| Rendering | Real Android runtime | Simulated via layoutlib | Skia (ImageComposeScene) |
| Accuracy | Pixel-identical to production | High (minor differences) | Approximate |
| API support | Full (camera, network, sensors) | Limited | Desktop APIs only |
| Hierarchy depth | Full (semantics + slot table) | Full (semantics + slot table) | Basic |
| Requires | Device/emulator + ADB | Android SDK (ANDROID_HOME) |
Nothing |
| Best for | Development, interactive inspection | CI pipelines, headless environments | KMP Desktop projects |
Each rendered preview includes a layout hierarchy. All dimensions are in dp — convert to pixels: px = dp * densityDpi / 160.
| Field | Description |
|---|---|
name |
Node type: Text, Button, Layout, Element, Clickable, Container
|
sourceFile |
Source file path (e.g., com/example/ui/Card.kt) |
sourceLine |
Line number of the @Preview function |
bounds |
Absolute position in dp {left, top, right, bottom}
|
boundsInParent |
Position relative to parent |
size |
Size in dp {width, height}
|
offsetFromParent |
Distance from parent edges in dp (inferred padding) |
semantics |
Semantic properties map |
children |
Child nodes |
Default set (always included):
| Property | Description |
|---|---|
text |
Text content from Text() composables |
contentDescription |
Accessibility description |
role |
Semantic role: Button, Checkbox, Switch, etc. |
onClick |
Whether element is clickable |
testTag |
Test tag for UI testing |
Color properties (from pixel sampling):
| Property | Description |
|---|---|
backgroundColor |
Dominant color at element edges |
foregroundColor |
Color at element center (if different from background) |
dominantColor |
Center color for leaf nodes |
Use --semantics all to include all available properties, or --semantics text,role,onClick for a custom set.
compose-buddy render --project ./my-app --module :app --output ./out --format json{
"version": "1.0.0",
"densityDpi": 420,
"availableSemantics": ["text", "contentDescription", "role", "onClick", "backgroundColor"],
"results": [{
"previewName": "com.example.ui.CardPreview",
"imagePath": "/out/com_example_ui_CardPreview.png",
"imageWidth": 562,
"imageHeight": 1000,
"hierarchy": {
"name": "Layout",
"sourceFile": "com/example/ui/Card.kt",
"sourceLine": 42,
"size": {"width": 135.0, "height": 48.0},
"offsetFromParent": {"left": 16.0, "top": 16.0, "right": 228.0, "bottom": 16.0},
"semantics": {"backgroundColor": "#FFE6E0E9"},
"children": [
{
"name": "Text",
"size": {"width": 86.0, "height": 24.0},
"semantics": {"text": "Hello World"}
}
]
}
}]
}For condensed AI agent output, use --agent:
compose-buddy render --project ./my-app --module :app --output ./out --agentCLI (compose-buddy-cli)
→ PreviewDiscovery (bytecode scanning for @Preview)
→ Renderer selection (android | android-direct | desktop | auto)
├── Android path:
│ → LayoutlibRenderer → ForkedRenderer → child JVM
│ → RenderWorker → Bridge.init() → PaparazziSdk
│ → ComposeView.setContent { previewFunction() }
│ → Snapshot → PNG + HierarchyNode tree
└── Desktop path:
→ DesktopRenderer → DesktopForkedRenderer → child JVM
→ DesktopRenderWorker → ImageComposeScene
→ Reflection-based composable invocation
→ Skia rendering → PNG + HierarchyNode tree
Key design decisions:
@Preview without loading classes (avoids Android framework deps)@Preview$Container on annotation classes~/.compose-buddy/cache/
| Project Type | Status |
|---|---|
| Android app/library | Supported |
| KMP with Android target | Supported |
| Multi-module builds | Supported (all module classes resolved) |
| Compose Desktop (KMP) | Supported (desktop renderer) |
Copyright 2026 Mike Penz
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.