compose-ai-tools

agent
Security Audit
Fail
Health Warn
  • License — License: Apache-2.0
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 5 GitHub stars
Code Fail
  • rm -rf — Recursive force deletion command in .github/actions/preview-baselines/action.yml
Permissions Pass
  • Permissions — No dangerous permissions requested
Purpose
This is a Gradle plugin and CLI tool that discovers and renders Jetpack Compose `@Preview` annotations into PNG images, allowing developers to view their composables outside of Android Studio.

Security Assessment
Overall Risk: Low. The tool does not request dangerous permissions or make external network requests to transmit data. However, there is a minor code finding: an `rm -rf` recursive force deletion command exists within a GitHub Actions workflow file (`.github/actions/preview-baselines/action.yml`). This is commonly used to clean up CI/CD cache or baseline directories and is not inherently malicious, though it triggered an automated alert. The tool runs locally, scans compiled class files, and uses a headless subprocess to render images. No hardcoded secrets were detected.

Quality Assessment
The project is actively maintained, evidenced by a repository push occurring just today. It uses the standard, permissive Apache-2.0 open-source license and has a clear, well-documented README that explains how the discovery and rendering processes work. The only drawback is its low community visibility; with only 5 GitHub stars, the tool has not yet been widely tested or adopted by the broader developer community.

Verdict
Safe to use, though it should be evaluated as an early-stage tool given its low community adoption.
SUMMARY

Helping the Agents Compose the Things

README.md

compose-ai-tools

A Gradle plugin and CLI that discovers @Preview composables and renders them to PNG — outside
of Android Studio. Works with both Android (Jetpack Compose) and Compose Multiplatform
Desktop projects.

See SKILL.md

$ compose-preview list --module sample-wear
com.example.samplewear.PreviewsKt.ActivityListPreview_Devices - Large Round  (com/example/samplewear/Previews.kt)
com.example.samplewear.PreviewsKt.ActivityListPreview_Devices - Small Round  (com/example/samplewear/Previews.kt)
com.example.samplewear.PreviewsKt.ActivityListFontScalesPreview_Fonts - Large  (com/example/samplewear/Previews.kt)
com.example.samplewear.PreviewsKt.ActivityListFontScalesPreview_Fonts - Larger  (com/example/samplewear/Previews.kt)
com.example.samplewear.PreviewsKt.ActivityListFontScalesPreview_Fonts - Largest  (com/example/samplewear/Previews.kt)
com.example.samplewear.PreviewsKt.ActivityListFontScalesPreview_Fonts - Medium  (com/example/samplewear/Previews.kt)
com.example.samplewear.PreviewsKt.ActivityListFontScalesPreview_Fonts - Normal  (com/example/samplewear/Previews.kt)
com.example.samplewear.PreviewsKt.ActivityListFontScalesPreview_Fonts - Small  (com/example/samplewear/Previews.kt)
com.example.samplewear.PreviewsKt.ButtonPreview_Devices - Large Round  (com/example/samplewear/Previews.kt)
com.example.samplewear.PreviewsKt.ButtonPreview_Devices - Small Round  (com/example/samplewear/Previews.kt)

Also provides a VS Code plugin that displays them

image

How it works

Discovery

Scan compiled class files for @Preview annotations → build/compose-previews/previews.json.

For each method in each compiled class:

  1. Check for direct @Preview or @Preview.Container annotations on the method.
     If found, extract preview parameters (name, device, dimensions, backgroundColor, etc.)
     and emit a preview entry.

  2. Otherwise, walk the method's annotations looking for multi-preview meta-annotations.
     For each annotation, check whether *its* annotation class carries @Preview.
     Recurse through meta-annotations (with cycle detection via a visited set).
     Emit a preview entry for each @Preview found transitively.

Deduplicate by fully-qualified name + preview name + device + dimensions.

Rendering (Desktop)

Launch a subprocess with the module's full classpath plus the renderer-desktop module.

1. Load the target class by name and resolve the composable function
   via the Compose runtime's reflection API.

2. Create a headless ImageComposeScene at the target dimensions (2x density).

3. Set the scene content to: a background fill (from the @Preview annotation's
   backgroundColor), with the composable function invoked inside it.
   LocalInspectionMode is enabled so preview-aware composables render correctly.

4. Render two frames (the second allows animations and effects to settle).

