Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions .github/workflows/electron.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ jobs:

# Move binary to where electron-builder expects it
- name: Place binary for bundling
shell: bash
run: |
mkdir -p electron/bin
cp electron/compose-preview* electron/bin/ || true
Expand All @@ -108,6 +109,10 @@ jobs:
working-directory: electron
run: npm install

- name: Install setuptools for node-gyp
if: runner.os != 'Windows'
run: pip3 install setuptools || true

- name: Rebuild native modules
working-directory: electron
run: npx electron-rebuild -f -w node-pty
Expand All @@ -117,3 +122,77 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx electron-builder ${{ matrix.electron-args }} --publish always

# Publish Homebrew cask for the Electron app (all platforms)
publish-cask:
needs: build-electron
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Wait for release assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# electron-builder --publish always uploads to the GitHub Release
# Wait a bit for all assets to be available
sleep 30

- name: Generate Homebrew cask
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION="${GITHUB_REF_NAME#v}"
REPO="ignaciotcrespo/compose-preview-cli"
BASE_URL="https://github.com/${REPO}/releases/download/v${VERSION}"

# Download release assets to compute SHA256
# macOS arm64
MAC_ARM64_URL="${BASE_URL}/Compose-Preview-${VERSION}-arm64.dmg"
MAC_ARM64_SHA=$(curl -sL "${MAC_ARM64_URL}" | sha256sum | cut -d' ' -f1)

# macOS x64
MAC_X64_URL="${BASE_URL}/Compose-Preview-${VERSION}.dmg"
MAC_X64_SHA=$(curl -sL "${MAC_X64_URL}" | sha256sum | cut -d' ' -f1)

cat > cask.rb << CASK
cask "compose-preview-app" do
version "${VERSION}"

if Hardware::CPU.arm?
url "${MAC_ARM64_URL}"
sha256 "${MAC_ARM64_SHA}"
else
url "${MAC_X64_URL}"
sha256 "${MAC_X64_SHA}"
end

name "Compose Preview"
desc "Browse and run Jetpack Compose previews without Android Studio"
homepage "https://github.com/${REPO}"

app "Compose Preview.app"

zap trash: [
"~/Library/Application Support/compose-preview-app",
"~/Library/Preferences/com.ignaciotcrespo.compose-preview.plist",
]
end
CASK

echo "Generated cask for v${VERSION}"
cat cask.rb

