screenwright 0.1.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 (106) hide show
  1. package/LICENSE +21 -0
  2. package/assets/click-ripple.svg +3 -0
  3. package/assets/cursor-default.svg +10 -0
  4. package/assets/cursor-pointer.svg +40 -0
  5. package/dist/bin/screenwright.d.ts +3 -0
  6. package/dist/bin/screenwright.d.ts.map +1 -0
  7. package/dist/bin/screenwright.js +18 -0
  8. package/dist/bin/screenwright.js.map +1 -0
  9. package/dist/src/commands/compose.d.ts +3 -0
  10. package/dist/src/commands/compose.d.ts.map +1 -0
  11. package/dist/src/commands/compose.js +154 -0
  12. package/dist/src/commands/compose.js.map +1 -0
  13. package/dist/src/commands/generate.d.ts +3 -0
  14. package/dist/src/commands/generate.d.ts.map +1 -0
  15. package/dist/src/commands/generate.js +70 -0
  16. package/dist/src/commands/generate.js.map +1 -0
  17. package/dist/src/commands/init.d.ts +3 -0
  18. package/dist/src/commands/init.d.ts.map +1 -0
  19. package/dist/src/commands/init.js +49 -0
  20. package/dist/src/commands/init.js.map +1 -0
  21. package/dist/src/commands/preview.d.ts +3 -0
  22. package/dist/src/commands/preview.d.ts.map +1 -0
  23. package/dist/src/commands/preview.js +62 -0
  24. package/dist/src/commands/preview.js.map +1 -0
  25. package/dist/src/composition/CursorOverlay.d.ts +11 -0
  26. package/dist/src/composition/CursorOverlay.d.ts.map +1 -0
  27. package/dist/src/composition/CursorOverlay.js +29 -0
  28. package/dist/src/composition/CursorOverlay.js.map +1 -0
  29. package/dist/src/composition/DemoVideo.d.ts +8 -0
  30. package/dist/src/composition/DemoVideo.d.ts.map +1 -0
  31. package/dist/src/composition/DemoVideo.js +18 -0
  32. package/dist/src/composition/DemoVideo.js.map +1 -0
  33. package/dist/src/composition/NarrationTrack.d.ts +9 -0
  34. package/dist/src/composition/NarrationTrack.d.ts.map +1 -0
  35. package/dist/src/composition/NarrationTrack.js +8 -0
  36. package/dist/src/composition/NarrationTrack.js.map +1 -0
  37. package/dist/src/composition/cursor-path.d.ts +31 -0
  38. package/dist/src/composition/cursor-path.d.ts.map +1 -0
  39. package/dist/src/composition/cursor-path.js +78 -0
  40. package/dist/src/composition/cursor-path.js.map +1 -0
  41. package/dist/src/composition/remotion-root.d.ts +3 -0
  42. package/dist/src/composition/remotion-root.d.ts.map +1 -0
  43. package/dist/src/composition/remotion-root.js +34 -0
  44. package/dist/src/composition/remotion-root.js.map +1 -0
  45. package/dist/src/composition/render.d.ts +8 -0
  46. package/dist/src/composition/render.d.ts.map +1 -0
  47. package/dist/src/composition/render.js +24 -0
  48. package/dist/src/composition/render.js.map +1 -0
  49. package/dist/src/config/config-schema.d.ts +40 -0
  50. package/dist/src/config/config-schema.d.ts.map +1 -0
  51. package/dist/src/config/config-schema.js +13 -0
  52. package/dist/src/config/config-schema.js.map +1 -0
  53. package/dist/src/config/defaults.d.ts +4 -0
  54. package/dist/src/config/defaults.d.ts.map +1 -0
  55. package/dist/src/config/defaults.js +17 -0
  56. package/dist/src/config/defaults.js.map +1 -0
  57. package/dist/src/generator/prompts.d.ts +3 -0
  58. package/dist/src/generator/prompts.d.ts.map +1 -0
  59. package/dist/src/generator/prompts.js +125 -0
  60. package/dist/src/generator/prompts.js.map +1 -0
  61. package/dist/src/generator/scenario-generator.d.ts +46 -0
  62. package/dist/src/generator/scenario-generator.d.ts.map +1 -0
  63. package/dist/src/generator/scenario-generator.js +86 -0
  64. package/dist/src/generator/scenario-generator.js.map +1 -0
  65. package/dist/src/index.d.ts +7 -0
  66. package/dist/src/index.d.ts.map +1 -0
  67. package/dist/src/index.js +2 -0
  68. package/dist/src/index.js.map +1 -0
  69. package/dist/src/runtime/action-helpers.d.ts +19 -0
  70. package/dist/src/runtime/action-helpers.d.ts.map +1 -0
  71. package/dist/src/runtime/action-helpers.js +138 -0
  72. package/dist/src/runtime/action-helpers.js.map +1 -0
  73. package/dist/src/runtime/instrumented-page.d.ts +21 -0
  74. package/dist/src/runtime/instrumented-page.d.ts.map +1 -0
  75. package/dist/src/runtime/instrumented-page.js +48 -0
  76. package/dist/src/runtime/instrumented-page.js.map +1 -0
  77. package/dist/src/runtime/timeline-collector.d.ts +20 -0
  78. package/dist/src/runtime/timeline-collector.d.ts.map +1 -0
  79. package/dist/src/runtime/timeline-collector.js +40 -0
  80. package/dist/src/runtime/timeline-collector.js.map +1 -0
  81. package/dist/src/timeline/schema.d.ts +297 -0
  82. package/dist/src/timeline/schema.d.ts.map +1 -0
  83. package/dist/src/timeline/schema.js +72 -0
  84. package/dist/src/timeline/schema.js.map +1 -0
  85. package/dist/src/timeline/types.d.ts +67 -0
  86. package/dist/src/timeline/types.d.ts.map +1 -0
  87. package/dist/src/timeline/types.js +2 -0
  88. package/dist/src/timeline/types.js.map +1 -0
  89. package/dist/src/version.d.ts +2 -0
  90. package/dist/src/version.d.ts.map +1 -0
  91. package/dist/src/version.js +2 -0
  92. package/dist/src/version.js.map +1 -0
  93. package/dist/src/voiceover/narration-timing.d.ts +12 -0
  94. package/dist/src/voiceover/narration-timing.d.ts.map +1 -0
  95. package/dist/src/voiceover/narration-timing.js +25 -0
  96. package/dist/src/voiceover/narration-timing.js.map +1 -0
  97. package/dist/src/voiceover/piper-engine.d.ts +6 -0
  98. package/dist/src/voiceover/piper-engine.d.ts.map +1 -0
  99. package/dist/src/voiceover/piper-engine.js +48 -0
  100. package/dist/src/voiceover/piper-engine.js.map +1 -0
  101. package/dist/src/voiceover/voice-models.d.ts +18 -0
  102. package/dist/src/voiceover/voice-models.d.ts.map +1 -0
  103. package/dist/src/voiceover/voice-models.js +123 -0
  104. package/dist/src/voiceover/voice-models.js.map +1 -0
  105. package/dist/tsconfig.tsbuildinfo +1 -0
  106. package/package.json +69 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NarrationTrack.d.ts","sourceRoot":"","sources":["../../../src/composition/NarrationTrack.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,UAAU,KAAK;IACb,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAY1C,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Audio, Sequence } from 'remotion';
