devrel-toolkit 0.1.2 → 0.1.3
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BrowserFrame.d.ts","sourceRoot":"","sources":["../../../src/compositor/components/BrowserFrame.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAW1B,UAAU,iBAAiB;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE;QACL,IAAI,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;QAC9D,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,
|
|
1
|
+
{"version":3,"file":"BrowserFrame.d.ts","sourceRoot":"","sources":["../../../src/compositor/components/BrowserFrame.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAW1B,UAAU,iBAAiB;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE;QACL,IAAI,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;QAC9D,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAoJpD,CAAC"}
|
|
@@ -3,8 +3,8 @@ import { AbsoluteFill, Img, staticFile, interpolate, spring, useCurrentFrame, us
|
|
|
3
3
|
export const BrowserFrame = ({ screenshotPath, url, zoom, durationFrames, }) => {
|
|
4
4
|
const frame = useCurrentFrame();
|
|
5
5
|
const { fps, width, height } = useVideoConfig();
|
|
6
|
-
// Chrome bar height
|
|
7
|
-
const chromeHeight =
|
|
6
|
+
// Chrome bar height (0 when chrome is hidden)
|
|
7
|
+
const chromeHeight = 0;
|
|
8
8
|
// Calculate zoom animation
|
|
9
9
|
let scale = 1;
|
|
10
10
|
let translateX = 0;
|
|
@@ -44,7 +44,9 @@ export const BrowserFrame = ({ screenshotPath, url, zoom, durationFrames, }) =>
|
|
|
44
44
|
extrapolateRight: "clamp",
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
// Skip browser chrome — it wastes vertical space and causes black bars
|
|
48
|
+
const showChrome = false;
|
|
49
|
+
return (_jsxs(AbsoluteFill, { style: { backgroundColor: "#0a0a0a" }, children: [showChrome && _jsxs("div", { style: {
|
|
48
50
|
height: chromeHeight,
|
|
49
51
|
backgroundColor: "#e8e8e8",
|
|
50
52
|
display: "flex",
|
|
@@ -91,7 +93,7 @@ export const BrowserFrame = ({ screenshotPath, url, zoom, durationFrames, }) =>
|
|
|
91
93
|
}, children: _jsx(Img, { src: staticFile(screenshotPath), style: {
|
|
92
94
|
width: "100%",
|
|
93
95
|
height: "100%",
|
|
94
|
-
objectFit: "
|
|
96
|
+
objectFit: "contain",
|
|
95
97
|
} }) }) })] }));
|
|
96
98
|
};
|
|
97
99
|
//# sourceMappingURL=BrowserFrame.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BrowserFrame.js","sourceRoot":"","sources":["../../../src/compositor/components/BrowserFrame.tsx"],"names":[],"mappings":";AACA,OAAO,EACL,YAAY,EACZ,GAAG,EACH,UAAU,EACV,WAAW,EACX,MAAM,EACN,eAAe,EACf,cAAc,GACf,MAAM,UAAU,CAAC;AAYlB,MAAM,CAAC,MAAM,YAAY,GAAgC,CAAC,EACxD,cAAc,EACd,GAAG,EACH,IAAI,EACJ,cAAc,GACf,EAAE,EAAE;IACH,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;IAChC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,cAAc,EAAE,CAAC;IAEhD,
|
|
1
|
+
{"version":3,"file":"BrowserFrame.js","sourceRoot":"","sources":["../../../src/compositor/components/BrowserFrame.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -40,7 +40,19 @@ npx devrel-toolkit doctor
|
|
|
40
40
|
|
|
41
41
|
If any checks fail, run `npx devrel-toolkit setup`.
|
|
42
42
|
|
|
43
|
-
**Browser automation**: You MUST use `browser-use` CLI for all browser interactions
|
|
43
|
+
**Browser automation**: You MUST use `browser-use` CLI for all browser interactions. It is a standalone Python CLI binary, NOT an npm package. Try running `browser-use` first. If not found in PATH, use the full path `~/.browser-use-env/bin/browser-use`. Never use `npx browser-use`.
|
|
44
|
+
|
|
45
|
+
If browser-use is not installed at all, install it:
|
|
46
|
+
```bash
|
|
47
|
+
curl -fsSL https://browser-use.com/cli/install.sh | bash
|
|
48
|
+
```
|
|
49
|
+
Then use `~/.browser-use-env/bin/browser-use` for all commands.
|
|
50
|
+
|
|
51
|
+
If browser-use fails with timeout errors on `open` (even after 120s), the machine likely has an enterprise policy blocking Chrome DevTools remote debugging (CDP). Browser-use depends on CDP and cannot work in this environment. Use Playwright instead — it ships its own Chromium binary that bypasses enterprise policies:
|
|
52
|
+
```bash
|
|
53
|
+
npx playwright install chromium # one-time setup
|
|
54
|
+
```
|
|
55
|
+
Then use Playwright's Node.js API — write a small `.mjs` script for each capture task using `chromium.launch()`, `page.goto()`, `page.screenshot()`, `page.locator().boundingBox()`.
|
|
44
56
|
|
|
45
57
|
**D-ID API key**: Check if `DID_API_KEY` is set (in `.env.local` or environment). If it is NOT set and the user did NOT pass `--no-avatar`:
|
|
46
58
|
- Ask the user: "I need a D-ID API key to generate the avatar presenter. You can get one at https://studio.d-id.com. Would you like to provide your key, or should I skip the avatar and create a video without a presenter?"
|
|
@@ -103,28 +115,38 @@ mkdir -p ./demo-work-$(date +%s)/screenshots
|
|
|
103
115
|
mkdir -p ./demo-work-$(date +%s)/avatars
|
|
104
116
|
```
|
|
105
117
|
|
|
106
|
-
|
|
118
|
+
Use `~/.browser-use-env/bin/browser-use` (full path) if `browser-use` is not in PATH.
|
|
107
119
|
|
|
120
|
+
**First command — open the URL** (use a long timeout, the first browser launch is slow):
|
|
108
121
|
```bash
|
|
109
|
-
browser-use
|
|
122
|
+
~/.browser-use-env/bin/browser-use open <url>
|
|
123
|
+
```
|
|
110
124
|
|
|
111
|
-
|
|
112
|
-
|
|
125
|
+
Do NOT use `--headed` or `--profile` flags — headed mode times out in Claude Code's bash, and `--profile` causes macOS permission errors on Chrome cookies. Use plain headless mode. The built-in Chromium works fine for screenshots.
|
|
126
|
+
|
|
127
|
+
After the first `open`, subsequent commands are fast (~50ms) because the daemon stays alive:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
browser-use state # See available elements with indices
|
|
113
131
|
browser-use click <index> # Click elements
|
|
114
132
|
browser-use input <index> "text" # Fill forms
|
|
115
133
|
browser-use scroll down # Scroll
|
|
116
134
|
browser-use wait text "loaded" # Wait for content
|
|
117
135
|
|
|
118
|
-
# Take screenshot —
|
|
136
|
+
# Take screenshot — viewport only, NOT full page
|
|
119
137
|
browser-use screenshot ./demo-work/screenshots/scene-<id>.png
|
|
120
|
-
browser-use screenshot --full ./demo-work/screenshots/scene-<id>.png # full page
|
|
121
138
|
|
|
122
|
-
# Get bounding boxes for
|
|
139
|
+
# Get bounding boxes for zoom targets
|
|
123
140
|
browser-use get bbox <index> # Returns { x, y, width, height }
|
|
141
|
+
|
|
142
|
+
# When done
|
|
143
|
+
browser-use close
|
|
124
144
|
```
|
|
125
145
|
|
|
126
146
|
Save all screenshots and record bounding box data for zoom targets.
|
|
127
147
|
|
|
148
|
+
**Screenshots must show content, not empty space.** Before taking each screenshot, scroll to the section you want to capture so it fills the viewport. If the page has a hero at the top, take the screenshot there. If you need to show a section further down, `browser-use scroll down` first until that section is visible, then screenshot. Never capture black/empty space above or below the content — the video frame should be filled with the actual UI.
|
|
149
|
+
|
|
128
150
|
**CRITICAL — Bounding boxes**: You MUST use `browser-use get bbox <index>` to get exact bounding box coordinates for every zoom target. NEVER guess or estimate bbox values. Wrong coordinates cause the zoom to frame empty space or cut off content. Run `browser-use state` to find the element index, then `browser-use get bbox <index>` to get the precise `{ x, y, width, height }`.
|
|
129
151
|
|
|
130
152
|
**Zoom framing**: The zoom bbox should include some padding around the target element (add ~50px on each side) so content isn't clipped at the edges. The zoom `level` controls magnification — use 1.3-1.5 for sections, 1.8-2.0 for small elements.
|
|
@@ -146,31 +168,60 @@ For scenes that need animated explainers, flow diagrams, or motion graphics inst
|
|
|
146
168
|
cd custom-animations && npm install
|
|
147
169
|
```
|
|
148
170
|
|
|
149
|
-
2. Write a React component for each animation scene.
|
|
171
|
+
2. Write a React component for each animation scene. **Animations MUST have motion** — elements appearing one by one, arrows drawing in, labels fading in sequentially. A static diagram rendered as a video is NOT an animation. Every element should animate in using `spring()` or `interpolate()` with staggered delays.
|
|
172
|
+
|
|
173
|
+
Example — a flow diagram where each step appears one after another:
|
|
150
174
|
```tsx
|
|
151
|
-
|
|
152
|
-
import { AbsoluteFill, useCurrentFrame, useVideoConfig, interpolate, spring } from "remotion";
|
|
175
|
+
import { AbsoluteFill, useCurrentFrame, useVideoConfig, spring, interpolate } from "remotion";
|
|
153
176
|
|
|
154
177
|
export const FlowDiagram: React.FC = () => {
|
|
155
178
|
const frame = useCurrentFrame();
|
|
156
179
|
const { fps } = useVideoConfig();
|
|
157
|
-
|
|
158
|
-
//
|
|
180
|
+
|
|
181
|
+
// Each step appears sequentially with spring animation
|
|
182
|
+
const step1 = spring({ frame, fps, config: { damping: 200 } });
|
|
183
|
+
const step2 = spring({ frame: frame - 20, fps, config: { damping: 200 } });
|
|
184
|
+
const step3 = spring({ frame: frame - 40, fps, config: { damping: 200 } });
|
|
185
|
+
const step4 = spring({ frame: frame - 60, fps, config: { damping: 200 } });
|
|
186
|
+
|
|
187
|
+
// Arrow draws in between steps
|
|
188
|
+
const arrow1Width = interpolate(step2, [0, 1], [0, 300]);
|
|
189
|
+
const arrow2Width = interpolate(step3, [0, 1], [0, 300]);
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<AbsoluteFill style={{ backgroundColor: "#0a0a0a", padding: 80 }}>
|
|
193
|
+
{/* Step 1 fades/scales in */}
|
|
194
|
+
<div style={{ opacity: step1, transform: `scale(${step1})` }}>
|
|
195
|
+
Client → GET /api/random
|
|
196
|
+
</div>
|
|
197
|
+
{/* Arrow draws from left to right */}
|
|
198
|
+
<div style={{ width: arrow1Width, height: 2, backgroundColor: "#4A90D9" }} />
|
|
199
|
+
{/* Step 2 appears after arrow */}
|
|
200
|
+
<div style={{ opacity: step2, transform: `translateY(${(1-step2)*20}px)` }}>
|
|
201
|
+
Server → 402 + config
|
|
202
|
+
</div>
|
|
203
|
+
{/* etc. — each step has a staggered delay */}
|
|
204
|
+
</AbsoluteFill>
|
|
205
|
+
);
|
|
159
206
|
};
|
|
160
207
|
```
|
|
161
208
|
|
|
209
|
+
The key pattern: `spring({ frame: frame - DELAY, fps })` where DELAY increases for each element. This creates the sequential reveal effect.
|
|
210
|
+
|
|
162
211
|
3. Register it as a composition in `src/Root.tsx` and render to MP4:
|
|
163
212
|
```bash
|
|
164
213
|
npx remotion render FlowDiagram ./demo-work/screenshots/scene-flow-diagram.mp4
|
|
165
214
|
```
|
|
166
215
|
|
|
167
|
-
4. Use the rendered MP4 as the `screenshotPath` for that scene in render-props.json.
|
|
216
|
+
4. Use the rendered MP4 as the `screenshotPath` for that scene in render-props.json.
|
|
168
217
|
|
|
169
|
-
**
|
|
170
|
-
-
|
|
171
|
-
-
|
|
218
|
+
**Animation rules**:
|
|
219
|
+
- **Every element must animate in** — nothing should just "be there" from frame 1
|
|
220
|
+
- Use staggered delays (20-30 frames apart) so elements appear one by one
|
|
221
|
+
- Arrows should draw in (width/height animating from 0 to full)
|
|
222
|
+
- Labels should fade + slide in (opacity 0→1 + translateY)
|
|
223
|
+
- Match the resolution (1920x1080) and dark background (#0a0a0a)
|
|
172
224
|
- Use the app's color scheme for visual consistency
|
|
173
|
-
- Prefer simple, clean motion graphics over complex 3D or particle effects
|
|
174
225
|
|
|
175
226
|
### Step 4: Generate Avatar Video
|
|
176
227
|
|
|
@@ -178,29 +229,71 @@ Skip this step if `--no-avatar` or `--preview` was specified.
|
|
|
178
229
|
|
|
179
230
|
Generate **one single continuous avatar video** with all the narration combined. Do NOT generate separate clips per scene — separate clips create jarring cuts between sentences. One continuous video gives natural speech flow.
|
|
180
231
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
```
|
|
232
|
+
**D-ID has two APIs — try Expressives first, fall back to Talks:**
|
|
233
|
+
|
|
234
|
+
**Option A: Expressives API (V4 avatars — via toolkit) — preferred**
|
|
185
235
|
|
|
186
|
-
|
|
236
|
+
1. Use this avatar by default: `public_oliver_sport_elegant@avt_VVIQYg` (Oliver — professional male presenter). Concatenate all narrations into one script:
|
|
187
237
|
```json
|
|
188
238
|
[
|
|
189
239
|
{
|
|
190
240
|
"id": "full-narration",
|
|
191
|
-
"narration": "Welcome to our platform.
|
|
192
|
-
"avatarId": "
|
|
241
|
+
"narration": "Welcome to our platform. ... And that's it — you're ready to go.",
|
|
242
|
+
"avatarId": "public_oliver_sport_elegant@avt_VVIQYg"
|
|
193
243
|
}
|
|
194
244
|
]
|
|
195
245
|
```
|
|
196
|
-
|
|
197
|
-
3. Generate:
|
|
246
|
+
2. Generate:
|
|
198
247
|
```bash
|
|
199
248
|
npx devrel-toolkit d-id generate \
|
|
200
249
|
--script ./demo-work/avatar-script.json \
|
|
201
250
|
--output ./demo-work/avatars/ \
|
|
202
|
-
--avatar "
|
|
251
|
+
--avatar "public_oliver_sport_elegant@avt_VVIQYg"
|
|
203
252
|
```
|
|
253
|
+
3. If that avatar fails, list available ones with `npx devrel-toolkit d-id avatars` and pick another.
|
|
254
|
+
|
|
255
|
+
**Option B: Talks API (works on all plans including free — via curl)**
|
|
256
|
+
|
|
257
|
+
If Expressives fails (subscription error, no avatars available), use the Talks API directly. It takes a source photo URL instead of an avatar ID:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
DID_KEY=$(grep DID_API_KEY .env.local | cut -d= -f2)
|
|
261
|
+
|
|
262
|
+
# Concatenate all narration into one string
|
|
263
|
+
NARRATION="Welcome to our platform. ... And that's it."
|
|
264
|
+
|
|
265
|
+
# Create the talk
|
|
266
|
+
RESPONSE=$(curl -s -X POST "https://api.d-id.com/talks" \
|
|
267
|
+
-H "Authorization: Basic $DID_KEY" \
|
|
268
|
+
-H "Content-Type: application/json" \
|
|
269
|
+
-d "{
|
|
270
|
+
\"source_url\": \"https://d-id-public-bucket.s3.us-west-2.amazonaws.com/alice.jpg\",
|
|
271
|
+
\"script\": {
|
|
272
|
+
\"type\": \"text\",
|
|
273
|
+
\"input\": \"$NARRATION\",
|
|
274
|
+
\"provider\": { \"type\": \"microsoft\", \"voice_id\": \"en-US-JennyNeural\" }
|
|
275
|
+
}
|
|
276
|
+
}")
|
|
277
|
+
|
|
278
|
+
TALK_ID=$(echo "$RESPONSE" | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])")
|
|
279
|
+
|
|
280
|
+
# Poll until done, then download
|
|
281
|
+
for i in $(seq 1 60); do
|
|
282
|
+
RESULT=$(curl -s "https://api.d-id.com/talks/$TALK_ID" -H "Authorization: Basic $DID_KEY")
|
|
283
|
+
STATUS=$(echo "$RESULT" | python3 -c "import json,sys; print(json.load(sys.stdin).get('status','unknown'))")
|
|
284
|
+
if [ "$STATUS" = "done" ]; then
|
|
285
|
+
RESULT_URL=$(echo "$RESULT" | python3 -c "import json,sys; print(json.load(sys.stdin).get('result_url',''))")
|
|
286
|
+
curl -sL "$RESULT_URL" -o ./demo-work/avatars/full-narration.mp4
|
|
287
|
+
echo "Downloaded avatar video!"
|
|
288
|
+
break
|
|
289
|
+
fi
|
|
290
|
+
if [ "$STATUS" = "error" ] || [ "$STATUS" = "rejected" ]; then
|
|
291
|
+
echo "FAILED: $RESULT"
|
|
292
|
+
break
|
|
293
|
+
fi
|
|
294
|
+
sleep 5
|
|
295
|
+
done
|
|
296
|
+
```
|
|
204
297
|
|
|
205
298
|
4. This takes 1–5 minutes. The output includes a `manifest.json` with the clip path and total duration. Use this single avatar clip as `avatarClipPath` for the **first scene only** — the PiP will play continuously across all scenes.
|
|
206
299
|
|
|
@@ -282,9 +375,10 @@ Wait for the render to complete. This may take a few minutes.
|
|
|
282
375
|
## Error Handling
|
|
283
376
|
|
|
284
377
|
**browser-use not found:**
|
|
285
|
-
-
|
|
378
|
+
- Install it: `curl -fsSL https://browser-use.com/cli/install.sh | bash`
|
|
379
|
+
- Or run `npx devrel-toolkit setup` which handles the installation
|
|
286
380
|
- Do NOT use `pip install browser-use` — that installs the Python SDK, not the CLI
|
|
287
|
-
-
|
|
381
|
+
- Do NOT fall back to Playwright — install browser-use
|
|
288
382
|
|
|
289
383
|
**Browser navigation fails:**
|
|
290
384
|
- Retry the action once
|