@biggora/claude-plugins 1.1.1 → 1.2.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.
Files changed (104) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/README.md +24 -17
  3. package/package.json +1 -1
  4. package/registry/registry.json +319 -244
  5. package/specs/coding.md +24 -0
  6. package/specs/pod.md +2 -0
  7. package/src/skills/captcha/README.md +221 -0
  8. package/src/skills/captcha/SKILL.md +355 -0
  9. package/src/skills/captcha/references/captcha-types.md +254 -0
  10. package/src/skills/captcha/references/services.md +172 -0
  11. package/src/skills/captcha/references/stealth.md +238 -0
  12. package/src/skills/captcha/scripts/solve_captcha.py +323 -0
  13. package/src/skills/captcha/scripts/solve_image_grid.py +350 -0
  14. package/src/skills/codex-cli/SKILL.md +21 -11
  15. package/src/skills/gemini-cli/SKILL.md +27 -13
  16. package/src/skills/gemini-cli/references/commands.md +21 -14
  17. package/src/skills/gemini-cli/references/configuration.md +23 -18
  18. package/src/skills/gemini-cli/references/headless-and-scripting.md +7 -17
  19. package/src/skills/gemini-cli/references/mcp-and-extensions.md +12 -6
  20. package/src/skills/google-merchant-api/SKILL.md +581 -0
  21. package/src/skills/google-merchant-api/references/accounts.md +247 -0
  22. package/src/skills/google-merchant-api/references/content-api-legacy.md +216 -0
  23. package/src/skills/google-merchant-api/references/datasources.md +233 -0
  24. package/src/skills/google-merchant-api/references/inventories.md +201 -0
  25. package/src/skills/google-merchant-api/references/migration.md +267 -0
  26. package/src/skills/google-merchant-api/references/products.md +316 -0
  27. package/src/skills/google-merchant-api/references/promotions.md +201 -0
  28. package/src/skills/google-merchant-api/references/reports.md +240 -0
  29. package/src/skills/lv-aggregators-api/SKILL.md +113 -0
  30. package/src/skills/lv-aggregators-api/references/integration-guide.md +368 -0
  31. package/src/skills/lv-aggregators-api/references/kurpirkt.md +103 -0
  32. package/src/skills/lv-aggregators-api/references/salidzini.md +122 -0
  33. package/src/skills/notebook-lm/SKILL.md +1 -1
  34. package/src/skills/screen-recording/SKILL.md +243 -213
  35. package/src/skills/screen-recording/references/design-patterns.md +4 -2
  36. package/src/skills/screen-recording/references/ffmpeg-recording.md +473 -0
  37. package/src/skills/screen-recording/references/{approach1-programmatic.md → programmatic-generation.md} +45 -22
  38. package/src/skills/screen-recording/references/python-fallback.md +222 -0
  39. package/src/skills/tailwindcss-best-practices/SKILL.md +180 -0
  40. package/src/skills/tailwindcss-best-practices/references/best-practices-utility-patterns.md +87 -0
  41. package/src/skills/tailwindcss-best-practices/references/core-installation.md +109 -0
  42. package/src/skills/tailwindcss-best-practices/references/core-preflight.md +200 -0
  43. package/src/skills/tailwindcss-best-practices/references/core-responsive.md +163 -0
  44. package/src/skills/tailwindcss-best-practices/references/core-source-detection.md +114 -0
  45. package/src/skills/tailwindcss-best-practices/references/core-theme.md +108 -0
  46. package/src/skills/tailwindcss-best-practices/references/core-utility-classes.md +59 -0
  47. package/src/skills/tailwindcss-best-practices/references/core-variants.md +204 -0
  48. package/src/skills/tailwindcss-best-practices/references/effects-form-controls.md +76 -0
  49. package/src/skills/tailwindcss-best-practices/references/effects-mask.md +91 -0
  50. package/src/skills/tailwindcss-best-practices/references/effects-scroll-snap.md +59 -0
  51. package/src/skills/tailwindcss-best-practices/references/effects-text-shadow.md +78 -0
  52. package/src/skills/tailwindcss-best-practices/references/effects-transition-animation.md +80 -0
  53. package/src/skills/tailwindcss-best-practices/references/effects-visibility-interactivity.md +82 -0
  54. package/src/skills/tailwindcss-best-practices/references/features-content-detection.md +175 -0
  55. package/src/skills/tailwindcss-best-practices/references/features-custom-styles.md +203 -0
  56. package/src/skills/tailwindcss-best-practices/references/features-dark-mode.md +137 -0
  57. package/src/skills/tailwindcss-best-practices/references/features-functions-directives.md +241 -0
  58. package/src/skills/tailwindcss-best-practices/references/features-upgrade.md +160 -0
  59. package/src/skills/tailwindcss-best-practices/references/layout-aspect-ratio.md +39 -0
  60. package/src/skills/tailwindcss-best-practices/references/layout-columns.md +80 -0
  61. package/src/skills/tailwindcss-best-practices/references/layout-display.md +110 -0
  62. package/src/skills/tailwindcss-best-practices/references/layout-flexbox.md +112 -0
  63. package/src/skills/tailwindcss-best-practices/references/layout-grid.md +87 -0
  64. package/src/skills/tailwindcss-best-practices/references/layout-height.md +97 -0
  65. package/src/skills/tailwindcss-best-practices/references/layout-inset.md +103 -0
  66. package/src/skills/tailwindcss-best-practices/references/layout-logical-properties.md +92 -0
  67. package/src/skills/tailwindcss-best-practices/references/layout-margin.md +126 -0
  68. package/src/skills/tailwindcss-best-practices/references/layout-min-max-sizing.md +63 -0
  69. package/src/skills/tailwindcss-best-practices/references/layout-object-fit-position.md +64 -0
  70. package/src/skills/tailwindcss-best-practices/references/layout-overflow.md +57 -0
  71. package/src/skills/tailwindcss-best-practices/references/layout-padding.md +77 -0
  72. package/src/skills/tailwindcss-best-practices/references/layout-position.md +85 -0
  73. package/src/skills/tailwindcss-best-practices/references/layout-tables.md +67 -0
  74. package/src/skills/tailwindcss-best-practices/references/layout-width.md +102 -0
  75. package/src/skills/tailwindcss-best-practices/references/transform-base.md +68 -0
  76. package/src/skills/tailwindcss-best-practices/references/transform-rotate.md +70 -0
  77. package/src/skills/tailwindcss-best-practices/references/transform-scale.md +83 -0
  78. package/src/skills/tailwindcss-best-practices/references/transform-skew.md +62 -0
  79. package/src/skills/tailwindcss-best-practices/references/transform-translate.md +77 -0
  80. package/src/skills/tailwindcss-best-practices/references/typography-font-text.md +142 -0
  81. package/src/skills/tailwindcss-best-practices/references/typography-list-style.md +65 -0
  82. package/src/skills/tailwindcss-best-practices/references/typography-text-align.md +60 -0
  83. package/src/skills/tailwindcss-best-practices/references/visual-background.md +76 -0
  84. package/src/skills/tailwindcss-best-practices/references/visual-border.md +108 -0
  85. package/src/skills/tailwindcss-best-practices/references/visual-effects.md +111 -0
  86. package/src/skills/tailwindcss-best-practices/references/visual-svg.md +82 -0
  87. package/src/skills/test-mobile-app/SKILL.md +11 -6
  88. package/src/skills/test-mobile-app/scripts/analyze_apk.py +15 -4
  89. package/src/skills/test-mobile-app/scripts/check_environment.py +5 -5
  90. package/src/skills/test-mobile-app/scripts/run_tests.py +1 -1
  91. package/src/skills/test-web-ui/SKILL.md +264 -84
  92. package/src/skills/test-web-ui/scripts/discover.py +25 -12
  93. package/src/skills/test-web-ui/scripts/run_tests.py +3 -2
  94. package/src/skills/tm-search/SKILL.md +242 -106
  95. package/src/skills/tm-search/references/scraping-fallback.md +60 -95
  96. package/src/skills/tm-search/scripts/tm_search.py +453 -375
  97. package/src/skills/vite-best-practices/SKILL.md +115 -0
  98. package/src/skills/vite-best-practices/references/build-and-ssr.md +255 -0
  99. package/src/skills/vite-best-practices/references/core-config.md +231 -0
  100. package/src/skills/vite-best-practices/references/core-features.md +222 -0
  101. package/src/skills/vite-best-practices/references/core-plugin-api.md +294 -0
  102. package/src/skills/vite-best-practices/references/environment-api.md +108 -0
  103. package/src/skills/vite-best-practices/references/rolldown-migration.md +242 -0
  104. package/src/skills/screen-recording/references/approach2-xvfb.md +0 -232