- name: Push cask to homebrew-tap
env:
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
run: |
git clone https://x-access-token:${HOMEBREW_TAP_GITHUB_TOKEN}@github.com/ignaciotcrespo/homebrew-tap.git tap
mkdir -p tap/Casks
cp cask.rb tap/Casks/compose-preview-app.rb
cd tap
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Casks/compose-preview-app.rb
git commit -m "Update compose-preview-app cask to ${GITHUB_REF_NAME}" || echo "No changes"
git push
126 changes: 76 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,37 @@ You want to check a Compose preview. You open Android Studio. You wait 2 minutes
```
Compose Preview Browser — umobKMP · Pixel_6a
/ Filter: login
╭ 1 Modules ─────────────╮╭ 2 Previews (8) ──────────────────────────────────╮
│ ▸ :composeApp (0) ││ ▸ LoginScreenEmptyDarkPreview (Login - Dark) │
│ :feature:auth (8) ││ LoginScreenEmptyLightPreview (Login - Light) │
│ :feature:booking (0) ││ LoginScreenFilledDarkPreview (Login - Filled) │
│ :feature:home (0) ││ LoginScreenLoadingDarkPreview (Login - Loading)│
│ :feature:map (0) ││ LoginScreenErrorDarkPreview (Login - Error) │
│ :feature:settings (0) ││ LoginFormContentEmptyDarkPreview (LoginForm) │
│ :shared:presentation ││ LoginFormContentFilledDarkPreview (LoginForm) │
│ (0) ││ LoginFormContentLoadingDarkPreview (LoginForm) │
│ ││ │
│ ││ │
│ ││ │
╰─────────────────────────╯╰──────────────────────────────────────────────────╯
╭ Details ────────────────────────────────────────────────────────────────────╮
│ FQN: com.example.feature.auth.LoginScreenPreviewKt.LoginScreenEmptyDark.. │
│ File: feature/auth/src/androidMain/.../preview/LoginScreenPreview.kt:42 │
│ Params: showBackground=true, backgroundColor=0xFF111111 │
╰─────────────────────────────────────────────────────────────────────────────╯
╭ 1 Modules ──────────╮╭ 2 Previews (8) ──────────────────────╮╭ 3 Preview ─────────╮
│ ▸ :composeApp (0) ││ ▸ LoginScreenEmptyDarkPreview ││ │
│ :feature:auth (8) ││ LoginScreenEmptyLightPreview ││ ┌───────────┐ │
│ :feature:booking ││ LoginScreenFilledDarkPreview ││ │ │ │
│ (0) ││ LoginScreenLoadingDarkPreview ││ │ preview │ │
│ :feature:home (0) ││ LoginScreenErrorDarkPreview ││ │ image │ │
│ :feature:map (0) ││ LoginFormContentEmptyDarkPreview ││ │ │ │
│ :feature:settings ││ LoginFormContentFilledDarkPreview ││ └───────────┘ │
│ (0) ││ LoginFormContentLoadingDarkPreview ││ │
│ ││ ││ w for HD preview │
│ ││ ││ in browser │
╰──────────────────────╯╰──────────────────────────────────────╯╰─────────────────────╯
╭ Details ────────────────────────────────────────────────────────────────────────────╮
│ FQN: com.example.feature.auth.LoginScreenPreviewKt.LoginScreenEmptyDarkPreview │
│ File: feature/auth/src/androidMain/.../preview/LoginScreenPreview.kt:42 │
│ Params: showBackground=true, backgroundColor=0xFF111111 │
╰─────────────────────────────────────────────────────────────────────────────────────╯
● Launched: LoginScreenEmptyDarkPreview (com.example.app.dev)
enter run · b build · / filter · q quit
enter run · s screenshot · w web · i install · / filter · d device · q quit
```

## What it does

- **Scan** — Discovers all `@Preview` composables across all Gradle modules automatically
- **Browse** — Navigate modules and previews in a two-panel TUI with keyboard and mouse
- **Browse** — Navigate modules and previews in a three-panel TUI with keyboard and mouse
- **Search** — Live filter bar (`/`) matches preview names across all modules, counts update in real time
- **Run** — Launch any preview on a connected device via ADB with `Enter`
- **Build** — Trigger Gradle install tasks (`b`) with automatic variant detection (dev, qa, accept, production)
- **Screenshot** — Capture a preview screenshot (`s`) displayed directly in the terminal
- **HD Web Preview** — Press `w` to open a local web viewer in your browser with full-quality preview rendering
- **Install** — Trigger Gradle install tasks (`i`) with automatic variant detection (dev, qa, accept, production)
- **Device / Emulator picker** — Press `d` to select a connected device or launch an AVD emulator
- **Details** — See fully qualified name, file path, line number, and `@Preview` parameters
- **Stale detection** — Warns when source files are newer than the installed APK

Expand All @@ -55,7 +57,7 @@ You want to check a Compose preview. You open Android Studio. You wait 2 minutes

```bash
brew tap ignaciotcrespo/tap
brew install compose-preview
brew install compose-preview-cli
```

### Go
Expand All @@ -81,28 +83,28 @@ compose-preview /path/to/android/project
### Layout

