mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-23 09:07:46 +02:00
feat(briefs): capture briefing page screenshots and pin playwright
Adds a screenshot-capture spec gated behind BRIEFS_CAPTURE_SCREENSHOTS=1 that renders the Briefing page fixture set and writes desktop, mobile, and empty-state PNGs into docs/pr-screenshots/pap-9963/ for UX handoff. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
0f84cb9aed
commit
3a0f9b5c83
9 files changed
+398
No files matched your search
@@ -0,0 +1,76 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Briefing — desktop</title>
|
||||
<style>
|
||||
:root {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--border: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--primary: oklch(0.985 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
}
|
||||
html, body { background: var(--background); color: var(--foreground); margin: 0; min-height: 100vh; }
|
||||
body { font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; }
|
||||
a { color: inherit; }
|
||||
@media (max-width: 700px) {
|
||||
[data-briefs-mobile-tabs] { display: flex !important; }
|
||||
[data-briefs-section] > header { display: none; }
|
||||
[data-briefs-section][data-mobile-hidden="true"] { display: none; }
|
||||
[data-briefs-grid] { grid-template-columns: 1fr !important; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; color: var(--foreground); padding: 20px clamp(12px, 4vw, 32px); max-width: 1280px; margin: 0 auto; min-height: 100vh;">
|
||||
<header style="display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px; margin-bottom: 6px;">
|
||||
<h1 style="margin: 0; font-size: 22px; font-weight: 600;">Briefing</h1>
|
||||
<div style="flex: 1; min-width: 0; font-size: 12px; color: var(--muted-foreground);">8 active · 2 pinned · refreshed just now</div>
|
||||
<div style="display: flex; gap: 6px; align-items: center;">
|
||||
<button style="padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border); background: var(--card); color: var(--foreground); font-size: 12px;">Preferences</button>
|
||||
<button style="padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border); background: var(--card); color: var(--foreground); font-size: 12px;">Refresh</button>
|
||||
</div>
|
||||
</header>
|
||||
<p style="margin: 0; margin-bottom: 18px; font-size: 13px; color: var(--muted-foreground);">Durable cards for areas of work that involve you. Pin the ones you always want to see.</p>
|
||||
|
||||
<section data-briefs-section="attention" style="display: flex; flex-direction: column; gap: 12px; margin-bottom: 24px;">
|
||||
<header style="display: flex; align-items: center; gap: 10px;">
|
||||
<h2 style="margin: 0; font-size: 12px; letter-spacing: 0.6px; color: var(--muted-foreground); text-transform: uppercase; font-weight: 600;">Needs your attention</h2>
|
||||
<span style="font-size: 12px; color: var(--muted-foreground);">3</span>
|
||||
<span style="flex: 1; border-bottom: 1px dashed var(--border);"></span>
|
||||
</header>
|
||||
<div data-briefs-grid style="display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 360px), 1fr)); gap: 12px;">
|
||||
<article data-briefs-card="true" data-state="waiting-user" data-summary-status="ok" data-pinned="true" style="position:relative;background:var(--card, oklch(0.205 0 0));border:1px solid var(--border, oklch(0.269 0 0));border-radius:10px;padding:12px 14px 12px 18px;display:flex;flex-direction:column;gap:8px;min-width:0;opacity:1"><span aria-hidden="true" style="position:absolute;top:8px;bottom:8px;left:6px;width:3px;border-radius:2px;background:oklch(0.72 0.15 70)"></span><header style="display:flex;align-items:flex-start;gap:8px;min-width:0"><button type="button" aria-pressed="true" aria-label="Unpin card" style="width:22px;height:22px;flex-shrink:0;border-radius:6px;border:1px solid oklch(0.8 0.13 80);background:oklch(0.4 0.13 80);color:oklch(0.95 0.1 80);font-size:12px;line-height:1;cursor:pointer;display:inline-flex;align-items:center;justify-content:center">★</button><h3 style="margin:0;flex:1;font-size:14px;font-weight:600;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">External-adapter plugin: spec review</h3><span data-briefs-state-badge="waiting-user" style="flex-shrink:0;padding:2px 8px;font-size:11px;font-weight:600;border-radius:999px;background:oklch(0.27 0.07 70);color:oklch(0.85 0.11 70);white-space:nowrap">Waiting on you</span></header><div style="display:flex;align-items:center;flex-wrap:wrap;gap:8px;font-size:11px;color:var(--muted-foreground, oklch(0.708 0 0));min-width:0"><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0))">PAP-9020</span><span>2 issues</span><span>· 14h ago</span></div><p data-briefs-summary="true" style="margin:0;font-size:13px;line-height:1.45;color:var(--foreground, oklch(0.985 0 0));overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3">External-adapter sandbox spec is waiting on your reply about credential scoping before publish.</p><ul data-briefs-source-rows="true" style="list-style:none;margin:0;padding:0;display:flex;flex-direction:column"><li style="display:flex;align-items:center;gap:6px;padding:6px 0;border-top:none;font-size:12px;min-width:0"><span aria-hidden="true" style="color:var(--muted-foreground, oklch(0.708 0 0));width:12px;text-align:center">?</span><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0));font-size:11px">PAP-9020</span><a href="/PAP/issues/PAP-9020#interaction-1" title="Confirm credential scoping" style="flex:1;color:var(--foreground, oklch(0.985 0 0));text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0">Confirm credential scoping</a><span data-briefs-row-tag="warning" style="flex-shrink:0;font-size:10px;padding:1px 6px;border-radius:999px;background:oklch(0.27 0.07 70);color:oklch(0.85 0.11 70);white-space:nowrap">asked you</span></li><li style="display:flex;align-items:center;gap:6px;padding:6px 0;border-top:1px dashed var(--border, oklch(0.269 0 0));font-size:12px;min-width:0"><span aria-hidden="true" style="color:var(--muted-foreground, oklch(0.708 0 0));width:12px;text-align:center">■</span><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0));font-size:11px">PAP-9001</span><a href="/PAP/issues/PAP-9963" title="Adapter spec draft" style="flex:1;color:var(--foreground, oklch(0.985 0 0));text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0">Adapter spec draft</a><span data-briefs-row-tag="violet" style="flex-shrink:0;font-size:10px;padding:1px 6px;border-radius:999px;background:oklch(0.27 0.07 305);color:oklch(0.84 0.11 305);white-space:nowrap">in_review</span></li></ul><footer style="display:flex;align-items:center;gap:8px;font-size:11px;color:var(--muted-foreground, oklch(0.708 0 0));padding-top:4px;margin-top:auto"><span aria-hidden="true"></span><span style="flex:1"></span><span>14h ago</span><a href="/PAP/issues/PAP-9963" style="color:var(--foreground, oklch(0.985 0 0));text-decoration:none;font-size:11px">Open tree →</a></footer></article><article data-briefs-card="true" data-state="error" data-summary-status="ok" data-pinned="false" style="position:relative;background:var(--card, oklch(0.205 0 0));border:1px solid var(--border, oklch(0.269 0 0));border-radius:10px;padding:12px 14px 12px 18px;display:flex;flex-direction:column;gap:8px;min-width:0;opacity:1"><span aria-hidden="true" style="position:absolute;top:8px;bottom:8px;left:6px;width:3px;border-radius:2px;background:oklch(0.62 0.21 25)"></span><header style="display:flex;align-items:flex-start;gap:8px;min-width:0"><button type="button" aria-pressed="false" aria-label="Pin card" style="width:22px;height:22px;flex-shrink:0;border-radius:6px;border:1px solid var(--border, oklch(0.269 0 0));background:transparent;color:var(--muted-foreground, oklch(0.708 0 0));font-size:12px;line-height:1;cursor:pointer;display:inline-flex;align-items:center;justify-content:cenLine truncated
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-briefs-section="live" style="display: flex; flex-direction: column; gap: 12px; margin-bottom: 24px;">
|
||||
<header style="display: flex; align-items: center; gap: 10px;">
|
||||
<h2 style="margin: 0; font-size: 12px; letter-spacing: 0.6px; color: var(--muted-foreground); text-transform: uppercase; font-weight: 600;">Live and in review</h2>
|
||||
<span style="font-size: 12px; color: var(--muted-foreground);">3</span>
|
||||
<span style="flex: 1; border-bottom: 1px dashed var(--border);"></span>
|
||||
</header>
|
||||
<div data-briefs-grid style="display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 360px), 1fr)); gap: 12px;">
|
||||
<article data-briefs-card="true" data-state="live" data-summary-status="ok" data-pinned="true" style="position:relative;background:var(--card, oklch(0.205 0 0));border:1px solid var(--border, oklch(0.269 0 0));border-radius:10px;padding:12px 14px 12px 18px;display:flex;flex-direction:column;gap:8px;min-width:0;opacity:1"><span aria-hidden="true" style="position:absolute;top:8px;bottom:8px;left:6px;width:3px;border-radius:2px;background:oklch(0.7 0.13 200)"></span><header style="display:flex;align-items:flex-start;gap:8px;min-width:0"><button type="button" aria-pressed="true" aria-label="Unpin card" style="width:22px;height:22px;flex-shrink:0;border-radius:6px;border:1px solid oklch(0.8 0.13 80);background:oklch(0.4 0.13 80);color:oklch(0.95 0.1 80);font-size:12px;line-height:1;cursor:pointer;display:inline-flex;align-items:center;justify-content:center">★</button><h3 style="margin:0;flex:1;font-size:14px;font-weight:600;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">Briefs plugin planning</h3><span data-briefs-state-badge="live" style="flex-shrink:0;padding:2px 8px;font-size:11px;font-weight:600;border-radius:999px;background:oklch(0.27 0.06 200);color:oklch(0.84 0.11 200);white-space:nowrap">Live</span></header><div style="display:flex;align-items:center;flex-wrap:wrap;gap:8px;font-size:11px;color:var(--muted-foreground, oklch(0.708 0 0));min-width:0"><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0))">PAP-9963</span><span>2 issues</span><span>· in 7h</span></div><p data-briefs-summary="true" style="margin:0;font-size:13px;line-height:1.45;color:var(--foreground, oklch(0.985 0 0));overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3">Phase 5 page UI in flight on PAP-9963; deterministic data is done and routines are paused for board approval.</p><ul data-briefs-source-rows="true" style="list-style:none;margin:0;padding:0;display:flex;flex-direction:column"><li style="display:flex;align-items:center;gap:6px;padding:6px 0;border-top:none;font-size:12px;min-width:0"><span aria-hidden="true" style="color:var(--muted-foreground, oklch(0.708 0 0));width:12px;text-align:center">■</span><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0));font-size:11px">PAP-9963</span><a href="/PAP/issues/PAP-9963" title="Wire briefing page UI" style="flex:1;color:var(--foreground, oklch(0.985 0 0));text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0">Wire briefing page UI</a><span data-briefs-row-tag="cyan" style="flex-shrink:0;font-size:10px;padding:1px 6px;border-radius:999px;background:oklch(0.27 0.06 200);color:oklch(0.84 0.11 200);white-space:nowrap">in_progress</span></li><li style="display:flex;align-items:center;gap:6px;padding:6px 0;border-top:1px dashed var(--border, oklch(0.269 0 0));font-size:12px;min-width:0"><span aria-hidden="true" style="color:var(--muted-foreground, oklch(0.708 0 0));width:12px;text-align:center">■</span><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0));font-size:11px">PAP-9961</span><a href="/PAP/issues/PAP-9963" title="Deterministic card service" style="flex:1;color:var(--foreground, oklch(0.985 0 0));text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0">Deterministic card service</a><span data-briefs-row-tag="green" style="flex-shrink:0;font-size:10px;padding:1px 6px;border-radius:999px;background:oklch(0.27 0.06 145);color:oklch(0.84 0.1 145);white-space:nowrap">done</span></li><li style="display:flex;align-items:center;gap:6px;padding:6px 0;border-top:1px dashed var(--border, oklch(0.269 0 0));font-size:12px;min-width:0"><span aria-hidden="true" style="color:var(--muted-foreground, oklch(0.708 0 0));width:12px;text-align:center">■</span><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0));font-size:11px">PAP-9962</span><a href="/PAP/issues/PAP-9963" title="Managed agent + routines" style="flex:1;color:var(--foreground, oklch(0.985 0 0));text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0">Managed agent + routines</a><span data-briefs-row-tag="muted" style="flex-shrink:0;font-size:10px;padding:1px 6px;border-radius:999px;background:oklch(0.25 0 0);color:oklch(0.75 0 0);white-space:nowrap">paused</span></li></ul><footer style="display:flex;align-items:center;gap:8px;font-size:11px;color:var(--muted-foreground, oklch(0.708 0 0));padding-top:4px;margin-top:auto"><span>+3 more in tree</span><span style="flex:1"></span><span>in 7h</span><a href="/PAP/issues/PAP-9963" style="color:var(--foreground, oklch(0.985 0 0));text-decoration:none;font-size:11px">Open tree →</a></footer></article><article data-briefs-card="true" data-state="live" data-summary-status="fallback" data-pinned="faLine truncated
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-briefs-section="settled" style="display: flex; flex-direction: column; gap: 12px; margin-bottom: 24px;">
|
||||
<header style="display: flex; align-items: center; gap: 10px;">
|
||||
<h2 style="margin: 0; font-size: 12px; letter-spacing: 0.6px; color: var(--muted-foreground); text-transform: uppercase; font-weight: 600;">Recently done & stale</h2>
|
||||
<span style="font-size: 12px; color: var(--muted-foreground);">2</span>
|
||||
<span style="flex: 1; border-bottom: 1px dashed var(--border);"></span>
|
||||
</header>
|
||||
<div data-briefs-grid style="display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 360px), 1fr)); gap: 12px;">
|
||||
<article data-briefs-card="true" data-state="done" data-summary-status="ok" data-pinned="false" style="position:relative;background:var(--card, oklch(0.205 0 0));border:1px solid var(--border, oklch(0.269 0 0));border-radius:10px;padding:12px 14px 12px 18px;display:flex;flex-direction:column;gap:8px;min-width:0;opacity:0.78"><span aria-hidden="true" style="position:absolute;top:8px;bottom:8px;left:6px;width:3px;border-radius:2px;background:oklch(0.65 0.16 145)"></span><header style="display:flex;align-items:flex-start;gap:8px;min-width:0"><button type="button" aria-pressed="false" aria-label="Pin card" style="width:22px;height:22px;flex-shrink:0;border-radius:6px;border:1px solid var(--border, oklch(0.269 0 0));background:transparent;color:var(--muted-foreground, oklch(0.708 0 0));font-size:12px;line-height:1;cursor:pointer;display:inline-flex;align-items:center;justify-content:center">☆</button><h3 style="margin:0;flex:1;font-size:14px;font-weight:600;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">Sidebar plugin slot hardening</h3><span data-briefs-state-badge="done" style="flex-shrink:0;padding:2px 8px;font-size:11px;font-weight:600;border-radius:999px;background:oklch(0.27 0.06 145);color:oklch(0.84 0.1 145);white-space:nowrap">Recently done</span></header><div style="display:flex;align-items:center;flex-wrap:wrap;gap:8px;font-size:11px;color:var(--muted-foreground, oklch(0.708 0 0));min-width:0"><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0))">PAP-8990</span><span>2 issues</span><span>· 5h ago</span></div><p data-briefs-summary="true" style="margin:0;font-size:13px;line-height:1.45;color:var(--foreground, oklch(0.985 0 0));overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3">Plugin sidebar slot order locked in; rolling tests green.</p><ul data-briefs-source-rows="true" style="list-style:none;margin:0;padding:0;display:flex;flex-direction:column"><li style="display:flex;align-items:center;gap:6px;padding:6px 0;border-top:none;font-size:12px;min-width:0"><span aria-hidden="true" style="color:var(--muted-foreground, oklch(0.708 0 0));width:12px;text-align:center">■</span><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0));font-size:11px">PAP-8990</span><a href="/PAP/issues/PAP-9963" title="Sidebar plugin slot hardening" style="flex:1;color:var(--foreground, oklch(0.985 0 0));text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0">Sidebar plugin slot hardening</a><span data-briefs-row-tag="green" style="flex-shrink:0;font-size:10px;padding:1px 6px;border-radius:999px;background:oklch(0.27 0.06 145);color:oklch(0.84 0.1 145);white-space:nowrap">done</span></li></ul><footer style="display:flex;align-items:center;gap:8px;font-size:11px;color:var(--muted-foreground, oklch(0.708 0 0));padding-top:4px;margin-top:auto"><span aria-hidden="true"></span><span style="flex:1"></span><span>5h ago</span><a href="/PAP/issues/PAP-9963" style="color:var(--foreground, oklch(0.985 0 0));text-decoration:none;font-size:11px">Open tree →</a></footer></article><article data-briefs-card="true" data-state="stale" data-summary-status="ok" data-pinned="false" style="position:relative;background:var(--card, oklch(0.205 0 0));border:1px solid var(--border, oklch(0.269 0 0));border-radius:10px;padding:12px 14px 12px 18px;display:flex;flex-direction:column;gap:8px;min-width:0;opacity:0.78"><span aria-hidden="true" style="position:absolute;top:8px;bottom:8px;left:6px;width:3px;border-radius:2px;background:oklch(0.5 0 0)"></span><header style="display:flex;align-items:flex-start;gap:8px;min-width:0"><button type="button" aria-pressed="false" aria-label="Pin card" style="width:22px;height:22px;flex-shrink:0;border-radius:6px;border:1px solid var(--border, oklch(0.269 0 0));background:transparent;color:var(--muted-foreground, oklch(0.708 0 0));font-size:12px;line-height:1;cursor:pointer;display:inline-flex;align-items:center;justify-content:center">☆</button><h3 style="margin:0;flex:1;font-size:14px;font-weight:600;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">GA migration spike</h3><span data-briefs-state-badge="stale" style="flex-shrink:0;padding:2px 8px;font-size:11px;font-weight:600;border-radius:999px;background:oklch(0.25 0 0);color:oklch(0.75 0 0);white-space:nowrap">Stale</span></header><div style="display:flex;align-items:center;flex-wrap:wrap;gap:8px;font-size:11px;color:var(--muted-foreground, oklch(0.708 0 0));min-width:0"><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0))">PAP-7000</span><span>2 issues</span><span>· 2w ago</span></div><p data-briefs-summary="true" style="margin:0;font-size:13px;line-height:1.45;color:var(--foreground, oklch(0.985 0 0));overflow:hidden;display:-webkit-box;-webkit-box-orient:vertiLine truncated
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,46 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Briefing — desktop</title>
|
||||
<style>
|
||||
:root {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--border: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--primary: oklch(0.985 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
}
|
||||
html, body { background: var(--background); color: var(--foreground); margin: 0; min-height: 100vh; }
|
||||
body { font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; }
|
||||
a { color: inherit; }
|
||||
@media (max-width: 700px) {
|
||||
[data-briefs-mobile-tabs] { display: flex !important; }
|
||||
[data-briefs-section] > header { display: none; }
|
||||
[data-briefs-section][data-mobile-hidden="true"] { display: none; }
|
||||
[data-briefs-grid] { grid-template-columns: 1fr !important; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; color: var(--foreground); padding: 20px clamp(12px, 4vw, 32px); max-width: 1280px; margin: 0 auto; min-height: 100vh;">
|
||||
<header style="display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px; margin-bottom: 6px;">
|
||||
<h1 style="margin: 0; font-size: 22px; font-weight: 600;">Briefing</h1>
|
||||
<div style="flex: 1; min-width: 0; font-size: 12px; color: var(--muted-foreground);">0 active · 0 pinned · refreshed just now</div>
|
||||
<div style="display: flex; gap: 6px; align-items: center;">
|
||||
<button style="padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border); background: var(--card); color: var(--foreground); font-size: 12px;">Preferences</button>
|
||||
<button style="padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border); background: var(--card); color: var(--foreground); font-size: 12px;">Refresh</button>
|
||||
</div>
|
||||
</header>
|
||||
<p style="margin: 0; margin-bottom: 18px; font-size: 13px; color: var(--muted-foreground);">Durable cards for areas of work that involve you. Pin the ones you always want to see.</p>
|
||||
<div data-briefs-empty style="padding: 40px 24px; border: 1px dashed var(--border); border-radius: 10px; text-align: center; color: var(--muted-foreground);">
|
||||
<div style="font-size: 15px; color: var(--foreground); font-weight: 600; margin-bottom: 4px;">No briefs yet</div>
|
||||
<div style="font-size: 13px;">Cards appear here once the Briefing Analyst picks up recent work. Pinned cards never expire.</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,76 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Briefing — mobile</title>
|
||||
<style>
|
||||
:root {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--border: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--primary: oklch(0.985 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
}
|
||||
html, body { background: var(--background); color: var(--foreground); margin: 0; min-height: 100vh; }
|
||||
body { font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; }
|
||||
a { color: inherit; }
|
||||
@media (max-width: 700px) {
|
||||
[data-briefs-mobile-tabs] { display: flex !important; }
|
||||
[data-briefs-section] > header { display: none; }
|
||||
[data-briefs-section][data-mobile-hidden="true"] { display: none; }
|
||||
[data-briefs-grid] { grid-template-columns: 1fr !important; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; color: var(--foreground); padding: 20px clamp(12px, 4vw, 32px); max-width: 1280px; margin: 0 auto; min-height: 100vh;">
|
||||
<header style="display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px; margin-bottom: 6px;">
|
||||
<h1 style="margin: 0; font-size: 22px; font-weight: 600;">Briefing</h1>
|
||||
<div style="flex: 1; min-width: 0; font-size: 12px; color: var(--muted-foreground);">8 active · 2 pinned · refreshed just now</div>
|
||||
<div style="display: flex; gap: 6px; align-items: center;">
|
||||
<button style="padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border); background: var(--card); color: var(--foreground); font-size: 12px;">Preferences</button>
|
||||
<button style="padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border); background: var(--card); color: var(--foreground); font-size: 12px;">Refresh</button>
|
||||
</div>
|
||||
</header>
|
||||
<p style="margin: 0; margin-bottom: 18px; font-size: 13px; color: var(--muted-foreground);">Durable cards for areas of work that involve you. Pin the ones you always want to see.</p>
|
||||
|
||||
<section data-briefs-section="attention" style="display: flex; flex-direction: column; gap: 12px; margin-bottom: 24px;">
|
||||
<header style="display: flex; align-items: center; gap: 10px;">
|
||||
<h2 style="margin: 0; font-size: 12px; letter-spacing: 0.6px; color: var(--muted-foreground); text-transform: uppercase; font-weight: 600;">Needs your attention</h2>
|
||||
<span style="font-size: 12px; color: var(--muted-foreground);">3</span>
|
||||
<span style="flex: 1; border-bottom: 1px dashed var(--border);"></span>
|
||||
</header>
|
||||
<div data-briefs-grid style="display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 360px), 1fr)); gap: 12px;">
|
||||
<article data-briefs-card="true" data-state="waiting-user" data-summary-status="ok" data-pinned="true" style="position:relative;background:var(--card, oklch(0.205 0 0));border:1px solid var(--border, oklch(0.269 0 0));border-radius:10px;padding:12px 14px 12px 18px;display:flex;flex-direction:column;gap:8px;min-width:0;opacity:1"><span aria-hidden="true" style="position:absolute;top:8px;bottom:8px;left:6px;width:3px;border-radius:2px;background:oklch(0.72 0.15 70)"></span><header style="display:flex;align-items:flex-start;gap:8px;min-width:0"><button type="button" aria-pressed="true" aria-label="Unpin card" style="width:22px;height:22px;flex-shrink:0;border-radius:6px;border:1px solid oklch(0.8 0.13 80);background:oklch(0.4 0.13 80);color:oklch(0.95 0.1 80);font-size:12px;line-height:1;cursor:pointer;display:inline-flex;align-items:center;justify-content:center">★</button><h3 style="margin:0;flex:1;font-size:14px;font-weight:600;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">External-adapter plugin: spec review</h3><span data-briefs-state-badge="waiting-user" style="flex-shrink:0;padding:2px 8px;font-size:11px;font-weight:600;border-radius:999px;background:oklch(0.27 0.07 70);color:oklch(0.85 0.11 70);white-space:nowrap">Waiting on you</span></header><div style="display:flex;align-items:center;flex-wrap:wrap;gap:8px;font-size:11px;color:var(--muted-foreground, oklch(0.708 0 0));min-width:0"><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0))">PAP-9020</span><span>2 issues</span><span>· 14h ago</span></div><p data-briefs-summary="true" style="margin:0;font-size:13px;line-height:1.45;color:var(--foreground, oklch(0.985 0 0));overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3">External-adapter sandbox spec is waiting on your reply about credential scoping before publish.</p><ul data-briefs-source-rows="true" style="list-style:none;margin:0;padding:0;display:flex;flex-direction:column"><li style="display:flex;align-items:center;gap:6px;padding:6px 0;border-top:none;font-size:12px;min-width:0"><span aria-hidden="true" style="color:var(--muted-foreground, oklch(0.708 0 0));width:12px;text-align:center">?</span><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0));font-size:11px">PAP-9020</span><a href="/PAP/issues/PAP-9020#interaction-1" title="Confirm credential scoping" style="flex:1;color:var(--foreground, oklch(0.985 0 0));text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0">Confirm credential scoping</a><span data-briefs-row-tag="warning" style="flex-shrink:0;font-size:10px;padding:1px 6px;border-radius:999px;background:oklch(0.27 0.07 70);color:oklch(0.85 0.11 70);white-space:nowrap">asked you</span></li><li style="display:flex;align-items:center;gap:6px;padding:6px 0;border-top:1px dashed var(--border, oklch(0.269 0 0));font-size:12px;min-width:0"><span aria-hidden="true" style="color:var(--muted-foreground, oklch(0.708 0 0));width:12px;text-align:center">■</span><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0));font-size:11px">PAP-9001</span><a href="/PAP/issues/PAP-9963" title="Adapter spec draft" style="flex:1;color:var(--foreground, oklch(0.985 0 0));text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0">Adapter spec draft</a><span data-briefs-row-tag="violet" style="flex-shrink:0;font-size:10px;padding:1px 6px;border-radius:999px;background:oklch(0.27 0.07 305);color:oklch(0.84 0.11 305);white-space:nowrap">in_review</span></li></ul><footer style="display:flex;align-items:center;gap:8px;font-size:11px;color:var(--muted-foreground, oklch(0.708 0 0));padding-top:4px;margin-top:auto"><span aria-hidden="true"></span><span style="flex:1"></span><span>14h ago</span><a href="/PAP/issues/PAP-9963" style="color:var(--foreground, oklch(0.985 0 0));text-decoration:none;font-size:11px">Open tree →</a></footer></article><article data-briefs-card="true" data-state="error" data-summary-status="ok" data-pinned="false" style="position:relative;background:var(--card, oklch(0.205 0 0));border:1px solid var(--border, oklch(0.269 0 0));border-radius:10px;padding:12px 14px 12px 18px;display:flex;flex-direction:column;gap:8px;min-width:0;opacity:1"><span aria-hidden="true" style="position:absolute;top:8px;bottom:8px;left:6px;width:3px;border-radius:2px;background:oklch(0.62 0.21 25)"></span><header style="display:flex;align-items:flex-start;gap:8px;min-width:0"><button type="button" aria-pressed="false" aria-label="Pin card" style="width:22px;height:22px;flex-shrink:0;border-radius:6px;border:1px solid var(--border, oklch(0.269 0 0));background:transparent;color:var(--muted-foreground, oklch(0.708 0 0));font-size:12px;line-height:1;cursor:pointer;display:inline-flex;align-items:center;justify-content:cenLine truncated
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-briefs-section="live" style="display: flex; flex-direction: column; gap: 12px; margin-bottom: 24px;">
|
||||
<header style="display: flex; align-items: center; gap: 10px;">
|
||||
<h2 style="margin: 0; font-size: 12px; letter-spacing: 0.6px; color: var(--muted-foreground); text-transform: uppercase; font-weight: 600;">Live and in review</h2>
|
||||
<span style="font-size: 12px; color: var(--muted-foreground);">3</span>
|
||||
<span style="flex: 1; border-bottom: 1px dashed var(--border);"></span>
|
||||
</header>
|
||||
<div data-briefs-grid style="display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 360px), 1fr)); gap: 12px;">
|
||||
<article data-briefs-card="true" data-state="live" data-summary-status="ok" data-pinned="true" style="position:relative;background:var(--card, oklch(0.205 0 0));border:1px solid var(--border, oklch(0.269 0 0));border-radius:10px;padding:12px 14px 12px 18px;display:flex;flex-direction:column;gap:8px;min-width:0;opacity:1"><span aria-hidden="true" style="position:absolute;top:8px;bottom:8px;left:6px;width:3px;border-radius:2px;background:oklch(0.7 0.13 200)"></span><header style="display:flex;align-items:flex-start;gap:8px;min-width:0"><button type="button" aria-pressed="true" aria-label="Unpin card" style="width:22px;height:22px;flex-shrink:0;border-radius:6px;border:1px solid oklch(0.8 0.13 80);background:oklch(0.4 0.13 80);color:oklch(0.95 0.1 80);font-size:12px;line-height:1;cursor:pointer;display:inline-flex;align-items:center;justify-content:center">★</button><h3 style="margin:0;flex:1;font-size:14px;font-weight:600;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">Briefs plugin planning</h3><span data-briefs-state-badge="live" style="flex-shrink:0;padding:2px 8px;font-size:11px;font-weight:600;border-radius:999px;background:oklch(0.27 0.06 200);color:oklch(0.84 0.11 200);white-space:nowrap">Live</span></header><div style="display:flex;align-items:center;flex-wrap:wrap;gap:8px;font-size:11px;color:var(--muted-foreground, oklch(0.708 0 0));min-width:0"><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0))">PAP-9963</span><span>2 issues</span><span>· in 7h</span></div><p data-briefs-summary="true" style="margin:0;font-size:13px;line-height:1.45;color:var(--foreground, oklch(0.985 0 0));overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3">Phase 5 page UI in flight on PAP-9963; deterministic data is done and routines are paused for board approval.</p><ul data-briefs-source-rows="true" style="list-style:none;margin:0;padding:0;display:flex;flex-direction:column"><li style="display:flex;align-items:center;gap:6px;padding:6px 0;border-top:none;font-size:12px;min-width:0"><span aria-hidden="true" style="color:var(--muted-foreground, oklch(0.708 0 0));width:12px;text-align:center">■</span><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0));font-size:11px">PAP-9963</span><a href="/PAP/issues/PAP-9963" title="Wire briefing page UI" style="flex:1;color:var(--foreground, oklch(0.985 0 0));text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0">Wire briefing page UI</a><span data-briefs-row-tag="cyan" style="flex-shrink:0;font-size:10px;padding:1px 6px;border-radius:999px;background:oklch(0.27 0.06 200);color:oklch(0.84 0.11 200);white-space:nowrap">in_progress</span></li><li style="display:flex;align-items:center;gap:6px;padding:6px 0;border-top:1px dashed var(--border, oklch(0.269 0 0));font-size:12px;min-width:0"><span aria-hidden="true" style="color:var(--muted-foreground, oklch(0.708 0 0));width:12px;text-align:center">■</span><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0));font-size:11px">PAP-9961</span><a href="/PAP/issues/PAP-9963" title="Deterministic card service" style="flex:1;color:var(--foreground, oklch(0.985 0 0));text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0">Deterministic card service</a><span data-briefs-row-tag="green" style="flex-shrink:0;font-size:10px;padding:1px 6px;border-radius:999px;background:oklch(0.27 0.06 145);color:oklch(0.84 0.1 145);white-space:nowrap">done</span></li><li style="display:flex;align-items:center;gap:6px;padding:6px 0;border-top:1px dashed var(--border, oklch(0.269 0 0));font-size:12px;min-width:0"><span aria-hidden="true" style="color:var(--muted-foreground, oklch(0.708 0 0));width:12px;text-align:center">■</span><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0));font-size:11px">PAP-9962</span><a href="/PAP/issues/PAP-9963" title="Managed agent + routines" style="flex:1;color:var(--foreground, oklch(0.985 0 0));text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0">Managed agent + routines</a><span data-briefs-row-tag="muted" style="flex-shrink:0;font-size:10px;padding:1px 6px;border-radius:999px;background:oklch(0.25 0 0);color:oklch(0.75 0 0);white-space:nowrap">paused</span></li></ul><footer style="display:flex;align-items:center;gap:8px;font-size:11px;color:var(--muted-foreground, oklch(0.708 0 0));padding-top:4px;margin-top:auto"><span>+3 more in tree</span><span style="flex:1"></span><span>in 7h</span><a href="/PAP/issues/PAP-9963" style="color:var(--foreground, oklch(0.985 0 0));text-decoration:none;font-size:11px">Open tree →</a></footer></article><article data-briefs-card="true" data-state="live" data-summary-status="fallback" data-pinned="faLine truncated
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-briefs-section="settled" style="display: flex; flex-direction: column; gap: 12px; margin-bottom: 24px;">
|
||||
<header style="display: flex; align-items: center; gap: 10px;">
|
||||
<h2 style="margin: 0; font-size: 12px; letter-spacing: 0.6px; color: var(--muted-foreground); text-transform: uppercase; font-weight: 600;">Recently done & stale</h2>
|
||||
<span style="font-size: 12px; color: var(--muted-foreground);">2</span>
|
||||
<span style="flex: 1; border-bottom: 1px dashed var(--border);"></span>
|
||||
</header>
|
||||
<div data-briefs-grid style="display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 360px), 1fr)); gap: 12px;">
|
||||
<article data-briefs-card="true" data-state="done" data-summary-status="ok" data-pinned="false" style="position:relative;background:var(--card, oklch(0.205 0 0));border:1px solid var(--border, oklch(0.269 0 0));border-radius:10px;padding:12px 14px 12px 18px;display:flex;flex-direction:column;gap:8px;min-width:0;opacity:0.78"><span aria-hidden="true" style="position:absolute;top:8px;bottom:8px;left:6px;width:3px;border-radius:2px;background:oklch(0.65 0.16 145)"></span><header style="display:flex;align-items:flex-start;gap:8px;min-width:0"><button type="button" aria-pressed="false" aria-label="Pin card" style="width:22px;height:22px;flex-shrink:0;border-radius:6px;border:1px solid var(--border, oklch(0.269 0 0));background:transparent;color:var(--muted-foreground, oklch(0.708 0 0));font-size:12px;line-height:1;cursor:pointer;display:inline-flex;align-items:center;justify-content:center">☆</button><h3 style="margin:0;flex:1;font-size:14px;font-weight:600;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">Sidebar plugin slot hardening</h3><span data-briefs-state-badge="done" style="flex-shrink:0;padding:2px 8px;font-size:11px;font-weight:600;border-radius:999px;background:oklch(0.27 0.06 145);color:oklch(0.84 0.1 145);white-space:nowrap">Recently done</span></header><div style="display:flex;align-items:center;flex-wrap:wrap;gap:8px;font-size:11px;color:var(--muted-foreground, oklch(0.708 0 0));min-width:0"><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0))">PAP-8990</span><span>2 issues</span><span>· 5h ago</span></div><p data-briefs-summary="true" style="margin:0;font-size:13px;line-height:1.45;color:var(--foreground, oklch(0.985 0 0));overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3">Plugin sidebar slot order locked in; rolling tests green.</p><ul data-briefs-source-rows="true" style="list-style:none;margin:0;padding:0;display:flex;flex-direction:column"><li style="display:flex;align-items:center;gap:6px;padding:6px 0;border-top:none;font-size:12px;min-width:0"><span aria-hidden="true" style="color:var(--muted-foreground, oklch(0.708 0 0));width:12px;text-align:center">■</span><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0));font-size:11px">PAP-8990</span><a href="/PAP/issues/PAP-9963" title="Sidebar plugin slot hardening" style="flex:1;color:var(--foreground, oklch(0.985 0 0));text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0">Sidebar plugin slot hardening</a><span data-briefs-row-tag="green" style="flex-shrink:0;font-size:10px;padding:1px 6px;border-radius:999px;background:oklch(0.27 0.06 145);color:oklch(0.84 0.1 145);white-space:nowrap">done</span></li></ul><footer style="display:flex;align-items:center;gap:8px;font-size:11px;color:var(--muted-foreground, oklch(0.708 0 0));padding-top:4px;margin-top:auto"><span aria-hidden="true"></span><span style="flex:1"></span><span>5h ago</span><a href="/PAP/issues/PAP-9963" style="color:var(--foreground, oklch(0.985 0 0));text-decoration:none;font-size:11px">Open tree →</a></footer></article><article data-briefs-card="true" data-state="stale" data-summary-status="ok" data-pinned="false" style="position:relative;background:var(--card, oklch(0.205 0 0));border:1px solid var(--border, oklch(0.269 0 0));border-radius:10px;padding:12px 14px 12px 18px;display:flex;flex-direction:column;gap:8px;min-width:0;opacity:0.78"><span aria-hidden="true" style="position:absolute;top:8px;bottom:8px;left:6px;width:3px;border-radius:2px;background:oklch(0.5 0 0)"></span><header style="display:flex;align-items:flex-start;gap:8px;min-width:0"><button type="button" aria-pressed="false" aria-label="Pin card" style="width:22px;height:22px;flex-shrink:0;border-radius:6px;border:1px solid var(--border, oklch(0.269 0 0));background:transparent;color:var(--muted-foreground, oklch(0.708 0 0));font-size:12px;line-height:1;cursor:pointer;display:inline-flex;align-items:center;justify-content:center">☆</button><h3 style="margin:0;flex:1;font-size:14px;font-weight:600;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">GA migration spike</h3><span data-briefs-state-badge="stale" style="flex-shrink:0;padding:2px 8px;font-size:11px;font-weight:600;border-radius:999px;background:oklch(0.25 0 0);color:oklch(0.75 0 0);white-space:nowrap">Stale</span></header><div style="display:flex;align-items:center;flex-wrap:wrap;gap:8px;font-size:11px;color:var(--muted-foreground, oklch(0.708 0 0));min-width:0"><span style="font-family:ui-monospace, SFMono-Regular, monospace;color:var(--foreground, oklch(0.985 0 0))">PAP-7000</span><span>2 issues</span><span>· 2w ago</span></div><p data-briefs-summary="true" style="margin:0;font-size:13px;line-height:1.45;color:var(--foreground, oklch(0.985 0 0));overflow:hidden;display:-webkit-box;-webkit-box-orient:vertiLine truncated
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 110 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 125 KiB |
@@ -60,6 +60,7 @@
|
||||
"@types/react": "^19.0.8",
|
||||
"@types/react-dom": "^19.0.3",
|
||||
"esbuild": "^0.27.3",
|
||||
"playwright": "^1.58.2",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"typescript": "^5.7.3",
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* Renders the Briefing page UI with a curated fixture set and writes
|
||||
* desktop + mobile + empty-state PNG snapshots into
|
||||
* `docs/pr-screenshots/pap-9963/`.
|
||||
*
|
||||
* Skipped by default — set `BRIEFS_CAPTURE_SCREENSHOTS=1` to opt in.
|
||||
*
|
||||
* The flag avoids dragging Playwright into the regular fast unit-test run while
|
||||
* still letting CI / agents capture deterministic fixtures from the same view
|
||||
* components the host renders.
|
||||
*/
|
||||
import path from "node:path";
|
||||
import fs from "node:fs/promises";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
import { afterAll, describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("@paperclipai/plugin-sdk/ui", () => {
|
||||
return {
|
||||
useHostNavigation: () => ({
|
||||
resolveHref: (to: string) => to,
|
||||
navigate: () => {},
|
||||
linkProps: (to: string) => ({ href: to, onClick: () => {} }),
|
||||
}),
|
||||
usePluginAction: () => vi.fn(async () => ({ ok: true })),
|
||||
usePluginData: () => ({ data: null, loading: false, error: null, refresh: () => {} }),
|
||||
usePluginToast: () => vi.fn(),
|
||||
useHostLocation: () => ({ pathname: "/PAP/briefs", search: "", hash: "" }),
|
||||
usePluginStream: () => ({ events: [], lastEvent: null, connecting: false, connected: false, error: null, close: () => {} }),
|
||||
};
|
||||
});
|
||||
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import { BriefCardView } from "../../src/ui/app.js";
|
||||
import { groupCardsIntoSections } from "../../src/ui/view-model.js";
|
||||
import { gallery } from "./fixtures.js";
|
||||
|
||||
const ENABLED = process.env.BRIEFS_CAPTURE_SCREENSHOTS === "1";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const repoRoot = path.resolve(__dirname, "../../../../..");
|
||||
const outDir = path.resolve(repoRoot, "docs/pr-screenshots/pap-9963");
|
||||
|
||||
const desktopWidth = 1440;
|
||||
const mobileWidth = 390;
|
||||
|
||||
function staticPage({ cards, viewportWidth }: { cards: ReturnType<typeof gallery>; viewportWidth: number }) {
|
||||
const isMobile = viewportWidth < 700;
|
||||
const sections = groupCardsIntoSections(cards);
|
||||
const visibleSections = isMobile
|
||||
? sections.filter((section) => section.cards.length > 0).slice(0, 1).concat(sections.slice(1))
|
||||
: sections;
|
||||
const active = cards.filter((c) => !c.hidden);
|
||||
const pinned = active.filter((c) => c.pinned).length;
|
||||
return `<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Briefing — ${isMobile ? "mobile" : "desktop"}</title>
|
||||
<style>
|
||||
:root {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--border: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--primary: oklch(0.985 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
}
|
||||
html, body { background: var(--background); color: var(--foreground); margin: 0; min-height: 100vh; }
|
||||
body { font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; }
|
||||
a { color: inherit; }
|
||||
@media (max-width: 700px) {
|
||||
[data-briefs-mobile-tabs] { display: flex !important; }
|
||||
[data-briefs-section] > header { display: none; }
|
||||
[data-briefs-section][data-mobile-hidden="true"] { display: none; }
|
||||
[data-briefs-grid] { grid-template-columns: 1fr !important; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; color: var(--foreground); padding: 20px clamp(12px, 4vw, 32px); max-width: 1280px; margin: 0 auto; min-height: 100vh;">
|
||||
<header style="display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px; margin-bottom: 6px;">
|
||||
<h1 style="margin: 0; font-size: 22px; font-weight: 600;">Briefing</h1>
|
||||
<div style="flex: 1; min-width: 0; font-size: 12px; color: var(--muted-foreground);">${active.length} active · ${pinned} pinned · refreshed just now</div>
|
||||
<div style="display: flex; gap: 6px; align-items: center;">
|
||||
<button style="padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border); background: var(--card); color: var(--foreground); font-size: 12px;">Preferences</button>
|
||||
<button style="padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border); background: var(--card); color: var(--foreground); font-size: 12px;">Refresh</button>
|
||||
</div>
|
||||
</header>
|
||||
<p style="margin: 0; margin-bottom: 18px; font-size: 13px; color: var(--muted-foreground);">Durable cards for areas of work that involve you. Pin the ones you always want to see.</p>
|
||||
${active.length === 0
|
||||
? `<div data-briefs-empty style="padding: 40px 24px; border: 1px dashed var(--border); border-radius: 10px; text-align: center; color: var(--muted-foreground);">
|
||||
<div style="font-size: 15px; color: var(--foreground); font-weight: 600; margin-bottom: 4px;">No briefs yet</div>
|
||||
<div style="font-size: 13px;">Cards appear here once the Briefing Analyst picks up recent work. Pinned cards never expire.</div>
|
||||
</div>`
|
||||
: visibleSections.map((section) => section.cards.length === 0 ? "" : `
|
||||
<section data-briefs-section="${section.key}" style="display: flex; flex-direction: column; gap: 12px; margin-bottom: 24px;">
|
||||
<header style="display: flex; align-items: center; gap: 10px;">
|
||||
<h2 style="margin: 0; font-size: 12px; letter-spacing: 0.6px; color: var(--muted-foreground); text-transform: uppercase; font-weight: 600;">${section.label}</h2>
|
||||
<span style="font-size: 12px; color: var(--muted-foreground);">${section.cards.length}</span>
|
||||
<span style="flex: 1; border-bottom: 1px dashed var(--border);"></span>
|
||||
</header>
|
||||
<div data-briefs-grid style="display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 360px), 1fr)); gap: 12px;">
|
||||
${section.cards.map((card) => renderToStaticMarkup(<BriefCardView card={card} onChanged={() => {}} />)).join("")}
|
||||
</div>
|
||||
</section>
|
||||
`).join("")}
|
||||
</div>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
describe.skipIf(!ENABLED)("Briefs screenshots", () => {
|
||||
let browser: import("playwright").Browser | null = null;
|
||||
|
||||
async function getBrowser() {
|
||||
if (browser) return browser;
|
||||
const playwright = await import("playwright");
|
||||
browser = await playwright.chromium.launch({ headless: true });
|
||||
return browser;
|
||||
}
|
||||
|
||||
afterAll(async () => {
|
||||
if (browser) await browser.close();
|
||||
});
|
||||
|
||||
it("captures desktop, mobile, and empty-state Briefing snapshots", async () => {
|
||||
await fs.mkdir(outDir, { recursive: true });
|
||||
const cards = gallery();
|
||||
|
||||
const desktopHtml = staticPage({ cards, viewportWidth: desktopWidth });
|
||||
const mobileHtml = staticPage({ cards, viewportWidth: mobileWidth });
|
||||
const emptyHtml = staticPage({ cards: [], viewportWidth: desktopWidth });
|
||||
|
||||
const desktopFile = path.join(outDir, "_briefing-desktop.html");
|
||||
const mobileFile = path.join(outDir, "_briefing-mobile.html");
|
||||
const emptyFile = path.join(outDir, "_briefing-empty.html");
|
||||
await fs.writeFile(desktopFile, desktopHtml);
|
||||
await fs.writeFile(mobileFile, mobileHtml);
|
||||
await fs.writeFile(emptyFile, emptyHtml);
|
||||
|
||||
const browser = await getBrowser();
|
||||
|
||||
await snap(browser, desktopFile, { width: desktopWidth, height: 900 }, path.join(outDir, "briefing-desktop.png"));
|
||||
await snap(browser, mobileFile, { width: mobileWidth, height: 844 }, path.join(outDir, "briefing-mobile.png"));
|
||||
await snap(browser, emptyFile, { width: desktopWidth, height: 700 }, path.join(outDir, "briefing-empty.png"));
|
||||
|
||||
for (const name of ["briefing-desktop.png", "briefing-mobile.png", "briefing-empty.png"]) {
|
||||
const stats = await fs.stat(path.join(outDir, name));
|
||||
expect(stats.size).toBeGreaterThan(1024);
|
||||
}
|
||||
}, 60_000);
|
||||
});
|
||||
|
||||
async function snap(browser: import("playwright").Browser, htmlPath: string, viewport: { width: number; height: number }, out: string): Promise<void> {
|
||||
const context = await browser.newContext({ viewport, colorScheme: "dark" });
|
||||
const page = await context.newPage();
|
||||
await page.goto(pathToFileURL(htmlPath).href, { waitUntil: "domcontentloaded" });
|
||||
await page.waitForTimeout(150);
|
||||
await page.screenshot({ path: out, fullPage: true });
|
||||
await context.close();
|
||||
}
|
||||
Generated
+34
@@ -513,6 +513,40 @@ importers:
|
||||
specifier: ^3.2.4
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0)
|
||||
|
||||
packages/plugins/plugin-briefs:
|
||||
dependencies:
|
||||
'@paperclipai/plugin-sdk':
|
||||
specifier: workspace:*
|
||||
version: link:../sdk
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^24.6.0
|
||||
version: 24.12.0
|
||||
'@types/react':
|
||||
specifier: ^19.0.8
|
||||
version: 19.2.14
|
||||
'@types/react-dom':
|
||||
specifier: ^19.0.3
|
||||
version: 19.2.3(@types/react@19.2.14)
|
||||
esbuild:
|
||||
specifier: ^0.27.3
|
||||
version: 0.27.3
|
||||
playwright:
|
||||
specifier: ^1.58.2
|
||||
version: 1.58.2
|
||||
react:
|
||||
specifier: ^19.0.0
|
||||
version: 19.2.4
|
||||
react-dom:
|
||||
specifier: ^19.0.0
|
||||
version: 19.2.4(react@19.2.4)
|
||||
typescript:
|
||||
specifier: ^5.7.3
|
||||
version: 5.9.3
|
||||
vitest:
|
||||
specifier: ^3.0.5
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0)
|
||||
|
||||
packages/plugins/plugin-llm-wiki:
|
||||
dependencies:
|
||||
react:
|
||||
|
||||
Reference in new issue
Block a user