@@ -0,0 +1,242 @@
1
+ ---
2
+ name: vite-rolldown
3
+ description: Vite 8 Rolldown bundler and Oxc transformer migration from Vite 7
4
+ ---
5
+
6
+ # Rolldown Migration (Vite 8)
7
+
8
+ Vite 8 (stable March 2026) replaces esbuild+Rollup with Rolldown, a unified Rust-based bundler. Performance gains of 10-30x for production builds are typical.
9
+
10
+ ## What Changed
11
+
12
+ | Before (Vite 7) | After (Vite 8) | Compat Layer |
13
+ |-----------------|----------------|--------------|
14
+ | esbuild (dev transform) | Oxc Transformer | `esbuild` auto-converts to `oxc` |
15
+ | esbuild (dep pre-bundling) | Rolldown | `esbuildOptions` auto-converts |
16
+ | esbuild (JS minification) | Oxc Minifier | `minify: 'esbuild'` = fallback |
17
+ | esbuild (CSS minification) | Lightning CSS | `cssMinify: 'esbuild'` = fallback |
18
+ | Rollup (production build) | Rolldown | `rollupOptions` auto-converts |
19
+ | `rollupOptions` | `rolldownOptions` | deprecated, auto-converts |
20
+ | `esbuild` option | `oxc` option | deprecated, auto-converts |
21
+
22
+ **Important:** A compatibility layer auto-converts old config names, so many projects upgrade with zero config changes. However, both `esbuild` and `rollupOptions` are deprecated and will be removed in a future version. Always use the new names in new code.
23
+
24
+ ## Config Migration
25
+
26
+ ### rollupOptions → rolldownOptions
27
+
28
+ Direct rename. Internal structure (`external`, `output.globals`) stays the same:
29
+
30
+ ```ts
31
+ // Before (Vite 7)
32
+ export default defineConfig({
33
+ build: {
34
+ rollupOptions: {
35
+ external: ['vue'],
36
+ output: { globals: { vue: 'Vue' } },
37
+ },
38
+ },
39
+ })
40
+
41
+ // After (Vite 8)
42
+ export default defineConfig({
43
+ build: {
44
+ rolldownOptions: {
45
+ external: ['vue'],
46
+ output: { globals: { vue: 'Vue' } },
47
+ },
48
+ },
49
+ })
50
+ ```
51
+
52
+ Also applies to `worker.rollupOptions` → `worker.rolldownOptions`.
53
+
54
+ ### esbuild → oxc (JavaScript Transforms)
55
+
56
+ Complete mapping of esbuild JSX options to Oxc:
57
+
58
+ | esbuild | oxc equivalent |
59
+ |---------|---------------|
60
+ | `jsx: 'transform'` | `jsx: { runtime: 'classic' }` |
61
+ | `jsx: 'automatic'` | `jsx: { runtime: 'automatic' }` |
62
+ | `jsx: 'preserve'` | `jsx: 'preserve'` |
63
+ | `jsxFactory: 'h'` | `jsx.pragma: 'h'` |
64
+ | `jsxFragment: 'Fragment'` | `jsx.pragmaFrag: 'Fragment'` |
65
+ | `jsxImportSource: 'react'` | `jsx.importSource: 'react'` |
66
+ | `jsxDev` | `jsx.development` |
67
+ | `jsxSideEffects` | `jsx.pure` |
68
+ | `jsxInject` | `jsxInject` (same) |
69
+ | `include`/`exclude` | `include`/`exclude` (same) |
70
+ | `define` | `define` (same) |
71
+
72
+ **Not supported in Oxc:** `esbuild.supported` option.
73
+
74
+ **Not supported:** Native decorator lowering — use Babel or SWC plugins.
75
+
76
+ **Example — Preact classic JSX:**
77
+
78
+ ```ts
79
+ // Before (Vite 7)
80
+ export default defineConfig({
81
+ esbuild: {
82
+ jsxFactory: 'h',
83
+ jsxFragment: 'Fragment',
84
+ },
85
+ })
86
+
87
+ // After (Vite 8)
88
+ export default defineConfig({
89
+ oxc: {
90
+ jsx: {
91
+ runtime: 'classic',
92
+ pragma: 'h',
93
+ pragmaFrag: 'Fragment',
94
+ },
95
+ },
96
+ })
97
+ ```
98
+
99
+ **Example — React automatic:**
100
+
101
+ ```ts
102
+ export default defineConfig({
103
+ oxc: {
104
+ jsx: {
105
+ runtime: 'automatic',
106
+ importSource: 'react',
107
+ },
108
+ },
109
+ })
110
+ ```
111
+
112
+ ### optimizeDeps.esbuildOptions → optimizeDeps.rolldownOptions
113
+
114
+ Auto-converted options:
115
+
116
+ | esbuildOptions | rolldownOptions equivalent |
117
+ |---------------|--------------------------|
118
+ | `minify` | `output.minify` |
119
+ | `treeShaking` | `treeshake` |
120
+ | `define` | `transform.define` |
121
+ | `loader` | `moduleTypes` |
122
+ | `preserveSymlinks` | `!resolve.symlinks` |
123
+ | `resolveExtensions` | `resolve.extensions` |
124
+ | `mainFields` | `resolve.mainFields` |
125
+ | `conditions` | `resolve.conditionNames` |
126
+ | `keepNames` | `output.keepNames` |
127
+ | `platform` | `platform` |
128
+ | `plugins` | `plugins` (partial support) |
129
+
130
+ ### Minification
131
+
132
+ ```ts
133
+ // Vite 8 default: Oxc minifier
134
+ build: { minify: true } // uses Oxc
135
+
136
+ // Fallback to esbuild (requires installing esbuild)
137
+ build: { minify: 'esbuild' }
138
+
139
+ // CSS: Lightning CSS is default
140
+ build: { cssMinify: true } // uses Lightning CSS
141
+ build: { cssMinify: 'esbuild' } // fallback (requires esbuild)
142
+ ```
143
+
144
+ **Not supported by Oxc minifier:** `mangleProps`, `reserveProps`, `mangleQuoted`, `mangleCache`.
145
+
146
+ ### esbuild.banner/footer
147
+
148
+ No direct equivalent. Use a custom plugin with the `transform` hook instead.
149
+
150
+ ## Breaking Changes
151
+
152
+ ### manualChunks
153
+
154
+ - Object form **removed**
155
+ - Function form **deprecated** → use `codeSplitting` option instead
156
+
157
+ ### CommonJS Interop
158
+
159
+ The `default` import from CJS modules follows new rules. If the importer is `.mjs`/`.mts`, or the closest `package.json` has `"type": "module"`, or `module.exports.__esModule` is not true, then `default` = `module.exports` (not `module.exports.default`).
160
+
161
+ Temporary workaround: `legacy.inconsistentCjsInterop: true`.
162
+
163
+ ### Format Sniffing Removed
164
+
165
+ Vite no longer sniffs file content to choose between `browser` and `module` fields. `resolve.mainFields` order is always respected.
166
+
167
+ ### build() Error Handling
168
+
169
+ `build()` now throws `BundleError` (not raw error) with `.errors?: RolldownError[]` array.
170
+
171
+ ### Removed Config Options
172
+
173
+ | Removed | Replacement |
174
+ |---------|-------------|
175
+ | `build.commonjsOptions` | No-op (Rolldown handles CJS natively) |
176
+ | `build.dynamicImportVarsOptions.warnOnError` | No-op |
177
+ | `resolve.alias[].customResolver` | Use custom plugin with `resolveId` hook |
178
+ | `rollupOptions.output.manualChunks` (object form) | Use function form or `codeSplitting` |
179
+ | `rollupOptions.watch.chokidar` | `rolldownOptions.watch.watcher` |
180
+
181
+ ### Unsupported Output Formats
182
+
183
+ `'system'` and `'amd'` output formats are not supported by Rolldown.
184
+
185
+ ### Node.js Requirements
186
+
187
+ Node.js 20.19+ or 22.12+ required (same as Vite 7).
188
+
189
+ ## Plugin Compatibility
190
+
191
+ Most Vite/Rollup plugins work unchanged because Rolldown supports the Rollup plugin API. If a plugin only works during build:
192
+
193
+ ```ts
194
+ {
195
+ ...rollupPlugin(),
196
+ enforce: 'post',
197
+ apply: 'build',
198
+ }
199
+ ```
200
+
201
+ **Note:** `moduleParsed` hook is NOT called during dev. Plugins relying on it need adjustment.
202
+
203
+ Detect Rolldown at runtime: `this.meta.rolldownVersion`.
204
+
205
+ ## Gradual Migration
206
+
207
+ For large projects, migrate via `rolldown-vite` first:
208
+
209
+ ```bash
210
+ # Step 1: Test with rolldown-vite (still Vite 7)
211
+ pnpm add -D rolldown-vite
212
+
213
+ # Replace vite import in config
214
+ import { defineConfig } from 'rolldown-vite'
215
+
216
+ # Step 2: Once stable, upgrade to Vite 8
217
+ pnpm add -D vite@8
218
+
219
+ # Revert the import
220
+ import { defineConfig } from 'vite'
221
+ ```
222
+
223
+ ## Overriding Vite in Frameworks
224
+
225
+ When framework depends on older Vite:
226
+
227
+ ```json
228
+ {
229
+ "pnpm": {
230
+ "overrides": {
231
+ "vite": "8.0.0"
232
+ }
233
+ }
234
+ }
235
+ ```
236
+
237
+ <!--
238
+ Source references:
239
+ - https://vite.dev/blog/announcing-vite8
240
+ - https://vite.dev/guide/migration
241
+ - https://vite.dev/config/shared-options#oxc
242
+ -->
@@ -1,232 +0,0 @@
1
- # Approach 2: Virtual Display Recording (Xvfb + FFmpeg x11grab)
2
-
3
- Capture a real running application on a virtual screen.
4
-
5
- ## How It Works
6
-
7
- ```
8
- Xvfb :99 ←── virtual display (invisible, in RAM)
9
-
10
- Your app runs here (DISPLAY=:99)
11
-
12
- FFmpeg x11grab ←── records the virtual display → MP4
13
- ```
14
-
15
- ## Full Working Template
16
-
17
- ```python
18
- #!/usr/bin/env python3
19
- """
20
- Virtual Display Screen Recorder
21
- Launches a real app on a virtual display and records it.
22
- """
23
-
24
- import subprocess, os, time, signal, shutil
25
-
26
- WIDTH, HEIGHT = 1280, 720
27
- DISPLAY_NUM = ":99"
28
- FPS = 24
29
- DURATION = 30 # seconds to record
30
- OUTPUT = "/home/claude/recording.mp4"
31
-
32
-
33
- def start_virtual_display():
34
- """Start Xvfb virtual display"""
35
- proc = subprocess.Popen([
36
- "Xvfb", DISPLAY_NUM,
37
- "-screen", "0", f"{WIDTH}x{HEIGHT}x24",
38
- "-ac", "-nolisten", "tcp"
39
- ], stderr=subprocess.DEVNULL)
40
- time.sleep(1.0) # wait for display to initialize
41
- print(f"✅ Virtual display {DISPLAY_NUM} started (PID {proc.pid})")
42
- return proc
43
-
44
-
45
- def start_recording(output_path, duration=None):
46
- """Start FFmpeg recording of virtual display"""
47
- cmd = [
48
- "ffmpeg",
49
- "-f", "x11grab",
50
- "-video_size", f"{WIDTH}x{HEIGHT}",
51
- "-framerate", str(FPS),
52
- "-i", DISPLAY_NUM,
53
- "-c:v", "libx264",
54
- "-preset", "ultrafast",
55
- "-pix_fmt", "yuv420p",
56
- ]
57
- if duration:
58
- cmd.extend(["-t", str(duration)])
59
- cmd.extend([output_path, "-y", "-loglevel", "quiet"])
60
-
61
- proc = subprocess.Popen(cmd)
62
- time.sleep(0.5)
63
- print(f"✅ Recording started → {output_path}")
64
- return proc
65
-
66
-
67
- def run_app_on_display(app_command, env_extra=None):
68
- """Run an application on the virtual display"""
69
- env = os.environ.copy()
70
- env["DISPLAY"] = DISPLAY_NUM
71
- if env_extra:
72
- env.update(env_extra)
73
- proc = subprocess.Popen(app_command, env=env, stderr=subprocess.DEVNULL)
74
- return proc
75
-
76
-
77
- def record_session(app_fn, duration=30, output=OUTPUT):
78
- """
79
- Full recording session:
80
- 1. Start virtual display
81
- 2. Start recording
82
- 3. Run app_fn(display) — your automation code
83
- 4. Stop recording, stop display
84
- """
85
- xvfb = start_virtual_display()
86
- recorder = start_recording(output, duration)
87
-
88
- try:
89
- app_fn(DISPLAY_NUM)
90
- # Wait remaining time or until done
91
- time.sleep(duration)
92
- finally:
93
- recorder.send_signal(signal.SIGINT)
94
- recorder.wait()
95
- xvfb.terminate()
96
- xvfb.wait()
97
- print(f"✅ Recording saved: {os.path.getsize(output):,} bytes")
98
-
99
- return output
100
-
101
-
102
- # ── EXAMPLE: Record a Python Tkinter app ─────────────────────────────────────
103
-
104
- def my_tkinter_app_script():
105
- return '''
106
- import tkinter as tk
107
- import time
108
-
109
- root = tk.Tk()
110
- root.title("My App Demo")
111
- root.geometry("1280x720")
112
- root.configure(bg="#0c0c20")
113
-
114
- label = tk.Label(root, text="Loading...", font=("Arial", 48),
115
- bg="#0c0c20", fg="white")
116
- label.pack(pady=200)
117
-
118
- def update():
119
- texts = ["Detecting issues...", "Processing...", "✅ Complete!", "100% Accurate"]
120
- for i, t in enumerate(texts):
121
- root.after(i * 2000, lambda t=t: label.configure(text=t))
122
- root.after(8000, root.destroy)
123
-
124
- root.after(500, update)
125
- root.mainloop()
126
- '''
127
-
128
- def run_demo(display):
129
- # Write app to temp file
130
- with open('/tmp/demo_app.py', 'w') as f:
131
- f.write(my_tkinter_app_script())
132
-
133
- env = os.environ.copy()
134
- env["DISPLAY"] = display
135
- subprocess.Popen(["python3", "/tmp/demo_app.py"], env=env)
136
- time.sleep(10) # let app run
137
-
138
-
139
- # ── EXAMPLE: Record a Chromium browser session ───────────────────────────────
140
-
141
- def record_browser(url, duration=20, output=OUTPUT):
142
- """Record a browser navigating to a URL"""
143
- xvfb = start_virtual_display()
144
- recorder = start_recording(output, duration)
145
-
146
- env = os.environ.copy()
147
- env["DISPLAY"] = DISPLAY_NUM
148
-
149
- # Launch Chromium in window mode (not headless — we WANT it visible)
150
- subprocess.Popen([
151
- "chromium", "--no-sandbox",
152
- f"--window-size={WIDTH},{HEIGHT}",
153
- "--window-position=0,0",
154
- "--start-maximized",
155
- url
156
- ], env=env, stderr=subprocess.DEVNULL)
157
-
158
- time.sleep(duration)
159
- recorder.send_signal(signal.SIGINT)
160
- recorder.wait()
161
- xvfb.terminate()
162
- print(f"✅ Browser recording saved: {output}")
163
- return output
164
-
165
-
166
- # ── POST-PROCESSING ───────────────────────────────────────────────────────────
167
-
168
- def add_narration(video_path, narration_text, output_path):
169
- """Add TTS narration to a recorded video"""
170
- import pyttsx3
171
-
172
- engine = pyttsx3.init()
173
- engine.setProperty('rate', 140)
174
- engine.save_to_file(narration_text, '/tmp/narration.wav')
175
- engine.runAndWait()
176
-
177
- subprocess.run([
178
- 'ffmpeg', '-i', '/tmp/narration.wav',
179
- '-c:a', 'libmp3lame', '-b:a', '128k',
180
- '/tmp/narration.mp3', '-y', '-loglevel', 'quiet'
181
- ])
182
-
183
- subprocess.run([
184
- 'ffmpeg', '-i', video_path, '-i', '/tmp/narration.mp3',
185
- '-c:v', 'copy', '-c:a', 'aac',
186
- '-shortest', output_path, '-y', '-loglevel', 'quiet'
187
- ])
188
- return output_path
189
-
190
-
191
- def add_overlay_text(video_path, text, position="bottom", output_path=None):
192
- """Add text overlay to recorded video using FFmpeg"""
193
- if not output_path:
194
- output_path = video_path.replace('.mp4', '_overlay.mp4')
195
-
196
- if position == "bottom":
197
- vf = f"drawtext=text='{text}':fontcolor=white:fontsize=24:x=(w-text_w)/2:y=h-th-20:box=1:boxcolor=black@0.5"
198
- else:
199
- vf = f"drawtext=text='{text}':fontcolor=white:fontsize=24:x=20:y=20"
200
-
201
- subprocess.run([
202
- 'ffmpeg', '-i', video_path, '-vf', vf,
203
- output_path, '-y', '-loglevel', 'quiet'
204
- ])
205
- return output_path
206
-
207
-
208
- if __name__ == "__main__":
209
- result = record_session(run_demo, duration=12, output=OUTPUT)
210
- # Add narration
211
- final = add_narration(result, "This is our automated UI demo.",
212
- "/home/claude/final_recording.mp4")
213
- shutil.copy(final, "/mnt/user-data/outputs/recording.mp4")
214
- print(f"🎉 Done: /mnt/user-data/outputs/recording.mp4")
215
- ```
216
-
217
- ## When to Use This Approach
218
-
219
- ✅ When you need to show a REAL running application
220
- ✅ When demonstrating web UI (real browser rendering)
221
- ✅ When the app has complex visual state hard to reproduce with Pillow
222
-
223
- ❌ Don't use for simple text/graphic demos (too slow — use Approach 1)
224
- ❌ Avoid if network access to the app is needed (may be blocked)
225
-
226
- ## Important Notes
227
-
228
- - Xvfb uses RAM for the framebuffer — 1280x720x24 ≈ 3.5MB per frame
229
- - Always kill both `ffmpeg` and `Xvfb` in a `finally` block to avoid orphan processes
230
- - Use `DISPLAY=:99` not `:0` to avoid conflicts with any host display
231
- - FFmpeg's `-preset ultrafast` is recommended for real-time capture
232
- - For longer recordings (>60s), consider `-crf 28` to reduce file size