ptywright 0.6.3 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/agent.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { a as formatAgentLaunchPlan, i as runAgentSpecPath, n as replayAgentRecordPath, r as runAgentSpec, t as defaultSpecNameForPath } from "./runner-RhWYhus1.mjs";
1
+ import { a as formatAgentLaunchPlan, i as runAgentSpecPath, n as replayAgentRecordPath, r as runAgentSpec, t as defaultSpecNameForPath } from "./runner-CB8OirHU.mjs";
2
2
  export { defaultSpecNameForPath, formatAgentLaunchPlan, replayAgentRecordPath, runAgentSpec, runAgentSpecPath };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bun
2
- import { t as main } from "../cli-uj8xaanE.mjs";
2
+ import { t as main } from "../cli-lwZjxEqE.mjs";
3
3
  //#region src/bin/ptywright.ts
4
4
  await main();
5
5
  //#endregion
@@ -1,9 +1,9 @@
1
1
  import { n as loadPtywrightConfig } from "./config-bGg636EW.mjs";
2
- import { C as writeAgentManifestPath, D as escapeHtml, E as escapeAttribute, O as normalizeAgentFlowSpec, S as validateAgentManifestFiles, T as readAgentCassettePath, _ as formatArgv, b as isAgentManifestLike, c as launchAgentBrowser, d as AGENT_RUN_RECORD_SCHEMA_URL, f as agentRunModeSchema, g as writeAgentRunRecordPath, h as readAgentRunRecordPath, i as runAgentSpecPath, k as sanitizeArtifactName, l as createAgentTemplateSpec, m as isAgentRunRecordLike, n as replayAgentRecordPath, o as resolveAgentLaunchTarget, p as formatAgentArgv, s as normalizeAgentFlowSpecWithConfig, u as loadAgentSpec, v as AGENT_MANIFEST_FILE_NAME, w as isAgentCassetteLike, x as readAgentManifestPath, y as agentManifestPath } from "./runner-RhWYhus1.mjs";
2
+ import { A as sanitizeArtifactName, C as validateAgentManifestFiles, D as escapeAttribute, E as readAgentCassettePath, O as escapeHtml, S as readAgentManifestPath, T as isAgentCassetteLike, _ as writeAgentRunRecordPath, b as agentManifestPath, c as launchAgentBrowser, d as loadAgentSpec, f as AGENT_RUN_RECORD_SCHEMA_URL, g as readAgentRunRecordPath, h as isAgentRunRecordLike, i as runAgentSpecPath, k as normalizeAgentFlowSpec, l as renderReportThemeCss, m as formatAgentArgv, n as replayAgentRecordPath, o as resolveAgentLaunchTarget, p as agentRunModeSchema, s as normalizeAgentFlowSpecWithConfig, u as createAgentTemplateSpec, v as formatArgv, w as writeAgentManifestPath, x as isAgentManifestLike, y as AGENT_MANIFEST_FILE_NAME } from "./runner-CB8OirHU.mjs";
3
3
  import { a as sameArgv, i as diffCommandMaps, r as formatZodIssues } from "./manifest_files-DW80c1H7.mjs";
4
4
  import { i as portableCliPath, n as mergeProcessEnv, o as relativeHref, s as samePath } from "./env-DPYHo-zH.mjs";
5
5
  import { d as resolvePtyBackend, u as createDefaultPtyAdapter } from "./runner-BHXXwxYp.mjs";
