@tangle-network/browser-agent-driver 0.10.0 → 0.12.0

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 (97) hide show
  1. package/dist/brain/index.d.ts +3 -0
  2. package/dist/brain/index.d.ts.map +1 -1
  3. package/dist/brain/index.js +103 -11
  4. package/dist/brain/index.js.map +1 -1
  5. package/dist/browser-launch.d.ts +3 -0
  6. package/dist/browser-launch.d.ts.map +1 -1
  7. package/dist/browser-launch.js +12 -2
  8. package/dist/browser-launch.js.map +1 -1
  9. package/dist/cli-auth.d.ts +19 -0
  10. package/dist/cli-auth.d.ts.map +1 -0
  11. package/dist/cli-auth.js +195 -0
  12. package/dist/cli-auth.js.map +1 -0
  13. package/dist/cli-design-audit.d.ts +8 -0
  14. package/dist/cli-design-audit.d.ts.map +1 -1
  15. package/dist/cli-design-audit.js +858 -31
  16. package/dist/cli-design-audit.js.map +1 -1
  17. package/dist/cli-showcase.d.ts +28 -0
  18. package/dist/cli-showcase.d.ts.map +1 -0
  19. package/dist/cli-showcase.js +131 -0
  20. package/dist/cli-showcase.js.map +1 -0
  21. package/dist/cli-ui.d.ts.map +1 -1
  22. package/dist/cli-ui.js +41 -2
  23. package/dist/cli-ui.js.map +1 -1
  24. package/dist/cli.js +418 -211
  25. package/dist/cli.js.map +1 -1
  26. package/dist/config.d.ts +4 -0
  27. package/dist/config.d.ts.map +1 -1
  28. package/dist/config.js +2 -1
  29. package/dist/config.js.map +1 -1
  30. package/dist/design/compare.d.ts +9 -0
  31. package/dist/design/compare.d.ts.map +1 -0
  32. package/dist/design/compare.js +339 -0
  33. package/dist/design/compare.js.map +1 -0
  34. package/dist/design/index.d.ts +6 -0
  35. package/dist/design/index.d.ts.map +1 -0
  36. package/dist/design/index.js +5 -0
  37. package/dist/design/index.js.map +1 -0
  38. package/dist/design/page-interaction.d.ts +24 -0
  39. package/dist/design/page-interaction.d.ts.map +1 -0
  40. package/dist/design/page-interaction.js +244 -0
  41. package/dist/design/page-interaction.js.map +1 -0
  42. package/dist/design/rip.d.ts +9 -0
  43. package/dist/design/rip.d.ts.map +1 -0
  44. package/dist/design/rip.js +362 -0
  45. package/dist/design/rip.js.map +1 -0
  46. package/dist/design/types.d.ts +126 -0
  47. package/dist/design/types.d.ts.map +1 -0
  48. package/dist/design/types.js +3 -0
  49. package/dist/design/types.js.map +1 -0
  50. package/dist/drivers/playwright.d.ts +1 -0
  51. package/dist/drivers/playwright.d.ts.map +1 -1
  52. package/dist/drivers/playwright.js +21 -5
  53. package/dist/drivers/playwright.js.map +1 -1
  54. package/dist/drivers/types.d.ts +8 -0
  55. package/dist/drivers/types.d.ts.map +1 -1
  56. package/dist/index.d.ts +5 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +4 -0
  59. package/dist/index.js.map +1 -1
  60. package/dist/runner/effect-verification.d.ts +12 -0
  61. package/dist/runner/effect-verification.d.ts.map +1 -0
  62. package/dist/runner/effect-verification.js +169 -0
  63. package/dist/runner/effect-verification.js.map +1 -0
  64. package/dist/runner/index.d.ts +1 -0
  65. package/dist/runner/index.d.ts.map +1 -1
  66. package/dist/runner/index.js +2 -0
  67. package/dist/runner/index.js.map +1 -1
  68. package/dist/runner/runner.d.ts.map +1 -1
  69. package/dist/runner/runner.js +34 -57
  70. package/dist/runner/runner.js.map +1 -1
  71. package/dist/runner.d.ts +1 -1
  72. package/dist/runner.d.ts.map +1 -1
  73. package/dist/runner.js +2 -0
  74. package/dist/runner.js.map +1 -1
  75. package/dist/showcase/annotate.d.ts +28 -0
  76. package/dist/showcase/annotate.d.ts.map +1 -0
  77. package/dist/showcase/annotate.js +95 -0
  78. package/dist/showcase/annotate.js.map +1 -0
  79. package/dist/showcase/assemble.d.ts +31 -0
  80. package/dist/showcase/assemble.d.ts.map +1 -0
  81. package/dist/showcase/assemble.js +107 -0
  82. package/dist/showcase/assemble.js.map +1 -0
  83. package/dist/showcase/demo-player.d.ts +53 -0
  84. package/dist/showcase/demo-player.d.ts.map +1 -0
  85. package/dist/showcase/demo-player.js +325 -0
  86. package/dist/showcase/demo-player.js.map +1 -0
  87. package/dist/showcase/index.d.ts +21 -0
  88. package/dist/showcase/index.d.ts.map +1 -0
  89. package/dist/showcase/index.js +289 -0
  90. package/dist/showcase/index.js.map +1 -0
  91. package/dist/showcase/types.d.ts +111 -0
  92. package/dist/showcase/types.d.ts.map +1 -0
  93. package/dist/showcase/types.js +3 -0
  94. package/dist/showcase/types.js.map +1 -0
  95. package/dist/types.d.ts +69 -0
  96. package/dist/types.d.ts.map +1 -1
  97. package/package.json +13 -1
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Frame assembly — combine screenshot sequence into GIF or video.
3
+ *
4
+ * Uses ffmpeg-static which is already a dependency (unused until now).
5
+ * Falls back gracefully if ffmpeg is not available.
6
+ */
7
+ import * as fs from 'node:fs';
8
+ import * as path from 'node:path';
9
+ import { execFileSync } from 'node:child_process';
10
+ let ffmpegPath = null;
11
+ function getFfmpeg() {
12
+ if (ffmpegPath !== null)
13
+ return ffmpegPath;
14
+ try {
15
+ // ffmpeg-static exports the path to the binary
16
+ ffmpegPath = require('ffmpeg-static');
17
+ if (!fs.existsSync(ffmpegPath))
18
+ ffmpegPath = null;
19
+ }
20
+ catch {
21
+ // Try system ffmpeg
22
+ try {
23
+ execFileSync('ffmpeg', ['-version'], { stdio: 'ignore' });
24
+ ffmpegPath = 'ffmpeg';
25
+ }
26
+ catch {
27
+ ffmpegPath = null;
28
+ }
29
+ }
30
+ return ffmpegPath;
31
+ }
32
+ /**
33
+ * Assemble PNG frames into an animated GIF.
34
+ *
35
+ * @param frames - Ordered screenshot frames
36
+ * @param outputPath - Where to write the GIF
37
+ * @param opts - Frame rate, loop count
38
+ * @returns true if successful, false if ffmpeg unavailable
39
+ */
40
+ export function assembleGif(frames, outputPath, opts) {
41
+ const ffmpeg = getFfmpeg();
42
+ if (!ffmpeg || frames.length === 0)
43
+ return false;
44
+ const fps = opts?.fps ?? 1;
45
+ const loop = opts?.loop ?? 0;
46
+ const maxWidth = opts?.maxWidth ?? 1200;
47
+ // Write frames to temp dir
48
+ const tmpDir = path.join(path.dirname(outputPath), '.gif-frames');
49
+ fs.mkdirSync(tmpDir, { recursive: true });
50
+ for (let i = 0; i < frames.length; i++) {
51
+ fs.writeFileSync(path.join(tmpDir, `frame-${String(i).padStart(4, '0')}.png`), frames[i].buffer);
52
+ }
53
+ try {
54
+ execFileSync(ffmpeg, [
55
+ '-y',
56
+ '-framerate', String(fps),
57
+ '-i', path.join(tmpDir, 'frame-%04d.png'),
58
+ '-vf', `scale='min(${maxWidth},iw)':-1:flags=lanczos,split[s0][s1];[s0]palettegen=max_colors=256:stats_mode=diff[p];[s1][p]paletteuse=dither=bayer:bayer_scale=5`,
59
+ '-loop', String(loop),
60
+ outputPath,
61
+ ], { stdio: 'pipe', timeout: 60_000 });
62
+ return true;
63
+ }
64
+ catch {
65
+ return false;
66
+ }
67
+ finally {
68
+ // Clean up temp frames
69
+ fs.rmSync(tmpDir, { recursive: true, force: true });
70
+ }
71
+ }
72
+ /**
73
+ * Assemble PNG frames into a WebM video.
74
+ */
75
+ export function assembleVideo(frames, outputPath, opts) {
76
+ const ffmpeg = getFfmpeg();
77
+ if (!ffmpeg || frames.length === 0)
78
+ return false;
79
+ const fps = opts?.fps ?? 1;
80
+ const maxWidth = opts?.maxWidth ?? 1440;
81
+ const tmpDir = path.join(path.dirname(outputPath), '.video-frames');
82
+ fs.mkdirSync(tmpDir, { recursive: true });
83
+ for (let i = 0; i < frames.length; i++) {
84
+ fs.writeFileSync(path.join(tmpDir, `frame-${String(i).padStart(4, '0')}.png`), frames[i].buffer);
85
+ }
86
+ try {
87
+ execFileSync(ffmpeg, [
88
+ '-y',
89
+ '-framerate', String(fps),
90
+ '-i', path.join(tmpDir, 'frame-%04d.png'),
91
+ '-vf', `scale='min(${maxWidth},iw)':-2`,
92
+ '-c:v', 'libvpx-vp9',
93
+ '-crf', '30',
94
+ '-b:v', '0',
95
+ '-pix_fmt', 'yuva420p',
96
+ outputPath,
97
+ ], { stdio: 'pipe', timeout: 60_000 });
98
+ return true;
99
+ }
100
+ catch {
101
+ return false;
102
+ }
103
+ finally {
104
+ fs.rmSync(tmpDir, { recursive: true, force: true });
105
+ }
106
+ }
107
+ //# sourceMappingURL=assemble.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assemble.js","sourceRoot":"","sources":["../../src/showcase/assemble.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Interactive demo player generator.
3
+ *
4
+ * Takes showcase walkthrough results (screenshots + step metadata) and produces
5
+ * a self-contained HTML file with an interactive step-by-step player.
6
+ *
7
+ * Features:
8
+ * - Click-through slideshow of annotated screenshots
9
+ * - Pulsing hotspot circles at click targets
10
+ * - Step description tooltips
11
+ * - Progress bar and step counter
12
+ * - Keyboard navigation (← →)
13
+ * - Self-contained — single HTML file, images base64-inlined
14
+ */
15
+ import type { ShowcaseStep } from './types.js';
16
+ export interface DemoStep {
17
+ /** Screenshot as base64 PNG. */
18
+ imageBase64: string;
19
+ /** Width of the screenshot. */
20
+ width: number;
21
+ /** Height of the screenshot. */
22
+ height: number;
23
+ /** Description shown as tooltip. */
24
+ description?: string;
25
+ /** Click hotspot position (percentage of image dimensions). */
26
+ hotspot?: {
27
+ xPercent: number;
28
+ yPercent: number;
29
+ label?: string;
30
+ };
31
+ /** Action that was taken at this step. */
32
+ action: string;
33
+ }
34
+ export interface DemoConfig {
35
+ title: string;
36
+ steps: DemoStep[];
37
+ /** Brand color for hotspots and UI. Default: #8e59ff */
38
+ accentColor?: string;
39
+ /** Auto-advance interval in ms. 0 = manual only. Default: 0 */
40
+ autoAdvance?: number;
41
+ }
42
+ /**
43
+ * Generate a self-contained HTML demo player.
44
+ */
45
+ export declare function generateDemoHtml(config: DemoConfig): string;
46
+ /**
47
+ * Build DemoSteps from showcase results + original walkthrough steps.
48
+ *
49
+ * Reads screenshot PNGs from disk, converts to base64, and maps
50
+ * step actions to hotspot positions.
51
+ */
52
+ export declare function buildDemoSteps(screenshotDir: string, walkthroughSteps: ShowcaseStep[], page?: import('playwright').Page): Promise<DemoStep[]>;
53
+ //# sourceMappingURL=demo-player.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demo-player.d.ts","sourceRoot":"","sources":["../../src/showcase/demo-player.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,MAAM,WAAW,QAAQ;IACvB,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAA;IACnB,+BAA+B;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,+DAA+D;IAC/D,OAAO,CAAC,EAAE;QACR,QAAQ,EAAE,MAAM,CAAA;QAChB,QAAQ,EAAE,MAAM,CAAA;QAChB,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,CAAA;IACD,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAiQ3D;AAMD;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,aAAa,EAAE,MAAM,EACrB,gBAAgB,EAAE,YAAY,EAAE,EAChC,IAAI,CAAC,EAAE,OAAO,YAAY,EAAE,IAAI,GAC/B,OAAO,CAAC,QAAQ,EAAE,CAAC,CA2CrB"}
@@ -0,0 +1,325 @@
1
+ /**
2
+ * Interactive demo player generator.
3
+ *
4
+ * Takes showcase walkthrough results (screenshots + step metadata) and produces
5
+ * a self-contained HTML file with an interactive step-by-step player.
6
+ *
7
+ * Features:
8
+ * - Click-through slideshow of annotated screenshots
9
+ * - Pulsing hotspot circles at click targets
10
+ * - Step description tooltips
11
+ * - Progress bar and step counter
12
+ * - Keyboard navigation (← →)
13
+ * - Self-contained — single HTML file, images base64-inlined
14
+ */
15
+ import * as fs from 'node:fs';
16
+ import * as path from 'node:path';
17
+ /**
18
+ * Generate a self-contained HTML demo player.
19
+ */
20
+ export function generateDemoHtml(config) {
21
+ const accent = config.accentColor ?? '#8e59ff';
22
+ const steps = JSON.stringify(config.steps.map(s => ({
23
+ image: s.imageBase64,
24
+ description: s.description ?? '',
25
+ hotspot: s.hotspot ?? null,
26
+ action: s.action,
27
+ })));
28
+ return `<!DOCTYPE html>
29
+ <html lang="en">
30
+ <head>
31
+ <meta charset="utf-8">
32
+ <meta name="viewport" content="width=device-width, initial-scale=1">
33
+ <title>${escapeHtml(config.title)}</title>
34
+ <style>
35
+ * { margin: 0; padding: 0; box-sizing: border-box; }
36
+ body {
37
+ background: #0a0a0a;
38
+ color: #fff;
39
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
40
+ display: flex;
41
+ flex-direction: column;
42
+ align-items: center;
43
+ justify-content: center;
44
+ min-height: 100vh;
45
+ padding: 1rem;
46
+ }
47
+
48
+ .demo-container {
49
+ position: relative;
50
+ max-width: 1200px;
51
+ width: 100%;
52
+ border-radius: 12px;
53
+ overflow: hidden;
54
+ border: 1px solid rgba(255,255,255,0.1);
55
+ background: #111;
56
+ box-shadow: 0 20px 60px rgba(0,0,0,0.5);
57
+ }
58
+
59
+ /* Browser chrome */
60
+ .demo-chrome {
61
+ display: flex;
62
+ align-items: center;
63
+ gap: 8px;
64
+ padding: 10px 16px;
65
+ background: #1a1a1a;
66
+ border-bottom: 1px solid rgba(255,255,255,0.06);
67
+ }
68
+ .demo-dot { width: 12px; height: 12px; border-radius: 50%; }
69
+ .demo-dot.r { background: rgba(255,95,87,0.8); }
70
+ .demo-dot.y { background: rgba(254,188,46,0.8); }
71
+ .demo-dot.p { background: ${accent}cc; }
72
+ .demo-title {
73
+ margin-left: 12px;
74
+ font-size: 13px;
75
+ color: rgba(255,255,255,0.4);
76
+ font-family: monospace;
77
+ }
78
+
79
+ /* Image viewport */
80
+ .demo-viewport {
81
+ position: relative;
82
+ width: 100%;
83
+ overflow: hidden;
84
+ cursor: pointer;
85
+ }
86
+ .demo-viewport img {
87
+ width: 100%;
88
+ display: block;
89
+ transition: opacity 0.3s ease;
90
+ }
91
+
92
+ /* Hotspot */
93
+ .hotspot {
94
+ position: absolute;
95
+ transform: translate(-50%, -50%);
96
+ pointer-events: none;
97
+ z-index: 10;
98
+ }
99
+ .hotspot-ring {
100
+ width: 40px;
101
+ height: 40px;
102
+ border-radius: 50%;
103
+ border: 2px solid ${accent};
104
+ animation: pulse 2s ease-in-out infinite;
105
+ }
106
+ .hotspot-dot {
107
+ position: absolute;
108
+ top: 50%;
109
+ left: 50%;
110
+ transform: translate(-50%, -50%);
111
+ width: 12px;
112
+ height: 12px;
113
+ border-radius: 50%;
114
+ background: ${accent};
115
+ }
116
+ .hotspot-label {
117
+ position: absolute;
118
+ top: -32px;
119
+ left: 50%;
120
+ transform: translateX(-50%);
121
+ background: ${accent};
122
+ color: white;
123
+ font-size: 12px;
124
+ font-weight: 600;
125
+ padding: 4px 12px;
126
+ border-radius: 6px;
127
+ white-space: nowrap;
128
+ font-family: -apple-system, sans-serif;
129
+ }
130
+
131
+ @keyframes pulse {
132
+ 0%, 100% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
133
+ 50% { transform: translate(-50%, -50%) scale(1.5); opacity: 0.5; }
134
+ }
135
+
136
+ /* Controls */
137
+ .demo-controls {
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: space-between;
141
+ padding: 12px 16px;
142
+ background: #1a1a1a;
143
+ border-top: 1px solid rgba(255,255,255,0.06);
144
+ }
145
+
146
+ .demo-step-info {
147
+ font-size: 13px;
148
+ color: rgba(255,255,255,0.5);
149
+ }
150
+
151
+ .demo-description {
152
+ font-size: 14px;
153
+ color: rgba(255,255,255,0.8);
154
+ text-align: center;
155
+ flex: 1;
156
+ padding: 0 1rem;
157
+ }
158
+
159
+ .demo-nav {
160
+ display: flex;
161
+ gap: 8px;
162
+ }
163
+ .demo-nav button {
164
+ background: rgba(255,255,255,0.06);
165
+ border: 1px solid rgba(255,255,255,0.1);
166
+ color: white;
167
+ padding: 6px 16px;
168
+ border-radius: 6px;
169
+ cursor: pointer;
170
+ font-size: 13px;
171
+ transition: background 0.2s;
172
+ }
173
+ .demo-nav button:hover {
174
+ background: ${accent}33;
175
+ border-color: ${accent}66;
176
+ }
177
+ .demo-nav button:disabled {
178
+ opacity: 0.3;
179
+ cursor: not-allowed;
180
+ }
181
+
182
+ /* Progress bar */
183
+ .demo-progress {
184
+ height: 3px;
185
+ background: rgba(255,255,255,0.06);
186
+ position: relative;
187
+ }
188
+ .demo-progress-fill {
189
+ height: 100%;
190
+ background: ${accent};
191
+ transition: width 0.3s ease;
192
+ }
193
+ </style>
194
+ </head>
195
+ <body>
196
+
197
+ <div class="demo-container">
198
+ <div class="demo-chrome">
199
+ <div class="demo-dot r"></div>
200
+ <div class="demo-dot y"></div>
201
+ <div class="demo-dot p"></div>
202
+ <span class="demo-title">${escapeHtml(config.title)}</span>
203
+ </div>
204
+
205
+ <div class="demo-progress">
206
+ <div class="demo-progress-fill" id="progress"></div>
207
+ </div>
208
+
209
+ <div class="demo-viewport" id="viewport" onclick="next()">
210
+ <img id="screenshot" alt="Demo step" />
211
+ <div class="hotspot" id="hotspot" style="display:none;">
212
+ <div class="hotspot-ring"></div>
213
+ <div class="hotspot-dot"></div>
214
+ <div class="hotspot-label" id="hotspot-label"></div>
215
+ </div>
216
+ </div>
217
+
218
+ <div class="demo-controls">
219
+ <div class="demo-step-info" id="step-info">Step 1 of N</div>
220
+ <div class="demo-description" id="description"></div>
221
+ <div class="demo-nav">
222
+ <button onclick="prev()" id="prev-btn">&larr; Back</button>
223
+ <button onclick="next()" id="next-btn">Next &rarr;</button>
224
+ </div>
225
+ </div>
226
+ </div>
227
+
228
+ <script>
229
+ const steps = ${steps};
230
+ let current = 0;
231
+
232
+ function render() {
233
+ const step = steps[current];
234
+ document.getElementById('screenshot').src = 'data:image/png;base64,' + step.image;
235
+ document.getElementById('step-info').textContent = 'Step ' + (current + 1) + ' of ' + steps.length;
236
+ document.getElementById('description').textContent = step.description || '';
237
+ document.getElementById('progress').style.width = ((current + 1) / steps.length * 100) + '%';
238
+ document.getElementById('prev-btn').disabled = current === 0;
239
+ document.getElementById('next-btn').disabled = current === steps.length - 1;
240
+ document.getElementById('next-btn').textContent = current === steps.length - 1 ? 'Done ✓' : 'Next →';
241
+
242
+ const hotspot = document.getElementById('hotspot');
243
+ const label = document.getElementById('hotspot-label');
244
+ if (step.hotspot) {
245
+ hotspot.style.display = 'block';
246
+ hotspot.style.left = step.hotspot.xPercent + '%';
247
+ hotspot.style.top = step.hotspot.yPercent + '%';
248
+ if (step.hotspot.label) {
249
+ label.textContent = step.hotspot.label;
250
+ label.style.display = 'block';
251
+ } else {
252
+ label.style.display = 'none';
253
+ }
254
+ } else {
255
+ hotspot.style.display = 'none';
256
+ }
257
+ }
258
+
259
+ function next() {
260
+ if (current < steps.length - 1) { current++; render(); }
261
+ }
262
+ function prev() {
263
+ if (current > 0) { current--; render(); }
264
+ }
265
+
266
+ document.addEventListener('keydown', (e) => {
267
+ if (e.key === 'ArrowRight' || e.key === ' ') { e.preventDefault(); next(); }
268
+ if (e.key === 'ArrowLeft') { e.preventDefault(); prev(); }
269
+ });
270
+
271
+ render();
272
+ </script>
273
+
274
+ </body>
275
+ </html>`;
276
+ }
277
+ function escapeHtml(s) {
278
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
279
+ }
280
+ /**
281
+ * Build DemoSteps from showcase results + original walkthrough steps.
282
+ *
283
+ * Reads screenshot PNGs from disk, converts to base64, and maps
284
+ * step actions to hotspot positions.
285
+ */
286
+ export async function buildDemoSteps(screenshotDir, walkthroughSteps, page) {
287
+ const demoSteps = [];
288
+ const pngFiles = fs.readdirSync(screenshotDir)
289
+ .filter(f => f.endsWith('.png') && !f.includes('full-page'))
290
+ .sort();
291
+ for (let i = 0; i < pngFiles.length; i++) {
292
+ const filePath = path.join(screenshotDir, pngFiles[i]);
293
+ const buffer = fs.readFileSync(filePath);
294
+ const base64 = buffer.toString('base64');
295
+ const step = walkthroughSteps[i];
296
+ let hotspot = undefined;
297
+ // If the next step has a click action with a selector, calculate hotspot position
298
+ const nextStep = walkthroughSteps[i + 1];
299
+ if (nextStep?.action === 'click' && nextStep.selector && page) {
300
+ try {
301
+ const el = page.locator(nextStep.selector).first();
302
+ const box = await el.boundingBox();
303
+ const viewport = page.viewportSize();
304
+ if (box && viewport) {
305
+ hotspot = {
306
+ xPercent: ((box.x + box.width / 2) / viewport.width) * 100,
307
+ yPercent: ((box.y + box.height / 2) / viewport.height) * 100,
308
+ label: nextStep.capture?.name ?? `Click ${nextStep.selector}`,
309
+ };
310
+ }
311
+ }
312
+ catch { /* element not found — skip hotspot */ }
313
+ }
314
+ demoSteps.push({
315
+ imageBase64: base64,
316
+ width: 1200, // Will be overridden by actual image dimensions if available
317
+ height: 800,
318
+ description: step?.capture?.name?.replace(/-/g, ' ').replace(/^\d+-/, '') ?? `Step ${i + 1}`,
319
+ hotspot,
320
+ action: step?.action ?? 'screenshot',
321
+ });
322
+ }
323
+ return demoSteps;
324
+ }
325
+ //# sourceMappingURL=demo-player.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demo-player.js","sourceRoot":"","sources":["../../src/showcase/demo-player.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Showcase — deterministic walkthrough capture for marketing assets.
3
+ *
4
+ * Unlike `bad run` (LLM-driven exploration) or `bad design-audit` (quality assessment),
5
+ * showcase executes a pre-scripted walkthrough and captures polished screenshots,
6
+ * GIFs, and videos. No LLM calls. No quality judgement. Just beautiful captures.
7
+ *
8
+ * Reuses:
9
+ * - page-interaction.ts: dismissModals() for cookie/popup cleanup
10
+ * - browser-launch.ts: buildBrowserLaunchPlan() for consistent browser config
11
+ * - cli-ui.ts: CliRenderer for progress output
12
+ */
13
+ import type { ShowcaseConfig, ShowcaseResult, QuickCaptureConfig } from './types.js';
14
+ export declare function runShowcase(config: ShowcaseConfig): Promise<ShowcaseResult>;
15
+ /**
16
+ * Quick capture mode — no script file needed.
17
+ * Captures named positions: 'hero' (viewport), 'full' (full page), 'scroll:N' (scroll N px first).
18
+ */
19
+ export declare function quickCapture(config: QuickCaptureConfig): Promise<ShowcaseResult>;
20
+ export type { ShowcaseConfig, ShowcaseStep, ShowcaseFrame, ShowcaseResult, QuickCaptureConfig } from './types.js';
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/showcase/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EACV,cAAc,EAGd,cAAc,EACd,kBAAkB,EACnB,MAAM,YAAY,CAAA;AAOnB,wBAAsB,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAgIjF;AAID;;;GAGG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,cAAc,CAAC,CA0DtF;AAmHD,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA"}