framewatch-mcp-server 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"vue.js","sourceRoot":"","sources":["../../src/engine/vue.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAG9G;;;;;;;;;;;;;GAaG;AAEH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAU;IACxC,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAeD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAU,EAAE,OAAqB;IACrE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC3B,MAAM,QAAQ,GAAG,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IAC1D,IAAI,GAAG,GAAmB,IAAI,CAAC;IAC/B,SAAS,CAAC;QACR,GAAG,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,GAAG,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ;YAAE,MAAM;QAClD,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9E,CAAC;IACD,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC1F,GAAG,GAAG,CAAC,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC;IACvC,CAAC;IACD,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;AAClD,CAAC;AAED,yFAAyF;AACzF,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAgB;IAChD,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,QAAQ,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,0BAA0B,EAAE,WAAW,EAAE,qBAAqB,EAAE,CAAC,CAAC;IAChI,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAOD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAU;IAC5C,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE;YACrC,SAAS,EAAE,wBAAwB;YACnC,QAAQ,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAAE,UAAU,CAAC;SAC/H,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAID,6DAA6D;AAC7D,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAU,EAAE,IAAY;IAC3D,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,gBAAgB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACvG,CAAC;AACH,CAAC;AAED;;;;GAIG;AAEH,SAAS,YAAY;IACnB,MAAM,GAAG,GAAI,UAAkB,CAAC,QAAQ,CAAC;IACzC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,OAAO,GAAG,GAAmC,EAAE;QACnD,MAAM,GAAG,GAAI,UAAkB,CAAC,QAAQ,CAAC;QACzC,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;QACjD,IAAI,MAAM,IAAI,MAAM,CAAC,WAAW;YAAE,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QACnF,MAAM,GAAG,GAAG,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW;gBAAE,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3E,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,OAAO,EAAE,CAAC;IACxB,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;QAC5B,0EAA0E;QAC1E,8DAA8D;QAC9D,MAAM,QAAQ,GAAG,GAAG,CAAC,SAAS,CAAC;QAC/B,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;QACzF,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAC1B,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC;QACxD,MAAM,MAAM,GAAG,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC;QACtC,IAAI,KAAkD,CAAC;QACvD,IAAI,MAAM,IAAI,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;YAC/D,MAAM,OAAO,GAAG,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC;YAC1C,KAAK,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3C,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI;gBAAE,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7F,CAAC;QACD,kEAAkE;QAClE,+DAA+D;QAC/D,IAAI,UAAU,GAAG,IAAI,CAAC;QACtB,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;QAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YACnD,IAAI,sBAAsB,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC1C,UAAU,GAAG,KAAK,CAAC;gBACnB,MAAM;YACR,CAAC;QACH,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,UAAU,GAAG,CAAC,CAAC,sBAAsB,IAAI,IAAI,CAAC,CAAC;QAC1E,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,CAAC;QAC3C,MAAM,GAAG,GAAY,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC;QAC1G,IAAI,KAAK;YAAE,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC;QAC7B,OAAO,GAAG,CAAC;IACb,CAAC;IAED,qDAAqD;IACrD,MAAM,GAAG,GAAG,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QAC1B,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK;YAAE,SAAS;QAC/B,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC;QAClC,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAE,UAAkB,CAAC,GAAG,IAAK,UAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC;QACtH,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;QAChC,MAAM,GAAG,GAAY,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;QAChF,IAAI,MAAM,IAAI,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YAC9B,GAAG,CAAC,KAAK,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YACvD,IAAI,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI;gBAAE,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC1E,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,MAA8B;IACvD,MAAM,OAAO,GAAG,GAAmC,EAAE;QACnD,MAAM,GAAG,GAAI,UAAkB,CAAC,QAAQ,CAAC;QACzC,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;QACjD,IAAI,MAAM,IAAI,MAAM,CAAC,WAAW;YAAE,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QACnF,MAAM,GAAG,GAAG,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW;gBAAE,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3E,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IACF,MAAM,KAAK,GAAG,OAAO,EAAE,CAAC;IACxB,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC;IACpE,MAAM,MAAM,GAAG,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC;IACtC,IAAI,KAAK,GAAG,IAAI,CAAC;IACjB,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;QACnD,KAAK,GAAG,MAAM,OAAO,CAAC,IAAI,CAAU;YAClC,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC;YAC9C,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;SACvF,CAAC,CAAC;IACL,CAAC;IACD,MAAM,GAAG,GAAI,UAAkB,CAAC,qBAAqB,CAAC;IACtD,IAAI,OAAO,GAAG,KAAK,UAAU,EAAE,CAAC;QAC9B,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,eAAe,CAAC,OAAY,EAAE,MAAkD;IACvF,MAAM,OAAO,GAAG,GAAmC,EAAE;QACnD,MAAM,GAAG,GAAI,UAAkB,CAAC,QAAQ,CAAC;QACzC,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;QACjD,IAAI,MAAM,IAAI,MAAM,CAAC,WAAW;YAAE,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QACnF,MAAM,GAAG,GAAG,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW;gBAAE,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3E,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IACF,MAAM,SAAS,GAAG,CAAC,KAAU,EAAU,EAAE;QACvC,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,MAAM,CAAC;QAClC,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,WAAW,CAAC;QAC5C,MAAM,IAAI,GAAG,OAAO,KAAK,CAAC;QAC1B,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACrC,OAAO,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;QAChH,CAAC;QACD,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;QACvF,IAAI,IAAI,KAAK,UAAU;YAAE,OAAO,IAAI,CAAC;QACrC,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO,QAAQ,CAAC;QACvC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,GAAG,GAAG,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC;QAC1D,IAAI,KAAK,YAAY,IAAI;YAAE,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC;QACtD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,WAAW,IAAI,KAAK;YAAE,OAAO,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC9F,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAChC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1C,OAAO,GAAG,GAAG,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC;QAC5D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC,CAAC;IACF,MAAM,OAAO,GAAG,CAAC,MAAW,EAAE,IAA+B,EAA0B,EAAE;QACvF,MAAM,GAAG,GAA2B,EAAE,CAAC;QACvC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,OAAO,GAAG,CAAC;QACtD,IAAI,IAAI,GAAa,EAAE,CAAC;QACxB,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,GAAG,CAAC;QACb,CAAC;QACD,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC;gBAAE,SAAS;YAChC,IAAI,KAAK,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;gBAChC,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC;gBACjD,MAAM;YACR,CAAC;YACD,IAAI,KAAU,CAAC;YACf,IAAI,CAAC;gBACH,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;YACtB,CAAC;YAAC,MAAM,CAAC;gBACP,KAAK,GAAG,SAAS,CAAC;YACpB,CAAC;YACD,GAAG,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;YAC5B,KAAK,EAAE,CAAC;QACV,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;IACF,MAAM,MAAM,GAAG,CAAC,QAAa,EAAU,EAAE;QACvC,MAAM,IAAI,GAAG,QAAQ,IAAI,QAAQ,CAAC,IAAI,CAAC;QACvC,IAAI,CAAC,IAAI;YAAE,OAAO,WAAW,CAAC;QAC9B,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACpF,OAAO,QAAQ,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC;IACxD,CAAC,CAAC;IAEF,iEAAiE;IACjE,IAAI,IAAI,GAAQ,OAAO,CAAC;IACxB,OAAO,IAAI,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QACnC,IAAI,sBAAsB,IAAI,IAAI,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAChE,MAAM,QAAQ,GAAG,IAAI,CAAC,oBAAoB,CAAC;YAC3C,MAAM,IAAI,GAAa,EAAE,CAAC;YAC1B,IAAI,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;YAC7B,OAAO,MAAM,EAAE,CAAC;gBACd,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;gBAC1B,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;YACzB,CAAC;YACD,OAAO;gBACL,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC;gBACtB,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAC9B,KAAK,EAAE;oBACL,GAAG,OAAO,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;oBACpF,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;iBAC/E;gBACD,IAAI;aACL,CAAC;QACJ,CAAC;QACD,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED,wDAAwD;IACxD,IAAI,GAAG,OAAO,CAAC;IACf,OAAO,IAAI,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QACnC,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC;QACjF,IAAI,IAAI,CAAC,WAAW;YAAE,OAAO,EAAE,WAAW,EAAE,kDAAkD,EAAE,CAAC;QACjG,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,EAAE,CAAC;IACxB,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,WAAW,EAAE,kDAAkD,EAAE,CAAC;IACtH,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,UAAU,CAAC,MAAiD;IACnE,MAAM,OAAO,GAAG,GAAmC,EAAE;QACnD,MAAM,GAAG,GAAI,UAAkB,CAAC,QAAQ,CAAC;QACzC,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;QACjD,IAAI,MAAM,IAAI,MAAM,CAAC,WAAW;YAAE,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QACnF,MAAM,GAAG,GAAG,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW;gBAAE,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3E,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IACF,MAAM,KAAK,GAAG,OAAO,EAAE,CAAC;IACxB,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAChD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC1C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,MAAM,GAAG,CAAC,QAAa,EAAU,EAAE;QACvC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC;QACjC,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACpF,OAAO,QAAQ,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC;IACxD,CAAC,CAAC;IACF,MAAM,KAAK,GAAG,CAAC,KAAU,EAAE,IAAqB,EAAQ,EAAE;QACxD,IAAI,CAAC,KAAK,IAAI,KAAK,IAAI,MAAM,CAAC,SAAS;YAAE,OAAO;QAChD,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC;YACjC,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC9B,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACvB,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;gBAC9B,OAAO;YACT,CAAC;YACD,MAAM,IAAI,GAAkB,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;YACnD,KAAK,EAAE,CAAC;YACR,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAChB,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvC,OAAO;QACT,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACnB,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;YACzC,OAAO;QACT,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YAClC,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,QAAQ;gBAAE,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACzD,CAAC;IACH,CAAC,CAAC;IACF,MAAM,YAAY,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC;IACzC,MAAM,IAAI,GAAkB,EAAE,IAAI,EAAE,MAAM,CAAC,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IACzE,KAAK,GAAG,CAAC,CAAC;IACV,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3C,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACzB,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,MAAwB;IACtD,MAAM,OAAO,GAAG,GAAmC,EAAE;QACnD,MAAM,GAAG,GAAI,UAAkB,CAAC,QAAQ,CAAC;QACzC,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;QACjD,IAAI,MAAM,IAAI,MAAM,CAAC,WAAW;YAAE,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QACnF,MAAM,GAAG,GAAG,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW;gBAAE,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3E,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IACF,MAAM,KAAK,GAAG,OAAO,EAAE,CAAC;IACxB,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC;IACnE,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC;IACpE,MAAM,MAAM,GAAG,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC;IACtC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,UAAU;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,2BAA2B,EAAE,CAAC;IAC3G,IAAI,CAAC;QACH,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YACzC,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC7C,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACpE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,mBAAmB,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;YACjE,CAAC;QACH,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QACrL,CAAC;QACD,MAAM,GAAG,GAAI,UAAkB,CAAC,qBAAqB,CAAC;QACtD,IAAI,OAAO,GAAG,KAAK,UAAU;YAAE,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;QACrG,MAAM,OAAO,GAAG,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC;QACjE,MAAM,KAAK,GAAoC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAC1G,IAAI,OAAO,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI;YAAE,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACtG,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IACtF,CAAC;AACH,CAAC","sourcesContent":["import type { Locator, Page } from \"playwright\";\nimport { MAX_COMPONENT_ENTRIES, MAX_COMPONENT_TREE_NODES, MAX_COMPONENT_VALUE_LENGTH } from \"../constants.js\";\nimport type { ComponentInfo, ComponentNode, ComponentUnavailable, VueInfo } from \"../utils/vue-rules.js\";\n\n/**\n * What a Vue app on the page is willing to tell us.\n *\n * Vue 3 leaves two handles behind in the DOM: the app on its container\n * (`__vue_app__`, and `data-v-app` on the same element), and on every element\n * a dev build rendered, the component instance that rendered it\n * (`__vueParentComponent`). From those come the version, the router and its\n * current route, the component name, props and state behind any element, the\n * whole component tree, and a way to navigate through the router instead of\n * the address bar. Production builds keep only the app handle, and say so.\n *\n * Everything in-page below is serialised into Chromium: it may not close over\n * a module value (CLAUDE.md, decision 50), so every cap is passed in.\n */\n\nexport async function detectVue(page: Page): Promise<VueInfo | null> {\n try {\n return await page.evaluate(detectInPage);\n } catch {\n return null;\n }\n}\n\nexport interface ReadyOptions {\n /** How long to keep looking for a mounted app. */\n detect_ms: number;\n /** Once found, how long to give the router to be ready. */\n ready_ms: number;\n}\n\nexport interface ReadyResult {\n vue: VueInfo | null;\n /** Wall time spent, whichever way it ended. */\n waited_ms: number;\n}\n\n/**\n * Wait for a Vue app to be mounted and its router (if any) to have resolved\n * its first navigation, then two frames so the first render is painted.\n * Returns as soon as that is true; a page with no Vue is given `detect_ms`\n * and then handed back untouched — no slower than the sleep it replaces.\n */\nexport async function waitForVueReady(page: Page, options: ReadyOptions): Promise<ReadyResult> {\n const started = Date.now();\n const deadline = started + Math.max(0, options.detect_ms);\n let vue: VueInfo | null = null;\n for (;;) {\n vue = await detectVue(page);\n if (vue !== null || Date.now() >= deadline) break;\n await page.waitForTimeout(Math.min(50, Math.max(1, deadline - Date.now())));\n }\n if (vue !== null) {\n await page.evaluate(readyInPage, { timeout_ms: options.ready_ms }).catch(() => undefined);\n vue = (await detectVue(page)) ?? vue;\n }\n return { vue, waited_ms: Date.now() - started };\n}\n\n/** The component that rendered the element `locator` points at, or why there is none. */\nexport async function componentOf(locator: Locator): Promise<ComponentInfo | ComponentUnavailable | null> {\n try {\n return await locator.evaluate(componentInPage, { max_value: MAX_COMPONENT_VALUE_LENGTH, max_entries: MAX_COMPONENT_ENTRIES });\n } catch {\n return null;\n }\n}\n\nexport interface ComponentTree {\n root: ComponentNode;\n total: number;\n}\n\nexport async function componentTree(page: Page): Promise<ComponentTree | null> {\n try {\n return await page.evaluate(treeInPage, {\n max_nodes: MAX_COMPONENT_TREE_NODES,\n builtins: [\"RouterView\", \"RouterLink\", \"Transition\", \"TransitionGroup\", \"KeepAlive\", \"BaseTransition\", \"Teleport\", \"Suspense\"],\n });\n } catch {\n return null;\n }\n}\n\nexport type RouterOutcome = { ok: true; route: { path: string; name?: string } } | { ok: false; error: string };\n\n/** `router.push(path)`, awaited, on the app's own router. */\nexport async function routerNavigate(page: Page, path: string): Promise<RouterOutcome> {\n try {\n return await page.evaluate(routerPushInPage, { path });\n } catch (error) {\n return { ok: false, error: (error instanceof Error ? error.message : String(error)).split(\"\\n\")[0] };\n }\n}\n\n/* ── In-page ──────────────────────────────────────────────────────────────\n * Each function below is serialised whole into Chromium, so each carries its\n * own copy of `findApp`: a shared module-level helper would be a\n * ReferenceError in the page (CLAUDE.md, decision 50).\n */\n\nfunction detectInPage(): VueInfo | null {\n const doc = (globalThis as any).document;\n if (!doc) return null;\n const findApp = (): { app: any; root: any } | null => {\n const doc = (globalThis as any).document;\n if (!doc) return null;\n const marked = doc.querySelector(\"[data-v-app]\");\n if (marked && marked.__vue_app__) return { app: marked.__vue_app__, root: marked };\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n if (all[i].__vue_app__) return { app: all[i].__vue_app__, root: all[i] };\n }\n return null;\n };\n\n const found = findApp();\n if (found) {\n const { app, root } = found;\n // A dev build keeps the root instance on the app; a production build does\n // not, but both stamp `data-v-app` on the container at mount.\n const instance = app._instance;\n const mounted = instance ? instance.isMounted === true : root.hasAttribute(\"data-v-app\");\n if (!mounted) return null;\n const props = app.config && app.config.globalProperties;\n const router = props && props.$router;\n let route: { path: string; name?: string } | undefined;\n if (router && router.currentRoute && router.currentRoute.value) {\n const current = router.currentRoute.value;\n route = { path: String(current.fullPath) };\n if (current.name !== undefined && current.name !== null) route.name = String(current.name);\n }\n // A dev build stamps every rendered element with its component; a\n // production build stamps none. Look at what the app rendered.\n let production = true;\n const rendered = root.querySelectorAll(\"*\");\n for (let i = 0; i < rendered.length && i < 50; i++) {\n if (\"__vueParentComponent\" in rendered[i]) {\n production = false;\n break;\n }\n }\n if (rendered.length === 0) production = !(\"__vueParentComponent\" in root);\n const version = String(app.version || \"3\");\n const out: VueInfo = { version, major: Number(version.split(\".\")[0]) || 3, router: !!router, production };\n if (route) out.route = route;\n return out;\n }\n\n // Vue 2 leaves `__vue__` on component root elements.\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n const vm = all[i].__vue__;\n if (!vm || !vm.$root) continue;\n const ctor = vm.$root.constructor;\n const version = String((ctor && ctor.version) || ((globalThis as any).Vue && (globalThis as any).Vue.version) || \"2\");\n const router = vm.$root.$router;\n const out: VueInfo = { version, major: 2, router: !!router, production: false };\n if (router && vm.$root.$route) {\n out.route = { path: String(vm.$root.$route.fullPath) };\n if (vm.$root.$route.name) out.route.name = String(vm.$root.$route.name);\n }\n return out;\n }\n return null;\n}\n\nasync function readyInPage(config: { timeout_ms: number }): Promise<boolean> {\n const findApp = (): { app: any; root: any } | null => {\n const doc = (globalThis as any).document;\n if (!doc) return null;\n const marked = doc.querySelector(\"[data-v-app]\");\n if (marked && marked.__vue_app__) return { app: marked.__vue_app__, root: marked };\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n if (all[i].__vue_app__) return { app: all[i].__vue_app__, root: all[i] };\n }\n return null;\n };\n const found = findApp();\n if (!found) return false;\n const props = found.app.config && found.app.config.globalProperties;\n const router = props && props.$router;\n let ready = true;\n if (router && typeof router.isReady === \"function\") {\n ready = await Promise.race<boolean>([\n router.isReady().then(() => true, () => false),\n new Promise<boolean>((resolve) => setTimeout(() => resolve(false), config.timeout_ms)),\n ]);\n }\n const raf = (globalThis as any).requestAnimationFrame;\n if (typeof raf === \"function\") {\n await new Promise<void>((resolve) => raf(() => raf(() => resolve())));\n }\n return ready;\n}\n\nfunction componentInPage(element: any, config: { max_value: number; max_entries: number }): ComponentInfo | ComponentUnavailable | null {\n const findApp = (): { app: any; root: any } | null => {\n const doc = (globalThis as any).document;\n if (!doc) return null;\n const marked = doc.querySelector(\"[data-v-app]\");\n if (marked && marked.__vue_app__) return { app: marked.__vue_app__, root: marked };\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n if (all[i].__vue_app__) return { app: all[i].__vue_app__, root: all[i] };\n }\n return null;\n };\n const serialise = (value: any): string => {\n if (value === null) return \"null\";\n if (value === undefined) return \"undefined\";\n const type = typeof value;\n if (type === \"string\") {\n const quoted = JSON.stringify(value);\n return quoted.length > config.max_value + 2 ? JSON.stringify(value.slice(0, config.max_value)) + \"…\" : quoted;\n }\n if (type === \"number\" || type === \"boolean\" || type === \"bigint\") return String(value);\n if (type === \"function\") return \"fn\";\n if (type === \"symbol\") return \"symbol\";\n if (Array.isArray(value)) return \"[\" + value.length + \"]\";\n if (value instanceof Date) return value.toISOString();\n if (value && typeof value === \"object\" && \"__v_isRef\" in value) return serialise(value.value);\n try {\n const keys = Object.keys(value);\n const shown = keys.slice(0, 4).join(\", \");\n return \"{\" + shown + (keys.length > 4 ? \", …\" : \"\") + \"}\";\n } catch {\n return \"{…}\";\n }\n };\n const entries = (source: any, skip?: (key: string) => boolean): Record<string, string> => {\n const out: Record<string, string> = {};\n if (!source || typeof source !== \"object\") return out;\n let keys: string[] = [];\n try {\n keys = Object.keys(source);\n } catch {\n return out;\n }\n let count = 0;\n for (const key of keys) {\n if (skip && skip(key)) continue;\n if (count >= config.max_entries) {\n out[\"…\"] = \"+\" + (keys.length - count) + \" more\";\n break;\n }\n let value: any;\n try {\n value = source[key];\n } catch {\n value = undefined;\n }\n out[key] = serialise(value);\n count++;\n }\n return out;\n };\n const nameOf = (instance: any): string => {\n const type = instance && instance.type;\n if (!type) return \"Anonymous\";\n if (type.name) return String(type.name);\n if (type.__name) return String(type.__name);\n if (type.__file) return String(type.__file).split(\"/\").pop()!.replace(/\\.vue$/, \"\");\n return instance.parent === null ? \"App\" : \"Anonymous\";\n };\n\n // Vue 3 dev build: the nearest element that knows its component.\n let node: any = element;\n while (node && node.nodeType === 1) {\n if (\"__vueParentComponent\" in node && node.__vueParentComponent) {\n const instance = node.__vueParentComponent;\n const path: string[] = [];\n let parent = instance.parent;\n while (parent) {\n path.push(nameOf(parent));\n parent = parent.parent;\n }\n return {\n name: nameOf(instance),\n props: entries(instance.props),\n state: {\n ...entries(instance.setupState, (key) => key.startsWith(\"_\") || key.startsWith(\"$\")),\n ...entries(instance.data, (key) => key.startsWith(\"_\") || key.startsWith(\"$\")),\n },\n path,\n };\n }\n node = node.parentElement;\n }\n\n // No component data. Say which kind of nothing this is.\n node = element;\n while (node && node.nodeType === 1) {\n if (node.__vue__) return { unavailable: \"Vue 2 — component details need Vue 3\" };\n if (node.__vue_app__) return { unavailable: \"production build — no component data on elements\" };\n node = node.parentElement;\n }\n const found = findApp();\n if (found && found.root.contains(element)) return { unavailable: \"production build — no component data on elements\" };\n return null;\n}\n\nfunction treeInPage(config: { max_nodes: number; builtins: string[] }): { root: ComponentNode; total: number } | null {\n const findApp = (): { app: any; root: any } | null => {\n const doc = (globalThis as any).document;\n if (!doc) return null;\n const marked = doc.querySelector(\"[data-v-app]\");\n if (marked && marked.__vue_app__) return { app: marked.__vue_app__, root: marked };\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n if (all[i].__vue_app__) return { app: all[i].__vue_app__, root: all[i] };\n }\n return null;\n };\n const found = findApp();\n if (!found || !found.app._instance) return null;\n const builtins = new Set(config.builtins);\n let total = 0;\n const nameOf = (instance: any): string => {\n const type = instance.type || {};\n if (type.name) return String(type.name);\n if (type.__name) return String(type.__name);\n if (type.__file) return String(type.__file).split(\"/\").pop()!.replace(/\\.vue$/, \"\");\n return instance.parent === null ? \"App\" : \"Anonymous\";\n };\n const visit = (vnode: any, into: ComponentNode[]): void => {\n if (!vnode || total >= config.max_nodes) return;\n if (vnode.component) {\n const instance = vnode.component;\n const name = nameOf(instance);\n if (builtins.has(name)) {\n visit(instance.subTree, into);\n return;\n }\n const node: ComponentNode = { name, children: [] };\n total++;\n into.push(node);\n visit(instance.subTree, node.children);\n return;\n }\n if (vnode.suspense) {\n visit(vnode.suspense.activeBranch, into);\n return;\n }\n if (Array.isArray(vnode.children)) {\n for (const child of vnode.children) visit(child, into);\n }\n };\n const rootInstance = found.app._instance;\n const root: ComponentNode = { name: nameOf(rootInstance), children: [] };\n total = 1;\n visit(rootInstance.subTree, root.children);\n return { root, total };\n}\n\nasync function routerPushInPage(config: { path: string }): Promise<RouterOutcome> {\n const findApp = (): { app: any; root: any } | null => {\n const doc = (globalThis as any).document;\n if (!doc) return null;\n const marked = doc.querySelector(\"[data-v-app]\");\n if (marked && marked.__vue_app__) return { app: marked.__vue_app__, root: marked };\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n if (all[i].__vue_app__) return { app: all[i].__vue_app__, root: all[i] };\n }\n return null;\n };\n const found = findApp();\n if (!found) return { ok: false, error: \"no Vue app on this page\" };\n const props = found.app.config && found.app.config.globalProperties;\n const router = props && props.$router;\n if (!router || typeof router.push !== \"function\") return { ok: false, error: \"no vue-router on this app\" };\n try {\n if (typeof router.resolve === \"function\") {\n const resolved = router.resolve(config.path);\n if (!resolved || !resolved.matched || resolved.matched.length === 0) {\n return { ok: false, error: \"no route matched \" + config.path };\n }\n }\n const failure = await router.push(config.path);\n if (failure) {\n return { ok: false, error: \"navigation \" + (failure.type === 8 ? \"cancelled\" : failure.type === 16 ? \"duplicated\" : \"aborted\") + (failure.message ? \": \" + failure.message : \"\") };\n }\n const raf = (globalThis as any).requestAnimationFrame;\n if (typeof raf === \"function\") await new Promise<void>((resolve) => raf(() => raf(() => resolve())));\n const current = router.currentRoute && router.currentRoute.value;\n const route: { path: string; name?: string } = { path: current ? String(current.fullPath) : config.path };\n if (current && current.name !== undefined && current.name !== null) route.name = String(current.name);\n return { ok: true, route };\n } catch (error) {\n return { ok: false, error: error instanceof Error ? error.message : String(error) };\n }\n}\n"]}
1
+ {"version":3,"file":"vue.js","sourceRoot":"","sources":["../../src/engine/vue.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Locator, Page } from \"playwright\";\nimport { MAX_COMPONENT_ENTRIES, MAX_COMPONENT_TREE_NODES, MAX_COMPONENT_VALUE_LENGTH } from \"../constants.js\";\nimport type { ComponentInfo, ComponentNode, ComponentUnavailable, VueInfo } from \"../utils/vue-rules.js\";\n\n/**\n * What a Vue app on the page is willing to tell us.\n *\n * Vue 3 leaves two handles behind in the DOM: the app on its container\n * (`__vue_app__`, and `data-v-app` on the same element), and on every element\n * a dev build rendered, the component instance that rendered it\n * (`__vueParentComponent`). From those come the version, the router and its\n * current route, the component name, props and state behind any element, the\n * whole component tree, and a way to navigate through the router instead of\n * the address bar. Production builds keep only the app handle, and say so.\n *\n * Everything in-page below is serialised into Chromium: it may not close over\n * a module value, so every cap is passed in.\n */\n\nexport async function detectVue(page: Page): Promise<VueInfo | null> {\n try {\n return await page.evaluate(detectInPage);\n } catch {\n return null;\n }\n}\n\nexport interface ReadyOptions {\n /** How long to keep looking for a mounted app. */\n detect_ms: number;\n /** Once found, how long to give the router to be ready. */\n ready_ms: number;\n}\n\nexport interface ReadyResult {\n vue: VueInfo | null;\n /** Wall time spent, whichever way it ended. */\n waited_ms: number;\n}\n\n/**\n * Wait for a Vue app to be mounted and its router (if any) to have resolved\n * its first navigation, then two frames so the first render is painted.\n * Returns as soon as that is true; a page with no Vue is given `detect_ms`\n * and then handed back untouched — no slower than the sleep it replaces.\n */\nexport async function waitForVueReady(page: Page, options: ReadyOptions): Promise<ReadyResult> {\n const started = Date.now();\n const deadline = started + Math.max(0, options.detect_ms);\n let vue: VueInfo | null = null;\n for (;;) {\n vue = await detectVue(page);\n if (vue !== null || Date.now() >= deadline) break;\n await page.waitForTimeout(Math.min(50, Math.max(1, deadline - Date.now())));\n }\n if (vue !== null) {\n await page.evaluate(readyInPage, { timeout_ms: options.ready_ms }).catch(() => undefined);\n vue = (await detectVue(page)) ?? vue;\n }\n return { vue, waited_ms: Date.now() - started };\n}\n\n/** The component that rendered the element `locator` points at, or why there is none. */\nexport async function componentOf(locator: Locator): Promise<ComponentInfo | ComponentUnavailable | null> {\n try {\n return await locator.evaluate(componentInPage, { max_value: MAX_COMPONENT_VALUE_LENGTH, max_entries: MAX_COMPONENT_ENTRIES });\n } catch {\n return null;\n }\n}\n\nexport interface ComponentTree {\n root: ComponentNode;\n total: number;\n}\n\nexport async function componentTree(page: Page): Promise<ComponentTree | null> {\n try {\n return await page.evaluate(treeInPage, {\n max_nodes: MAX_COMPONENT_TREE_NODES,\n builtins: [\"RouterView\", \"RouterLink\", \"Transition\", \"TransitionGroup\", \"KeepAlive\", \"BaseTransition\", \"Teleport\", \"Suspense\"],\n });\n } catch {\n return null;\n }\n}\n\nexport type RouterOutcome = { ok: true; route: { path: string; name?: string } } | { ok: false; error: string };\n\n/** `router.push(path)`, awaited, on the app's own router. */\nexport async function routerNavigate(page: Page, path: string): Promise<RouterOutcome> {\n try {\n return await page.evaluate(routerPushInPage, { path });\n } catch (error) {\n return { ok: false, error: (error instanceof Error ? error.message : String(error)).split(\"\\n\")[0] };\n }\n}\n\n/* ── In-page ──────────────────────────────────────────────────────────────\n * Each function below is serialised whole into Chromium, so each carries its\n * own copy of `findApp`: a shared module-level helper would be a\n * ReferenceError in the page.\n */\n\nfunction detectInPage(): VueInfo | null {\n const doc = (globalThis as any).document;\n if (!doc) return null;\n const findApp = (): { app: any; root: any } | null => {\n const doc = (globalThis as any).document;\n if (!doc) return null;\n const marked = doc.querySelector(\"[data-v-app]\");\n if (marked && marked.__vue_app__) return { app: marked.__vue_app__, root: marked };\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n if (all[i].__vue_app__) return { app: all[i].__vue_app__, root: all[i] };\n }\n return null;\n };\n\n const found = findApp();\n if (found) {\n const { app, root } = found;\n // A dev build keeps the root instance on the app; a production build does\n // not, but both stamp `data-v-app` on the container at mount.\n const instance = app._instance;\n const mounted = instance ? instance.isMounted === true : root.hasAttribute(\"data-v-app\");\n if (!mounted) return null;\n const props = app.config && app.config.globalProperties;\n const router = props && props.$router;\n let route: { path: string; name?: string } | undefined;\n if (router && router.currentRoute && router.currentRoute.value) {\n const current = router.currentRoute.value;\n route = { path: String(current.fullPath) };\n if (current.name !== undefined && current.name !== null) route.name = String(current.name);\n }\n // A dev build stamps every rendered element with its component; a\n // production build stamps none. Look at what the app rendered.\n let production = true;\n const rendered = root.querySelectorAll(\"*\");\n for (let i = 0; i < rendered.length && i < 50; i++) {\n if (\"__vueParentComponent\" in rendered[i]) {\n production = false;\n break;\n }\n }\n if (rendered.length === 0) production = !(\"__vueParentComponent\" in root);\n const version = String(app.version || \"3\");\n const out: VueInfo = { version, major: Number(version.split(\".\")[0]) || 3, router: !!router, production };\n if (route) out.route = route;\n return out;\n }\n\n // Vue 2 leaves `__vue__` on component root elements.\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n const vm = all[i].__vue__;\n if (!vm || !vm.$root) continue;\n const ctor = vm.$root.constructor;\n const version = String((ctor && ctor.version) || ((globalThis as any).Vue && (globalThis as any).Vue.version) || \"2\");\n const router = vm.$root.$router;\n const out: VueInfo = { version, major: 2, router: !!router, production: false };\n if (router && vm.$root.$route) {\n out.route = { path: String(vm.$root.$route.fullPath) };\n if (vm.$root.$route.name) out.route.name = String(vm.$root.$route.name);\n }\n return out;\n }\n return null;\n}\n\nasync function readyInPage(config: { timeout_ms: number }): Promise<boolean> {\n const findApp = (): { app: any; root: any } | null => {\n const doc = (globalThis as any).document;\n if (!doc) return null;\n const marked = doc.querySelector(\"[data-v-app]\");\n if (marked && marked.__vue_app__) return { app: marked.__vue_app__, root: marked };\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n if (all[i].__vue_app__) return { app: all[i].__vue_app__, root: all[i] };\n }\n return null;\n };\n const found = findApp();\n if (!found) return false;\n const props = found.app.config && found.app.config.globalProperties;\n const router = props && props.$router;\n let ready = true;\n if (router && typeof router.isReady === \"function\") {\n ready = await Promise.race<boolean>([\n router.isReady().then(() => true, () => false),\n new Promise<boolean>((resolve) => setTimeout(() => resolve(false), config.timeout_ms)),\n ]);\n }\n const raf = (globalThis as any).requestAnimationFrame;\n if (typeof raf === \"function\") {\n await new Promise<void>((resolve) => raf(() => raf(() => resolve())));\n }\n return ready;\n}\n\nfunction componentInPage(element: any, config: { max_value: number; max_entries: number }): ComponentInfo | ComponentUnavailable | null {\n const findApp = (): { app: any; root: any } | null => {\n const doc = (globalThis as any).document;\n if (!doc) return null;\n const marked = doc.querySelector(\"[data-v-app]\");\n if (marked && marked.__vue_app__) return { app: marked.__vue_app__, root: marked };\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n if (all[i].__vue_app__) return { app: all[i].__vue_app__, root: all[i] };\n }\n return null;\n };\n const serialise = (value: any): string => {\n if (value === null) return \"null\";\n if (value === undefined) return \"undefined\";\n const type = typeof value;\n if (type === \"string\") {\n const quoted = JSON.stringify(value);\n return quoted.length > config.max_value + 2 ? JSON.stringify(value.slice(0, config.max_value)) + \"…\" : quoted;\n }\n if (type === \"number\" || type === \"boolean\" || type === \"bigint\") return String(value);\n if (type === \"function\") return \"fn\";\n if (type === \"symbol\") return \"symbol\";\n if (Array.isArray(value)) return \"[\" + value.length + \"]\";\n if (value instanceof Date) return value.toISOString();\n if (value && typeof value === \"object\" && \"__v_isRef\" in value) return serialise(value.value);\n try {\n const keys = Object.keys(value);\n const shown = keys.slice(0, 4).join(\", \");\n return \"{\" + shown + (keys.length > 4 ? \", …\" : \"\") + \"}\";\n } catch {\n return \"{…}\";\n }\n };\n const entries = (source: any, skip?: (key: string) => boolean): Record<string, string> => {\n const out: Record<string, string> = {};\n if (!source || typeof source !== \"object\") return out;\n let keys: string[] = [];\n try {\n keys = Object.keys(source);\n } catch {\n return out;\n }\n let count = 0;\n for (const key of keys) {\n if (skip && skip(key)) continue;\n if (count >= config.max_entries) {\n out[\"…\"] = \"+\" + (keys.length - count) + \" more\";\n break;\n }\n let value: any;\n try {\n value = source[key];\n } catch {\n value = undefined;\n }\n out[key] = serialise(value);\n count++;\n }\n return out;\n };\n const nameOf = (instance: any): string => {\n const type = instance && instance.type;\n if (!type) return \"Anonymous\";\n if (type.name) return String(type.name);\n if (type.__name) return String(type.__name);\n if (type.__file) return String(type.__file).split(\"/\").pop()!.replace(/\\.vue$/, \"\");\n return instance.parent === null ? \"App\" : \"Anonymous\";\n };\n\n // Vue 3 dev build: the nearest element that knows its component.\n let node: any = element;\n while (node && node.nodeType === 1) {\n if (\"__vueParentComponent\" in node && node.__vueParentComponent) {\n const instance = node.__vueParentComponent;\n const path: string[] = [];\n let parent = instance.parent;\n while (parent) {\n path.push(nameOf(parent));\n parent = parent.parent;\n }\n return {\n name: nameOf(instance),\n props: entries(instance.props),\n state: {\n ...entries(instance.setupState, (key) => key.startsWith(\"_\") || key.startsWith(\"$\")),\n ...entries(instance.data, (key) => key.startsWith(\"_\") || key.startsWith(\"$\")),\n },\n path,\n };\n }\n node = node.parentElement;\n }\n\n // No component data. Say which kind of nothing this is.\n node = element;\n while (node && node.nodeType === 1) {\n if (node.__vue__) return { unavailable: \"Vue 2 — component details need Vue 3\" };\n if (node.__vue_app__) return { unavailable: \"production build — no component data on elements\" };\n node = node.parentElement;\n }\n const found = findApp();\n if (found && found.root.contains(element)) return { unavailable: \"production build — no component data on elements\" };\n return null;\n}\n\nfunction treeInPage(config: { max_nodes: number; builtins: string[] }): { root: ComponentNode; total: number } | null {\n const findApp = (): { app: any; root: any } | null => {\n const doc = (globalThis as any).document;\n if (!doc) return null;\n const marked = doc.querySelector(\"[data-v-app]\");\n if (marked && marked.__vue_app__) return { app: marked.__vue_app__, root: marked };\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n if (all[i].__vue_app__) return { app: all[i].__vue_app__, root: all[i] };\n }\n return null;\n };\n const found = findApp();\n if (!found || !found.app._instance) return null;\n const builtins = new Set(config.builtins);\n let total = 0;\n const nameOf = (instance: any): string => {\n const type = instance.type || {};\n if (type.name) return String(type.name);\n if (type.__name) return String(type.__name);\n if (type.__file) return String(type.__file).split(\"/\").pop()!.replace(/\\.vue$/, \"\");\n return instance.parent === null ? \"App\" : \"Anonymous\";\n };\n const visit = (vnode: any, into: ComponentNode[]): void => {\n if (!vnode || total >= config.max_nodes) return;\n if (vnode.component) {\n const instance = vnode.component;\n const name = nameOf(instance);\n if (builtins.has(name)) {\n visit(instance.subTree, into);\n return;\n }\n const node: ComponentNode = { name, children: [] };\n total++;\n into.push(node);\n visit(instance.subTree, node.children);\n return;\n }\n if (vnode.suspense) {\n visit(vnode.suspense.activeBranch, into);\n return;\n }\n if (Array.isArray(vnode.children)) {\n for (const child of vnode.children) visit(child, into);\n }\n };\n const rootInstance = found.app._instance;\n const root: ComponentNode = { name: nameOf(rootInstance), children: [] };\n total = 1;\n visit(rootInstance.subTree, root.children);\n return { root, total };\n}\n\nasync function routerPushInPage(config: { path: string }): Promise<RouterOutcome> {\n const findApp = (): { app: any; root: any } | null => {\n const doc = (globalThis as any).document;\n if (!doc) return null;\n const marked = doc.querySelector(\"[data-v-app]\");\n if (marked && marked.__vue_app__) return { app: marked.__vue_app__, root: marked };\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n if (all[i].__vue_app__) return { app: all[i].__vue_app__, root: all[i] };\n }\n return null;\n };\n const found = findApp();\n if (!found) return { ok: false, error: \"no Vue app on this page\" };\n const props = found.app.config && found.app.config.globalProperties;\n const router = props && props.$router;\n if (!router || typeof router.push !== \"function\") return { ok: false, error: \"no vue-router on this app\" };\n try {\n if (typeof router.resolve === \"function\") {\n const resolved = router.resolve(config.path);\n if (!resolved || !resolved.matched || resolved.matched.length === 0) {\n return { ok: false, error: \"no route matched \" + config.path };\n }\n }\n const failure = await router.push(config.path);\n if (failure) {\n return { ok: false, error: \"navigation \" + (failure.type === 8 ? \"cancelled\" : failure.type === 16 ? \"duplicated\" : \"aborted\") + (failure.message ? \": \" + failure.message : \"\") };\n }\n const raf = (globalThis as any).requestAnimationFrame;\n if (typeof raf === \"function\") await new Promise<void>((resolve) => raf(() => raf(() => resolve())));\n const current = router.currentRoute && router.currentRoute.value;\n const route: { path: string; name?: string } = { path: current ? String(current.fullPath) : config.path };\n if (current && current.name !== undefined && current.name !== null) route.name = String(current.name);\n return { ok: true, route };\n } catch (error) {\n return { ok: false, error: error instanceof Error ? error.message : String(error) };\n }\n}\n"]}
@@ -319,8 +319,7 @@ export interface CaptureRun {
319
319
  export declare function runCapture(input: ParsedCaptureInput, hooks?: CaptureHooks): Promise<CaptureRun>;
320
320
  /**
321
321
  * The main FrameWatch tool: record a page for `duration_ms` and return only
322
- * the meaningful visual changes as diff cards (see CLAUDE.md "Diff Cards" and
323
- * "MCP Response Format").
322
+ * the meaningful visual changes as diff cards (see the README, framewatch_capture).
324
323
  *
325
324
  * Flow: navigate (waiting only for the navigation to commit, so loading and
326
325
  * splash animations are recorded from their first frame) → optionally wait
@@ -183,8 +183,7 @@ export async function runCapture(input, hooks = {}) {
183
183
  }
184
184
  /**
185
185
  * The main FrameWatch tool: record a page for `duration_ms` and return only
186
- * the meaningful visual changes as diff cards (see CLAUDE.md "Diff Cards" and
187
- * "MCP Response Format").
186
+ * the meaningful visual changes as diff cards (see the README, framewatch_capture).
188
187
  *
189
188
  * Flow: navigate (waiting only for the navigation to commit, so loading and
190
189
  * splash animations are recorded from their first frame) → optionally wait
@@ -1 +1 @@
1
- {"version":3,"file":"capture.js","sourceRoot":"","sources":["../../src/tools/capture.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\nimport type { BrowserContext, BrowserContextOptions, Page } from \"playwright\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport {\n CAPTURE_INTERVAL_MS,\n DEFAULT_CAPTURE_DURATION_MS,\n DEFAULT_MAX_FRAMES,\n DEFAULT_SENSITIVITY,\n DEFAULT_VIEWPORT,\n MAX_CAPTURE_DURATION_MS,\n MAX_VIEWPORT_HEIGHT,\n MAX_VIEWPORT_WIDTH,\n MAX_FRAMES_CAP,\n MAX_INTERACTIONS,\n MIN_CAPTURE_DURATION_MS,\n NAVIGATION_TIMEOUT_MS,\n PAGE_INFO_TIMEOUT_MS,\n SELECTOR_TIMEOUT_MS,\n} from \"../constants.js\";\nimport { withPage } from \"../engine/browser.js\";\nimport { buildDiffCards } from \"../engine/differ.js\";\nimport { applyContext, attachLayers, summariseContext, type CapturedContext } from \"../engine/layers/index.js\";\nimport {\n CAPTURE_ACTIONS,\n describeInteraction,\n executeInteraction,\n interactionFieldShape,\n needsTouch,\n refineInteraction,\n type Interaction,\n} from \"../engine/interaction.js\";\nimport { recordFrames, type FrameRecorder } from \"../engine/recorder.js\";\nimport { formatDiffCards, type InteractionReport } from \"../utils/format.js\";\nimport type { DiffCard, Viewport } from \"../types.js\";\nimport { describeAuth, loginFormVisible, resolveStorageState, storageStateField, type ResolvedAuth } from \"../utils/storage-state.js\";\n\nexport const CAPTURE_TOOL_NAME = \"framewatch_capture\";\n\n/**\n * Re-exported where it is used: the capture summary's context lines. It lives\n * with the layers themselves now that `framewatch_interact` reports them too.\n */\nexport { summariseContext };\n\n/**\n * One step of a replayable interaction script. `delay_ms` is a wait *before*\n * the action, so a `wait` step is just a delay with nothing after it, and the\n * delays of successive steps accumulate over the recording.\n */\nexport const captureInteractionSchema = z\n .object({\n action: z\n .enum(CAPTURE_ACTIONS)\n .describe(\"What to do: click, tap, type, key, scroll, swipe, hover, select, wait or navigate\"),\n ...interactionFieldShape,\n delay_ms: z.number().int().min(0).default(0).describe(\"Wait this long (ms) before performing this action\"),\n })\n .superRefine(refineInteraction);\n\nexport const captureInputShape = {\n url: z.string().url().describe(\"URL to capture, e.g. http://localhost:3000 (http, https and file URLs are accepted)\"),\n duration_ms: z\n .number()\n .int()\n .min(MIN_CAPTURE_DURATION_MS)\n .max(MAX_CAPTURE_DURATION_MS)\n .default(DEFAULT_CAPTURE_DURATION_MS)\n .describe(\"How long to record (ms)\"),\n sensitivity: z\n .number()\n .min(0)\n .max(1)\n .default(DEFAULT_SENSITIVITY)\n .describe(\"Frame change threshold (0 = keep all, 1 = keep none). 0.06 means ~6% of grid cells must change.\"),\n max_frames: z\n .number()\n .int()\n .min(1)\n .max(MAX_FRAMES_CAP)\n .default(DEFAULT_MAX_FRAMES)\n .describe(\"Maximum diff cards to return\"),\n interval_ms: z\n .number()\n .int()\n .min(16)\n .max(2000)\n .default(CAPTURE_INTERVAL_MS)\n .describe(\"Raw frame capture interval (ms) — 100 = 10fps\"),\n viewport: z\n .object({\n width: z.number().int().min(1).max(MAX_VIEWPORT_WIDTH).default(DEFAULT_VIEWPORT.width),\n height: z.number().int().min(1).max(MAX_VIEWPORT_HEIGHT).default(DEFAULT_VIEWPORT.height),\n })\n .optional()\n .describe(\"Viewport size (defaults to 1280x720)\"),\n wait_for: z.string().optional().describe(\"CSS selector to wait for (visible) before recording starts\"),\n wait_for_timeout_ms: z\n .number()\n .int()\n .min(1)\n .default(SELECTOR_TIMEOUT_MS)\n .describe(\"Max time (ms) to wait for `wait_for` to appear (must be > 0)\"),\n interactions: z\n .array(captureInteractionSchema)\n .max(MAX_INTERACTIONS)\n .optional()\n .describe(\n \"Sequence of user interactions to replay during the recording. Selectors may be CSS or Playwright's \" +\n 'role form, e.g. role=button[name=\"Sign in\"] — take the role and name from framewatch_snapshot',\n ),\n interaction_timeout_ms: z\n .number()\n .int()\n .min(1)\n .default(SELECTOR_TIMEOUT_MS)\n .describe(\"Max time (ms) an interaction step may spend waiting for its target element\"),\n include_console: z\n .boolean()\n .default(true)\n .describe(\"Attach console logs, uncaught errors and unhandled rejections to the frames they happened between\"),\n include_network: z\n .boolean()\n .default(false)\n .describe(\"Attach network requests (method, url, status, duration) to the frames they settled between\"),\n include_dom: z\n .boolean()\n .default(false)\n .describe(\"Attach a summary of the DOM mutations between frames — which elements were added, removed or restyled\"),\n include_performance: z\n .boolean()\n .default(false)\n .describe(\"Attach paint timing, largest contentful paint and layout shifts to the frames they were measured at\"),\n storage_state: storageStateField,\n};\n\nexport const captureInputSchema = z.object(captureInputShape);\nexport type CaptureInput = z.input<typeof captureInputSchema>;\nexport type ParsedCaptureInput = z.output<typeof captureInputSchema>;\n\n/**\n * Hooks a caller can run inside a capture session.\n *\n * `framewatch_api_mock` uses `prepare` to install its request routes before\n * anything navigates; `framewatch_capture` itself passes none.\n */\nexport interface CaptureHooks {\n /**\n * Run against the page and its context before the layers are attached and\n * before the navigation. Anything that has to be in place for the very first\n * request the page makes belongs here.\n */\n prepare?: (page: Page, context: BrowserContext) => Promise<void>;\n /**\n * Run once the recording has stopped, while the page is still open. For\n * releasing anything `prepare` installed that would otherwise outlive the\n * call — a route still sleeping out a delay has nothing left to answer.\n * A failure here is swallowed: the frames have already been taken.\n */\n finish?: (page: Page, context: BrowserContext) => Promise<void>;\n}\n\n/** Everything one capture session produced, before anything formats it. */\nexport interface CaptureRun {\n cards: DiffCard[];\n /** Raw frames the recorder took, before the differ chose between them. */\n total_frames: number;\n duration_ms: number;\n /** URL that was requested. */\n url: string;\n /** URL the page ended on, when it could be read. */\n final_url?: string;\n title?: string;\n /** Frames the recorder had to drop (screenshot failures). */\n dropped: number;\n context: CapturedContext;\n /** Present only when an interaction script was replayed. */\n interactions?: InteractionReport;\n /** The viewport the frames were recorded at. */\n viewport: Viewport;\n /** The saved auth that was applied, if any, and whether the page still showed a login form afterwards. */\n auth: ResolvedAuth | null;\n login_visible: boolean;\n}\n\n/**\n * Run one capture session: navigate, record, replay the script, diff.\n *\n * Throws on failure rather than returning an error result — each caller has\n * its own wording for what went wrong (see `describeCaptureFailure`).\n *\n * Shared with `framewatch_api_mock`, which needs this exact flow with its\n * routes installed first. One navigation, one recorder, one differ, and one\n * place to fix any of them.\n */\nexport async function runCapture(input: ParsedCaptureInput, hooks: CaptureHooks = {}): Promise<CaptureRun> {\n const viewport = input.viewport ?? { ...DEFAULT_VIEWPORT };\n const interactions = input.interactions ?? [];\n // Touch has to be decided when the context is created, so read it off the\n // script rather than turning it on for every capture: `hasTouch` puts\n // `ontouchstart` on window, which flips feature detection in most UI\n // libraries and would silently change what a plain capture records.\n const contextOptions: BrowserContextOptions = needsTouch(interactions) ? { hasTouch: true } : {};\n\n const report: InteractionReport | undefined =\n interactions.length > 0 ? { total: interactions.length, completed: 0, steps: [] } : undefined;\n\n const auth = await resolveStorageState(input.storage_state);\n if (auth) contextOptions.storageState = auth.state;\n\n const recording = await withPage({ viewport, contextOptions }, async (page, context) => {\n // Before anything else, including the layers: whatever the caller needs in\n // place for the page's very first request.\n if (hooks.prepare) await hooks.prepare(page, context);\n\n // Layers go on before the navigation: a script that throws while the page\n // loads, the request that never comes back and first paint all happen\n // before frame 0 exists, and they are the most useful things here.\n const layers = await attachLayers(page, {\n console: input.include_console,\n network: input.include_network,\n dom: input.include_dom,\n performance: input.include_performance,\n });\n try {\n // \"commit\" rather than \"load\": splash/loading animations must be recorded from the very start.\n await page.goto(input.url, { waitUntil: \"commit\", timeout: NAVIGATION_TIMEOUT_MS });\n if (input.wait_for) {\n await page.waitForSelector(input.wait_for, { state: \"visible\", timeout: input.wait_for_timeout_ms });\n }\n const result = await recordFrames(\n page,\n { duration_ms: input.duration_ms, interval_ms: input.interval_ms },\n report && ((recorder) => replayScript(page, recorder, interactions, input.interaction_timeout_ms, report)),\n );\n // Drain the layers before anything else can close the page. `collect`\n // reads only what Node already holds, so a page that froze, navigated\n // away or crashed still yields everything it managed to report.\n const collected = layers.collect(result.started_at);\n // The page may have died mid-recording (crash, closed browser). Frames\n // already captured are still worth returning, so never let reading the\n // title or url turn a partial recording into an error.\n // A login form on the page after restoring a session is the expiry signal.\n const loginVisible = auth ? await loginFormVisible(page) : false;\n return { ...result, context: collected, loginVisible, ...(await describePage(page)) };\n } finally {\n layers.detach();\n if (hooks.finish) await hooks.finish(page, context).catch(() => {});\n }\n });\n\n const { cards, total_frames } = await buildDiffCards(recording.frames, {\n sensitivity: input.sensitivity,\n max_frames: input.max_frames,\n });\n applyContext(cards, recording.context);\n\n return {\n cards,\n total_frames,\n duration_ms: recording.duration_ms,\n url: input.url,\n final_url: recording.finalUrl,\n title: recording.title,\n dropped: recording.dropped,\n context: recording.context,\n interactions: report,\n viewport,\n auth,\n login_visible: recording.loginVisible,\n };\n}\n\n/**\n * The main FrameWatch tool: record a page for `duration_ms` and return only\n * the meaningful visual changes as diff cards (see CLAUDE.md \"Diff Cards\" and\n * \"MCP Response Format\").\n *\n * Flow: navigate (waiting only for the navigation to commit, so loading and\n * splash animations are recorded from their first frame) → optionally wait\n * for `wait_for` → record raw frames every `interval_ms`, replaying\n * `interactions` as the recording runs → smart-diff them into at most\n * `max_frames` cards → format. All failures — including invalid input — are\n * reported as `isError` results rather than thrown so the MCP client sees a\n * useful message.\n */\nexport async function capturePage(rawInput: CaptureInput): Promise<CallToolResult> {\n const parsed = captureInputSchema.safeParse(rawInput);\n if (!parsed.success) {\n const issues = parsed.error.issues.map((i) => `${i.path.join(\".\") || \"input\"}: ${i.message}`).join(\"; \");\n return errorResult(`Capture failed: invalid input — ${issues}`);\n }\n const input = parsed.data;\n\n let run: CaptureRun;\n try {\n run = await runCapture(input);\n } catch (error) {\n return errorResult(describeCaptureFailure(input, error));\n }\n\n return formatDiffCards({\n cards: run.cards,\n total_frames: run.total_frames,\n duration_ms: run.duration_ms,\n url: run.url,\n final_url: run.final_url,\n title: run.title,\n dropped: run.dropped,\n interactions: run.interactions,\n viewport: run.viewport,\n notes: [...(summariseContext(run.context, run.cards.length) ?? []), ...authLines(run)],\n });\n}\n\n/** The auth line for a run's summary, when there is one (see `describeAuth`). */\nexport function authLines(run: Pick<CaptureRun, \"auth\" | \"login_visible\">): string[] {\n const line = run.auth ? describeAuth(run.auth, run.login_visible) : null;\n return line ? [line] : [];\n}\n\n/**\n * Replay the interaction script while the recorder runs, forcing a frame after\n * every step so the differ always keeps the result of each action.\n *\n * A failing step ends the script but never the capture: the frames recorded so\n * far are the most useful thing FrameWatch can hand back (they show the state\n * the page was actually in when the selector did not match), so the failure is\n * recorded in `report` and a forced \"error\" frame is captured instead of\n * throwing. The recording then plays out the rest of its duration.\n */\nasync function replayScript(\n page: Page,\n recorder: FrameRecorder,\n interactions: Interaction[],\n timeoutMs: number,\n report: InteractionReport,\n): Promise<void> {\n for (let i = 0; i < interactions.length; i++) {\n const step = interactions[i];\n let outcome;\n try {\n outcome = await executeInteraction(page, step, { timeout_ms: timeoutMs });\n } catch (error) {\n report.error = error instanceof Error ? error.message : String(error);\n report.failed_index = i + 1;\n await recorder.captureNow(\"error\");\n return;\n }\n report.completed++;\n report.steps.push(describeInteraction(step) + (outcome.note ?? \"\"));\n await recorder.captureNow(\"interaction\");\n }\n}\n\n/**\n * Title and final url of a page. Both are cosmetic, so neither is allowed to\n * fail or delay the result: the page may have died during the recording, and\n * `page.title()` blocks for Playwright's full default timeout while the main\n * thread is busy — exactly the case a developer points this tool at.\n */\nasync function describePage(page: Page): Promise<{ title?: string; finalUrl?: string }> {\n if (page.isClosed()) return {};\n const finalUrl = safely(() => page.url());\n // Settle the losing side too: a page.title() that outlives the race would\n // otherwise reject unobserved when the context is closed underneath it.\n const title = page.title().catch(() => undefined);\n let timer: NodeJS.Timeout | undefined;\n const giveUp = new Promise<undefined>((resolve) => {\n timer = setTimeout(() => resolve(undefined), PAGE_INFO_TIMEOUT_MS);\n });\n try {\n return { title: await Promise.race([title, giveUp]), finalUrl };\n } finally {\n clearTimeout(timer);\n }\n}\n\nfunction safely<T>(read: () => T): T | undefined {\n try {\n return read();\n } catch {\n return undefined;\n }\n}\n\nfunction errorResult(text: string): CallToolResult {\n return { isError: true, content: [{ type: \"text\", text }] };\n}\n\n/**\n * Turn a Playwright/Node error into a one-line, actionable message. Mirrors\n * `describeFailure` in screenshot.ts: matches on the failing Playwright call\n * (the message prefix) rather than on substrings of user-supplied selectors,\n * so a navigation failure is never blamed on an element.\n */\nexport function describeCaptureFailure(\n input: Pick<ParsedCaptureInput, \"url\" | \"wait_for\" | \"wait_for_timeout_ms\">,\n error: unknown,\n): string {\n const message = error instanceof Error ? error.message : String(error);\n const firstLine = message.split(\"\\n\")[0];\n const prefix = `Capture of ${input.url} failed:`;\n\n if (/Executable doesn't exist|browserType\\.launch/i.test(message)) {\n return (\n `${prefix} Playwright's Chromium browser is not installed. ` +\n `Run \\`npx playwright install chromium\\` and try again. (${firstLine})`\n );\n }\n if (input.wait_for && /^page\\.waitForSelector:/.test(message)) {\n return `${prefix} selector \"${input.wait_for}\" did not become visible within ${input.wait_for_timeout_ms}ms.`;\n }\n return `${prefix} ${firstLine}`;\n}\n\nexport function registerCaptureTool(server: McpServer): void {\n server.registerTool(\n CAPTURE_TOOL_NAME,\n {\n title: \"Capture\",\n description:\n \"Record a web page for a few seconds and return only the meaningful visual changes as diff cards: \" +\n \"a full-frame PNG plus a crop of the changed region and its position for each kept frame. \" +\n \"Use it to check animations, splash/loading screens, transitions and anything that changes over time. \" +\n \"Lower `sensitivity` to keep more frames; `max_frames` caps the output. \" +\n \"Each frame can also carry the context for the interval that produced it: console output and uncaught \" +\n \"errors (on by default), plus network requests, DOM mutations and paint/layout-shift timing via \" +\n \"`include_network`, `include_dom` and `include_performance` — that is how you find out *why* a frame \" +\n \"looks wrong, not just that it does.\",\n inputSchema: captureInputShape,\n annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: true },\n },\n async (args) => capturePage(args),\n );\n}\n"]}
1
+ {"version":3,"file":"capture.js","sourceRoot":"","sources":["../../src/tools/capture.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\nimport type { BrowserContext, BrowserContextOptions, Page } from \"playwright\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport {\n CAPTURE_INTERVAL_MS,\n DEFAULT_CAPTURE_DURATION_MS,\n DEFAULT_MAX_FRAMES,\n DEFAULT_SENSITIVITY,\n DEFAULT_VIEWPORT,\n MAX_CAPTURE_DURATION_MS,\n MAX_VIEWPORT_HEIGHT,\n MAX_VIEWPORT_WIDTH,\n MAX_FRAMES_CAP,\n MAX_INTERACTIONS,\n MIN_CAPTURE_DURATION_MS,\n NAVIGATION_TIMEOUT_MS,\n PAGE_INFO_TIMEOUT_MS,\n SELECTOR_TIMEOUT_MS,\n} from \"../constants.js\";\nimport { withPage } from \"../engine/browser.js\";\nimport { buildDiffCards } from \"../engine/differ.js\";\nimport { applyContext, attachLayers, summariseContext, type CapturedContext } from \"../engine/layers/index.js\";\nimport {\n CAPTURE_ACTIONS,\n describeInteraction,\n executeInteraction,\n interactionFieldShape,\n needsTouch,\n refineInteraction,\n type Interaction,\n} from \"../engine/interaction.js\";\nimport { recordFrames, type FrameRecorder } from \"../engine/recorder.js\";\nimport { formatDiffCards, type InteractionReport } from \"../utils/format.js\";\nimport type { DiffCard, Viewport } from \"../types.js\";\nimport { describeAuth, loginFormVisible, resolveStorageState, storageStateField, type ResolvedAuth } from \"../utils/storage-state.js\";\n\nexport const CAPTURE_TOOL_NAME = \"framewatch_capture\";\n\n/**\n * Re-exported where it is used: the capture summary's context lines. It lives\n * with the layers themselves now that `framewatch_interact` reports them too.\n */\nexport { summariseContext };\n\n/**\n * One step of a replayable interaction script. `delay_ms` is a wait *before*\n * the action, so a `wait` step is just a delay with nothing after it, and the\n * delays of successive steps accumulate over the recording.\n */\nexport const captureInteractionSchema = z\n .object({\n action: z\n .enum(CAPTURE_ACTIONS)\n .describe(\"What to do: click, tap, type, key, scroll, swipe, hover, select, wait or navigate\"),\n ...interactionFieldShape,\n delay_ms: z.number().int().min(0).default(0).describe(\"Wait this long (ms) before performing this action\"),\n })\n .superRefine(refineInteraction);\n\nexport const captureInputShape = {\n url: z.string().url().describe(\"URL to capture, e.g. http://localhost:3000 (http, https and file URLs are accepted)\"),\n duration_ms: z\n .number()\n .int()\n .min(MIN_CAPTURE_DURATION_MS)\n .max(MAX_CAPTURE_DURATION_MS)\n .default(DEFAULT_CAPTURE_DURATION_MS)\n .describe(\"How long to record (ms)\"),\n sensitivity: z\n .number()\n .min(0)\n .max(1)\n .default(DEFAULT_SENSITIVITY)\n .describe(\"Frame change threshold (0 = keep all, 1 = keep none). 0.06 means ~6% of grid cells must change.\"),\n max_frames: z\n .number()\n .int()\n .min(1)\n .max(MAX_FRAMES_CAP)\n .default(DEFAULT_MAX_FRAMES)\n .describe(\"Maximum diff cards to return\"),\n interval_ms: z\n .number()\n .int()\n .min(16)\n .max(2000)\n .default(CAPTURE_INTERVAL_MS)\n .describe(\"Raw frame capture interval (ms) — 100 = 10fps\"),\n viewport: z\n .object({\n width: z.number().int().min(1).max(MAX_VIEWPORT_WIDTH).default(DEFAULT_VIEWPORT.width),\n height: z.number().int().min(1).max(MAX_VIEWPORT_HEIGHT).default(DEFAULT_VIEWPORT.height),\n })\n .optional()\n .describe(\"Viewport size (defaults to 1280x720)\"),\n wait_for: z.string().optional().describe(\"CSS selector to wait for (visible) before recording starts\"),\n wait_for_timeout_ms: z\n .number()\n .int()\n .min(1)\n .default(SELECTOR_TIMEOUT_MS)\n .describe(\"Max time (ms) to wait for `wait_for` to appear (must be > 0)\"),\n interactions: z\n .array(captureInteractionSchema)\n .max(MAX_INTERACTIONS)\n .optional()\n .describe(\n \"Sequence of user interactions to replay during the recording. Selectors may be CSS or Playwright's \" +\n 'role form, e.g. role=button[name=\"Sign in\"] — take the role and name from framewatch_snapshot',\n ),\n interaction_timeout_ms: z\n .number()\n .int()\n .min(1)\n .default(SELECTOR_TIMEOUT_MS)\n .describe(\"Max time (ms) an interaction step may spend waiting for its target element\"),\n include_console: z\n .boolean()\n .default(true)\n .describe(\"Attach console logs, uncaught errors and unhandled rejections to the frames they happened between\"),\n include_network: z\n .boolean()\n .default(false)\n .describe(\"Attach network requests (method, url, status, duration) to the frames they settled between\"),\n include_dom: z\n .boolean()\n .default(false)\n .describe(\"Attach a summary of the DOM mutations between frames — which elements were added, removed or restyled\"),\n include_performance: z\n .boolean()\n .default(false)\n .describe(\"Attach paint timing, largest contentful paint and layout shifts to the frames they were measured at\"),\n storage_state: storageStateField,\n};\n\nexport const captureInputSchema = z.object(captureInputShape);\nexport type CaptureInput = z.input<typeof captureInputSchema>;\nexport type ParsedCaptureInput = z.output<typeof captureInputSchema>;\n\n/**\n * Hooks a caller can run inside a capture session.\n *\n * `framewatch_api_mock` uses `prepare` to install its request routes before\n * anything navigates; `framewatch_capture` itself passes none.\n */\nexport interface CaptureHooks {\n /**\n * Run against the page and its context before the layers are attached and\n * before the navigation. Anything that has to be in place for the very first\n * request the page makes belongs here.\n */\n prepare?: (page: Page, context: BrowserContext) => Promise<void>;\n /**\n * Run once the recording has stopped, while the page is still open. For\n * releasing anything `prepare` installed that would otherwise outlive the\n * call — a route still sleeping out a delay has nothing left to answer.\n * A failure here is swallowed: the frames have already been taken.\n */\n finish?: (page: Page, context: BrowserContext) => Promise<void>;\n}\n\n/** Everything one capture session produced, before anything formats it. */\nexport interface CaptureRun {\n cards: DiffCard[];\n /** Raw frames the recorder took, before the differ chose between them. */\n total_frames: number;\n duration_ms: number;\n /** URL that was requested. */\n url: string;\n /** URL the page ended on, when it could be read. */\n final_url?: string;\n title?: string;\n /** Frames the recorder had to drop (screenshot failures). */\n dropped: number;\n context: CapturedContext;\n /** Present only when an interaction script was replayed. */\n interactions?: InteractionReport;\n /** The viewport the frames were recorded at. */\n viewport: Viewport;\n /** The saved auth that was applied, if any, and whether the page still showed a login form afterwards. */\n auth: ResolvedAuth | null;\n login_visible: boolean;\n}\n\n/**\n * Run one capture session: navigate, record, replay the script, diff.\n *\n * Throws on failure rather than returning an error result — each caller has\n * its own wording for what went wrong (see `describeCaptureFailure`).\n *\n * Shared with `framewatch_api_mock`, which needs this exact flow with its\n * routes installed first. One navigation, one recorder, one differ, and one\n * place to fix any of them.\n */\nexport async function runCapture(input: ParsedCaptureInput, hooks: CaptureHooks = {}): Promise<CaptureRun> {\n const viewport = input.viewport ?? { ...DEFAULT_VIEWPORT };\n const interactions = input.interactions ?? [];\n // Touch has to be decided when the context is created, so read it off the\n // script rather than turning it on for every capture: `hasTouch` puts\n // `ontouchstart` on window, which flips feature detection in most UI\n // libraries and would silently change what a plain capture records.\n const contextOptions: BrowserContextOptions = needsTouch(interactions) ? { hasTouch: true } : {};\n\n const report: InteractionReport | undefined =\n interactions.length > 0 ? { total: interactions.length, completed: 0, steps: [] } : undefined;\n\n const auth = await resolveStorageState(input.storage_state);\n if (auth) contextOptions.storageState = auth.state;\n\n const recording = await withPage({ viewport, contextOptions }, async (page, context) => {\n // Before anything else, including the layers: whatever the caller needs in\n // place for the page's very first request.\n if (hooks.prepare) await hooks.prepare(page, context);\n\n // Layers go on before the navigation: a script that throws while the page\n // loads, the request that never comes back and first paint all happen\n // before frame 0 exists, and they are the most useful things here.\n const layers = await attachLayers(page, {\n console: input.include_console,\n network: input.include_network,\n dom: input.include_dom,\n performance: input.include_performance,\n });\n try {\n // \"commit\" rather than \"load\": splash/loading animations must be recorded from the very start.\n await page.goto(input.url, { waitUntil: \"commit\", timeout: NAVIGATION_TIMEOUT_MS });\n if (input.wait_for) {\n await page.waitForSelector(input.wait_for, { state: \"visible\", timeout: input.wait_for_timeout_ms });\n }\n const result = await recordFrames(\n page,\n { duration_ms: input.duration_ms, interval_ms: input.interval_ms },\n report && ((recorder) => replayScript(page, recorder, interactions, input.interaction_timeout_ms, report)),\n );\n // Drain the layers before anything else can close the page. `collect`\n // reads only what Node already holds, so a page that froze, navigated\n // away or crashed still yields everything it managed to report.\n const collected = layers.collect(result.started_at);\n // The page may have died mid-recording (crash, closed browser). Frames\n // already captured are still worth returning, so never let reading the\n // title or url turn a partial recording into an error.\n // A login form on the page after restoring a session is the expiry signal.\n const loginVisible = auth ? await loginFormVisible(page) : false;\n return { ...result, context: collected, loginVisible, ...(await describePage(page)) };\n } finally {\n layers.detach();\n if (hooks.finish) await hooks.finish(page, context).catch(() => {});\n }\n });\n\n const { cards, total_frames } = await buildDiffCards(recording.frames, {\n sensitivity: input.sensitivity,\n max_frames: input.max_frames,\n });\n applyContext(cards, recording.context);\n\n return {\n cards,\n total_frames,\n duration_ms: recording.duration_ms,\n url: input.url,\n final_url: recording.finalUrl,\n title: recording.title,\n dropped: recording.dropped,\n context: recording.context,\n interactions: report,\n viewport,\n auth,\n login_visible: recording.loginVisible,\n };\n}\n\n/**\n * The main FrameWatch tool: record a page for `duration_ms` and return only\n * the meaningful visual changes as diff cards (see the README, framewatch_capture).\n *\n * Flow: navigate (waiting only for the navigation to commit, so loading and\n * splash animations are recorded from their first frame) → optionally wait\n * for `wait_for` → record raw frames every `interval_ms`, replaying\n * `interactions` as the recording runs → smart-diff them into at most\n * `max_frames` cards → format. All failures — including invalid input — are\n * reported as `isError` results rather than thrown so the MCP client sees a\n * useful message.\n */\nexport async function capturePage(rawInput: CaptureInput): Promise<CallToolResult> {\n const parsed = captureInputSchema.safeParse(rawInput);\n if (!parsed.success) {\n const issues = parsed.error.issues.map((i) => `${i.path.join(\".\") || \"input\"}: ${i.message}`).join(\"; \");\n return errorResult(`Capture failed: invalid input — ${issues}`);\n }\n const input = parsed.data;\n\n let run: CaptureRun;\n try {\n run = await runCapture(input);\n } catch (error) {\n return errorResult(describeCaptureFailure(input, error));\n }\n\n return formatDiffCards({\n cards: run.cards,\n total_frames: run.total_frames,\n duration_ms: run.duration_ms,\n url: run.url,\n final_url: run.final_url,\n title: run.title,\n dropped: run.dropped,\n interactions: run.interactions,\n viewport: run.viewport,\n notes: [...(summariseContext(run.context, run.cards.length) ?? []), ...authLines(run)],\n });\n}\n\n/** The auth line for a run's summary, when there is one (see `describeAuth`). */\nexport function authLines(run: Pick<CaptureRun, \"auth\" | \"login_visible\">): string[] {\n const line = run.auth ? describeAuth(run.auth, run.login_visible) : null;\n return line ? [line] : [];\n}\n\n/**\n * Replay the interaction script while the recorder runs, forcing a frame after\n * every step so the differ always keeps the result of each action.\n *\n * A failing step ends the script but never the capture: the frames recorded so\n * far are the most useful thing FrameWatch can hand back (they show the state\n * the page was actually in when the selector did not match), so the failure is\n * recorded in `report` and a forced \"error\" frame is captured instead of\n * throwing. The recording then plays out the rest of its duration.\n */\nasync function replayScript(\n page: Page,\n recorder: FrameRecorder,\n interactions: Interaction[],\n timeoutMs: number,\n report: InteractionReport,\n): Promise<void> {\n for (let i = 0; i < interactions.length; i++) {\n const step = interactions[i];\n let outcome;\n try {\n outcome = await executeInteraction(page, step, { timeout_ms: timeoutMs });\n } catch (error) {\n report.error = error instanceof Error ? error.message : String(error);\n report.failed_index = i + 1;\n await recorder.captureNow(\"error\");\n return;\n }\n report.completed++;\n report.steps.push(describeInteraction(step) + (outcome.note ?? \"\"));\n await recorder.captureNow(\"interaction\");\n }\n}\n\n/**\n * Title and final url of a page. Both are cosmetic, so neither is allowed to\n * fail or delay the result: the page may have died during the recording, and\n * `page.title()` blocks for Playwright's full default timeout while the main\n * thread is busy — exactly the case a developer points this tool at.\n */\nasync function describePage(page: Page): Promise<{ title?: string; finalUrl?: string }> {\n if (page.isClosed()) return {};\n const finalUrl = safely(() => page.url());\n // Settle the losing side too: a page.title() that outlives the race would\n // otherwise reject unobserved when the context is closed underneath it.\n const title = page.title().catch(() => undefined);\n let timer: NodeJS.Timeout | undefined;\n const giveUp = new Promise<undefined>((resolve) => {\n timer = setTimeout(() => resolve(undefined), PAGE_INFO_TIMEOUT_MS);\n });\n try {\n return { title: await Promise.race([title, giveUp]), finalUrl };\n } finally {\n clearTimeout(timer);\n }\n}\n\nfunction safely<T>(read: () => T): T | undefined {\n try {\n return read();\n } catch {\n return undefined;\n }\n}\n\nfunction errorResult(text: string): CallToolResult {\n return { isError: true, content: [{ type: \"text\", text }] };\n}\n\n/**\n * Turn a Playwright/Node error into a one-line, actionable message. Mirrors\n * `describeFailure` in screenshot.ts: matches on the failing Playwright call\n * (the message prefix) rather than on substrings of user-supplied selectors,\n * so a navigation failure is never blamed on an element.\n */\nexport function describeCaptureFailure(\n input: Pick<ParsedCaptureInput, \"url\" | \"wait_for\" | \"wait_for_timeout_ms\">,\n error: unknown,\n): string {\n const message = error instanceof Error ? error.message : String(error);\n const firstLine = message.split(\"\\n\")[0];\n const prefix = `Capture of ${input.url} failed:`;\n\n if (/Executable doesn't exist|browserType\\.launch/i.test(message)) {\n return (\n `${prefix} Playwright's Chromium browser is not installed. ` +\n `Run \\`npx playwright install chromium\\` and try again. (${firstLine})`\n );\n }\n if (input.wait_for && /^page\\.waitForSelector:/.test(message)) {\n return `${prefix} selector \"${input.wait_for}\" did not become visible within ${input.wait_for_timeout_ms}ms.`;\n }\n return `${prefix} ${firstLine}`;\n}\n\nexport function registerCaptureTool(server: McpServer): void {\n server.registerTool(\n CAPTURE_TOOL_NAME,\n {\n title: \"Capture\",\n description:\n \"Record a web page for a few seconds and return only the meaningful visual changes as diff cards: \" +\n \"a full-frame PNG plus a crop of the changed region and its position for each kept frame. \" +\n \"Use it to check animations, splash/loading screens, transitions and anything that changes over time. \" +\n \"Lower `sensitivity` to keep more frames; `max_frames` caps the output. \" +\n \"Each frame can also carry the context for the interval that produced it: console output and uncaught \" +\n \"errors (on by default), plus network requests, DOM mutations and paint/layout-shift timing via \" +\n \"`include_network`, `include_dom` and `include_performance` — that is how you find out *why* a frame \" +\n \"looks wrong, not just that it does.\",\n inputSchema: captureInputShape,\n annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: true },\n },\n async (args) => capturePage(args),\n );\n}\n"]}
@@ -6,7 +6,7 @@ import { applyContext, layersFor, summariseContext } from "../engine/layers/inde
6
6
  import { INTERACT_ACTIONS, describeInteraction, executeInteraction, interactionFieldShape, needsTouch, refineInteraction, } from "../engine/interaction.js";
7
7
  import { takeSnapshot } from "../engine/snapshot.js";
8
8
  import { describeScale, formatCardMeta } from "../utils/format.js";
9
- import { refSelector } from "../utils/snapshot-rules.js";
9
+ import { REF_SHAPE, refSelector } from "../utils/snapshot-rules.js";
10
10
  import { describeAuth, loginFormVisible, resolveStorageState, storageStateField } from "../utils/storage-state.js";
11
11
  import { DEFAULT_SNAPSHOT_MAX_CHARS, VUE_DETECT_MS, VUE_READY_TIMEOUT_MS } from "../constants.js";
12
12
  import { hmrFor } from "../engine/hmr.js";
@@ -22,7 +22,7 @@ export const interactInputShape = {
22
22
  ...interactionFieldShape,
23
23
  ref: z
24
24
  .string()
25
- .regex(/^e\d+$/, "a ref looks like `e8` — take one from framewatch_snapshot")
25
+ .regex(REF_SHAPE, "a ref looks like `e8` (or `f1e8`) — take one from framewatch_snapshot")
26
26
  .optional()
27
27
  .describe("Element ref from framewatch_snapshot (e.g. `e8`) to act on, instead of a `selector`"),
28
28
  url: z
@@ -1 +1 @@
1
- {"version":3,"file":"interact.js","sourceRoot":"","sources":["../../src/tools/interact.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport {\n DEFAULT_INTERACT_WAIT_MS,\n MAX_VIEWPORT_HEIGHT,\n MAX_VIEWPORT_WIDTH,\n NAVIGATION_TIMEOUT_MS,\n SELECTOR_TIMEOUT_MS,\n} from \"../constants.js\";\nimport { getSessionPage, withSessionLock } from \"../engine/browser.js\";\nimport { buildDiffCards } from \"../engine/differ.js\";\nimport { applyContext, layersFor, summariseContext, type LayerFlags } from \"../engine/layers/index.js\";\nimport {\n INTERACT_ACTIONS,\n describeInteraction,\n executeInteraction,\n interactionFieldShape,\n needsTouch,\n refineInteraction,\n type Interaction,\n} from \"../engine/interaction.js\";\nimport { takeSnapshot } from \"../engine/snapshot.js\";\nimport type { RawFrame } from \"../types.js\";\nimport { describeScale, formatCardMeta } from \"../utils/format.js\";\nimport { refSelector } from \"../utils/snapshot-rules.js\";\nimport { describeAuth, loginFormVisible, resolveStorageState, storageStateField } from \"../utils/storage-state.js\";\nimport { DEFAULT_SNAPSHOT_MAX_CHARS, VUE_DETECT_MS, VUE_READY_TIMEOUT_MS } from \"../constants.js\";\nimport { hmrFor } from \"../engine/hmr.js\";\nimport { detectVue, waitForVueReady } from \"../engine/vue.js\";\nimport { describeVue, type VueInfo } from \"../utils/vue-rules.js\";\nimport { describeCounts } from \"./snapshot.js\";\nimport { markImage } from \"../utils/budget.js\";\n\nexport const INTERACT_TOOL_NAME = \"framewatch_interact\";\n\nexport const interactInputShape = {\n action: z\n .enum(INTERACT_ACTIONS)\n .describe(\"What to do: click, tap, type, key, scroll, swipe, navigate, select or hover\"),\n ...interactionFieldShape,\n ref: z\n .string()\n .regex(/^e\\d+$/, \"a ref looks like `e8` — take one from framewatch_snapshot\")\n .optional()\n .describe(\"Element ref from framewatch_snapshot (e.g. `e8`) to act on, instead of a `selector`\"),\n url: z\n .string()\n .url()\n .optional()\n .describe(\"Open this URL first. Omit to act on the page left open by the previous call.\"),\n wait_ms: z\n .number()\n .int()\n .min(0)\n .default(DEFAULT_INTERACT_WAIT_MS)\n .describe(\"Wait time (ms) after the action before the 'after' screenshot, so animations can settle\"),\n timeout_ms: z\n .number()\n .int()\n .min(1)\n .default(SELECTOR_TIMEOUT_MS)\n .describe(\"Max time (ms) to wait for the target element\"),\n viewport: z\n .object({\n width: z.number().int().min(1).max(MAX_VIEWPORT_WIDTH),\n height: z.number().int().min(1).max(MAX_VIEWPORT_HEIGHT),\n })\n .optional()\n .describe(\"Resize the page to this before acting (defaults to leaving it as it is)\"),\n include_console: z\n .boolean()\n .default(true)\n .describe(\"Report console logs, uncaught errors and unhandled rejections the action caused\"),\n include_network: z\n .boolean()\n .default(false)\n .describe(\"Report network requests (method, url, status, duration) the action caused\"),\n include_dom: z\n .boolean()\n .default(false)\n .describe(\"Report the DOM mutations the action caused — which elements were added, removed or restyled\"),\n include_performance: z\n .boolean()\n .default(false)\n .describe(\"Report paint timing and layout shifts measured around the action\"),\n include_snapshot: z\n .boolean()\n .default(false)\n .describe(\"Append a framewatch_snapshot of the page after the action, with fresh refs for the next call\"),\n storage_state: storageStateField,\n};\n\nexport const interactInputSchema = z.object(interactInputShape).superRefine((value, ctx) => {\n if (value.ref !== undefined && value.selector !== undefined) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: \"pass either `ref` or `selector`, not both\", path: [\"ref\"] });\n return;\n }\n // A ref is a selector Playwright understands, so the step is validated as one.\n refineInteraction(value.ref !== undefined ? { ...value, selector: refSelector(value.ref) } : value, ctx);\n});\nexport type InteractInput = z.input<typeof interactInputSchema>;\n\n/**\n * Perform one interaction on the current page and show what it did.\n *\n * Unlike every other FrameWatch tool this one is stateful on purpose: the page\n * stays open between calls (see `getSessionPage`), which is what makes\n * click → look → type → look iteration possible. Pass `url` to open or move\n * the page; omit it to keep working on what is already there.\n *\n * The result is a two-frame diff card sequence — before, after, and the\n * change region between them — reusing the same diff engine as\n * `framewatch_capture` so the change bbox and crop mean the same thing in\n * both tools.\n *\n * Calls are serialised through `withSessionLock`, which every tool that\n * touches the session page shares: one page, one hand.\n */\nexport function performInteraction(rawInput: InteractInput): Promise<CallToolResult> {\n return withSessionLock(() => runInteraction(rawInput));\n}\n\nasync function runInteraction(rawInput: InteractInput): Promise<CallToolResult> {\n const parsed = interactInputSchema.safeParse(rawInput);\n if (!parsed.success) {\n const issues = parsed.error.issues.map((i) => `${i.path.join(\".\") || \"input\"}: ${i.message}`).join(\"; \");\n return errorResult(`Interaction failed: invalid input — ${issues}`);\n }\n const input = parsed.data;\n const selector = input.ref !== undefined ? refSelector(input.ref) : input.selector;\n const step: Interaction = {\n action: input.action,\n ...(selector !== undefined ? { selector } : {}),\n ...(input.value !== undefined ? { value: input.value } : {}),\n ...(input.x !== undefined ? { x: input.x } : {}),\n ...(input.y !== undefined ? { y: input.y } : {}),\n ...(input.delta_x !== undefined ? { delta_x: input.delta_x } : {}),\n ...(input.delta_y !== undefined ? { delta_y: input.delta_y } : {}),\n };\n\n const flags: LayerFlags = {\n console: input.include_console,\n network: input.include_network,\n dom: input.include_dom,\n performance: input.include_performance,\n };\n\n try {\n // Cookies and storage are fixed when a context is created, so a state named\n // here applies to the page this call opens; a session already running on\n // the same state simply carries on (see `getSessionPage`).\n const auth = await resolveStorageState(input.storage_state);\n const storageState = auth ? { path: auth.path, state: auth.state } : undefined;\n\n const { page, previousUrl, reopenedFor } = await getSessionPage({\n ...(input.viewport ? { viewport: input.viewport } : {}),\n hasTouch: needsTouch([step]),\n ...(storageState ? { storageState } : {}),\n });\n\n // The layers belong to the page, not to this call (see SessionLayers), so\n // they are installed once and emptied here — this call reports what this\n // action caused, not what the last twenty did. Both happen before the\n // navigation, so a page opened by `url` has its load watched too.\n const layers = layersFor(page);\n await layers.ensure(flags);\n layers.clear();\n\n // `previousUrl` means the session had to be reopened (for touch, or to take\n // a different auth state); go back to where the user was unless they asked\n // for somewhere else.\n const target = input.url ?? previousUrl;\n if (target !== undefined) {\n await page.goto(target, { waitUntil: \"load\", timeout: NAVIGATION_TIMEOUT_MS });\n // A Vue app is acted on once it is mounted and its router has resolved;\n // a plain page gets no extra wait beyond the brief look for one.\n await waitForVueReady(page, { detect_ms: VUE_DETECT_MS, ready_ms: VUE_READY_TIMEOUT_MS });\n }\n if (page.url() === \"about:blank\") {\n return errorResult(\n \"Interaction failed: no page is open yet — pass `url` to open one (e.g. http://localhost:3000).\",\n );\n }\n\n const vueBefore = await detectVue(page);\n // A password field before the action and none after it is a login that\n // just succeeded — the one moment the agent is thinking about auth.\n const loginBefore = await loginFormVisible(page);\n const before = await page.screenshot({ type: \"png\" });\n const startedAt = Date.now();\n const outcome = await executeInteraction(page, step, { timeout_ms: input.timeout_ms });\n if (input.wait_ms > 0) {\n await page.waitForTimeout(input.wait_ms);\n }\n const after = await page.screenshot({ type: \"png\" });\n const vueAfter = vueBefore || step.action === \"navigate\" ? await detectVue(page) : null;\n const loginAfter = await loginFormVisible(page);\n\n const frames: RawFrame[] = [\n { buffer: before, timestamp_ms: 0, is_interaction: false },\n { buffer: after, timestamp_ms: Date.now() - startedAt, is_interaction: true },\n ];\n // sensitivity 0 keeps both frames however small the change is — the point\n // of this tool is to show what one action did, including \"nothing\".\n const { cards } = await buildDiffCards(frames, { sensitivity: 0, max_frames: 2 });\n if (cards.length < 2) {\n return errorResult(`Interaction failed: ${describeInteraction(step)} ran, but the screenshots could not be compared.`);\n }\n\n // `startedAt` is the instant of the \"before\" frame, which puts the split\n // exactly where it belongs: everything from opening the page lands on the\n // before card (negative, i.e. \"how we got here\") and everything the action\n // caused lands on the after card.\n const context = layers.collect(startedAt, flags);\n applyContext(cards, context);\n\n const [beforeCard, afterCard] = cards;\n const viewport = page.viewportSize() ?? { width: 0, height: 0 };\n const headline = [\n `${summarise(step, page.url(), afterCard.change_region?.change_percent ?? 0, reopenedFor, input.ref, outcome.note)} — ${describeScale(viewport)}`,\n ...describeRoute(vueBefore, vueAfter),\n ...(summariseContext(context, cards.length) ?? []),\n ...authLines(auth, input.url !== undefined && loginBefore, loginBefore && !loginAfter),\n ].join(\"\\n\");\n\n const content: CallToolResult[\"content\"] = [\n { type: \"text\", text: headline },\n { type: \"image\", data: beforeCard.full_frame, mimeType: \"image/png\" },\n { type: \"text\", text: `Before — ${formatCardMeta(beforeCard)}` },\n { type: \"image\", data: afterCard.full_frame, mimeType: \"image/png\" },\n { type: \"text\", text: `After — ${formatCardMeta(afterCard)}` },\n ];\n const crop = afterCard.change_region?.crop;\n if (crop) {\n content.push(markImage({ type: \"image\", data: crop, mimeType: \"image/png\" }, { role: \"crop\" }));\n }\n if (input.include_snapshot) {\n // Refs are reassigned by every snapshot, so the ones the caller used are\n // stale the moment this runs — which is the point: these are the fresh ones.\n const snapshot = await takeSnapshot(page, { mode: \"full\", max_chars: DEFAULT_SNAPSHOT_MAX_CHARS });\n const lines = [`Snapshot — ${describeCounts(snapshot)} (refs valid until the page changes)`, \"\", snapshot.text];\n if (snapshot.cut_lines > 0) lines.push(`… ${snapshot.cut_lines} more lines cut — use framewatch_snapshot with \\`selector\\` or mode \"interactive\".`);\n content.push({ type: \"text\", text: lines.join(\"\\n\") });\n }\n hmrFor(page).markSeen();\n return { content };\n } catch (error) {\n return errorResult(describeInteractFailure(input, error));\n }\n}\n\n/**\n * The auth lines: what saved state was applied (and whether it worked, when\n * this call opened the page), and — when a login form was there before the\n * action and gone after it — the reminder to save the session now.\n */\nfunction authLines(auth: Awaited<ReturnType<typeof resolveStorageState>>, expired: boolean, justSignedIn: boolean): string[] {\n const lines: string[] = [];\n const applied = auth ? describeAuth(auth, expired) : null;\n if (applied) lines.push(applied);\n if (justSignedIn) {\n lines.push(\n \"Signed in? Save this session with framewatch_save_auth (same steps, from the login page) so every later call \" +\n \"starts here instead of replaying the login.\",\n );\n }\n return lines;\n}\n\n/**\n * The Vue line: the app and its route, and — when the action moved the\n * router — where it went, by name.\n */\nfunction describeRoute(before: VueInfo | null, after: VueInfo | null): string[] {\n const current = after ?? before;\n if (!current) return [];\n const from = before?.route;\n const to = after?.route;\n if (from && to && from.path !== to.path) {\n const name = (route: { path: string; name?: string }): string => `${route.path}${route.name ? ` (${route.name})` : \"\"}`;\n return [`Vue ${current.version} — route ${name(from)} → ${name(to)}`];\n }\n return [describeVue(current)];\n}\n\n/** One line: what was done, where, and how much of the frame it moved. */\nfunction summarise(\n step: Interaction,\n url: string,\n changePercent: number,\n reopenedFor?: \"touch\" | \"auth\",\n ref?: string,\n note?: string,\n): string {\n const reopened =\n reopenedFor === \"touch\"\n ? \" (page reopened to enable touch — page state was reset)\"\n : reopenedFor === \"auth\"\n ? \" (page reopened with the saved auth state — page state was reset)\"\n : \"\";\n const changed = changePercent > 0 ? `${changePercent.toFixed(1)}% of the frame changed` : \"no visual change\";\n // The step carries the ref as an `aria-ref=` selector; the caller wrote `e8`.\n const what = (ref !== undefined ? describeInteraction(step).replaceAll(`\"${refSelector(ref)}\"`, ref) : describeInteraction(step)) + (note ?? \"\");\n return `${what} on ${url}${reopened} — ${changed}`;\n}\n\nfunction errorResult(text: string): CallToolResult {\n return { isError: true, content: [{ type: \"text\", text }] };\n}\n\n/**\n * Turn a failure into one actionable line. Interaction errors already name the\n * step and the reason (see `executeInteraction`), so they are passed through\n * with the page appended for context; everything else is a navigation or\n * browser problem, worded as in the other tools.\n */\nexport function describeInteractFailure(input: { url?: string; ref?: string }, error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n const firstLine = message.split(\"\\n\")[0];\n\n if (input.ref !== undefined && message.includes(refSelector(input.ref))) {\n return (\n `Interaction failed: ref ${input.ref} did not resolve — the page has changed since that snapshot, or none was ` +\n `taken on this page. Run framewatch_snapshot again and use a ref from it. (${firstLine.replaceAll(`\"${refSelector(input.ref)}\"`, input.ref)})`\n );\n }\n\n if (/Executable doesn't exist|browserType\\.launch/i.test(message)) {\n return (\n \"Interaction failed: Playwright's Chromium browser is not installed. \" +\n `Run \\`npx playwright install chromium\\` and try again. (${firstLine})`\n );\n }\n if (/^page\\.goto:/.test(message)) {\n return `Interaction failed: could not open ${input.url ?? \"the page\"} — ${firstLine}`;\n }\n return input.url ? `${firstLine} (page: ${input.url})` : firstLine;\n}\n\nexport function registerInteractTool(server: McpServer): void {\n server.registerTool(\n INTERACT_TOOL_NAME,\n {\n title: \"Interact\",\n description:\n \"Perform one interaction (click, tap, type, scroll, swipe, hover, select, navigate) on a page and return \" +\n \"before/after screenshots plus a crop of what changed. The page stays open between calls, so you can \" +\n \"click, look, type and look again without replaying the whole flow — pass `url` only to open or move it. \" +\n \"Target an element by `ref` from framewatch_snapshot (e.g. `e8`) instead of guessing a selector; \" +\n \"`include_snapshot` returns the fresh refs after the action. \" +\n \"Each call also reports the context its own action produced: console output and uncaught errors (on by \" +\n \"default), plus network requests, DOM mutations and paint/layout-shift timing via `include_network`, \" +\n \"`include_dom` and `include_performance` — that is how you find out why a click did nothing.\",\n inputSchema: interactInputShape,\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },\n },\n async (args) => performInteraction(args),\n );\n}\n"]}
1
+ {"version":3,"file":"interact.js","sourceRoot":"","sources":["../../src/tools/interact.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport {\n DEFAULT_INTERACT_WAIT_MS,\n MAX_VIEWPORT_HEIGHT,\n MAX_VIEWPORT_WIDTH,\n NAVIGATION_TIMEOUT_MS,\n SELECTOR_TIMEOUT_MS,\n} from \"../constants.js\";\nimport { getSessionPage, withSessionLock } from \"../engine/browser.js\";\nimport { buildDiffCards } from \"../engine/differ.js\";\nimport { applyContext, layersFor, summariseContext, type LayerFlags } from \"../engine/layers/index.js\";\nimport {\n INTERACT_ACTIONS,\n describeInteraction,\n executeInteraction,\n interactionFieldShape,\n needsTouch,\n refineInteraction,\n type Interaction,\n} from \"../engine/interaction.js\";\nimport { takeSnapshot } from \"../engine/snapshot.js\";\nimport type { RawFrame } from \"../types.js\";\nimport { describeScale, formatCardMeta } from \"../utils/format.js\";\nimport { REF_SHAPE, refSelector } from \"../utils/snapshot-rules.js\";\nimport { describeAuth, loginFormVisible, resolveStorageState, storageStateField } from \"../utils/storage-state.js\";\nimport { DEFAULT_SNAPSHOT_MAX_CHARS, VUE_DETECT_MS, VUE_READY_TIMEOUT_MS } from \"../constants.js\";\nimport { hmrFor } from \"../engine/hmr.js\";\nimport { detectVue, waitForVueReady } from \"../engine/vue.js\";\nimport { describeVue, type VueInfo } from \"../utils/vue-rules.js\";\nimport { describeCounts } from \"./snapshot.js\";\nimport { markImage } from \"../utils/budget.js\";\n\nexport const INTERACT_TOOL_NAME = \"framewatch_interact\";\n\nexport const interactInputShape = {\n action: z\n .enum(INTERACT_ACTIONS)\n .describe(\"What to do: click, tap, type, key, scroll, swipe, navigate, select or hover\"),\n ...interactionFieldShape,\n ref: z\n .string()\n .regex(REF_SHAPE, \"a ref looks like `e8` (or `f1e8`) — take one from framewatch_snapshot\")\n .optional()\n .describe(\"Element ref from framewatch_snapshot (e.g. `e8`) to act on, instead of a `selector`\"),\n url: z\n .string()\n .url()\n .optional()\n .describe(\"Open this URL first. Omit to act on the page left open by the previous call.\"),\n wait_ms: z\n .number()\n .int()\n .min(0)\n .default(DEFAULT_INTERACT_WAIT_MS)\n .describe(\"Wait time (ms) after the action before the 'after' screenshot, so animations can settle\"),\n timeout_ms: z\n .number()\n .int()\n .min(1)\n .default(SELECTOR_TIMEOUT_MS)\n .describe(\"Max time (ms) to wait for the target element\"),\n viewport: z\n .object({\n width: z.number().int().min(1).max(MAX_VIEWPORT_WIDTH),\n height: z.number().int().min(1).max(MAX_VIEWPORT_HEIGHT),\n })\n .optional()\n .describe(\"Resize the page to this before acting (defaults to leaving it as it is)\"),\n include_console: z\n .boolean()\n .default(true)\n .describe(\"Report console logs, uncaught errors and unhandled rejections the action caused\"),\n include_network: z\n .boolean()\n .default(false)\n .describe(\"Report network requests (method, url, status, duration) the action caused\"),\n include_dom: z\n .boolean()\n .default(false)\n .describe(\"Report the DOM mutations the action caused — which elements were added, removed or restyled\"),\n include_performance: z\n .boolean()\n .default(false)\n .describe(\"Report paint timing and layout shifts measured around the action\"),\n include_snapshot: z\n .boolean()\n .default(false)\n .describe(\"Append a framewatch_snapshot of the page after the action, with fresh refs for the next call\"),\n storage_state: storageStateField,\n};\n\nexport const interactInputSchema = z.object(interactInputShape).superRefine((value, ctx) => {\n if (value.ref !== undefined && value.selector !== undefined) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: \"pass either `ref` or `selector`, not both\", path: [\"ref\"] });\n return;\n }\n // A ref is a selector Playwright understands, so the step is validated as one.\n refineInteraction(value.ref !== undefined ? { ...value, selector: refSelector(value.ref) } : value, ctx);\n});\nexport type InteractInput = z.input<typeof interactInputSchema>;\n\n/**\n * Perform one interaction on the current page and show what it did.\n *\n * Unlike every other FrameWatch tool this one is stateful on purpose: the page\n * stays open between calls (see `getSessionPage`), which is what makes\n * click → look → type → look iteration possible. Pass `url` to open or move\n * the page; omit it to keep working on what is already there.\n *\n * The result is a two-frame diff card sequence — before, after, and the\n * change region between them — reusing the same diff engine as\n * `framewatch_capture` so the change bbox and crop mean the same thing in\n * both tools.\n *\n * Calls are serialised through `withSessionLock`, which every tool that\n * touches the session page shares: one page, one hand.\n */\nexport function performInteraction(rawInput: InteractInput): Promise<CallToolResult> {\n return withSessionLock(() => runInteraction(rawInput));\n}\n\nasync function runInteraction(rawInput: InteractInput): Promise<CallToolResult> {\n const parsed = interactInputSchema.safeParse(rawInput);\n if (!parsed.success) {\n const issues = parsed.error.issues.map((i) => `${i.path.join(\".\") || \"input\"}: ${i.message}`).join(\"; \");\n return errorResult(`Interaction failed: invalid input — ${issues}`);\n }\n const input = parsed.data;\n const selector = input.ref !== undefined ? refSelector(input.ref) : input.selector;\n const step: Interaction = {\n action: input.action,\n ...(selector !== undefined ? { selector } : {}),\n ...(input.value !== undefined ? { value: input.value } : {}),\n ...(input.x !== undefined ? { x: input.x } : {}),\n ...(input.y !== undefined ? { y: input.y } : {}),\n ...(input.delta_x !== undefined ? { delta_x: input.delta_x } : {}),\n ...(input.delta_y !== undefined ? { delta_y: input.delta_y } : {}),\n };\n\n const flags: LayerFlags = {\n console: input.include_console,\n network: input.include_network,\n dom: input.include_dom,\n performance: input.include_performance,\n };\n\n try {\n // Cookies and storage are fixed when a context is created, so a state named\n // here applies to the page this call opens; a session already running on\n // the same state simply carries on (see `getSessionPage`).\n const auth = await resolveStorageState(input.storage_state);\n const storageState = auth ? { path: auth.path, state: auth.state } : undefined;\n\n const { page, previousUrl, reopenedFor } = await getSessionPage({\n ...(input.viewport ? { viewport: input.viewport } : {}),\n hasTouch: needsTouch([step]),\n ...(storageState ? { storageState } : {}),\n });\n\n // The layers belong to the page, not to this call (see SessionLayers), so\n // they are installed once and emptied here — this call reports what this\n // action caused, not what the last twenty did. Both happen before the\n // navigation, so a page opened by `url` has its load watched too.\n const layers = layersFor(page);\n await layers.ensure(flags);\n layers.clear();\n\n // `previousUrl` means the session had to be reopened (for touch, or to take\n // a different auth state); go back to where the user was unless they asked\n // for somewhere else.\n const target = input.url ?? previousUrl;\n if (target !== undefined) {\n await page.goto(target, { waitUntil: \"load\", timeout: NAVIGATION_TIMEOUT_MS });\n // A Vue app is acted on once it is mounted and its router has resolved;\n // a plain page gets no extra wait beyond the brief look for one.\n await waitForVueReady(page, { detect_ms: VUE_DETECT_MS, ready_ms: VUE_READY_TIMEOUT_MS });\n }\n if (page.url() === \"about:blank\") {\n return errorResult(\n \"Interaction failed: no page is open yet — pass `url` to open one (e.g. http://localhost:3000).\",\n );\n }\n\n const vueBefore = await detectVue(page);\n // A password field before the action and none after it is a login that\n // just succeeded — the one moment the agent is thinking about auth.\n const loginBefore = await loginFormVisible(page);\n const before = await page.screenshot({ type: \"png\" });\n const startedAt = Date.now();\n const outcome = await executeInteraction(page, step, { timeout_ms: input.timeout_ms });\n if (input.wait_ms > 0) {\n await page.waitForTimeout(input.wait_ms);\n }\n const after = await page.screenshot({ type: \"png\" });\n const vueAfter = vueBefore || step.action === \"navigate\" ? await detectVue(page) : null;\n const loginAfter = await loginFormVisible(page);\n\n const frames: RawFrame[] = [\n { buffer: before, timestamp_ms: 0, is_interaction: false },\n { buffer: after, timestamp_ms: Date.now() - startedAt, is_interaction: true },\n ];\n // sensitivity 0 keeps both frames however small the change is — the point\n // of this tool is to show what one action did, including \"nothing\".\n const { cards } = await buildDiffCards(frames, { sensitivity: 0, max_frames: 2 });\n if (cards.length < 2) {\n return errorResult(`Interaction failed: ${describeInteraction(step)} ran, but the screenshots could not be compared.`);\n }\n\n // `startedAt` is the instant of the \"before\" frame, which puts the split\n // exactly where it belongs: everything from opening the page lands on the\n // before card (negative, i.e. \"how we got here\") and everything the action\n // caused lands on the after card.\n const context = layers.collect(startedAt, flags);\n applyContext(cards, context);\n\n const [beforeCard, afterCard] = cards;\n const viewport = page.viewportSize() ?? { width: 0, height: 0 };\n const headline = [\n `${summarise(step, page.url(), afterCard.change_region?.change_percent ?? 0, reopenedFor, input.ref, outcome.note)} — ${describeScale(viewport)}`,\n ...describeRoute(vueBefore, vueAfter),\n ...(summariseContext(context, cards.length) ?? []),\n ...authLines(auth, input.url !== undefined && loginBefore, loginBefore && !loginAfter),\n ].join(\"\\n\");\n\n const content: CallToolResult[\"content\"] = [\n { type: \"text\", text: headline },\n { type: \"image\", data: beforeCard.full_frame, mimeType: \"image/png\" },\n { type: \"text\", text: `Before — ${formatCardMeta(beforeCard)}` },\n { type: \"image\", data: afterCard.full_frame, mimeType: \"image/png\" },\n { type: \"text\", text: `After — ${formatCardMeta(afterCard)}` },\n ];\n const crop = afterCard.change_region?.crop;\n if (crop) {\n content.push(markImage({ type: \"image\", data: crop, mimeType: \"image/png\" }, { role: \"crop\" }));\n }\n if (input.include_snapshot) {\n // Refs are reassigned by every snapshot, so the ones the caller used are\n // stale the moment this runs — which is the point: these are the fresh ones.\n const snapshot = await takeSnapshot(page, { mode: \"full\", max_chars: DEFAULT_SNAPSHOT_MAX_CHARS });\n const lines = [`Snapshot — ${describeCounts(snapshot)} (refs valid until the page changes)`, \"\", snapshot.text];\n if (snapshot.cut_lines > 0) lines.push(`… ${snapshot.cut_lines} more lines cut — use framewatch_snapshot with \\`selector\\` or mode \"interactive\".`);\n content.push({ type: \"text\", text: lines.join(\"\\n\") });\n }\n hmrFor(page).markSeen();\n return { content };\n } catch (error) {\n return errorResult(describeInteractFailure(input, error));\n }\n}\n\n/**\n * The auth lines: what saved state was applied (and whether it worked, when\n * this call opened the page), and — when a login form was there before the\n * action and gone after it — the reminder to save the session now.\n */\nfunction authLines(auth: Awaited<ReturnType<typeof resolveStorageState>>, expired: boolean, justSignedIn: boolean): string[] {\n const lines: string[] = [];\n const applied = auth ? describeAuth(auth, expired) : null;\n if (applied) lines.push(applied);\n if (justSignedIn) {\n lines.push(\n \"Signed in? Save this session with framewatch_save_auth (same steps, from the login page) so every later call \" +\n \"starts here instead of replaying the login.\",\n );\n }\n return lines;\n}\n\n/**\n * The Vue line: the app and its route, and — when the action moved the\n * router — where it went, by name.\n */\nfunction describeRoute(before: VueInfo | null, after: VueInfo | null): string[] {\n const current = after ?? before;\n if (!current) return [];\n const from = before?.route;\n const to = after?.route;\n if (from && to && from.path !== to.path) {\n const name = (route: { path: string; name?: string }): string => `${route.path}${route.name ? ` (${route.name})` : \"\"}`;\n return [`Vue ${current.version} — route ${name(from)} → ${name(to)}`];\n }\n return [describeVue(current)];\n}\n\n/** One line: what was done, where, and how much of the frame it moved. */\nfunction summarise(\n step: Interaction,\n url: string,\n changePercent: number,\n reopenedFor?: \"touch\" | \"auth\",\n ref?: string,\n note?: string,\n): string {\n const reopened =\n reopenedFor === \"touch\"\n ? \" (page reopened to enable touch — page state was reset)\"\n : reopenedFor === \"auth\"\n ? \" (page reopened with the saved auth state — page state was reset)\"\n : \"\";\n const changed = changePercent > 0 ? `${changePercent.toFixed(1)}% of the frame changed` : \"no visual change\";\n // The step carries the ref as an `aria-ref=` selector; the caller wrote `e8`.\n const what = (ref !== undefined ? describeInteraction(step).replaceAll(`\"${refSelector(ref)}\"`, ref) : describeInteraction(step)) + (note ?? \"\");\n return `${what} on ${url}${reopened} — ${changed}`;\n}\n\nfunction errorResult(text: string): CallToolResult {\n return { isError: true, content: [{ type: \"text\", text }] };\n}\n\n/**\n * Turn a failure into one actionable line. Interaction errors already name the\n * step and the reason (see `executeInteraction`), so they are passed through\n * with the page appended for context; everything else is a navigation or\n * browser problem, worded as in the other tools.\n */\nexport function describeInteractFailure(input: { url?: string; ref?: string }, error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n const firstLine = message.split(\"\\n\")[0];\n\n if (input.ref !== undefined && message.includes(refSelector(input.ref))) {\n return (\n `Interaction failed: ref ${input.ref} did not resolve — the page has changed since that snapshot, or none was ` +\n `taken on this page. Run framewatch_snapshot again and use a ref from it. (${firstLine.replaceAll(`\"${refSelector(input.ref)}\"`, input.ref)})`\n );\n }\n\n if (/Executable doesn't exist|browserType\\.launch/i.test(message)) {\n return (\n \"Interaction failed: Playwright's Chromium browser is not installed. \" +\n `Run \\`npx playwright install chromium\\` and try again. (${firstLine})`\n );\n }\n if (/^page\\.goto:/.test(message)) {\n return `Interaction failed: could not open ${input.url ?? \"the page\"} — ${firstLine}`;\n }\n return input.url ? `${firstLine} (page: ${input.url})` : firstLine;\n}\n\nexport function registerInteractTool(server: McpServer): void {\n server.registerTool(\n INTERACT_TOOL_NAME,\n {\n title: \"Interact\",\n description:\n \"Perform one interaction (click, tap, type, scroll, swipe, hover, select, navigate) on a page and return \" +\n \"before/after screenshots plus a crop of what changed. The page stays open between calls, so you can \" +\n \"click, look, type and look again without replaying the whole flow — pass `url` only to open or move it. \" +\n \"Target an element by `ref` from framewatch_snapshot (e.g. `e8`) instead of guessing a selector; \" +\n \"`include_snapshot` returns the fresh refs after the action. \" +\n \"Each call also reports the context its own action produced: console output and uncaught errors (on by \" +\n \"default), plus network requests, DOM mutations and paint/layout-shift timing via `include_network`, \" +\n \"`include_dom` and `include_performance` — that is how you find out why a click did nothing.\",\n inputSchema: interactInputShape,\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },\n },\n async (args) => performInteraction(args),\n );\n}\n"]}
@@ -45,8 +45,8 @@ export interface CaptureSummary {
45
45
  notes?: string[];
46
46
  }
47
47
  /**
48
- * Build the MCP CallToolResult for a capture, following the "MCP Response
49
- * Format" in CLAUDE.md: one summary text block, then per card an image block,
48
+ * Build the MCP CallToolResult for a capture, in the shape the README
49
+ * documents for framewatch_capture: one summary text block, then per card an image block,
50
50
  * a metadata text block and (when present) the change-region crop image.
51
51
  */
52
52
  export declare function formatDiffCards(summary: CaptureSummary): CallToolResult;
@@ -15,8 +15,8 @@ export function describeScale(viewport) {
15
15
  return `${base}, images ${OUTPUT_MAX_WIDTH}px wide (${factor}×) — coordinates and regions are in viewport px`;
16
16
  }
17
17
  /**
18
- * Build the MCP CallToolResult for a capture, following the "MCP Response
19
- * Format" in CLAUDE.md: one summary text block, then per card an image block,
18
+ * Build the MCP CallToolResult for a capture, in the shape the README
19
+ * documents for framewatch_capture: one summary text block, then per card an image block,
20
20
  * a metadata text block and (when present) the change-region crop image.
21
21
  */
22
22
  export function formatDiffCards(summary) {
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { OUTPUT_MAX_WIDTH } from \"../constants.js\";\nimport { markImage } from \"./budget.js\";\nimport type { DiffCard, Viewport } from \"../types.js\";\n\n/**\n * How the images relate to the page. Frames are shrunk to OUTPUT_MAX_WIDTH,\n * but every coordinate a tool prints — change regions, boxes, click points —\n * is in viewport pixels. A reader who measures a position off the image and\n * clicks there lands 1.6× off on the default viewport, so every tool that\n * returns frames says which it is.\n */\nexport function describeScale(viewport: Viewport): string {\n const base = `viewport ${viewport.width}x${viewport.height}`;\n if (viewport.width <= OUTPUT_MAX_WIDTH) return `${base}, images at full size`;\n const factor = (OUTPUT_MAX_WIDTH / viewport.width).toFixed(2);\n return `${base}, images ${OUTPUT_MAX_WIDTH}px wide (${factor}×) — coordinates and regions are in viewport px`;\n}\n\n/** What became of a replayed interaction script. */\nexport interface InteractionReport {\n /** Steps in the script. */\n total: number;\n /** Steps that ran successfully. */\n completed: number;\n /** One-line description of each completed step, in order. */\n steps: string[];\n /** Message from the step that failed, if any (already a single line). */\n error?: string;\n /** 1-based position of the failing step. */\n failed_index?: number;\n}\n\n/** Everything the formatter needs to describe one capture session. */\nexport interface CaptureSummary {\n cards: DiffCard[];\n total_frames: number;\n duration_ms: number;\n /** URL that was requested. */\n url: string;\n /** URL the page ended on (after redirects / in-page navigation). */\n final_url?: string;\n title?: string;\n /** Frames dropped by the recorder (screenshot failures). */\n dropped?: number;\n /** Present only when an interaction script was replayed. */\n interactions?: InteractionReport;\n /** The viewport the frames were taken at; adds the image-scale line when known. */\n viewport?: Viewport;\n /**\n * Remarks about the capture itself rather than about any one frame — a\n * context layer that hit its cap, requests still in flight when the\n * recording ended. One line each, after the summary.\n */\n notes?: string[];\n}\n\n/**\n * Build the MCP CallToolResult for a capture, following the \"MCP Response\n * Format\" in CLAUDE.md: one summary text block, then per card an image block,\n * a metadata text block and (when present) the change-region crop image.\n */\nexport function formatDiffCards(summary: CaptureSummary): CallToolResult {\n const lines = [formatSummaryLine(summary)];\n if (summary.viewport) {\n const scale = describeScale(summary.viewport);\n lines.push(scale.charAt(0).toUpperCase() + scale.slice(1));\n }\n if (summary.interactions) {\n lines.push(formatInteractionLine(summary.interactions));\n }\n for (const note of summary.notes ?? []) {\n lines.push(note);\n }\n const content: CallToolResult[\"content\"] = [{ type: \"text\", text: lines.join(\"\\n\") }];\n const last = summary.cards.length - 1;\n summary.cards.forEach((card, i) => {\n // First, last and event frames survive the image budget longest; crops go first.\n const keep = i === 0 || i === last || card.trigger !== \"animation\";\n content.push(markImage({ type: \"image\", data: card.full_frame, mimeType: \"image/png\" }, { role: \"frame\", keep }));\n content.push({ type: \"text\", text: formatCardMeta(card) });\n if (card.change_region?.crop) {\n content.push(markImage({ type: \"image\", data: card.change_region.crop, mimeType: \"image/png\" }, { role: \"crop\" }));\n }\n });\n return { content };\n}\n\n/**\n * One-line description of the whole capture. Always names the requested url;\n * appends the final url only when the page ended somewhere else, the page\n * title when known, and the dropped-frame count only when frames were lost.\n */\nfunction formatSummaryLine(summary: CaptureSummary): string {\n const { cards, total_frames, duration_ms, url, final_url, title, dropped } = summary;\n const recording = `from ${total_frames} raw frames (${duration_ms}ms recording) of ${url}`;\n let text = cards.length === 0 ? `No frames captured ${recording}` : `Captured ${cards.length} meaningful frames ${recording}`;\n if (final_url !== undefined && !isSameUrl(url, final_url)) {\n text += ` → ${final_url}`;\n }\n if (title) {\n text += ` — \"${title}\"`;\n }\n if (dropped !== undefined && dropped > 0) {\n text += ` (${dropped} frames dropped)`;\n }\n return text;\n}\n\n/**\n * How the interaction script went: how many steps ran, what they were, and —\n * when one failed — which one and why. A failed step is a finding about the\n * page, not a tool failure, so it is reported here alongside the frames rather\n * than replacing them with an error.\n */\nexport function formatInteractionLine(report: InteractionReport): string {\n let text = `Interactions: ${report.completed}/${report.total} replayed`;\n if (report.steps.length > 0) {\n text += ` — ${report.steps.join(\", \")}`;\n }\n if (report.error !== undefined) {\n text += `. Step ${report.failed_index ?? report.completed + 1}: ${report.error}`;\n }\n return text;\n}\n\n/**\n * Compare two URLs as URLs, not as strings: `page.url()` returns the\n * WHATWG-normalised form, so a request for `http://localhost:3000` comes back\n * as `http://localhost:3000/` without anything having navigated.\n */\nfunction isSameUrl(a: string, b: string): boolean {\n if (a === b) return true;\n try {\n return new URL(a).href === new URL(b).href;\n } catch {\n return false;\n }\n}\n\n/**\n * The metadata text block for one card (exported for tests and for reuse by\n * the interact tool). Line 1 is always `Frame N @ Tms [trigger]`; the\n * optional sections follow in a fixed order — Changed, Console, Network,\n * Performance, DOM — and are omitted entirely when their data is absent.\n */\nexport function formatCardMeta(card: DiffCard): string {\n const lines: string[] = [`Frame ${card.index} @ ${card.timestamp_ms}ms [${card.trigger}]`];\n\n const region = card.change_region;\n if (region) {\n if (region.change_percent === 0) {\n lines.push(\"Changed: 0.0% — no visual change since previous frame\");\n } else {\n const { bbox } = region;\n let changed = `Changed: ${region.change_percent.toFixed(1)}% — region: ${bbox.x},${bbox.y} ${bbox.width}x${bbox.height}`;\n if (!region.crop) {\n changed += \" (full-frame change, see frame image)\";\n }\n lines.push(changed);\n }\n }\n\n if (card.console_entries?.length) {\n lines.push(\"Console:\");\n for (const entry of card.console_entries) {\n lines.push(` [${entry.level}] ${entry.text}`);\n }\n }\n\n if (card.network_events?.length) {\n lines.push(\"Network:\");\n for (const event of card.network_events) {\n // A request that never got a response has no status to print; what\n // stopped it (or that it is still running) is the useful part.\n const outcome = event.status > 0 ? String(event.status) : (event.error ?? \"no response\");\n const why = event.status > 0 && event.error !== undefined ? ` ${event.error}` : \"\";\n lines.push(` ${event.method} ${event.url} → ${outcome}${why} (${event.duration_ms}ms)`);\n }\n }\n\n const perf = card.performance;\n if (perf) {\n lines.push(\"Performance:\");\n if (perf.paint_time_ms !== undefined) lines.push(` paint ${perf.paint_time_ms}ms`);\n if (perf.layout_shifts !== undefined) {\n const score = perf.layout_shift_score !== undefined ? ` (score ${perf.layout_shift_score})` : \"\";\n lines.push(` layout shifts ${perf.layout_shifts}${score}`);\n }\n if (perf.lcp_ms !== undefined) lines.push(` lcp ${perf.lcp_ms}ms`);\n }\n\n if (card.dom_snapshot) {\n lines.push(`DOM:\\n${card.dom_snapshot}`);\n }\n\n return lines.join(\"\\n\");\n}\n"]}
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,QAAkB;IAC9C,MAAM,IAAI,GAAG,YAAY,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;IAC7D,IAAI,QAAQ,CAAC,KAAK,IAAI,gBAAgB;QAAE,OAAO,GAAG,IAAI,uBAAuB,CAAC;IAC9E,MAAM,MAAM,GAAG,CAAC,gBAAgB,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC9D,OAAO,GAAG,IAAI,YAAY,gBAAgB,YAAY,MAAM,iDAAiD,CAAC;AAChH,CAAC;AAwCD;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,OAAuB;IACrD,MAAM,KAAK,GAAG,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3C,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC9C,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D,CAAC;IACD,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC;IAC1D,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;QACvC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IACD,MAAM,OAAO,GAA8B,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACtF,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IACtC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;QAChC,iFAAiF;QACjF,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,OAAO,KAAK,WAAW,CAAC;QACnE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAClH,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC3D,IAAI,IAAI,CAAC,aAAa,EAAE,IAAI,EAAE,CAAC;YAC7B,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QACrH,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,OAAO,EAAE,CAAC;AACrB,CAAC;AAED;;;;GAIG;AACH,SAAS,iBAAiB,CAAC,OAAuB;IAChD,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IACrF,MAAM,SAAS,GAAG,QAAQ,YAAY,gBAAgB,WAAW,oBAAoB,GAAG,EAAE,CAAC;IAC3F,IAAI,IAAI,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,sBAAsB,SAAS,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,CAAC,MAAM,sBAAsB,SAAS,EAAE,CAAC;IAC9H,IAAI,SAAS,KAAK,SAAS,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE,CAAC;QAC1D,IAAI,IAAI,MAAM,SAAS,EAAE,CAAC;IAC5B,CAAC;IACD,IAAI,KAAK,EAAE,CAAC;QACV,IAAI,IAAI,OAAO,KAAK,GAAG,CAAC;IAC1B,CAAC;IACD,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QACzC,IAAI,IAAI,KAAK,OAAO,kBAAkB,CAAC;IACzC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAyB;IAC7D,IAAI,IAAI,GAAG,iBAAiB,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,KAAK,WAAW,CAAC;IACxE,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,IAAI,IAAI,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IAC1C,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,IAAI,IAAI,UAAU,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,SAAS,GAAG,CAAC,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC;IACnF,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,SAAS,SAAS,CAAC,CAAS,EAAE,CAAS;IACrC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzB,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,IAAc;IAC3C,MAAM,KAAK,GAAa,CAAC,SAAS,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,YAAY,OAAO,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;IAE3F,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC;IAClC,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,MAAM,CAAC,cAAc,KAAK,CAAC,EAAE,CAAC;YAChC,KAAK,CAAC,IAAI,CAAC,uDAAuD,CAAC,CAAC;QACtE,CAAC;aAAM,CAAC;YACN,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC;YACxB,IAAI,OAAO,GAAG,YAAY,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACzH,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;gBACjB,OAAO,IAAI,uCAAuC,CAAC;YACrD,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,IAAI,IAAI,CAAC,eAAe,EAAE,MAAM,EAAE,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzC,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IAED,IAAI,IAAI,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxC,mEAAmE;YACnE,+DAA+D;YAC/D,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,aAAa,CAAC,CAAC;YACzF,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnF,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,OAAO,GAAG,GAAG,KAAK,KAAK,CAAC,WAAW,KAAK,CAAC,CAAC;QAC3F,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC;IAC9B,IAAI,IAAI,EAAE,CAAC;QACT,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC3B,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,aAAa,IAAI,CAAC,CAAC;QACpF,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;YACrC,MAAM,KAAK,GAAG,IAAI,CAAC,kBAAkB,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACjG,KAAK,CAAC,IAAI,CAAC,mBAAmB,IAAI,CAAC,aAAa,GAAG,KAAK,EAAE,CAAC,CAAC;QAC9D,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;IACtE,CAAC;IAED,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC","sourcesContent":["import type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { OUTPUT_MAX_WIDTH } from \"../constants.js\";\nimport { markImage } from \"./budget.js\";\nimport type { DiffCard, Viewport } from \"../types.js\";\n\n/**\n * How the images relate to the page. Frames are shrunk to OUTPUT_MAX_WIDTH,\n * but every coordinate a tool prints — change regions, boxes, click points —\n * is in viewport pixels. A reader who measures a position off the image and\n * clicks there lands 1.6× off on the default viewport, so every tool that\n * returns frames says which it is.\n */\nexport function describeScale(viewport: Viewport): string {\n const base = `viewport ${viewport.width}x${viewport.height}`;\n if (viewport.width <= OUTPUT_MAX_WIDTH) return `${base}, images at full size`;\n const factor = (OUTPUT_MAX_WIDTH / viewport.width).toFixed(2);\n return `${base}, images ${OUTPUT_MAX_WIDTH}px wide (${factor}×) — coordinates and regions are in viewport px`;\n}\n\n/** What became of a replayed interaction script. */\nexport interface InteractionReport {\n /** Steps in the script. */\n total: number;\n /** Steps that ran successfully. */\n completed: number;\n /** One-line description of each completed step, in order. */\n steps: string[];\n /** Message from the step that failed, if any (already a single line). */\n error?: string;\n /** 1-based position of the failing step. */\n failed_index?: number;\n}\n\n/** Everything the formatter needs to describe one capture session. */\nexport interface CaptureSummary {\n cards: DiffCard[];\n total_frames: number;\n duration_ms: number;\n /** URL that was requested. */\n url: string;\n /** URL the page ended on (after redirects / in-page navigation). */\n final_url?: string;\n title?: string;\n /** Frames dropped by the recorder (screenshot failures). */\n dropped?: number;\n /** Present only when an interaction script was replayed. */\n interactions?: InteractionReport;\n /** The viewport the frames were taken at; adds the image-scale line when known. */\n viewport?: Viewport;\n /**\n * Remarks about the capture itself rather than about any one frame — a\n * context layer that hit its cap, requests still in flight when the\n * recording ended. One line each, after the summary.\n */\n notes?: string[];\n}\n\n/**\n * Build the MCP CallToolResult for a capture, in the shape the README\n * documents for framewatch_capture: one summary text block, then per card an image block,\n * a metadata text block and (when present) the change-region crop image.\n */\nexport function formatDiffCards(summary: CaptureSummary): CallToolResult {\n const lines = [formatSummaryLine(summary)];\n if (summary.viewport) {\n const scale = describeScale(summary.viewport);\n lines.push(scale.charAt(0).toUpperCase() + scale.slice(1));\n }\n if (summary.interactions) {\n lines.push(formatInteractionLine(summary.interactions));\n }\n for (const note of summary.notes ?? []) {\n lines.push(note);\n }\n const content: CallToolResult[\"content\"] = [{ type: \"text\", text: lines.join(\"\\n\") }];\n const last = summary.cards.length - 1;\n summary.cards.forEach((card, i) => {\n // First, last and event frames survive the image budget longest; crops go first.\n const keep = i === 0 || i === last || card.trigger !== \"animation\";\n content.push(markImage({ type: \"image\", data: card.full_frame, mimeType: \"image/png\" }, { role: \"frame\", keep }));\n content.push({ type: \"text\", text: formatCardMeta(card) });\n if (card.change_region?.crop) {\n content.push(markImage({ type: \"image\", data: card.change_region.crop, mimeType: \"image/png\" }, { role: \"crop\" }));\n }\n });\n return { content };\n}\n\n/**\n * One-line description of the whole capture. Always names the requested url;\n * appends the final url only when the page ended somewhere else, the page\n * title when known, and the dropped-frame count only when frames were lost.\n */\nfunction formatSummaryLine(summary: CaptureSummary): string {\n const { cards, total_frames, duration_ms, url, final_url, title, dropped } = summary;\n const recording = `from ${total_frames} raw frames (${duration_ms}ms recording) of ${url}`;\n let text = cards.length === 0 ? `No frames captured ${recording}` : `Captured ${cards.length} meaningful frames ${recording}`;\n if (final_url !== undefined && !isSameUrl(url, final_url)) {\n text += ` → ${final_url}`;\n }\n if (title) {\n text += ` — \"${title}\"`;\n }\n if (dropped !== undefined && dropped > 0) {\n text += ` (${dropped} frames dropped)`;\n }\n return text;\n}\n\n/**\n * How the interaction script went: how many steps ran, what they were, and —\n * when one failed — which one and why. A failed step is a finding about the\n * page, not a tool failure, so it is reported here alongside the frames rather\n * than replacing them with an error.\n */\nexport function formatInteractionLine(report: InteractionReport): string {\n let text = `Interactions: ${report.completed}/${report.total} replayed`;\n if (report.steps.length > 0) {\n text += ` — ${report.steps.join(\", \")}`;\n }\n if (report.error !== undefined) {\n text += `. Step ${report.failed_index ?? report.completed + 1}: ${report.error}`;\n }\n return text;\n}\n\n/**\n * Compare two URLs as URLs, not as strings: `page.url()` returns the\n * WHATWG-normalised form, so a request for `http://localhost:3000` comes back\n * as `http://localhost:3000/` without anything having navigated.\n */\nfunction isSameUrl(a: string, b: string): boolean {\n if (a === b) return true;\n try {\n return new URL(a).href === new URL(b).href;\n } catch {\n return false;\n }\n}\n\n/**\n * The metadata text block for one card (exported for tests and for reuse by\n * the interact tool). Line 1 is always `Frame N @ Tms [trigger]`; the\n * optional sections follow in a fixed order — Changed, Console, Network,\n * Performance, DOM — and are omitted entirely when their data is absent.\n */\nexport function formatCardMeta(card: DiffCard): string {\n const lines: string[] = [`Frame ${card.index} @ ${card.timestamp_ms}ms [${card.trigger}]`];\n\n const region = card.change_region;\n if (region) {\n if (region.change_percent === 0) {\n lines.push(\"Changed: 0.0% — no visual change since previous frame\");\n } else {\n const { bbox } = region;\n let changed = `Changed: ${region.change_percent.toFixed(1)}% — region: ${bbox.x},${bbox.y} ${bbox.width}x${bbox.height}`;\n if (!region.crop) {\n changed += \" (full-frame change, see frame image)\";\n }\n lines.push(changed);\n }\n }\n\n if (card.console_entries?.length) {\n lines.push(\"Console:\");\n for (const entry of card.console_entries) {\n lines.push(` [${entry.level}] ${entry.text}`);\n }\n }\n\n if (card.network_events?.length) {\n lines.push(\"Network:\");\n for (const event of card.network_events) {\n // A request that never got a response has no status to print; what\n // stopped it (or that it is still running) is the useful part.\n const outcome = event.status > 0 ? String(event.status) : (event.error ?? \"no response\");\n const why = event.status > 0 && event.error !== undefined ? ` ${event.error}` : \"\";\n lines.push(` ${event.method} ${event.url} → ${outcome}${why} (${event.duration_ms}ms)`);\n }\n }\n\n const perf = card.performance;\n if (perf) {\n lines.push(\"Performance:\");\n if (perf.paint_time_ms !== undefined) lines.push(` paint ${perf.paint_time_ms}ms`);\n if (perf.layout_shifts !== undefined) {\n const score = perf.layout_shift_score !== undefined ? ` (score ${perf.layout_shift_score})` : \"\";\n lines.push(` layout shifts ${perf.layout_shifts}${score}`);\n }\n if (perf.lcp_ms !== undefined) lines.push(` lcp ${perf.lcp_ms}ms`);\n }\n\n if (card.dom_snapshot) {\n lines.push(`DOM:\\n${card.dom_snapshot}`);\n }\n\n return lines.join(\"\\n\");\n}\n"]}
@@ -7,8 +7,10 @@
7
7
  * so the counting, the filtering and the truncation can be tested in
8
8
  * milliseconds against a tree written by hand.
9
9
  */
10
- /** A bare ref as the caller writes it: `e8`. */
10
+ /** A bare ref as the caller writes it: `e8`, or `f1e8` after a navigation. */
11
11
  export declare function isRef(value: string): boolean;
12
+ /** The shape of a ref, for input validation as well as `isRef`. */
13
+ export declare const REF_SHAPE: RegExp;
12
14
  /** The Playwright selector that resolves a snapshot ref. */
13
15
  export declare function refSelector(ref: string): string;
14
16
  export interface SnapshotCounts {