clipwise 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ko.md CHANGED
@@ -5,7 +5,7 @@
5
5
  YAML 시나리오를 작성하면 시네마틱 데모 영상(MP4/GIF)을 자동으로 만들어주는 스크린 레코더. Playwright CDP 기반.
6
6
 
7
7
  <p align="center">
8
- <img src="https://kwakseongjae.github.io/clipwise/demo.gif" alt="Clipwise 데모" width="100%" />
8
+ <img src="./docs/demo.gif" alt="Clipwise 데모" width="100%" />
9
9
  </p>
10
10
 
11
11
  > *`npx clipwise demo` 한 줄로 생성된 영상입니다 — YAML 파일 1개, 239줄.*
@@ -337,6 +337,37 @@ VideoToolbox는 런타임에 자동 감지되며, 사용 불가 시 `libx264`로
337
337
 
338
338
  [PROMPTS.md](./PROMPTS.md)에 바로 사용할 수 있는 AI 프롬프트 템플릿이 있습니다. ChatGPT나 Claude에 복붙하고 내 사이트 URL만 넣으면 YAML 시나리오를 생성해줍니다.
339
339
 
340
+ ## Claude Code 스킬
341
+
342
+ Clipwise에는 [Claude Code](https://claude.com/claude-code) 스킬이 내장되어 있습니다. 설치 후 Claude Code에서 `/clipwise`를 입력하면 자연어로 YAML 시나리오 생성, 검증, 녹화까지 한 번에 할 수 있습니다.
343
+
344
+ ### 스킬 설치
345
+
346
+ ```bash
347
+ npx clipwise install-skill
348
+ ```
349
+
350
+ `.claude/skills/clipwise.md`에 스킬 파일이 복사됩니다 (`.claude/` 디렉토리가 있으면 프로젝트 레벨, 없으면 `~/.claude/skills/`에 설치).
351
+
352
+ ### 사용법
353
+
354
+ Claude Code 세션에서:
355
+
356
+ ```
357
+ /clipwise
358
+ > http://localhost:3000 대시보드 데모 녹화해줘
359
+ — 로그인 버튼 클릭, 이메일/비밀번호 입력, 분석 페이지 이동
360
+ ```
361
+
362
+ Claude가 자동으로:
363
+ 1. `clipwise.yaml` 시나리오 생성
364
+ 2. `npx clipwise validate`로 검증
365
+ 3. `npx clipwise record`로 MP4 녹화
366
+
367
+ ### 업데이트
368
+
369
+ clipwise 업그레이드 후 `npx clipwise install-skill`을 다시 실행하면 최신 스킬로 업데이트됩니다.
370
+
340
371
  ## GitHub Pages
341
372
 
342
373
  `docs/` 폴더에 문서 사이트와 라이브 데모 대시보드가 포함되어 있습니다:
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  Scriptable cinematic screen recorder for product demos — YAML in, polished MP4 out. Powered by Playwright CDP.
6
6
 
7
7
  <p align="center">
8
- <img src="https://kwakseongjae.github.io/clipwise/demo.gif" alt="Clipwise demo" width="100%" />
8
+ <img src="./docs/demo.gif" alt="Clipwise demo" width="100%" />
9
9
  </p>
10
10
 
11
11
  > *Generated with `npx clipwise demo` — 1 YAML file, 239 lines, one command.*
@@ -421,6 +421,37 @@ npx clipwise record my-scenario.yaml -f mp4 -o ./output
421
421
 
422
422
  See [PROMPTS.md](./PROMPTS.md) for a ready-to-use prompt template. Copy-paste it to ChatGPT or Claude with your site URL, and get a working YAML scenario back.
423
423
 
424
+ ## Claude Code Skill
425
+
426
+ Clipwise ships a built-in [Claude Code](https://claude.com/claude-code) skill. Once installed, type `/clipwise` in Claude Code to generate YAML scenarios, validate, and record — all through natural language.
427
+
428
+ ### Install the skill
429
+
430
+ ```bash
431
+ npx clipwise install-skill
432
+ ```
433
+
434
+ This copies the skill file to `.claude/skills/clipwise.md` (project-level if `.claude/` exists, otherwise `~/.claude/skills/`).
435
+
436
+ ### Usage
437
+
438
+ In any Claude Code session:
439
+
440
+ ```
441
+ /clipwise
442
+ > Record a demo of my dashboard at http://localhost:3000
443
+ — click the login button, type credentials, navigate to analytics
444
+ ```
445
+
446
+ Claude will:
447
+ 1. Generate a complete `clipwise.yaml` scenario
448
+ 2. Run `npx clipwise validate` to check for errors
449
+ 3. Run `npx clipwise record` to produce the MP4
450
+
451
+ ### Update
452
+
453
+ Re-run `npx clipwise install-skill` after upgrading clipwise to get the latest skill.
454
+
424
455
  ## GitHub Pages
425
456
 
426
457
  Clipwise includes a documentation site and a live demo dashboard in the `docs/` folder. To host it:
package/dist/cli/index.js CHANGED
@@ -2881,17 +2881,17 @@ var StreamingSession = class extends EventEmitter {
2881
2881
  };
2882
2882
 
2883
2883
  // src/cli/index.ts
2884
- import { writeFile as writeFile2, mkdir as mkdir2, access } from "fs/promises";
2884
+ import { writeFile as writeFile2, mkdir as mkdir2, access, copyFile, readFile as readFile3 } from "fs/promises";
2885
2885
  import { join as join2, resolve, dirname } from "path";
2886
- import { pathToFileURL } from "url";
2886
+ import { pathToFileURL, fileURLToPath as fileURLToPath2 } from "url";
2887
+ import { homedir } from "os";
2887
2888
  var program = new Command();
2888
2889
  program.name("clipwise").description(
2889
2890
  "Playwright-based cinematic screen recorder for product demos"
2890
2891
  ).version("0.1.0");
2891
2892
  program.command("record").description("Record a demo from a YAML scenario file").argument("<scenario>", "Path to YAML scenario file").option("-o, --output <dir>", "Output directory", "./output").option(
2892
2893
  "-f, --format <format>",
2893
- "Output format (gif|mp4|png-sequence)",
2894
- "gif"
2894
+ "Output format (gif|mp4|png-sequence)"
2895
2895
  ).option("--no-effects", "Disable all effects").action(async (scenarioPath, options) => {
2896
2896
  const spinner = ora();
2897
2897
  try {
@@ -3359,4 +3359,52 @@ Error: ${message}`));
3359
3359
  process.exit(1);
3360
3360
  }
3361
3361
  });
3362
+ program.command("install-skill").description("Install the Clipwise skill for Claude Code").action(async () => {
3363
+ try {
3364
+ const __dirname = dirname(fileURLToPath2(import.meta.url));
3365
+ const skillSource = resolve(__dirname, "..", "..", "skills", "clipwise.md");
3366
+ try {
3367
+ await access(skillSource);
3368
+ } catch {
3369
+ console.error(chalk.red("Error: Skill file not found in clipwise package."));
3370
+ console.error(chalk.yellow("This may happen if you're running from source. Try: npm rebuild clipwise"));
3371
+ process.exit(1);
3372
+ }
3373
+ const projectSkillDir = resolve(".claude", "skills");
3374
+ const globalSkillDir = join2(homedir(), ".claude", "skills");
3375
+ let targetDir;
3376
+ try {
3377
+ await access(resolve(".claude"));
3378
+ targetDir = projectSkillDir;
3379
+ } catch {
3380
+ targetDir = globalSkillDir;
3381
+ }
3382
+ await mkdir2(targetDir, { recursive: true });
3383
+ const targetPath = join2(targetDir, "clipwise.md");
3384
+ try {
3385
+ const existing = await readFile3(targetPath, "utf-8");
3386
+ const incoming = await readFile3(skillSource, "utf-8");
3387
+ if (existing === incoming) {
3388
+ console.log(chalk.green("Clipwise skill is already up to date."));
3389
+ console.log(` Location: ${chalk.bold(targetPath)}`);
3390
+ console.log(`
3391
+ Use ${chalk.bold("/clipwise")} in Claude Code to get started.`);
3392
+ return;
3393
+ }
3394
+ } catch {
3395
+ }
3396
+ await copyFile(skillSource, targetPath);
3397
+ console.log(chalk.green("Clipwise skill installed successfully!"));
3398
+ console.log(` Location: ${chalk.bold(targetPath)}`);
3399
+ console.log(`
3400
+ Usage in Claude Code:`);
3401
+ console.log(` ${chalk.bold("/clipwise")} \u2014 Generate YAML scenarios, validate, and record demos`);
3402
+ console.log(`
3403
+ To update the skill later, run this command again.`);
3404
+ } catch (error) {
3405
+ const message = error instanceof Error ? error.message : String(error);
3406
+ console.error(chalk.red(`Failed to install skill: ${message}`));
3407
+ process.exit(1);
3408
+ }
3409
+ });
3362
3410
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clipwise",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Scriptable cinematic screen recorder for product demos — YAML in, polished MP4 out",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -10,6 +10,7 @@
10
10
  },
11
11
  "files": [
12
12
  "dist",
13
+ "skills",
13
14
  "README.md",
14
15
  "LICENSE"
15
16
  ],
@@ -0,0 +1,373 @@
1
+ # Clipwise — Cinematic Screen Recorder
2
+
3
+ You are an expert at creating Clipwise YAML scenarios. Clipwise is a Playwright + CDP-based scriptable screen recorder that turns YAML scenarios into polished MP4/GIF demo videos with cinematic effects (zoom, cursor trail, device frame, keystroke HUD, etc.).
4
+
5
+ TRIGGER: when the user wants to record a demo video, create a screen recording scenario, generate a product demo, or mentions "clipwise".
6
+
7
+ ## Setup Check
8
+
9
+ Before creating a scenario, verify clipwise is installed:
10
+
11
+ ```bash
12
+ npx clipwise --version
13
+ ```
14
+
15
+ If not installed:
16
+ ```bash
17
+ npm install -D clipwise
18
+ ```
19
+
20
+ ffmpeg is required for MP4 output:
21
+ ```bash
22
+ # macOS
23
+ brew install ffmpeg
24
+ # Ubuntu
25
+ sudo apt install ffmpeg
26
+ ```
27
+
28
+ ## YAML Schema Reference
29
+
30
+ ### Top-Level Structure
31
+
32
+ ```yaml
33
+ name: string # Scenario name (required)
34
+ description: string # Optional description
35
+
36
+ viewport:
37
+ width: 1280 # Browser width (100-3840, default: 1280)
38
+ height: 800 # Browser height (100-3840, default: 800)
39
+
40
+ effects: # All optional, sensible defaults
41
+ zoom: ...
42
+ cursor: ...
43
+ background: ...
44
+ deviceFrame: ...
45
+ keystroke: ...
46
+ watermark: ...
47
+ speedRamp: ...
48
+
49
+ output:
50
+ format: mp4 # mp4 | gif | png-sequence
51
+ width: 1280 # Output width
52
+ height: 800 # Output height
53
+ fps: 30 # 1-60
54
+ preset: balanced # social | balanced | archive
55
+ outputDir: "./output"
56
+ filename: "my-recording"
57
+
58
+ steps: [] # Array of steps (min 1, first must have navigate)
59
+ ```
60
+
61
+ ### Step Structure
62
+
63
+ ```yaml
64
+ - name: "Step name" # Optional label
65
+ captureDelay: 50 # ms to wait after actions before capturing (50-100 for snappy)
66
+ holdDuration: 700 # ms to hold on result (500-800 for snappy)
67
+ transition: none # none | fade
68
+ actions: [] # Array of actions
69
+ ```
70
+
71
+ ### Actions (12 types)
72
+
73
+ #### Basic Actions
74
+
75
+ 1. **navigate** — Open a URL (MUST be the first action in step 1)
76
+ ```yaml
77
+ - action: navigate
78
+ url: "https://example.com"
79
+ waitUntil: load # load | domcontentloaded | networkidle (default)
80
+ ```
81
+
82
+ 2. **click** — Click an element
83
+ ```yaml
84
+ - action: click
85
+ selector: "#my-button"
86
+ delay: 0 # Optional click delay (ms)
87
+ timeout: 15000 # Optional element wait timeout
88
+ ```
89
+
90
+ 3. **type** — Type text character-by-character (auto-focuses the element)
91
+ ```yaml
92
+ - action: type
93
+ selector: "#email-input"
94
+ text: "user@example.com"
95
+ delay: 18 # ms per character (15-25 recommended, default: 50)
96
+ timeout: 15000 # Optional
97
+ ```
98
+
99
+ 4. **hover** — Hover over an element
100
+ ```yaml
101
+ - action: hover
102
+ selector: ".card"
103
+ timeout: 15000 # Optional
104
+ ```
105
+
106
+ 5. **scroll** — Scroll the page
107
+ ```yaml
108
+ - action: scroll
109
+ y: 400 # Vertical px (positive=down, negative=up)
110
+ x: 0 # Horizontal px
111
+ selector: ".container" # Optional: scroll within element
112
+ smooth: true # Default: true
113
+ timeout: 15000 # Optional
114
+ ```
115
+
116
+ 6. **wait** — Pause for a fixed duration
117
+ ```yaml
118
+ - action: wait
119
+ duration: 1000 # ms
120
+ ```
121
+
122
+ 7. **screenshot** — Capture marker (for png-sequence)
123
+ ```yaml
124
+ - action: screenshot
125
+ name: "result" # Optional label
126
+ fullPage: false # Default: false
127
+ ```
128
+
129
+ #### Async Wait Actions (for dynamic/API content)
130
+
131
+ 8. **waitForSelector** — Wait for element state
132
+ ```yaml
133
+ - action: waitForSelector
134
+ selector: ".result-panel"
135
+ state: visible # visible (default) | attached | hidden
136
+ timeout: 15000
137
+ ```
138
+
139
+ 9. **waitForNavigation** — Wait for page load
140
+ ```yaml
141
+ - action: waitForNavigation
142
+ waitUntil: networkidle # load | domcontentloaded | networkidle
143
+ timeout: 15000
144
+ ```
145
+
146
+ 10. **waitForURL** — Wait for URL match
147
+ ```yaml
148
+ - action: waitForURL
149
+ url: "https://example.com/dashboard"
150
+ timeout: 15000
151
+ ```
152
+
153
+ 11. **waitForFunction** — Wait for JS expression to be truthy
154
+ ```yaml
155
+ - action: waitForFunction
156
+ expression: "document.querySelector('.done') !== null"
157
+ polling: raf # raf (default) | number in ms (e.g. 500)
158
+ timeout: 30000
159
+ ```
160
+
161
+ 12. **waitForResponse** — Wait for network response (URL substring match)
162
+ ```yaml
163
+ - action: waitForResponse
164
+ url: "/api/chat/completions"
165
+ status: 200 # Optional HTTP status filter
166
+ timeout: 30000
167
+ ```
168
+
169
+ ### Effects Configuration
170
+
171
+ #### Zoom — Adaptive zoom follows cursor on clicks
172
+ ```yaml
173
+ zoom:
174
+ enabled: true
175
+ intensity: moderate # subtle(1.15x) | light(1.25x) | moderate(1.35x) | strong(1.5x) | dramatic(1.8x)
176
+ # scale: 1.35 # Or use numeric value (overridden by intensity)
177
+ duration: 500 # Zoom animation ms
178
+ easing: ease-in-out # ease-in-out | ease-in | ease-out | linear
179
+ autoZoom:
180
+ followCursor: true
181
+ transitionDuration: 300
182
+ padding: 200
183
+ ```
184
+
185
+ #### Cursor — Custom cursor with click effect, trail, highlight
186
+ ```yaml
187
+ cursor:
188
+ enabled: true
189
+ size: 20
190
+ color: "#000000"
191
+ speed: fast # fast (~72ms) | normal (~144ms) | slow (~288ms)
192
+ smoothing: true
193
+ clickEffect: true
194
+ clickColor: "rgba(59, 130, 246, 0.3)"
195
+ clickRadius: 30
196
+ trail: true
197
+ trailLength: 8
198
+ trailColor: "rgba(59, 130, 246, 0.2)"
199
+ highlight: true
200
+ highlightRadius: 40
201
+ highlightColor: "rgba(255, 215, 0, 0.18)"
202
+ ```
203
+
204
+ #### Background — Gradient/solid padding with corners and shadow
205
+ ```yaml
206
+ background:
207
+ type: gradient # gradient | solid | image
208
+ value: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
209
+ padding: 48
210
+ borderRadius: 14
211
+ shadow: true
212
+ ```
213
+
214
+ #### Device Frame — Wraps recording in a device mockup
215
+ ```yaml
216
+ deviceFrame:
217
+ enabled: true
218
+ type: browser # browser | macbook | iphone | ipad | android | none
219
+ darkMode: true
220
+ ```
221
+
222
+ | Type | Description |
223
+ |------|-------------|
224
+ | browser | macOS browser chrome with traffic lights |
225
+ | macbook | MacBook Pro frame |
226
+ | iphone | iPhone 15 Pro with Dynamic Island |
227
+ | ipad | iPad Pro with camera dot |
228
+ | android | Android with punch-hole camera |
229
+
230
+ #### Keystroke HUD — Shows typed keys on screen
231
+ ```yaml
232
+ keystroke:
233
+ enabled: true
234
+ showTyping: false # true = show regular typing; false = shortcuts only (industry default)
235
+ position: bottom-center # bottom-center | bottom-left | bottom-right
236
+ fontSize: 16
237
+ backgroundColor: "rgba(0, 0, 0, 0.75)"
238
+ textColor: "#ffffff"
239
+ padding: 8
240
+ fadeAfter: 1500
241
+ ```
242
+
243
+ #### Watermark — Text overlay at corner
244
+ ```yaml
245
+ watermark:
246
+ enabled: true
247
+ text: "My App"
248
+ position: bottom-right # top-left | top-right | bottom-left | bottom-right
249
+ opacity: 0.5
250
+ fontSize: 14
251
+ color: "#ffffff"
252
+ ```
253
+
254
+ #### Speed Ramp — Auto-adjusts speed near actions
255
+ ```yaml
256
+ speedRamp:
257
+ enabled: true
258
+ idleSpeed: 3.0 # Skip factor for idle frames (0.5-8)
259
+ actionSpeed: 0.8 # Slow factor near clicks (0.25-2)
260
+ transitionFrames: 15
261
+ ```
262
+
263
+ ### Output Presets
264
+
265
+ | Preset | Use case | Approx size (30s) |
266
+ |--------|----------|--------------------|
267
+ | social | Twitter, LinkedIn, Loom | ~2-4 MB |
268
+ | balanced | General purpose, portfolio | ~4-6 MB |
269
+ | archive | High-fidelity storage | larger |
270
+
271
+ ## Critical Rules
272
+
273
+ 1. **First step MUST contain a `navigate` action** — the browser needs a page to start
274
+ 2. **Selectors**: use CSS selectors (`#id`, `.class`, `[data-testid="..."]`). No control chars, semicolons, backticks, or backslashes
275
+ 3. **Type needs focus**: the `type` action auto-focuses, but the element must exist and be visible
276
+ 4. **Scroll before interact**: if an element is below the fold, `scroll` to it first
277
+ 5. **Prefer async waits over fixed `wait`**: use `waitForSelector`, `waitForFunction`, `waitForResponse` instead of guessing durations
278
+ 6. **Viewport = output**: if viewport and output dimensions differ, output will be scaled (a warning is shown)
279
+ 7. **Mobile scenarios**: use `viewport: {width: 390, height: 844}` with `deviceFrame.type: iphone` and `output: {width: 540, height: 1080}`
280
+
281
+ ## Timing Presets
282
+
283
+ ### Snappy demo (~30s)
284
+ - `captureDelay: 50-100`
285
+ - `holdDuration: 500-800`
286
+ - `type.delay: 15-25`
287
+
288
+ ### Cinematic demo (~60s)
289
+ - `captureDelay: 200-400`
290
+ - `holdDuration: 1500-2500`
291
+ - `type.delay: 40-60`
292
+
293
+ ## CLI Commands
294
+
295
+ ```bash
296
+ # Record from YAML scenario
297
+ npx clipwise record <scenario.yaml> -f mp4 -o ./output
298
+
299
+ # Instant demo with built-in dashboard
300
+ npx clipwise demo
301
+ npx clipwise demo --device iphone
302
+ npx clipwise demo --url https://my-app.com
303
+
304
+ # Create template YAML
305
+ npx clipwise init
306
+
307
+ # Validate scenario without recording
308
+ npx clipwise validate <scenario.yaml>
309
+ ```
310
+
311
+ ## Workflow
312
+
313
+ 1. Ask the user for: target URL, what actions to demo, and preferred style (snappy/cinematic)
314
+ 2. Generate a complete `clipwise.yaml` scenario
315
+ 3. Run `npx clipwise validate clipwise.yaml` to check for errors
316
+ 4. If valid, run `npx clipwise record clipwise.yaml -f mp4 -o ./output`
317
+ 5. If the user has specific selectors, use them. Otherwise suggest inspecting the page first
318
+
319
+ ## Selector Discovery
320
+
321
+ If the user doesn't know selectors, help them find them:
322
+ ```bash
323
+ # Open the target URL in a browser and inspect elements
324
+ npx playwright open <url>
325
+ ```
326
+
327
+ Or read the page HTML to find appropriate selectors:
328
+ ```bash
329
+ curl -s <url> | head -200
330
+ ```
331
+
332
+ ## Example: Minimal Scenario
333
+
334
+ ```yaml
335
+ name: "My App Demo"
336
+ viewport:
337
+ width: 1280
338
+ height: 800
339
+
340
+ effects:
341
+ deviceFrame:
342
+ enabled: true
343
+ type: browser
344
+ cursor:
345
+ enabled: true
346
+ clickEffect: true
347
+ highlight: true
348
+ background:
349
+ padding: 48
350
+ borderRadius: 14
351
+ shadow: true
352
+
353
+ output:
354
+ format: mp4
355
+ fps: 30
356
+ preset: balanced
357
+
358
+ steps:
359
+ - name: "Open app"
360
+ captureDelay: 100
361
+ holdDuration: 1000
362
+ actions:
363
+ - action: navigate
364
+ url: "http://localhost:3000"
365
+ waitUntil: load
366
+
367
+ - name: "Click CTA"
368
+ captureDelay: 50
369
+ holdDuration: 800
370
+ actions:
371
+ - action: click
372
+ selector: "#cta-button"
373
+ ```