playwriter 0.0.80 → 0.0.89
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/a11y-client.js +18 -8
- package/dist/aria-snapshot.d.ts.map +1 -1
- package/dist/aria-snapshot.js +3 -1
- package/dist/aria-snapshot.js.map +1 -1
- package/dist/bippy.js +1 -1
- package/dist/cdp-relay.d.ts.map +1 -1
- package/dist/cdp-relay.js +84 -0
- package/dist/cdp-relay.js.map +1 -1
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +8 -6
- package/dist/executor.js.map +1 -1
- package/dist/ffmpeg.d.ts +6 -6
- package/dist/ffmpeg.d.ts.map +1 -1
- package/dist/ffmpeg.js +6 -6
- package/dist/ffmpeg.js.map +1 -1
- package/dist/ghost-cursor-client.js +15 -9
- package/dist/prompt.md +71 -337
- package/dist/readability.js +16 -2
- package/dist/recording-ghost-cursor.d.ts.map +1 -1
- package/dist/recording-ghost-cursor.js +1 -1
- package/dist/recording-ghost-cursor.js.map +1 -1
- package/dist/relay-client.js +1 -1
- package/dist/relay-client.js.map +1 -1
- package/dist/relay-core.test.d.ts.map +1 -1
- package/dist/relay-core.test.js +344 -16
- package/dist/relay-core.test.js.map +1 -1
- package/dist/relay-navigation.test.d.ts.map +1 -1
- package/dist/relay-navigation.test.js +115 -0
- package/dist/relay-navigation.test.js.map +1 -1
- package/dist/screen-recording.d.ts +24 -0
- package/dist/screen-recording.d.ts.map +1 -1
- package/dist/screen-recording.js +62 -0
- package/dist/screen-recording.js.map +1 -1
- package/dist/screen-recording.test.d.ts +2 -0
- package/dist/screen-recording.test.d.ts.map +1 -0
- package/dist/screen-recording.test.js +102 -0
- package/dist/screen-recording.test.js.map +1 -0
- package/dist/selector-generator.js +1 -1
- package/package.json +2 -2
- package/src/aria-snapshot.ts +3 -1
- package/src/aria-snapshots/github-interactive.txt +2 -0
- package/src/aria-snapshots/github-raw.txt +4 -0
- package/src/aria-snapshots/hackernews-interactive.txt +238 -241
- package/src/aria-snapshots/hackernews-raw.txt +267 -271
- package/src/assets/aria-labels-hacker-news.png +0 -0
- package/src/cdp-relay.ts +110 -0
- package/src/executor.ts +8 -6
- package/src/ffmpeg.ts +8 -8
- package/src/ghost-cursor-client.ts +3 -2
- package/src/recording-ghost-cursor.ts +7 -1
- package/src/relay-client.ts +1 -1
- package/src/relay-core.test.ts +378 -17
- package/src/relay-navigation.test.ts +132 -0
- package/src/screen-recording.test.ts +111 -0
- package/src/screen-recording.ts +81 -0
- package/src/skill.md +71 -339
- package/src/snapshots/shadcn-ui-accessibility-full.md +182 -180
- package/src/snapshots/shadcn-ui-accessibility-interactive.md +120 -118
|
Binary file
|
package/src/cdp-relay.ts
CHANGED
|
@@ -83,6 +83,7 @@ export async function startPlayWriterCDPRelayServer({
|
|
|
83
83
|
} = {}): Promise<RelayServer> {
|
|
84
84
|
const emitter = new EventEmitter()
|
|
85
85
|
const store = relayState.createRelayStore()
|
|
86
|
+
const extensionDownloadBehavior = new Map<string, Protocol.Browser.SetDownloadBehaviorRequest>()
|
|
86
87
|
|
|
87
88
|
const resolvedCdpLogger = cdpLogger || createCdpLogger()
|
|
88
89
|
const logCdpJson = (entry: CdpLogEntry) => {
|
|
@@ -557,6 +558,92 @@ export async function startPlayWriterCDPRelayServer({
|
|
|
557
558
|
}
|
|
558
559
|
}
|
|
559
560
|
|
|
561
|
+
function getPageTargetSessionIds({ extensionId }: { extensionId: string }): string[] {
|
|
562
|
+
const extensionState = store.getState().extensions.get(extensionId)
|
|
563
|
+
if (!extensionState) {
|
|
564
|
+
return []
|
|
565
|
+
}
|
|
566
|
+
return Array.from(extensionState.connectedTargets.values())
|
|
567
|
+
.filter((target) => {
|
|
568
|
+
return target.targetInfo.type === 'page'
|
|
569
|
+
})
|
|
570
|
+
.map((target) => {
|
|
571
|
+
return target.sessionId
|
|
572
|
+
})
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
function maybeEmitBrowserDownloadCompatEvent({
|
|
576
|
+
method,
|
|
577
|
+
params,
|
|
578
|
+
extensionId,
|
|
579
|
+
}: {
|
|
580
|
+
method: string
|
|
581
|
+
params: unknown
|
|
582
|
+
extensionId: string
|
|
583
|
+
}): void {
|
|
584
|
+
const browserEventMethod =
|
|
585
|
+
method === 'Page.downloadWillBegin'
|
|
586
|
+
? 'Browser.downloadWillBegin'
|
|
587
|
+
: method === 'Page.downloadProgress'
|
|
588
|
+
? 'Browser.downloadProgress'
|
|
589
|
+
: null
|
|
590
|
+
if (!browserEventMethod) {
|
|
591
|
+
return
|
|
592
|
+
}
|
|
593
|
+
sendToPlaywright({
|
|
594
|
+
message: {
|
|
595
|
+
method: browserEventMethod,
|
|
596
|
+
params,
|
|
597
|
+
} as CDPEventBase,
|
|
598
|
+
source: 'server',
|
|
599
|
+
extensionId,
|
|
600
|
+
})
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
async function applyDownloadBehaviorToTargets({
|
|
604
|
+
extensionId,
|
|
605
|
+
behavior,
|
|
606
|
+
source,
|
|
607
|
+
targetSessionIds,
|
|
608
|
+
}: {
|
|
609
|
+
extensionId: string
|
|
610
|
+
behavior: Protocol.Browser.SetDownloadBehaviorRequest
|
|
611
|
+
source?: CDPCommand['source']
|
|
612
|
+
targetSessionIds?: string[]
|
|
613
|
+
}): Promise<void> {
|
|
614
|
+
const pageBehavior: Protocol.Page.SetDownloadBehaviorRequest['behavior'] =
|
|
615
|
+
behavior.behavior === 'allowAndName' ? 'allow' : behavior.behavior
|
|
616
|
+
const pageParams: Protocol.Page.SetDownloadBehaviorRequest = (() => {
|
|
617
|
+
if (pageBehavior === 'allow' && behavior.downloadPath) {
|
|
618
|
+
return { behavior: pageBehavior, downloadPath: behavior.downloadPath }
|
|
619
|
+
}
|
|
620
|
+
return { behavior: pageBehavior }
|
|
621
|
+
})()
|
|
622
|
+
const sessions = targetSessionIds || getPageTargetSessionIds({ extensionId })
|
|
623
|
+
if (sessions.length === 0) {
|
|
624
|
+
return
|
|
625
|
+
}
|
|
626
|
+
await Promise.all(
|
|
627
|
+
sessions.map(async (targetSessionId) => {
|
|
628
|
+
try {
|
|
629
|
+
await sendToExtension({
|
|
630
|
+
extensionId,
|
|
631
|
+
method: 'forwardCDPCommand',
|
|
632
|
+
params: {
|
|
633
|
+
sessionId: targetSessionId,
|
|
634
|
+
method: 'Page.setDownloadBehavior',
|
|
635
|
+
params: pageParams,
|
|
636
|
+
source,
|
|
637
|
+
},
|
|
638
|
+
})
|
|
639
|
+
} catch (error) {
|
|
640
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
641
|
+
logger?.log(pc.yellow(`[Server] Failed to apply Page.setDownloadBehavior to ${targetSessionId}: ${message}`))
|
|
642
|
+
}
|
|
643
|
+
}),
|
|
644
|
+
)
|
|
645
|
+
}
|
|
646
|
+
|
|
560
647
|
async function routeCdpCommand({
|
|
561
648
|
extensionId,
|
|
562
649
|
method,
|
|
@@ -585,6 +672,18 @@ export async function startPlayWriterCDPRelayServer({
|
|
|
585
672
|
}
|
|
586
673
|
|
|
587
674
|
case 'Browser.setDownloadBehavior': {
|
|
675
|
+
const downloadBehaviorParams = params as Protocol.Browser.SetDownloadBehaviorRequest | undefined
|
|
676
|
+
if (!downloadBehaviorParams?.behavior) {
|
|
677
|
+
throw new Error('behavior is required for Browser.setDownloadBehavior')
|
|
678
|
+
}
|
|
679
|
+
if (resolvedExtensionId) {
|
|
680
|
+
extensionDownloadBehavior.set(resolvedExtensionId, downloadBehaviorParams)
|
|
681
|
+
await applyDownloadBehaviorToTargets({
|
|
682
|
+
extensionId: resolvedExtensionId,
|
|
683
|
+
behavior: downloadBehaviorParams,
|
|
684
|
+
source,
|
|
685
|
+
})
|
|
686
|
+
}
|
|
588
687
|
return {}
|
|
589
688
|
}
|
|
590
689
|
|
|
@@ -1336,6 +1435,8 @@ export async function startPlayWriterCDPRelayServer({
|
|
|
1336
1435
|
const cdpEvent: CDPEventBase = { method, sessionId, params }
|
|
1337
1436
|
emitter.emit('cdp:event', { event: cdpEvent, sessionId })
|
|
1338
1437
|
|
|
1438
|
+
maybeEmitBrowserDownloadCompatEvent({ method, params, extensionId: connectionId })
|
|
1439
|
+
|
|
1339
1440
|
if (method === 'Target.attachedToTarget') {
|
|
1340
1441
|
const targetParams = params as Protocol.Target.AttachedToTargetEvent
|
|
1341
1442
|
const incomingSessionId = sessionId
|
|
@@ -1396,6 +1497,15 @@ export async function startPlayWriterCDPRelayServer({
|
|
|
1396
1497
|
}),
|
|
1397
1498
|
)
|
|
1398
1499
|
|
|
1500
|
+
const cachedDownloadBehavior = extensionDownloadBehavior.get(connectionId)
|
|
1501
|
+
if (cachedDownloadBehavior && targetParams.targetInfo.type === 'page') {
|
|
1502
|
+
void applyDownloadBehaviorToTargets({
|
|
1503
|
+
extensionId: connectionId,
|
|
1504
|
+
behavior: cachedDownloadBehavior,
|
|
1505
|
+
targetSessionIds: [targetParams.sessionId],
|
|
1506
|
+
})
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1399
1509
|
// Only forward to Playwright if this is a new target to avoid duplicates
|
|
1400
1510
|
if (!alreadyConnected) {
|
|
1401
1511
|
sendToPlaywright({
|
package/src/executor.ts
CHANGED
|
@@ -592,9 +592,10 @@ export class PlaywrightExecutor {
|
|
|
592
592
|
const contexts = browser.contexts()
|
|
593
593
|
const context = contexts.length > 0 ? contexts[0] : await browser.newContext()
|
|
594
594
|
|
|
595
|
-
// Action timeout (click, fill, hover, etc.) is
|
|
596
|
-
//
|
|
597
|
-
|
|
595
|
+
// Action timeout (click, fill, hover, etc.) is longer to tolerate slower
|
|
596
|
+
// SPA/Turbo navigations and post-click settling on real sites.
|
|
597
|
+
// Navigation timeout (goto, reload) remains separate.
|
|
598
|
+
context.setDefaultTimeout(60000)
|
|
598
599
|
context.setDefaultNavigationTimeout(10000)
|
|
599
600
|
|
|
600
601
|
context.on('page', (page) => {
|
|
@@ -674,9 +675,10 @@ export class PlaywrightExecutor {
|
|
|
674
675
|
const contexts = browser.contexts()
|
|
675
676
|
const context = contexts.length > 0 ? contexts[0] : await browser.newContext()
|
|
676
677
|
|
|
677
|
-
// Action timeout (click, fill, hover, etc.) is
|
|
678
|
-
//
|
|
679
|
-
|
|
678
|
+
// Action timeout (click, fill, hover, etc.) is longer to tolerate slower
|
|
679
|
+
// SPA/Turbo navigations and post-click settling on real sites.
|
|
680
|
+
// Navigation timeout (goto, reload) remains separate.
|
|
681
|
+
context.setDefaultTimeout(60000)
|
|
680
682
|
context.setDefaultNavigationTimeout(10000)
|
|
681
683
|
|
|
682
684
|
context.on('page', (page) => {
|
package/src/ffmpeg.ts
CHANGED
|
@@ -14,8 +14,8 @@ import path from 'node:path'
|
|
|
14
14
|
// Constants
|
|
15
15
|
// ---------------------------------------------------------------------------
|
|
16
16
|
|
|
17
|
-
/** Seconds of normal-speed buffer kept before and after each execution */
|
|
18
|
-
export const INTERACTION_BUFFER_SECONDS =
|
|
17
|
+
/** Seconds of normal-speed buffer kept before and after each execution (0.5s each side = 1s total) */
|
|
18
|
+
export const INTERACTION_BUFFER_SECONDS = 0.5
|
|
19
19
|
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
21
21
|
// Hardware encoder detection
|
|
@@ -635,13 +635,13 @@ export interface ExecutionTimestamp {
|
|
|
635
635
|
export function computeIdleSections({
|
|
636
636
|
executionTimestamps,
|
|
637
637
|
totalDurationMs,
|
|
638
|
-
speed =
|
|
638
|
+
speed = 6,
|
|
639
639
|
bufferSeconds = INTERACTION_BUFFER_SECONDS,
|
|
640
640
|
}: {
|
|
641
641
|
executionTimestamps: ExecutionTimestamp[]
|
|
642
642
|
/** Total recording duration in milliseconds (from stopRecording result) */
|
|
643
643
|
totalDurationMs: number
|
|
644
|
-
/** Speed multiplier for idle sections (default
|
|
644
|
+
/** Speed multiplier for idle sections (default 6) */
|
|
645
645
|
speed?: number
|
|
646
646
|
/** Override the default buffer around each execution (seconds) */
|
|
647
647
|
bufferSeconds?: number
|
|
@@ -711,7 +711,7 @@ export interface CreateDemoVideoOptions {
|
|
|
711
711
|
durationMs: number
|
|
712
712
|
/** Execution timestamps (from stopRecording result) */
|
|
713
713
|
executionTimestamps: ExecutionTimestamp[]
|
|
714
|
-
/** Speed multiplier for idle sections (default
|
|
714
|
+
/** Speed multiplier for idle sections (default 6) */
|
|
715
715
|
speed?: number
|
|
716
716
|
/** Output file path (defaults to recordingPath with `-demo` suffix) */
|
|
717
717
|
outputFile?: string
|
|
@@ -722,8 +722,8 @@ export interface CreateDemoVideoOptions {
|
|
|
722
722
|
* Create a demo video from a recording by speeding up idle sections
|
|
723
723
|
* (gaps between execute() calls) while keeping interactions at normal speed.
|
|
724
724
|
*
|
|
725
|
-
* A
|
|
726
|
-
* interaction so viewers see context before and after each action.
|
|
725
|
+
* A 0.5-second buffer (INTERACTION_BUFFER_SECONDS) is preserved on each side of
|
|
726
|
+
* an interaction (1 second total) so viewers see context before and after each action.
|
|
727
727
|
*
|
|
728
728
|
* Requires `ffmpeg` and `ffprobe` installed on the system.
|
|
729
729
|
*
|
|
@@ -736,7 +736,7 @@ export async function createDemoVideo(
|
|
|
736
736
|
recordingPath,
|
|
737
737
|
durationMs,
|
|
738
738
|
executionTimestamps,
|
|
739
|
-
speed =
|
|
739
|
+
speed = 6,
|
|
740
740
|
signal,
|
|
741
741
|
} = options
|
|
742
742
|
|
|
@@ -247,7 +247,8 @@ function applyRuntimeVisualOptions(): void {
|
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
if (runtime.options.style === 'minimal') {
|
|
250
|
-
|
|
250
|
+
// White fill with dark border stroke, like a standard macOS cursor
|
|
251
|
+
const triangleSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="-1 -1 26 26"><path fill="white" stroke="${runtime.options.color}" stroke-width="1.5" stroke-linejoin="round" d="m23.284 19.124l-6.866-6.895a.4.4 0 0 1-.118-.296a.43.43 0 0 1 .163-.282l4.439-3.077a1.48 1.48 0 0 0 .621-1.48a1.48 1.48 0 0 0-1.036-1.198L1.623.302a1.14 1.14 0 0 0-1.11.282A1.13 1.13 0 0 0 .29 1.649L5.928 20.44a1.48 1.48 0 0 0 1.183 1.035a1.48 1.48 0 0 0 1.48-.621l3.078-4.44a.37.37 0 0 1 .31-.118a.43.43 0 0 1 .296.104l6.91 6.91a1.48 1.48 0 0 0 2.087 0l2.086-2.086a1.48 1.48 0 0 0-.074-2.101"/></svg>`
|
|
251
252
|
const triangleDataUrl = `url("data:image/svg+xml,${encodeURIComponent(triangleSvg)}")`
|
|
252
253
|
runtime.cursorElement.style.borderRadius = '0'
|
|
253
254
|
runtime.cursorElement.style.border = 'none'
|
|
@@ -258,7 +259,7 @@ function applyRuntimeVisualOptions(): void {
|
|
|
258
259
|
runtime.cursorElement.style.backgroundPosition = 'left top'
|
|
259
260
|
runtime.cursorElement.style.backdropFilter = 'none'
|
|
260
261
|
runtime.cursorElement.style.boxShadow = 'none'
|
|
261
|
-
runtime.cursorElement.style.filter = 'drop-shadow(0 1px
|
|
262
|
+
runtime.cursorElement.style.filter = 'drop-shadow(0 1px 2px rgba(0, 0, 0, 0.4))'
|
|
262
263
|
runtime.cursorElement.style.opacity = getBaseOpacity()
|
|
263
264
|
return
|
|
264
265
|
}
|
|
@@ -4,7 +4,12 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { BrowserContext, Page } from '@xmorse/playwright-core'
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
applyGhostCursorMouseAction,
|
|
9
|
+
disableGhostCursor,
|
|
10
|
+
enableGhostCursor,
|
|
11
|
+
type GhostCursorClientOptions,
|
|
12
|
+
} from './ghost-cursor.js'
|
|
8
13
|
|
|
9
14
|
interface RecordingGhostCursorLogger {
|
|
10
15
|
error: (...args: unknown[]) => void
|
|
@@ -53,6 +58,7 @@ export class RecordingGhostCursorController {
|
|
|
53
58
|
|
|
54
59
|
try {
|
|
55
60
|
await enableGhostCursor({ page })
|
|
61
|
+
|
|
56
62
|
if (!this.previousMouseActionByPage.has(page)) {
|
|
57
63
|
this.previousMouseActionByPage.set(page, page.onMouseAction)
|
|
58
64
|
}
|
package/src/relay-client.ts
CHANGED
|
@@ -27,7 +27,7 @@ export type ExtensionStatus = {
|
|
|
27
27
|
export async function getRelayServerVersion(port: number = RELAY_PORT): Promise<string | null> {
|
|
28
28
|
try {
|
|
29
29
|
const response = await fetch(`http://127.0.0.1:${port}/version`, {
|
|
30
|
-
signal: AbortSignal.timeout(
|
|
30
|
+
signal: AbortSignal.timeout(2000),
|
|
31
31
|
})
|
|
32
32
|
if (!response.ok) {
|
|
33
33
|
return null
|