Skip to content

Commit 1ebaa08

Browse files
authored
feat: add Swift CLI preview-tool with declaration-level dependency resolution (#5)
Replace Ruby-based SPM preview path (preview-spm.sh + xcodeproj gem) with native Swift implementation via preview-tool preview-spm subcommand. Add SPMProjectCreator to build temporary Xcode projects for SPM package previews. Update scripts/preview to route SPM packages to preview-tool. Remove dead Ruby files (inject-preview-target.rb, preview-spm.sh). Update docs to remove Ruby prerequisite.
1 parent fa3516a commit 1ebaa08

32 files changed

+8706
-1200
lines changed

.claude/commands/preview.md

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,49 +30,46 @@ This will automatically:
3030

3131
### Preview Modes
3232

33-
#### Mode 1: Dynamic Preview Injection (Fastest - Recommended)
33+
The unified `preview` script auto-detects the best mode. You can also be explicit:
3434

35-
For files with `#Preview` in an Xcode project, this injects a minimal PreviewHost target:
35+
#### Xcode Project with #Preview (Fastest)
36+
37+
Injects a minimal PreviewHost target, builds only required modules (~3-4 seconds):
3638

3739
```bash
38-
"${PREVIEW_BUILD_PATH:-$HOME/XcodePreviews}"/scripts/preview-dynamic.sh \
40+
"${PREVIEW_BUILD_PATH:-$HOME/XcodePreviews}"/scripts/preview \
3941
"<path-to-file.swift>" \
4042
--project "<path.xcodeproj>" \
4143
--output /tmp/preview.png
4244
```
4345

44-
**Advantages:**
45-
- Builds only the required modules (3-4 seconds vs minutes)
46-
- Works with static modules/libraries
47-
- Automatically includes resource bundles (Tuist and standard naming)
48-
- No pre-existing preview scheme required
49-
50-
#### Mode 2: SPM Package
46+
#### SPM Package
5147

52-
For files in Swift Package Manager packages:
48+
Auto-detected from Package.swift in parent directories:
5349

5450
```bash
55-
"${PREVIEW_BUILD_PATH:-$HOME/XcodePreviews}"/scripts/preview-spm.sh \
51+
"${PREVIEW_BUILD_PATH:-$HOME/XcodePreviews}"/scripts/preview \
5652
"<path-to-file.swift>" \
5753
--output /tmp/preview.png
5854
```
5955

60-
#### Mode 3: Standalone Swift File (No Dependencies)
56+
#### Standalone Swift File (No Dependencies)
6157

6258
For Swift files that only use system frameworks (SwiftUI, UIKit, Foundation):
6359

6460
```bash
65-
"${PREVIEW_BUILD_PATH:-$HOME/XcodePreviews}"/scripts/preview-minimal.sh \
61+
"${PREVIEW_BUILD_PATH:-$HOME/XcodePreviews}"/scripts/preview \
6662
"<path-to-file.swift>" \
6763
--output /tmp/preview.png
6864
```
6965

70-
#### Mode 4: Capture Current Simulator
66+
#### Capture Current Simulator
7167

7268
Just screenshot whatever is currently on screen:
7369

7470
```bash
75-
"${PREVIEW_BUILD_PATH:-$HOME/XcodePreviews}"/scripts/capture-simulator.sh \
71+
"${PREVIEW_BUILD_PATH:-$HOME/XcodePreviews}"/scripts/preview \
72+
--capture \
7673
--output /tmp/preview.png
7774
```
7875

@@ -96,7 +93,7 @@ Read /tmp/preview.png
9693
```
9794
User: /preview ~/MyApp/Modules/Components/Button.swift
9895
→ Auto-detects MyApp.xcodeproj
99-
Uses preview-dynamic.sh for fast build (~3 seconds)
96+
Injects PreviewHost target for fast build (~3 seconds)
10097
→ Capture screenshot showing Button component
10198
→ Analyze visual output
10299
```
@@ -105,7 +102,7 @@ User: /preview ~/MyApp/Modules/Components/Button.swift
105102
```
106103
User: /preview ~/MyPackage/Sources/UI/Card.swift
107104
→ Detects Package.swift
108-
Uses preview-spm.sh
105+
Creates temporary Xcode project with SPM dependency
109106
→ Capture screenshot
110107
→ Analyze visual output
111108
```
@@ -114,7 +111,7 @@ User: /preview ~/MyPackage/Sources/UI/Card.swift
114111
```
115112
User: /preview MyView.swift
116113
→ Detects system-only imports
117-
Use preview-minimal.sh to build minimal host
114+
Builds minimal host app
118115
→ Capture screenshot
119116
→ Analyze visual output
120117
```

.codex/skills/xcode-preview-capture/SKILL.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,7 @@ Use this skill to build SwiftUI previews and inspect screenshots.
1010
## Prerequisites
1111

1212
- Xcode + iOS Simulator installed
13-
- Ruby gem dependency:
14-
15-
```bash
16-
gem install xcodeproj --user-install
17-
```
13+
- Swift toolchain (preview-tool auto-builds on first run)
1814
- `PREVIEW_BUILD_PATH` set to this repository root
1915
- Example: `export PREVIEW_BUILD_PATH=/absolute/path/to/XcodePreviews`
2016

.cursor/rules/preview.mdc

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,19 @@ A toolkit for building and capturing SwiftUI previews for visual analysis.
2828
| Project Type | Approach | Build Time | Script |
2929
|--------------|----------|------------|--------|
3030
| **Standalone Swift** | Minimal host app | ~5 seconds | `preview-minimal.sh` |
31-
| **Xcode project** | Dynamic target injection | ~3-4 seconds | `preview-dynamic.sh` |
32-
| **SPM package** | Temporary project creation | ~20 seconds | `preview-spm.sh` |
31+
| **Xcode project** | Dynamic target injection | ~3-4 seconds | `preview-tool` |
32+
| **SPM package** | Temporary project creation | ~20 seconds | `preview-tool` |
3333

34-
## Scripts
34+
## Scripts & Tools
3535

36-
| Script | Purpose |
37-
|--------|---------|
38-
| `preview` | Unified entry point - auto-detects best approach |
39-
| `preview-dynamic.sh` | Dynamic target injection for Xcode projects |
40-
| `preview-spm.sh` | Preview from SPM packages |
41-
| `preview-minimal.sh` | Build standalone Swift files |
42-
| `capture-simulator.sh` | Screenshot current simulator |
43-
| `sim-manager.sh` | Simulator management |
36+
| Path | Purpose |
37+
|------|---------|
38+
| `scripts/preview` | Unified entry point - auto-detects best approach |
39+
| `scripts/preview-minimal.sh` | Build standalone Swift files |
40+
| `scripts/xcode-preview.sh` | Build full Xcode project schemes |
41+
| `scripts/capture-simulator.sh` | Screenshot current simulator |
42+
| `scripts/sim-manager.sh` | Simulator management |
43+
| `tools/preview-tool` | Swift CLI for dynamic preview injection, SPM preview, dependency resolution |
4444

4545
## Workflow for Cursor
4646

@@ -59,4 +59,4 @@ When asked to preview a SwiftUI view:
5959

6060
- macOS with Xcode installed
6161
- iOS Simulator
62-
- Ruby with `xcodeproj` gem: `gem install xcodeproj --user-install`
62+
- Swift toolchain (preview-tool auto-builds on first run)

.cursor/skills/preview/SKILL.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,23 @@ Auto-detects project type and uses the best approach:
3232
--output /tmp/preview-capture.png
3333
```
3434

35-
### Dynamic Preview Injection (Xcode Projects)
35+
### Xcode Project with #Preview
3636

37-
Fast builds by injecting a minimal PreviewHost target:
37+
Fast builds by injecting a minimal PreviewHost target (handled automatically by the unified script):
3838

3939
```bash
40-
"${PREVIEW_BUILD_PATH:-$HOME/XcodePreviews}"/scripts/preview-dynamic.sh \
40+
"${PREVIEW_BUILD_PATH:-$HOME/XcodePreviews}"/scripts/preview \
4141
<path-to-file.swift> \
4242
--project <path.xcodeproj> \
4343
--output /tmp/preview.png
4444
```
4545

4646
### SPM Package Preview
4747

48+
Auto-detected from Package.swift in parent directories:
49+
4850
```bash
49-
"${PREVIEW_BUILD_PATH:-$HOME/XcodePreviews}"/scripts/preview-spm.sh \
51+
"${PREVIEW_BUILD_PATH:-$HOME/XcodePreviews}"/scripts/preview \
5052
<path-to-file.swift> \
5153
--output /tmp/preview.png
5254
```
@@ -56,7 +58,7 @@ Fast builds by injecting a minimal PreviewHost target:
5658
Build a standalone Swift file with system frameworks only:
5759

5860
```bash
59-
"${PREVIEW_BUILD_PATH:-$HOME/XcodePreviews}"/scripts/preview-minimal.sh \
61+
"${PREVIEW_BUILD_PATH:-$HOME/XcodePreviews}"/scripts/preview \
6062
<path-to-file.swift> \
6163
--output /tmp/preview.png
6264
```

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ DerivedData/
1717
build/
1818
*.app
1919
*.o
20+
tools/preview-tool/.build/
2021

2122
# Temporary files
2223
*.swp

CLAUDE.md

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ A toolkit for building and capturing SwiftUI previews for visual analysis.
2525
| Project Type | Approach | Build Time | Script |
2626
|--------------|----------|------------|--------|
2727
| **Standalone Swift** | Minimal host app | ~5 seconds | `preview-minimal.sh` |
28-
| **Xcode project** | Dynamic target injection | ~3-4 seconds | `preview-dynamic.sh` |
29-
| **SPM package** | Temporary project creation | ~20 seconds | `preview-spm.sh` |
28+
| **Xcode project** | Dynamic target injection | ~3-4 seconds | `preview-tool` |
29+
| **SPM package** | Temporary project creation | ~20 seconds | `preview-tool` |
3030

3131
### Dynamic Preview Injection
3232

@@ -41,17 +41,16 @@ For Xcode projects, the toolkit:
4141

4242
This is much faster than building a full app scheme.
4343

44-
## Scripts
44+
## Scripts & Tools
4545

46-
| Script | Purpose |
47-
|--------|---------|
48-
| `preview` | Unified entry point - auto-detects best approach |
49-
| `preview-dynamic.sh` | Dynamic target injection for Xcode projects |
50-
| `preview-spm.sh` | Preview from SPM packages |
51-
| `preview-minimal.sh` | Build standalone Swift files |
52-
| `xcode-preview.sh` | Build full Xcode project schemes |
53-
| `capture-simulator.sh` | Screenshot current simulator |
54-
| `sim-manager.sh` | Simulator management |
46+
| Path | Purpose |
47+
|------|---------|
48+
| `scripts/preview` | Unified entry point - auto-detects best approach |
49+
| `scripts/preview-minimal.sh` | Build standalone Swift files |
50+
| `scripts/xcode-preview.sh` | Build full Xcode project schemes |
51+
| `scripts/capture-simulator.sh` | Screenshot current simulator |
52+
| `scripts/sim-manager.sh` | Simulator management |
53+
| `tools/preview-tool` | Swift CLI for dynamic preview injection, SPM preview, dependency resolution |
5554

5655
## Usage Examples
5756

@@ -87,10 +86,7 @@ This is much faster than building a full app scheme.
8786

8887
- macOS with Xcode installed
8988
- iOS Simulator
90-
- Ruby with `xcodeproj` gem:
91-
```bash
92-
gem install xcodeproj --user-install
93-
```
89+
- Swift toolchain (preview-tool auto-builds on first run)
9490

9591
## Workflow for Claude
9692

README.md

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,10 @@ A CLI toolset for building and capturing SwiftUI previews programmatically. Desi
1414

1515
- macOS with Xcode installed
1616
- iOS Simulator
17-
- Ruby (comes with macOS) with `xcodeproj` gem
17+
- Swift toolchain (preview-tool auto-builds on first run)
1818

1919
## Installation
2020

21-
### Prerequisites
22-
23-
```bash
24-
gem install xcodeproj --user-install
25-
```
26-
2721
### Claude Code: Plugin Marketplace (Recommended)
2822

2923
Install via the Claude Code plugin marketplace:
@@ -110,16 +104,6 @@ The `preview` script auto-detects the best approach:
110104
111105
### Direct Scripts
112106

113-
**For Xcode projects with `#Preview` blocks:**
114-
```bash
115-
./scripts/preview-dynamic.sh MyView.swift --project MyApp.xcodeproj
116-
```
117-
118-
**For SPM packages:**
119-
```bash
120-
./scripts/preview-spm.sh MyView.swift --package /path/to/Package.swift
121-
```
122-
123107
**For standalone Swift files (system imports only):**
124108
```bash
125109
./scripts/preview-minimal.sh MyView.swift
@@ -201,16 +185,15 @@ Ask Codex to preview a SwiftUI file. The skill instructs Codex to:
201185
2. Capture `/tmp/preview*.png`
202186
3. Read the screenshot and provide UI analysis
203187

204-
## Scripts
188+
## Scripts & Tools
205189

206-
| Script | Purpose |
207-
|--------|---------|
208-
| `preview` | Unified entry point |
209-
| `preview-dynamic.sh` | Xcode project preview injection |
210-
| `preview-spm.sh` | SPM package preview |
211-
| `preview-minimal.sh` | Standalone files |
212-
| `capture-simulator.sh` | Screenshot capture |
213-
| `sim-manager.sh` | Simulator utilities |
190+
| Path | Purpose |
191+
|------|---------|
192+
| `scripts/preview` | Unified entry point (auto-detects best approach) |
193+
| `scripts/preview-minimal.sh` | Standalone files (system imports only) |
194+
| `scripts/capture-simulator.sh` | Screenshot capture |
195+
| `scripts/sim-manager.sh` | Simulator utilities |
196+
| `tools/preview-tool` | Swift CLI for dynamic preview injection + SPM preview |
214197

215198
## Project Structure
216199

@@ -238,11 +221,14 @@ XcodePreviews/
238221
│ └── SKILL.md # Claude Code plugin skill
239222
├── scripts/
240223
│ ├── preview # Unified entry point
241-
│ ├── preview-dynamic.sh # Xcode project injection
242-
│ ├── preview-spm.sh # SPM package preview
243224
│ ├── preview-minimal.sh # Standalone files
244225
│ ├── capture-simulator.sh
245226
│ └── sim-manager.sh
227+
├── tools/
228+
│ └── preview-tool/ # Swift CLI (auto-built on first run)
229+
│ ├── Package.swift
230+
│ ├── Sources/
231+
│ └── Tests/
246232
└── templates/
247233
```
248234

0 commit comments

Comments
 (0)