```
┌─────────────────────────────────────────────────────────────────────┐
│ Compose Preview Browser — <project> · <device> │ header
├─────────────────────────────────────────────────────────────────────┤
│ / Press / to filter previews │ search bar
├────────────────────────────────────────────────────────────────────┤
│ 1 Modules │ 2 Previews (88) │
│ ▸ :composeApp (2) │ ▸ AppAndroidPreview │
│ :feature:auth │ AppPreview │ main panels
│ (30)
│ :feature:home
│ (11)
│ ...
├────────────────────────────────────────────────────────────────────┤
│ Details │
│ FQN: com.example.MainActivityKt.AppAndroidPreview │ details
│ File: composeApp/src/androidMain/.../MainActivity.kt:41 │
├─────────────────────────────────────────────────────────────────────┤
│ ⚠ sources changed since last build — press 'b' to rebuild │ status
├─────────────────────────────────────────────────────────────────────┤
│ enter run · b build · / filter · q quit │ help
└─────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────
│ Compose Preview Browser — <project> · <device> (d to change) │ header
├──────────────────────────────────────────────────────────────────────────────
│ / Press / to filter previews │ search bar
├────────────────────────────────────────┬────────────────────────────────────┤
│ 1 Modules │ 2 Previews (88) │ 3 Preview
│ ▸ :app (2) │ ▸ AppAndroidPreview ┌────────────┐
│ :feature: │ AppPreview │ preview
auth (30)│ │ screenshot│ screenshot panel
│ :feature: └────────────┘
home (11)
│ ... │ │ w for HD preview in browser
├────────────────────────────────────────┴────────────────────────────────────┤
│ Details
│ FQN: com.example.MainActivityKt.AppAndroidPreview │ details
│ File: composeApp/src/androidMain/.../MainActivity.kt:41
├──────────────────────────────────────────────────────────────────────────────
│ ⚠ sources changed since last build — press 'i' to install │ status
├──────────────────────────────────────────────────────────────────────────────
│ enter run · s screenshot · w web · i install · / filter · d device · q quit │ help
└──────────────────────────────────────────────────────────────────────────────
```

### Key bindings
Expand All @@ -111,10 +113,14 @@ compose-preview /path/to/android/project
|-----|--------|
| `/` | Focus search bar — type to filter previews live |
| `Tab` | Exit search / switch between Modules and Previews panels |
| `Enter` | Run selected preview on device (or confirm search) |
| `Enter` | Run selected preview on device (auto-captures screenshot) |
| `Esc` | Clear filter and exit search |
| `j/k` or `↑/↓` | Navigate items in focused panel |
| `b` | Build & install debug APK (auto-detects build variants) |
| `s` | Capture screenshot of the selected preview |
| `w` | Toggle HD web preview viewer in browser |
| `i` | Install APK via Gradle (auto-detects build variants) |
| `d` | Open device / emulator picker |
| `R` | Refresh project scan |
| `1` / `2` | Focus Modules / Previews panel directly |
| `q` | Quit |
| Mouse click | Select item in any panel |
Expand All @@ -138,15 +144,35 @@ Press `/` to activate the search bar. As you type, previews are filtered across

Module counts update to show only matching previews. Press `Tab` to move to the panels with the filter active, or `Esc` to clear it.

### Build variants
### Screenshots

Press `s` to capture a screenshot of the selected preview. The screenshot is rendered directly in the terminal using half-block characters. Screenshots are cached — a dot marker (`◉`) next to a preview name indicates a cached screenshot.

Running a preview with `Enter` also auto-captures a screenshot after a short delay.

### HD Web Preview

The terminal screenshot is low resolution. For full-quality rendering, press `w` to start a local web viewer. This opens your browser with an HD version of the preview, served from a local web server. Press `w` again to stop the server.

### Device / Emulator picker

Press `d` to open a modal listing connected devices and available AVD emulators. Select a device to target, or pick an emulator to launch it.

### Install variants

When you press `b`, compose-preview queries Gradle for all available install tasks. If your project has multiple build variants (dev, qa, accept, production), you get a quick-select prompt:
When you press `i`, compose-preview queries Gradle for all available install tasks. If your project has multiple build variants (dev, qa, accept, production), a picker modal appears:

```
Build variant: [D]evDebug [A]cceptDebug [Q]aDebug [P]roductionDebug
╭ Select Install Task ─────────────╮
│ ▸ installDevDebug │
│ installAcceptDebug │
│ installQaDebug │
│ installProductionDebug │
│ ↑↓ navigate · enter select · esc │
╰──────────────────────────────────╯
```

Press the highlighted letter to select. The choice is remembered for subsequent builds.
Select a task with `Enter`. The choice is remembered for subsequent installs.

## Requirements

Expand Down
Loading
Loading