3
+ export const NarrationTrack = ({ narrations, fps }) => {
4
+ return (_jsx(_Fragment, { children: narrations
5
+ .filter(n => n.audioFile)
6
+ .map((n, i) => (_jsx(Sequence, { from: Math.round((n.timestampMs / 1000) * fps), children: _jsx(Audio, { src: n.audioFile }) }, n.id))) }));
7
+ };
8
+ //# sourceMappingURL=NarrationTrack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NarrationTrack.js","sourceRoot":"","sources":["../../../src/composition/NarrationTrack.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAc,MAAM,UAAU,CAAC;AAQvD,MAAM,CAAC,MAAM,cAAc,GAAoB,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE,EAAE;IACrE,OAAO,CACL,4BACG,UAAU;aACR,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;aACxB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CACb,KAAC,QAAQ,IAAY,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,YACjE,KAAC,KAAK,IAAC,GAAG,EAAE,CAAC,CAAC,SAAU,GAAI,IADf,CAAC,CAAC,EAAE,CAER,CACZ,CAAC,GACH,CACJ,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { CursorTargetEvent } from '../timeline/types.js';
2
+ interface Point {
3
+ x: number;
4
+ y: number;
5
+ }
6
+ export interface CursorWaypoint {
7
+ x: number;
8
+ y: number;
9
+ t: number;
10
+ }
11
+ /**
12
+ * Pre-compute waypoints along a bezier curve for a cursor movement event.
13
+ * Returns N waypoints (default 20) for smooth interpolation.
14
+ */
15
+ export declare function computeWaypoints(from: Point, to: Point, steps?: number): CursorWaypoint[];
16
+ export interface CursorEventWithWaypoints extends CursorTargetEvent {
17
+ waypoints: CursorWaypoint[];
18
+ }
19
+ /**
20
+ * Pre-compute waypoints for all cursor events in a timeline.
21
+ * Called once before rendering — Remotion components index into
22
+ * the waypoints array by progress.
23
+ */
24
+ export declare function precomputeCursorPaths(events: CursorTargetEvent[]): CursorEventWithWaypoints[];
25
+ /**
26
+ * Find cursor position at a given time in milliseconds.
27
+ * Searches through cursor events with precomputed waypoints.
28
+ */
29
+ export declare function getCursorPosition(events: CursorEventWithWaypoints[], timeMs: number): Point;
30
+ export {};
31
+ //# sourceMappingURL=cursor-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-path.d.ts","sourceRoot":"","sources":["../../../src/composition/cursor-path.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,UAAU,KAAK;IACb,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAmCD,MAAM,WAAW,cAAc;IAC7B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,SAAK,GAAG,cAAc,EAAE,CAcrF;AAED,MAAM,WAAW,wBAAyB,SAAQ,iBAAiB;IACjE,SAAS,EAAE,cAAc,EAAE,CAAC;CAC7B;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,wBAAwB,EAAE,CAQ7F;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,wBAAwB,EAAE,EAClC,MAAM,EAAE,MAAM,GACb,KAAK,CAwBP"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Generate bezier control points for natural cursor movement.
3
+ * Produces a slightly curved path with randomized control points
4
+ * to avoid perfectly straight lines.
5
+ */
6
+ function bezierControlPoints(from, to) {
7
+ const dx = to.x - from.x;
8
+ const dy = to.y - from.y;
9
+ // Perpendicular offset for curve (5-15% of distance)
10
+ const dist = Math.sqrt(dx * dx + dy * dy);
11
+ const offset = dist * (0.05 + Math.random() * 0.1);
12
+ // Normal vector
13
+ const nx = -dy / (dist || 1);
14
+ const ny = dx / (dist || 1);
15
+ // Place control points at 1/3 and 2/3 along the path with perpendicular offset
16
+ const sign = Math.random() > 0.5 ? 1 : -1;
17
+ return [
18
+ { x: from.x + dx * 0.33 + nx * offset * sign, y: from.y + dy * 0.33 + ny * offset * sign },
19
+ { x: from.x + dx * 0.66 - nx * offset * sign * 0.5, y: from.y + dy * 0.66 - ny * offset * sign * 0.5 },
20
+ ];
21
+ }
22
+ /**
23
+ * Evaluate cubic bezier at parameter t (0..1).
24
+ */
25
+ function cubicBezier(t, p0, p1, p2, p3) {
26
+ const u = 1 - t;
27
+ return u * u * u * p0 + 3 * u * u * t * p1 + 3 * u * t * t * p2 + t * t * t * p3;
28
+ }
29
+ /**
30
+ * Pre-compute waypoints along a bezier curve for a cursor movement event.
31
+ * Returns N waypoints (default 20) for smooth interpolation.
32
+ */
33
+ export function computeWaypoints(from, to, steps = 20) {
34
+ const [cp1, cp2] = bezierControlPoints(from, to);
35
+ const waypoints = [];
36
+ for (let i = 0; i <= steps; i++) {
37
+ const t = i / steps;
38
+ waypoints.push({
39
+ x: cubicBezier(t, from.x, cp1.x, cp2.x, to.x),
40
+ y: cubicBezier(t, from.y, cp1.y, cp2.y, to.y),
41
+ t,
42
+ });
43
+ }
44
+ return waypoints;
45
+ }
46
+ /**
47
+ * Pre-compute waypoints for all cursor events in a timeline.
48
+ * Called once before rendering — Remotion components index into
49
+ * the waypoints array by progress.
50
+ */
51
+ export function precomputeCursorPaths(events) {
52
+ return events.map(event => ({
53
+ ...event,
54
+ waypoints: computeWaypoints({ x: event.fromX, y: event.fromY }, { x: event.toX, y: event.toY }),
55
+ }));
56
+ }
57
+ /**
58
+ * Find cursor position at a given time in milliseconds.
59
+ * Searches through cursor events with precomputed waypoints.
60
+ */
61
+ export function getCursorPosition(events, timeMs) {
62
+ // Find active movement
63
+ const active = events.find(e => timeMs >= e.timestampMs && timeMs <= e.timestampMs + e.moveDurationMs);
64
+ if (active) {
65
+ const progress = (timeMs - active.timestampMs) / active.moveDurationMs;
66
+ const idx = Math.min(active.waypoints.length - 1, Math.floor(progress * (active.waypoints.length - 1)));
67
+ return active.waypoints[idx];
68
+ }
69
+ // Hold at last known position
70
+ const past = events.filter(e => e.timestampMs + e.moveDurationMs <= timeMs);
71
+ const last = past[past.length - 1];
72
+ if (last) {
73
+ return { x: last.toX, y: last.toY };
74
+ }
75
+ // Default: center of viewport
76
+ return { x: 640, y: 360 };
77
+ }
78
+ //# sourceMappingURL=cursor-path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-path.js","sourceRoot":"","sources":["../../../src/composition/cursor-path.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ export declare const RemotionRoot: React.FC;
3
+ //# sourceMappingURL=remotion-root.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remotion-root.d.ts","sourceRoot":"","sources":["../../../src/composition/remotion-root.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAU1B,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAyChC,CAAC"}
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Composition } from 'remotion';
3
+ import { z } from 'zod';
4
+ import { DemoVideo } from './DemoVideo.js';
5
+ import { timelineSchema } from '../timeline/schema.js';
6
+ const propsSchema = z.object({
7
+ timeline: timelineSchema,
8
+ });
9
+ export const RemotionRoot = () => {
10
+ return (_jsx(_Fragment, { children: _jsx(Composition, { id: "DemoVideo", lazyComponent: () => Promise.resolve({ default: DemoVideo }), schema: propsSchema, durationInFrames: 300, fps: 30, width: 1280, height: 720, defaultProps: {
11
+ timeline: {
12
+ version: 1,
13
+ metadata: {
14
+ testFile: '',
15
+ scenarioFile: '',
16
+ recordedAt: new Date().toISOString(),
17
+ viewport: { width: 1280, height: 720 },
18
+ videoDurationMs: 0,
19
+ videoFile: '',
20
+ },
21
+ events: [],
22
+ },
23
+ }, calculateMetadata: ({ props }) => {
24
+ const fps = 30;
25
+ const durationInFrames = Math.max(30, Math.ceil((props.timeline.metadata.videoDurationMs / 1000) * fps));
26
+ return {
27
+ durationInFrames,
28
+ fps,
29
+ width: props.timeline.metadata.viewport.width,
30
+ height: props.timeline.metadata.viewport.height,
31
+ };
32
+ } }) }));
33
+ };
34
+ //# sourceMappingURL=remotion-root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remotion-root.js","sourceRoot":"","sources":["../../../src/composition/remotion-root.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,QAAQ,EAAE,cAAc;CACzB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,YAAY,GAAa,GAAG,EAAE;IACzC,OAAO,CACL,4BACE,KAAC,WAAW,IACV,EAAE,EAAC,WAAW,EACd,aAAa,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,SAAgB,EAAE,CAAC,EACnE,MAAM,EAAE,WAAW,EACnB,gBAAgB,EAAE,GAAG,EACrB,GAAG,EAAE,EAAE,EACP,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,GAAG,EACX,YAAY,EAAE;gBACZ,QAAQ,EAAE;oBACR,OAAO,EAAE,CAAU;oBACnB,QAAQ,EAAE;wBACR,QAAQ,EAAE,EAAE;wBACZ,YAAY,EAAE,EAAE;wBAChB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;wBACpC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;wBACtC,eAAe,EAAE,CAAC;wBAClB,SAAS,EAAE,EAAE;qBACd;oBACD,MAAM,EAAE,EAAE;iBACX;aACF,EACD,iBAAiB,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE;gBAC/B,MAAM,GAAG,GAAG,EAAE,CAAC;gBACf,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAC/B,EAAE,EACF,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,CAClE,CAAC;gBACF,OAAO;oBACL,gBAAgB;oBAChB,GAAG;oBACH,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK;oBAC7C,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM;iBAChD,CAAC;YACJ,CAAC,GACD,GACD,CACJ,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { Timeline } from '../timeline/types.js';
2
+ export interface RenderOptions {
3
+ timeline: Timeline;
4
+ outputPath: string;
5
+ entryPoint?: string;
6
+ }
7
+ export declare function renderDemoVideo(opts: RenderOptions): Promise<string>;
8
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/composition/render.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAuB1E"}
@@ -0,0 +1,24 @@
1
+ import { bundle } from '@remotion/bundler';
2
+ import { renderMedia, selectComposition } from '@remotion/renderer';
3
+ import { resolve } from 'node:path';
4
+ export async function renderDemoVideo(opts) {
5
+ const entryPoint = opts.entryPoint ?? resolve(import.meta.dirname, 'remotion-root.js');
6
+ const bundlePath = await bundle({
7
+ entryPoint,
8
+ // Remotion bundles with webpack — no extra config needed
9
+ });
10
+ const composition = await selectComposition({
11
+ serveUrl: bundlePath,
12
+ id: 'DemoVideo',
13
+ inputProps: { timeline: opts.timeline },
14
+ });
15
+ await renderMedia({
16
+ composition,
17
+ serveUrl: bundlePath,
18
+ codec: 'h264',
19
+ outputLocation: opts.outputPath,
20
+ inputProps: { timeline: opts.timeline },
21
+ });
22
+ return opts.outputPath;
23
+ }
24
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/composition/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAmB;IACvD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;IAEvF,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC;QAC9B,UAAU;QACV,yDAAyD;KAC1D,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,MAAM,iBAAiB,CAAC;QAC1C,QAAQ,EAAE,UAAU;QACpB,EAAE,EAAE,WAAW;QACf,UAAU,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE;KACxC,CAAC,CAAC;IAEH,MAAM,WAAW,CAAC;QAChB,WAAW;QACX,QAAQ,EAAE,UAAU;QACpB,KAAK,EAAE,MAAM;QACb,cAAc,EAAE,IAAI,CAAC,UAAU;QAC/B,UAAU,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE;KACxC,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC,UAAU,CAAC;AACzB,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { z } from 'zod';
2
+ export declare const configSchema: z.ZodObject<{
3
+ voice: z.ZodDefault<z.ZodString>;
4
+ resolution: z.ZodDefault<z.ZodObject<{
5
+ width: z.ZodNumber;
6
+ height: z.ZodNumber;
7
+ }, "strip", z.ZodTypeAny, {
8
+ width: number;
9
+ height: number;
10
+ }, {
11
+ width: number;
12
+ height: number;
13
+ }>>;
14
+ outputDir: z.ZodDefault<z.ZodString>;
15
+ locale: z.ZodDefault<z.ZodString>;
16
+ colorScheme: z.ZodDefault<z.ZodEnum<["light", "dark"]>>;
17
+ timezoneId: z.ZodDefault<z.ZodString>;
18
+ }, "strip", z.ZodTypeAny, {
19
+ voice: string;
20
+ resolution: {
21
+ width: number;
22
+ height: number;
23
+ };
24
+ outputDir: string;
25
+ locale: string;
26
+ colorScheme: "light" | "dark";
27
+ timezoneId: string;
28
+ }, {
29
+ voice?: string | undefined;
30
+ resolution?: {
31
+ width: number;
32
+ height: number;
33
+ } | undefined;
34
+ outputDir?: string | undefined;
35
+ locale?: string | undefined;
36
+ colorScheme?: "light" | "dark" | undefined;
37
+ timezoneId?: string | undefined;
38
+ }>;
39
+ export type ScreenwrightConfig = z.infer<typeof configSchema>;
40
+ //# sourceMappingURL=config-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-schema.d.ts","sourceRoot":"","sources":["../../../src/config/config-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUvB,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { z } from 'zod';
2
+ export const configSchema = z.object({
3
+ voice: z.string().default('en_US-amy-medium'),
4
+ resolution: z.object({
5
+ width: z.number().int().positive(),
6
+ height: z.number().int().positive(),
7
+ }).default({ width: 1280, height: 720 }),
8
+ outputDir: z.string().default('./output'),
9
+ locale: z.string().default('en-US'),
10
+ colorScheme: z.enum(['light', 'dark']).default('light'),
11
+ timezoneId: z.string().default('America/New_York'),
12
+ });
13
+ //# sourceMappingURL=config-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-schema.js","sourceRoot":"","sources":["../../../src/config/config-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,kBAAkB,CAAC;IAC7C,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;QACnB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;QAClC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;KACpC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IACxC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC;IACzC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC;IACnC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IACvD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,kBAAkB,CAAC;CACnD,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ScreenwrightConfig } from './config-schema.js';
2
+ export declare const defaultConfig: ScreenwrightConfig;
3
+ export declare function serializeConfig(config: ScreenwrightConfig): string;
4
+ //# sourceMappingURL=defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../../src/config/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,eAAO,MAAM,aAAa,EAAE,kBAO3B,CAAC;AAEF,wBAAgB,eAAe,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAOlE"}
@@ -0,0 +1,17 @@
1
+ export const defaultConfig = {
2
+ voice: 'en_US-amy-medium',
3
+ resolution: { width: 1280, height: 720 },
4
+ outputDir: './output',
5
+ locale: 'en-US',
6
+ colorScheme: 'light',
7
+ timezoneId: 'America/New_York',
8
+ };
9
+ export function serializeConfig(config) {
10
+ return `import type { ScreenwrightConfig } from 'screenwright';
11
+
12
+ const config: ScreenwrightConfig = ${JSON.stringify(config, null, 2)};
13
+
14
+ export default config;
15
+ `;
16
+ }
17
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../../src/config/defaults.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAAuB;IAC/C,KAAK,EAAE,kBAAkB;IACzB,UAAU,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;IACxC,SAAS,EAAE,UAAU;IACrB,MAAM,EAAE,OAAO;IACf,WAAW,EAAE,OAAO;IACpB,UAAU,EAAE,kBAAkB;CAC/B,CAAC;AAEF,MAAM,UAAU,eAAe,CAAC,MAA0B;IACxD,OAAO;;qCAE4B,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;;;CAGnE,CAAC;AACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare const SYSTEM_PROMPT = "You are Screenwright, a tool that converts Playwright E2E tests into cinematic product demo scenarios.\n\nGiven a Playwright test file, generate a new TypeScript file that uses the Screenwright helpers API to create a polished demo with:\n- Natural pacing (deliberate waits between actions)\n- Human-friendly data (replace faker/test data with realistic values)\n- Narration cues (explain what's happening to the viewer)\n- Scene boundaries (organize the demo into logical chapters)\n\nThe output must be a valid TypeScript file with a default export function that takes a ScreenwrightHelpers parameter.\n\nAvailable helpers:\n- sw.scene(title, description?) \u2014 mark a scene/chapter boundary\n- sw.navigate(url, { narration? }) \u2014 navigate to a URL\n- sw.click(selector, { narration? }) \u2014 click an element\n- sw.fill(selector, value, { narration? }) \u2014 type into an input\n- sw.hover(selector, { narration? }) \u2014 hover over an element\n- sw.press(key, { narration? }) \u2014 press a keyboard key\n- sw.wait(ms) \u2014 pause for pacing\n- sw.narrate(text) \u2014 speak narration without an action\n\nRules:\n1. Import only ScreenwrightHelpers from 'screenwright'\n2. Export a default async function\n3. Replace ALL test/faker data with realistic human-friendly values\n4. Add narration to key actions explaining what the user is doing\n5. Add sw.wait() calls between logical steps for pacing\n6. Use sw.scene() to organize into 2-5 scenes\n7. Keep the same user flow as the original test\n8. Do NOT include assertions \u2014 this is a demo, not a test\n\nOutput format \u2014 always follow this exact structure:\n\n```typescript\nimport type { ScreenwrightHelpers } from 'screenwright';\n\nexport default async function scenario(sw: ScreenwrightHelpers) {\n // Scene 1\n await sw.scene('...');\n // ... actions ...\n\n // Scene 2\n await sw.scene('...');\n // ... actions ...\n}\n```\n\nCommon mistakes \u2014 do NOT do any of these:\n- Do NOT use page.click(), page.fill(), or any page.* methods \u2014 use sw.click(), sw.fill() etc.\n- Do NOT import expect, assert, or any test library\n- Do NOT call expect() or assert()\n- Do NOT use page.evaluate() or page.waitForSelector()\n- Do NOT add test(), describe(), or beforeEach() wrappers\n- Do NOT use page.locator().click() \u2014 use sw.click(selector) directly";
2
+ export declare function buildUserPrompt(testSource: string, narrationStyle: 'brief' | 'detailed', appDescription?: string): string;
3
+ //# sourceMappingURL=prompts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../../src/generator/prompts.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,yyEAoD4C,CAAC;AAEvE,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,OAAO,GAAG,UAAU,EACpC,cAAc,CAAC,EAAE,MAAM,GACtB,MAAM,CAuER"}
@@ -0,0 +1,125 @@
1
+ export const SYSTEM_PROMPT = `You are Screenwright, a tool that converts Playwright E2E tests into cinematic product demo scenarios.
2
+
3
+ Given a Playwright test file, generate a new TypeScript file that uses the Screenwright helpers API to create a polished demo with:
4
+ - Natural pacing (deliberate waits between actions)
5
+ - Human-friendly data (replace faker/test data with realistic values)
6
+ - Narration cues (explain what's happening to the viewer)
7
+ - Scene boundaries (organize the demo into logical chapters)
8
+
9
+ The output must be a valid TypeScript file with a default export function that takes a ScreenwrightHelpers parameter.
10
+
11
+ Available helpers:
12
+ - sw.scene(title, description?) — mark a scene/chapter boundary
13
+ - sw.navigate(url, { narration? }) — navigate to a URL
14
+ - sw.click(selector, { narration? }) — click an element
15
+ - sw.fill(selector, value, { narration? }) — type into an input
16
+ - sw.hover(selector, { narration? }) — hover over an element
17
+ - sw.press(key, { narration? }) — press a keyboard key
18
+ - sw.wait(ms) — pause for pacing
19
+ - sw.narrate(text) — speak narration without an action
20
+
21
+ Rules:
22
+ 1. Import only ScreenwrightHelpers from 'screenwright'
23
+ 2. Export a default async function
24
+ 3. Replace ALL test/faker data with realistic human-friendly values
25
+ 4. Add narration to key actions explaining what the user is doing
26
+ 5. Add sw.wait() calls between logical steps for pacing
27
+ 6. Use sw.scene() to organize into 2-5 scenes
28
+ 7. Keep the same user flow as the original test
29
+ 8. Do NOT include assertions — this is a demo, not a test
30
+
31
+ Output format — always follow this exact structure:
32
+
33
+ \`\`\`typescript
34
+ import type { ScreenwrightHelpers } from 'screenwright';
35
+
36
+ export default async function scenario(sw: ScreenwrightHelpers) {
37
+ // Scene 1
38
+ await sw.scene('...');
39
+ // ... actions ...
40
+
41
+ // Scene 2
42
+ await sw.scene('...');
43
+ // ... actions ...
44
+ }
45
+ \`\`\`
46
+
47
+ Common mistakes — do NOT do any of these:
48
+ - Do NOT use page.click(), page.fill(), or any page.* methods — use sw.click(), sw.fill() etc.
49
+ - Do NOT import expect, assert, or any test library
50
+ - Do NOT call expect() or assert()
51
+ - Do NOT use page.evaluate() or page.waitForSelector()
52
+ - Do NOT add test(), describe(), or beforeEach() wrappers
53
+ - Do NOT use page.locator().click() — use sw.click(selector) directly`;
54
+ export function buildUserPrompt(testSource, narrationStyle, appDescription) {
55
+ let prompt = `Convert this Playwright test into a Screenwright demo scenario.
56
+
57
+ Narration style: ${narrationStyle}
58
+ ${narrationStyle === 'brief' ? 'Use short, concise narration (1 sentence per narration cue).' : 'Use detailed narration that explains each step clearly.'}
59
+
60
+ ${appDescription ? `App context: ${appDescription}\n\n` : ''}Here is an example conversion for reference:
61
+
62
+ Input:
63
+ \`\`\`typescript
64
+ import { test, expect } from '@playwright/test';
65
+
66
+ test('checkout flow', async ({ page }) => {
67
+ await page.goto('http://localhost:3000');
68
+ await page.click('[data-testid="product-laptop"]');
69
+ await page.click('[data-testid="add-to-cart"]');
70
+ await page.fill('[data-testid="email"]', 'test@example.com');
71
+ await page.click('[data-testid="checkout"]');
72
+ await expect(page.locator('.confirmation')).toBeVisible();
73
+ });
74
+ \`\`\`
75
+
76
+ Output:
77
+ \`\`\`typescript
78
+ import type { ScreenwrightHelpers } from 'screenwright';
79
+
80
+ export default async function scenario(sw: ScreenwrightHelpers) {
81
+ await sw.scene('Shopping for a Laptop');
82
+ await sw.navigate('http://localhost:3000', {
83
+ narration: 'Let\\'s browse the electronics store.',
84
+ });
85
+ await sw.wait(1500);
86
+
87
+ await sw.click('[data-testid="product-laptop"]', {
88
+ narration: 'We\\'ll select the MacBook Pro.',
89
+ });
90
+ await sw.wait(1000);
91
+
92
+ await sw.scene('Adding to Cart');
93
+ await sw.click('[data-testid="add-to-cart"]', {
94
+ narration: 'Add it to our cart.',
95
+ });
96
+ await sw.wait(1500);
97
+
98
+ await sw.scene('Checkout');
99
+ await sw.fill('[data-testid="email"]', 'sarah.chen@acme.co', {
100
+ narration: 'Enter our email address for the order confirmation.',
101
+ });
102
+ await sw.wait(1000);
103
+
104
+ await sw.click('[data-testid="checkout"]', {
105
+ narration: 'Complete the purchase.',
106
+ });
107
+ await sw.wait(2000);
108
+ await sw.narrate('The order has been confirmed successfully.');
109
+ }
110
+ \`\`\`
111
+
112
+ Edge cases:
113
+ - If the test file has multiple tests, convert only the first test
114
+ - If there is a beforeEach hook, incorporate its actions at the start of the scenario
115
+ - Simplify complex locators to CSS selectors where possible
116
+
117
+ Now convert this test:
118
+ \`\`\`typescript
119
+ ${testSource}
120
+ \`\`\`
121
+
122
+ Generate the complete TypeScript scenario file:`;
123
+ return prompt;
124
+ }
125
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../../src/generator/prompts.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sEAoDyC,CAAC;AAEvE,MAAM,UAAU,eAAe,CAC7B,UAAkB,EAClB,cAAoC,EACpC,cAAuB;IAEvB,IAAI,MAAM,GAAG;;mBAEI,cAAc;EAC/B,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,8DAA8D,CAAC,CAAC,CAAC,yDAAyD;;EAEvJ,cAAc,CAAC,CAAC,CAAC,gBAAgB,cAAc,MAAM,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2D1D,UAAU;;;gDAGoC,CAAC;IAE/C,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,46 @@
1
+ export interface GenerateOptions {
2
+ testPath: string;
3
+ narrationStyle: 'brief' | 'detailed';
4
+ appDescription?: string;
5
+ }
6
+ export interface GenerateResult {
7
+ scenarioSource: string;
8
+ systemPrompt: string;
9
+ userPrompt: string;
10
+ }
11
+ export interface ValidationError {
12
+ code: string;
13
+ message: string;
14
+ }
15
+ export interface ValidationResult {
16
+ valid: boolean;
17
+ errors: ValidationError[];
18
+ warnings: ValidationError[];
19
+ }
20
+ /**
21
+ * Read a Playwright test file and produce the LLM prompt pair
22
+ * needed to generate a demo scenario.
23
+ *
24
+ * The actual LLM call happens in the skill layer (Claude Code),
25
+ * not here. This function prepares the prompt and returns it
26
+ * along with any post-processing.
27
+ *
28
+ * When used standalone (without the skill), the user can pipe
29
+ * the prompts to any LLM.
30
+ */
31
+ export declare function prepareGeneration(opts: GenerateOptions): Promise<{
32
+ systemPrompt: string;
33
+ userPrompt: string;
34
+ }>;
35
+ /**
36
+ * Extract TypeScript code from an LLM response that may contain
37
+ * markdown code fences. When multiple fences are found, returns the
38
+ * longest one (LLMs often produce a small snippet first, then the full code).
39
+ */
40
+ export declare function extractScenarioCode(llmResponse: string): string;
41
+ /**
42
+ * Validate that a string looks like a valid Screenwright scenario.
43
+ * Returns errors (fatal) and warnings (non-fatal).
44
+ */
45
+ export declare function validateScenarioCode(code: string): ValidationResult;
46
+ //# sourceMappingURL=scenario-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scenario-generator.d.ts","sourceRoot":"","sources":["../../../src/generator/scenario-generator.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,OAAO,GAAG,UAAU,CAAC;IACrC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC;IACtE,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC,CAOD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAgB/D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAkDnE"}
@@ -0,0 +1,86 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { SYSTEM_PROMPT, buildUserPrompt } from './prompts.js';
3
+ /**
4
+ * Read a Playwright test file and produce the LLM prompt pair
5
+ * needed to generate a demo scenario.
6
+ *
7
+ * The actual LLM call happens in the skill layer (Claude Code),
8
+ * not here. This function prepares the prompt and returns it
9
+ * along with any post-processing.
10
+ *
11
+ * When used standalone (without the skill), the user can pipe
12
+ * the prompts to any LLM.
13
+ */
14
+ export async function prepareGeneration(opts) {
15
+ const testSource = await readFile(opts.testPath, 'utf-8');
16
+ return {
17
+ systemPrompt: SYSTEM_PROMPT,
18
+ userPrompt: buildUserPrompt(testSource, opts.narrationStyle, opts.appDescription),
19
+ };
20
+ }
21
+ /**
22
+ * Extract TypeScript code from an LLM response that may contain
23
+ * markdown code fences. When multiple fences are found, returns the
24
+ * longest one (LLMs often produce a small snippet first, then the full code).
25
+ */
26
+ export function extractScenarioCode(llmResponse) {
27
+ const fenceRegex = /```(?:typescript|ts)?[\r\n]+([\s\S]*?)```/g;
28
+ let best = null;
29
+ let match;
30
+ while ((match = fenceRegex.exec(llmResponse)) !== null) {
31
+ const code = match[1].trim();
32
+ if (best === null || code.length > best.length) {
33
+ best = code;
34
+ }
35
+ }
36
+ if (best !== null)
37
+ return best;
38
+ // No fence found — assume the whole response is code
39
+ return llmResponse.trim();
40
+ }
41
+ /**
42
+ * Validate that a string looks like a valid Screenwright scenario.
43
+ * Returns errors (fatal) and warnings (non-fatal).
44
+ */
45
+ export function validateScenarioCode(code) {
46
+ const errors = [];
47
+ const warnings = [];
48
+ // Empty input
49
+ if (!code || !code.trim()) {
50
+ return { valid: false, errors: [{ code: 'EMPTY_INPUT', message: 'Scenario code is empty' }], warnings: [] };
51
+ }
52
+ // Missing ScreenwrightHelpers import
53
+ if (!/import\s+(?:type\s+)?\{[^}]*(?:type\s+)?ScreenwrightHelpers[^}]*\}\s+from\s+['"]screenwright['"]/.test(code)) {
54
+ errors.push({ code: 'MISSING_IMPORT', message: 'Missing ScreenwrightHelpers import from screenwright' });
55
+ }
56
+ // Missing default async export
57
+ if (!/export\s+default\s+async\s+function/.test(code)) {
58
+ errors.push({ code: 'MISSING_DEFAULT_EXPORT', message: 'Missing export default async function' });
59
+ }
60
+ // Raw page.* calls (but not sw.page.*)
61
+ if (/(?<!sw\.)(?<!\w\.)page\.\w+\s*\(/.test(code)) {
62
+ errors.push({ code: 'RAW_PAGE_CALL', message: 'Raw page.*() calls are not allowed — use sw.* helpers instead' });
63
+ }
64
+ // Assertion imports
65
+ if (/(?:import|require)[^;]*(?:expect|assert)/.test(code)) {
66
+ errors.push({ code: 'ASSERTION_IMPORT', message: 'Assertion library imports are not allowed in scenarios' });
67
+ }
68
+ // Assertion calls
69
+ if (/\b(?:expect|assert)\s*[.(]/.test(code)) {
70
+ errors.push({ code: 'ASSERTION_CALL', message: 'Assertion calls (expect/assert) are not allowed in scenarios' });
71
+ }
72
+ // Warnings — no scenes
73
+ if (!/\.scene\s*\(/.test(code)) {
74
+ warnings.push({ code: 'NO_SCENES', message: 'No sw.scene() calls found — consider adding scene boundaries' });
75
+ }
76
+ // Warnings — no waits
77
+ if (!/\.wait\s*\(/.test(code)) {
78
+ warnings.push({ code: 'NO_WAITS', message: 'No sw.wait() calls found — consider adding pacing' });
79
+ }
80
+ // Warnings — no narration
81
+ if (!/narration/.test(code)) {
82
+ warnings.push({ code: 'NO_NARRATION', message: 'No narration found — consider adding narration to actions' });
83
+ }
84
+ return { valid: errors.length === 0, errors, warnings };
85
+ }
86
+ //# sourceMappingURL=scenario-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scenario-generator.js","sourceRoot":"","sources":["../../../src/generator/scenario-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export type { ScreenwrightHelpers, ActionOptions } from './runtime/action-helpers.js';
2
+ export type { ScenarioFn } from './runtime/instrumented-page.js';
3
+ export type { Timeline, TimelineEvent, SceneEvent, ActionEvent, CursorTargetEvent, NarrationEvent, WaitEvent } from './timeline/types.js';
4
+ export type { ScreenwrightConfig } from './config/config-schema.js';
5
+ export { validateScenarioCode, extractScenarioCode } from './generator/scenario-generator.js';
6
+ export type { ValidationResult, ValidationError, GenerateOptions } from './generator/scenario-generator.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACtF,YAAY,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAC1I,YAAY,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAC9F,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { validateScenarioCode, extractScenarioCode } from './generator/scenario-generator.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC"}