5. Encode the Skia surface to PNG and write to the output file.

Rendering (Android)

Launch a subprocess inside a Robolectric sandbox with native graphics.

1. Bootstrap a ComponentActivity through Robolectric's activity lifecycle.
   Configure the shadow display to match the target dimensions.

2. Set the activity content to the composable (same as Desktop:
   background fill + reflected composable invocation + inspection mode).

3. Advance the main looper frame-by-frame (16ms per frame, up to 20 frames).
   After each frame, sample ~64 pixels and compute a checksum.
   Stop when the checksum is stable for 2 consecutive frames.

4. Draw the activity's root view to a bitmap, compress as PNG, write to file.

Caching

Both discovery and rendering are Gradle cacheable tasks with declared input/output
contracts. Unchanged source files produce no re-work on subsequent runs.
Configuration caching is strict (problems=fail).

Setup

The plugin is published to Maven Central.
No authentication, no PAT — just apply it.

1. Apply the plugin

// <module>/build.gradle.kts
plugins {
    id("ee.schimke.composeai.preview") version "0.3.5"
}

Most projects already have mavenCentral() in their
pluginManagement.repositories (AGP and the Kotlin Gradle Plugin are both
on Central). If yours doesn't, add it:

// settings.gradle.kts
pluginManagement {
    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
    }
}

CMP Desktop projects additionally need
implementation(compose.components.uiToolingPreview) — the bundled
@Preview annotation has SOURCE retention and is invisible to ClassGraph.

Check Releases for
the latest version.

Testing against a snapshot

Every push to main publishes a -SNAPSHOT build to the Central snapshots
repository. To try an unreleased change, add the snapshots repo to
pluginManagement and bump the plugin version to the next patch
-SNAPSHOT:

// settings.gradle.kts
pluginManagement {
    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
        maven("https://central.sonatype.com/repository/maven-snapshots/") {
            mavenContent { snapshotsOnly() }
        }
    }
}
// <module>/build.gradle.kts
plugins {
    id("ee.schimke.composeai.preview") version "0.3.5-SNAPSHOT"
}

The snapshot version is the next patch ahead of the latest release
(e.g. last tag v0.3.40.3.5-SNAPSHOT). Snapshots are unsigned. See
docs/RELEASING.md for more detail.

Install the CLI

Download compose-preview-0.3.5.tar.gz (or .zip) from the
v0.3.5 release
and put the bin/ directory on your PATH:

curl -L -o compose-preview.tar.gz \
  https://github.com/yschimke/compose-ai-tools/releases/download/v0.3.5/compose-preview-0.3.4.tar.gz
tar -xzf compose-preview.tar.gz
export PATH="$PWD/compose-preview-0.3.5/bin:$PATH"

compose-preview --help

Requires Java 21 on PATH (or JAVA_HOME).

Install the VS Code extension

Install Compose Preview
from the VS Code Marketplace, or from the command line:

code --install-extension yuri-schimke.compose-preview

The extension uses the Gradle plugin to render previews, so apply
ee.schimke.composeai.preview version 0.3.5 in your project as shown above.

Usage

Run discovery and rendering:

./gradlew :app:discoverPreviews    # scan for @Preview annotations
./gradlew :app:renderAllPreviews   # discover + render to PNG

Configuration

composePreview {
    variant.set("debug")     // Android build variant (default: "debug")
    sdkVersion.set(35)       // Robolectric SDK version (default: 35)
    enabled.set(true)        // disable to skip registration (default: true)
}

Project structure

Module Purpose
gradle-plugin/ Gradle plugin — discovery, rendering task orchestration
renderer-desktop/ Desktop renderer — ImageComposeScene + Skia PNG capture
renderer-android/ Android renderer — Robolectric harness
cli/ CLI with GraalVM native-image support
sample-android/ Android sample with colored box @Preview composables
sample-cmp/ CMP Desktop sample with colored box @Preview composables

Requirements

  • Gradle 9.4.1+
  • Java 21
  • AGP 9.1.0 (Android projects)
  • Kotlin 2.2.21
  • Compose Multiplatform 1.10.3 (Desktop projects)

Contributing

See docs/DEVELOPMENT.md for building the plugin, CLI, and
VS Code extension from source and running them locally against the bundled
samples.

Reviews (0)

No results found