6
- import { a as resolveScriptManifestPath, c as relocateScriptManifestCommands, d as resolveScriptRunSummaryPath, f as runScriptPath, i as readScriptManifestPath, l as resolveManifestPrimaryPath$1, n as runAllScripts, o as validateScriptManifest, r as findScriptSummaryManifest, t as createPtywrightServer, u as readScriptRunSummaryPath } from "./server-B4Bbuluz.mjs";
6
+ import { a as resolveScriptManifestPath, c as relocateScriptManifestCommands, d as resolveScriptRunSummaryPath, f as runScriptPath, i as readScriptManifestPath, l as resolveManifestPrimaryPath$1, n as runAllScripts, o as validateScriptManifest, r as findScriptSummaryManifest, t as createPtywrightServer, u as readScriptRunSummaryPath } from "./server-Bt7CXCz8.mjs";
7
7
  import { c as createPtyCassetteReplay, i as formatPtyCassetteInspectLines, l as readPtyCassettePath, o as inspectPtyCassettePath, r as createPtyCassetteRecorder, t as wrapPtyLike, v as validatePtyCassette } from "./pty_like-DWIlWGgA.mjs";
8
8
  import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
9
9
  import { basename, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
@@ -1598,128 +1598,275 @@ function renderFailedEntryReport(result) {
1598
1598
  <meta name="viewport" content="width=device-width, initial-scale=1" />
1599
1599
  <title>${escapeHtml(result.name)} failed replay</title>
1600
1600
  <style>
1601
- :root { color-scheme: light; font-family: ui-sans-serif, system-ui, sans-serif; }
1602
- body { margin: 0; padding: 32px; background: oklch(97.5% 0.008 210); color: oklch(19% 0.018 230); }
1603
- main { display: grid; gap: 16px; max-width: 960px; }
1604
- pre { overflow: auto; border-radius: 8px; background: oklch(20% 0.015 230); color: oklch(92% 0.012 230); padding: 14px; }
1601
+ ${renderReportThemeCss()}
1602
+
1603
+ .hero {
1604
+ margin-bottom: 24px;
1605
+ }
1606
+ .hero h1 {
1607
+ margin-bottom: 12px;
1608
+ }
1609
+ .error-block {
1610
+ margin-top: 20px;
1611
+ }
1612
+ .error-block h2 {
1613
+ margin-bottom: 10px;
1614
+ color: var(--fail);
1615
+ }
1605
1616
  </style>
1606
1617
  </head>
1607
1618
  <body>
1608
- <main>
1609
- <h1>${escapeHtml(result.name)}</h1>
1610
- <p>Replay failed before the agent runner could start.</p>
1611
- <pre>${escapeHtml(result.errors.join("\n"))}</pre>
1612
- </main>
1619
+ <div class="rail fail"></div>
1620
+ <div class="shell">
1621
+ <div class="hero">
1622
+ <div class="statusline fail">
1623
+ <span class="dot"></span>
1624
+ <span>FAILED</span>
1625
+ </div>
1626
+ <h1>${escapeHtml(result.name)}</h1>
1627
+ <p style="color: var(--muted); margin: 8px 0 0;">Replay failed before the agent runner could start.</p>
1628
+ </div>
1629
+ <div class="panel error-block">
1630
+ <h2>Error Details</h2>
1631
+ <div class="codeblock">
1632
+ <pre>${escapeHtml(result.errors.join("\n"))}</pre>
1633
+ </div>
1634
+ </div>
1635
+ </div>
1613
1636
  </body>
1614
1637
  </html>`;
1615
1638
  }
1616
1639
  function writeReplayAllReport(path, args) {
1617
1640
  mkdirSync(dirname(path), { recursive: true });
1618
- const rows = args.entries.map((entry) => renderEntry(entry, path)).join("\n");
1641
+ const rows = [...args.entries].sort((a, b) => {
1642
+ if (a.result.ok !== b.result.ok) return a.result.ok ? 1 : -1;
1643
+ return a.result.name.localeCompare(b.result.name);
1644
+ }).map((entry) => renderEntry(entry, path)).join("\n");
1619
1645
  const ok = args.entries.every((entry) => entry.result.ok);
1646
+ const passCount = args.entries.filter((e) => e.result.ok).length;
1647
+ const failCount = args.entries.length - passCount;
1648
+ const passRate = args.entries.length > 0 ? passCount / args.entries.length * 100 : 0;
1620
1649
  writeFileSync(path, `<!doctype html>
1621
1650
  <html lang="en">
1622
1651
  <head>
1623
1652
  <meta charset="utf-8" />
1624
1653
  <meta name="viewport" content="width=device-width, initial-scale=1" />
1625
- <title>ptywright agent replay report</title>
1654
+ <title>Agent Replay Report · ${ok ? "Passed" : "Failed"}</title>
1626
1655
  <style>
1627
- :root {
1628
- color-scheme: light;
1629
- --bg: oklch(97.5% 0.008 210);
1630
- --ink: oklch(19% 0.018 230);
1631
- --muted: oklch(48% 0.02 230);
1632
- --line: oklch(86% 0.018 230);
1633
- --panel: oklch(99% 0.006 210);
1634
- --good: oklch(55% 0.15 155);
1635
- --bad: oklch(58% 0.19 25);
1636
- --focus: oklch(55% 0.14 235);
1637
- font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
1638
- }
1639
- * { box-sizing: border-box; }
1640
- body { margin: 0; background: var(--bg); color: var(--ink); }
1641
- main {
1642
- display: grid;
1643
- gap: 22px;
1644
- width: min(1180px, calc(100vw - 32px));
1645
- margin: 0 auto;
1646
- padding: 32px 0 48px;
1647
- }
1648
- header {
1649
- display: grid;
1650
- gap: 10px;
1651
- border-bottom: 1px solid var(--line);
1652
- padding-bottom: 20px;
1653
- }
1654
- h1 { margin: 0; font-size: 28px; line-height: 1.15; letter-spacing: 0; }
1655
- .meta { display: flex; flex-wrap: wrap; gap: 8px; }
1656
- .pill {
1657
- display: inline-flex;
1658
- min-height: 32px;
1659
- align-items: center;
1660
- border: 1px solid var(--line);
1661
- border-radius: 999px;
1662
- padding: 0 12px;
1663
- color: var(--muted);
1664
- font-size: 13px;
1665
- }
1666
- .pill.pass { color: var(--good); border-color: color-mix(in oklch, var(--good) 42%, var(--line)); }
1667
- .pill.fail { color: var(--bad); border-color: color-mix(in oklch, var(--bad) 42%, var(--line)); }
1668
- .entries { display: grid; gap: 10px; }
1669
- .entry {
1670
- display: grid;
1671
- grid-template-columns: 92px minmax(0, 1fr) auto;
1672
- gap: 14px;
1673
- align-items: center;
1674
- border: 1px solid var(--line);
1675
- border-radius: 8px;
1676
- background: var(--panel);
1677
- padding: 12px;
1678
- }
1679
- .badge {
1680
- justify-self: start;
1681
- border-radius: 999px;
1682
- padding: 5px 9px;
1683
- background: color-mix(in oklch, var(--line) 52%, transparent);
1684
- color: var(--muted);
1685
- font-size: 12px;
1686
- font-weight: 700;
1687
- }
1688
- .badge.pass { background: color-mix(in oklch, var(--good) 12%, var(--panel)); color: var(--good); }
1689
- .badge.fail { background: color-mix(in oklch, var(--bad) 12%, var(--panel)); color: var(--bad); }
1690
- a { color: var(--focus); font-weight: 700; text-decoration: none; }
1691
- code { color: var(--muted); overflow-wrap: anywhere; }
1692
- .commands {
1693
- display: grid;
1694
- gap: 4px;
1695
- margin-top: 8px;
1696
- }
1697
- .commands code {
1698
- display: block;
1699
- }
1700
- @media (max-width: 720px) {
1701
- main { width: min(100vw - 20px, 1180px); padding-top: 18px; }
1702
- .entry { grid-template-columns: 1fr; }
1703
- }
1656
+ ${renderReportThemeCss()}
1657
+
1658
+ /* Hero section with pass-rate visualization */
1659
+ .hero {
1660
+ margin-bottom: 28px;
1661
+ }
1662
+ .hero-title {
1663
+ display: flex;
1664
+ align-items: center;
1665
+ justify-content: space-between;
1666
+ gap: 16px;
1667
+ margin-bottom: 18px;
1668
+ flex-wrap: wrap;
1669
+ }
1670
+ .hero-stats {
1671
+ display: grid;
1672
+ grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
1673
+ gap: 12px;
1674
+ margin-bottom: 16px;
1675
+ }
1676
+ .hero-stat {
1677
+ display: flex;
1678
+ flex-direction: column;
1679
+ gap: 4px;
1680
+ }
1681
+ .hero-stat-value {
1682
+ font-size: 32px;
1683
+ font-weight: 700;
1684
+ line-height: 1;
1685
+ font-variant-numeric: tabular-nums;
1686
+ letter-spacing: -0.02em;
1687
+ }
1688
+ .hero-stat-value.pass { color: var(--pass); }
1689
+ .hero-stat-value.fail { color: var(--fail); }
1690
+ .hero-stat-label {
1691
+ font-size: 12px;
1692
+ color: var(--muted);
1693
+ text-transform: uppercase;
1694
+ letter-spacing: 0.05em;
1695
+ font-weight: 600;
1696
+ }
1697
+ .meter-wrapper {
1698
+ margin-bottom: 20px;
1699
+ }
1700
+ .meter-label {
1701
+ display: flex;
1702
+ justify-content: space-between;
1703
+ margin-bottom: 8px;
1704
+ font-size: 13px;
1705
+ color: var(--muted);
1706
+ }
1707
+ .meter-label strong {
1708
+ color: var(--ink);
1709
+ font-weight: 660;
1710
+ }
1711
+
1712
+ /* Entry list */
1713
+ .entries {
1714
+ display: grid;
1715
+ gap: 12px;
1716
+ }
1717
+ .entry {
1718
+ border: 1px solid var(--line);
1719
+ border-radius: var(--radius);
1720
+ background: var(--panel);
1721
+ padding: 16px;
1722
+ box-shadow: var(--shadow);
1723
+ transition: border-color 0.15s ease;
1724
+ }
1725
+ .entry:hover {
1726
+ border-color: var(--line-strong);
1727
+ }
1728
+ .entry-header {
1729
+ display: flex;
1730
+ align-items: flex-start;
1731
+ gap: 12px;
1732
+ margin-bottom: 12px;
1733
+ }
1734
+ .entry-title {
1735
+ flex: 1;
1736
+ min-width: 0;
1737
+ }
1738
+ .entry-title a {
1739
+ font-size: 15px;
1740
+ font-weight: 640;
1741
+ color: var(--ink);
1742
+ }
1743
+ .entry-title a:hover {
1744
+ color: var(--accent);
1745
+ }
1746
+ .entry-meta {
1747
+ display: flex;
1748
+ flex-wrap: wrap;
1749
+ gap: 8px;
1750
+ margin-top: 10px;
1751
+ font-size: 12px;
1752
+ color: var(--muted);
1753
+ font-family: var(--font-mono);
1754
+ }
1755
+ .entry-meta > span {
1756
+ display: flex;
1757
+ align-items: center;
1758
+ gap: 4px;
1759
+ }
1760
+ .entry-paths {
1761
+ margin-top: 8px;
1762
+ padding-top: 12px;
1763
+ border-top: 1px solid var(--line);
1764
+ }
1765
+ .entry-path {
1766
+ font-size: 12px;
1767
+ color: var(--faint);
1768
+ font-family: var(--font-mono);
1769
+ margin-bottom: 4px;
1770
+ overflow: hidden;
1771
+ text-overflow: ellipsis;
1772
+ white-space: nowrap;
1773
+ }
1774
+ .entry-commands {
1775
+ margin-top: 12px;
1776
+ padding: 10px;
1777
+ background: var(--canvas);
1778
+ border-radius: var(--radius-xs);
1779
+ font-size: 11.5px;
1780
+ font-family: var(--font-mono);
1781
+ color: var(--muted);
1782
+ }
1783
+ .entry-commands > div {
1784
+ margin-bottom: 3px;
1785
+ }
1786
+ .entry-commands > div:last-child {
1787
+ margin-bottom: 0;
1788
+ }
1789
+ .entry-failed-artifacts {
1790
+ margin-top: 12px;
1791
+ padding: 10px;
1792
+ background: var(--fail-soft);
1793
+ border-radius: var(--radius-xs);
1794
+ border: 1px solid color-mix(in oklab, var(--fail) 25%, var(--line));
1795
+ }
1796
+ .entry-failed-artifacts > div {
1797
+ margin-bottom: 6px;
1798
+ font-size: 12px;
1799
+ }
1800
+ .entry-failed-artifacts > div:last-child {
1801
+ margin-bottom: 0;
1802
+ }
1803
+ .entry-errors {
1804
+ margin-top: 12px;
1805
+ padding: 10px;
1806
+ background: var(--fail-soft);
1807
+ border-radius: var(--radius-xs);
1808
+ border: 1px solid color-mix(in oklab, var(--fail) 25%, var(--line));
1809
+ color: var(--fail);
1810
+ font-size: 12px;
1811
+ font-family: var(--font-mono);
1812
+ }
1704
1813
  </style>
1705
1814
  </head>
1706
1815
  <body>
1707
- <main>
1708
- <header>
1709
- <h1>ptywright agent replay report</h1>
1710
- <div class="meta">
1711
- <span class="pill ${ok ? "pass" : "fail"}">${ok ? "passed" : "failed"}</span>
1712
- <span class="pill">${args.entries.length} entries</span>
1713
- <span class="pill">${args.updateSnapshots ? "update snapshots" : "compare snapshots"}</span>
1714
- <span class="pill">${args.durationMs}ms</span>
1715
- <span class="pill">${escapeHtml(args.dir)}</span>
1716
- <a class="pill" href="${escapeAttribute(relativeHref(path, args.summaryPath))}">agent-replay.summary.json</a>
1816
+ <div class="rail ${ok ? "pass" : "fail"}"></div>
1817
+ <div class="shell">
1818
+ <div class="hero">
1819
+ <div class="hero-title">
1820
+ <h1>Agent Replay Report</h1>
1821
+ <div class="statusline ${ok ? "pass" : "fail"}">
1822
+ <span class="dot"></span>
1823
+ <span>${ok ? "PASSED" : "FAILED"}</span>
1824
+ </div>
1825
+ </div>
1826
+
1827
+ <div class="hero-stats">
1828
+ <div class="hero-stat">
1829
+ <div class="hero-stat-value">${args.entries.length}</div>
1830
+ <div class="hero-stat-label">Total Entries</div>
1831
+ </div>
1832
+ <div class="hero-stat">
1833
+ <div class="hero-stat-value pass">${passCount}</div>
1834
+ <div class="hero-stat-label">Passed</div>
1835
+ </div>
1836
+ <div class="hero-stat">
1837
+ <div class="hero-stat-value fail">${failCount}</div>
1838
+ <div class="hero-stat-label">Failed</div>
1839
+ </div>
1840
+ <div class="hero-stat">
1841
+ <div class="hero-stat-value">${passRate.toFixed(1)}%</div>
1842
+ <div class="hero-stat-label">Pass Rate</div>
1843
+ </div>
1717
1844
  </div>
1718
- </header>
1845
+
1846
+ <div class="meter-wrapper">
1847
+ <div class="meter-label">
1848
+ <span><strong>${passCount}</strong> passed</span>
1849
+ <span><strong>${failCount}</strong> failed</span>
1850
+ </div>
1851
+ <div class="meter ${failCount > 0 ? "has-fail" : ""}">
1852
+ <i style="width: ${passRate}%"></i>
1853
+ </div>
1854
+ </div>
1855
+
1856
+ <div class="chip-row">
1857
+ <span class="chip mono">${escapeHtml(args.dir)}</span>
1858
+ <span class="chip">${args.updateSnapshots ? "Update Snapshots" : "Compare Snapshots"}</span>
1859
+ <span class="chip">${args.durationMs}ms</span>
1860
+ <a class="chip" href="${escapeAttribute(relativeHref(path, args.summaryPath))}">
1861
+ 📄 summary.json
1862
+ </a>
1863
+ </div>
1864
+ </div>
1865
+
1719
1866
  <section class="entries">
1720
- ${rows || "<p>No replay artifacts were found.</p>"}
1867
+ ${rows || "<div class=\"panel\"><p style=\"color: var(--muted); margin: 0;\">No replay artifacts were found.</p></div>"}
1721
1868
  </section>
1722
- </main>
1869
+ </div>
1723
1870
  </body>
1724
1871
  </html>`, "utf8");
1725
1872
  }
@@ -1728,33 +1875,62 @@ function renderEntry(entry, reportPath) {
1728
1875
  const source = entry.result.replaySourceCassettePath ?? entry.result.cassettePath;
1729
1876
  const failedArtifacts = entry.result.artifacts.filter((artifact) => !artifact.ok);
1730
1877
  return `<article class="entry">
1731
- <span class="badge ${state}">${state}</span>
1732
- <div>
1733
- <a href="${escapeAttribute(relativeHref(reportPath, entry.result.reportPath))}">${escapeHtml(entry.result.name)}</a>
1734
- <div><code>${escapeHtml(entry.filePath)}</code></div>
1735
- <div><code>${escapeHtml(source)}</code></div>
1736
- <div class="commands">
1737
- <code>replay ${escapeHtml(formatAgentArgv(entry.result.commands.replay.argv))}</code>
1738
- <code>update ${escapeHtml(formatAgentArgv(entry.result.commands.updateSnapshots.argv))}</code>
1739
- <code>commands ${escapeHtml(formatAgentArgv([
1878
+ <div class="entry-header">
1879
+ <span class="dot-sm ${state}"></span>
1880
+ <div class="entry-title">
1881
+ <a href="${escapeAttribute(relativeHref(reportPath, entry.result.reportPath))}">${escapeHtml(entry.result.name)}</a>
1882
+ <div class="entry-meta">
1883
+ <span>📊 ${entry.result.mode}</span>
1884
+ <span>🎞️ ${entry.result.cassetteFrameCount} frames</span>
1885
+ <span>⏱️ ${entry.durationMs}ms</span>
1886
+ </div>
1887
+ </div>
1888
+ <div class="statusline ${state}">
1889
+ <span class="dot"></span>
1890
+ <span>${state.toUpperCase()}</span>
1891
+ </div>
1892
+ </div>
1893
+
1894
+ <div class="entry-paths">
1895
+ <div class="entry-path" title="${escapeAttribute(entry.filePath)}">📁 ${escapeHtml(entry.filePath)}</div>
1896
+ <div class="entry-path" title="${escapeAttribute(source)}">🎬 ${escapeHtml(source)}</div>
1897
+ </div>
1898
+
1899
+ ${failedArtifacts.length > 0 ? `<div class="entry-failed-artifacts">
1900
+ <strong style="display: block; margin-bottom: 8px; color: var(--fail);">⚠️ Failed Artifacts (${failedArtifacts.length})</strong>
1901
+ ${failedArtifacts.map((artifact) => renderFailedArtifact(artifact, reportPath)).join("")}
1902
+ </div>` : ""}
1903
+
1904
+ ${entry.result.errors.length > 0 ? `<div class="entry-errors">
1905
+ <strong style="display: block; margin-bottom: 6px;">❌ Errors</strong>
1906
+ ${entry.result.errors.map((error) => `<div>${escapeHtml(error)}</div>`).join("")}
1907
+ </div>` : ""}
1908
+
1909
+ <details class="entry-commands">
1910
+ <summary style="cursor: pointer; font-weight: 600; color: var(--ink); margin-bottom: 8px;">🔧 Commands</summary>
1911
+ <div><strong>Replay:</strong> ${escapeHtml(formatAgentArgv(entry.result.commands.replay.argv))}</div>
1912
+ <div><strong>Update:</strong> ${escapeHtml(formatAgentArgv(entry.result.commands.updateSnapshots.argv))}</div>
1913
+ <div><strong>Commands:</strong> ${escapeHtml(formatAgentArgv([
1740
1914
  "ptywright",
1741
1915
  "agent",
1742
1916
  "commands",
1743
1917
  entry.result.recordPath,
1744
1918
  "--json"
1745
- ]))}</code>
1746
- </div>
1747
- ${failedArtifacts.map((artifact) => renderFailedArtifact(artifact, reportPath)).join("")}
1748
- ${entry.result.errors.map((error) => `<div><code>${escapeHtml(error)}</code></div>`).join("")}
1749
- </div>
1750
- <code>${entry.result.mode} / ${entry.result.cassetteFrameCount} frames / ${entry.durationMs}ms</code>
1919
+ ]))}</div>
1920
+ </details>
1751
1921
  </article>`;
1752
1922
  }
1753
1923
  function renderFailedArtifact(artifact, reportPath) {
1754
- const diffLink = artifact.diffPath ? `<a href="${escapeAttribute(relativeHref(reportPath, artifact.diffPath))}">diff</a>` : "";
1755
- return `<div>
1756
- ${`<a href="${escapeAttribute(relativeHref(reportPath, artifact.path))}">${escapeHtml(artifact.kind)}</a>`}${diffLink ? ` ${diffLink}` : ""}
1757
- <code>${escapeHtml(artifact.viewport)} / ${escapeHtml(artifact.name)}${artifact.error ? ` / ${artifact.error}` : ""}</code>
1924
+ const diffLink = artifact.diffPath ? ` · <a href="${escapeAttribute(relativeHref(reportPath, artifact.diffPath))}" style="color: var(--changed);">view diff</a>` : "";
1925
+ const artifactLink = `<a href="${escapeAttribute(relativeHref(reportPath, artifact.path))}">${escapeHtml(artifact.kind)}</a>`;
1926
+ const errorMsg = artifact.error ? ` <span style="color: var(--fail);">· ${escapeHtml(artifact.error)}</span>` : "";
1927
+ return `<div style="padding: 6px 0; border-bottom: 1px solid var(--line); font-size: 12px;">
1928
+ <div style="margin-bottom: 3px;">
1929
+ ${artifactLink}${diffLink}
1930
+ </div>
1931
+ <div style="color: var(--muted); font-family: var(--font-mono); font-size: 11px;">
1932
+ ${escapeHtml(artifact.viewport)} · ${escapeHtml(artifact.name)}${errorMsg}
1933
+ </div>
1758
1934
  </div>`;
1759
1935
  }
1760
1936
  //#endregion
package/dist/cli.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { t as main } from "./cli-uj8xaanE.mjs";
1
+ import { t as main } from "./cli-lwZjxEqE.mjs";
2
2
  export { main };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as createPtywrightServer } from "./server-B4Bbuluz.mjs";
1
+ import { t as createPtywrightServer } from "./server-Bt7CXCz8.mjs";
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
3
  //#region src/index.ts
4
4
  const { server, sessions } = createPtywrightServer();
package/dist/mcp.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { t as createPtywrightServer } from "./server-B4Bbuluz.mjs";
1
+ import { t as createPtywrightServer } from "./server-Bt7CXCz8.mjs";
2
2
  export { createPtywrightServer };