@capagents/forgeqe 2.4.0 → 2.4.2
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/HOW_TO_USE.md +28 -7
- package/README.md +4 -2
- package/dist/forgeqe.js +1041 -1007
- package/dist/web/assets/ApiTests-B-0nk720.js +2 -0
- package/dist/web/assets/Chat-Be6J6POH.js +3 -0
- package/dist/web/assets/{Data-Qolfhsd7.js → Data-BtcD7_T2.js} +4 -4
- package/dist/web/assets/Features-DHIGL43K.js +1 -0
- package/dist/web/assets/Jobs-C-cEyrrH.js +3 -0
- package/dist/web/assets/Quality-C6hKykoO.js +1 -0
- package/dist/web/assets/Record-B5395d_b.js +1 -0
- package/dist/web/assets/Regression-BS5g4T3v.js +2 -0
- package/dist/web/assets/Reports-CHqfhNR8.js +2 -0
- package/dist/web/assets/Response-P2oku3_B.js +8 -0
- package/dist/web/assets/Runs-DPmd5QPL.js +2 -0
- package/dist/web/assets/chevron-right-Cwpv0kJi.js +1 -0
- package/dist/web/assets/circle-dashed-COka5oNi.js +1 -0
- package/dist/web/assets/external-link-B9T-hxHX.js +1 -0
- package/dist/web/assets/file-chart-column-increasing-Ch7H145u.js +1 -0
- package/dist/web/assets/filters-C5e4Y0To.js +1 -0
- package/dist/web/assets/index-BI_5kNwE.css +2 -0
- package/dist/web/assets/index-KUNDUjo0.js +26 -0
- package/dist/web/assets/{markdown-GKy06efH.js → markdown-DclrGj2e.js} +7 -7
- package/dist/web/assets/rotate-ccw-BizUsxuR.js +1 -0
- package/dist/web/assets/sticky-note-CFIxnO7K.js +1 -0
- package/dist/web/assets/terminal-BnhGgg6d.js +1 -0
- package/dist/web/assets/trash-CtWVl2Xk.js +1 -0
- package/dist/web/assets/trending-up-BnzWZEhg.js +1 -0
- package/dist/web/assets/when-CHnpLdYw.js +1 -0
- package/dist/web/index.html +2 -3
- package/docs/ARCHITECTURE.md +5 -2
- package/docs/user-guide.html +32 -13
- package/evals/prompts/brain/browser-test-intent.yaml +30 -0
- package/forgeqe.example.yaml +4 -3
- package/package.json +1 -1
- package/prompts/brain.md +4 -1
- package/py/forgeqe_engine/flowmatch.py +72 -1
- package/py/forgeqe_engine/logins.py +17 -5
- package/py/forgeqe_engine/loops.py +26 -3
- package/py/forgeqe_engine/memory.py +11 -0
- package/py/forgeqe_engine/service.py +29 -1
- package/dist/web/assets/ApiTests-CSSed9hU.js +0 -2
- package/dist/web/assets/Chat-CFfiv7wI.js +0 -2
- package/dist/web/assets/Features-BM48eq6x.js +0 -1
- package/dist/web/assets/Jobs-BjVNQUaK.js +0 -3
- package/dist/web/assets/Quality-F0sKErYg.js +0 -1
- package/dist/web/assets/Record-CkwV9gN8.js +0 -1
- package/dist/web/assets/Regression-DKaNfFu1.js +0 -2
- package/dist/web/assets/Reports-o_82URX6.js +0 -3
- package/dist/web/assets/Response-YL9AIB8R.js +0 -8
- package/dist/web/assets/Runs-Dp-DzyW1.js +0 -2
- package/dist/web/assets/circle-dashed-DbpKfamm.js +0 -1
- package/dist/web/assets/external-link-pvVHJFAZ.js +0 -1
- package/dist/web/assets/file-chart-column-increasing-b-Yh6rmz.js +0 -1
- package/dist/web/assets/index-Bkx3fVbr.js +0 -21
- package/dist/web/assets/index-DSEYuBEd.css +0 -2
- package/dist/web/assets/rotate-ccw-BL6YOAPy.js +0 -1
- package/dist/web/assets/search-YLg3uzAx.js +0 -1
- package/dist/web/assets/status-CL_clA0J.js +0 -3
- package/dist/web/assets/sticky-note-DIQ2FWMw.js +0 -1
- package/dist/web/assets/terminal-BdTFlGZR.js +0 -1
- package/dist/web/assets/timer-NLZ0XmVl.js +0 -1
- package/dist/web/assets/trash-DIUJbRpw.js +0 -1
|
@@ -1109,13 +1109,24 @@ def flow_step(action: str, params: Dict[str, Any], target: Optional[Dict[str, An
|
|
|
1109
1109
|
return step
|
|
1110
1110
|
|
|
1111
1111
|
|
|
1112
|
+
def same_day(step: Dict[str, Any], entry: Dict[str, Any]) -> bool:
|
|
1113
|
+
"""
|
|
1114
|
+
False when the step's recorded label names a calendar day and the element found names another (or none). Page
|
|
1115
|
+
memory keeps one entry per position in a date grid, so its locators follow whatever day that cell shows now.
|
|
1116
|
+
"""
|
|
1117
|
+
recorded = flowmatch.dates(step.get("label"))
|
|
1118
|
+
return not recorded or flowmatch.dates(element_label(entry["facts"])) == recorded
|
|
1119
|
+
|
|
1120
|
+
|
|
1112
1121
|
def resolve_step(
|
|
1113
1122
|
step: Dict[str, Any], snapshot: Dict[int, Dict[str, Any]], store: Optional[MemoryStore] = None
|
|
1114
1123
|
) -> Tuple[Optional[int], Optional[LocKey], List[LocKey]]:
|
|
1115
1124
|
"""
|
|
1116
1125
|
Find a recorded step's element in the current page snapshot (from MemoryStore.observe).
|
|
1117
1126
|
Tries locators from most to least confident; returns (index, locator used, locators that matched nothing).
|
|
1127
|
+
An element that names another day than the step recorded is never the step's (same_day).
|
|
1118
1128
|
"""
|
|
1129
|
+
snapshot = {i: e for i, e in snapshot.items() if same_day(step, e)}
|
|
1119
1130
|
locs = list(step.get("locators") or [])
|
|
1120
1131
|
if store and step.get("page") and step.get("element"):
|
|
1121
1132
|
entry = store.element(step["page"], step["element"])
|
|
@@ -253,6 +253,26 @@ _BLUEPRINT_STEP_EFFICIENCY = """
|
|
|
253
253
|
""".strip()
|
|
254
254
|
|
|
255
255
|
|
|
256
|
+
# The share of browser.timeout_seconds after which the agent is told to finish: past it, a step or two is left.
|
|
257
|
+
DEADLINE_SHARE = 0.8
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def deadline_note(state: Dict[str, Any], now: float, timeout: float) -> Optional[str]:
|
|
261
|
+
"""
|
|
262
|
+
Once, when the run is past DEADLINE_SHARE of its time: the message that tells the agent to finish now. A run that
|
|
263
|
+
already holds the answer was otherwise cut off at the timeout and failed with it (the Booking.com log).
|
|
264
|
+
"""
|
|
265
|
+
if state.get("warned") or timeout <= 0 or now - float(state.get("start") or now) < DEADLINE_SHARE * timeout:
|
|
266
|
+
return None
|
|
267
|
+
state["warned"] = True
|
|
268
|
+
left = max(0, round(timeout - (now - float(state["start"]))))
|
|
269
|
+
return (
|
|
270
|
+
f"ForgeQE: this run has about {left}s left before it is stopped, and a stopped run fails. Finish now: when what you "
|
|
271
|
+
"have already found answers the task, call done with it in this step. Otherwise call done with success false and "
|
|
272
|
+
"say what is still missing. Do not start anything new."
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
|
|
256
276
|
def _compose_agent_extend_system_message(extra: Optional[str], *, include_error_recovery: bool = True) -> Optional[str]:
|
|
257
277
|
"""Merge default recovery instructions with optional caller-provided system extension."""
|
|
258
278
|
conventions = prompt_text("conventions", CONVENTIONS)
|
|
@@ -1121,7 +1141,7 @@ async def run_browser_task(req: RunRequest, emit: Emit, cancel_event: asyncio.Ev
|
|
|
1121
1141
|
loop_break_state.setdefault("from", since + first)
|
|
1122
1142
|
if not loop_break_state.get("nudged") and loop_break_state.get("can_nudge"):
|
|
1123
1143
|
loop_break_state.update({"nudged": True, "since": len(collected_steps)})
|
|
1124
|
-
nudges.append(_loop_nudge_message(line=line, count=count, window=loop_window))
|
|
1144
|
+
nudges.append(_loop_nudge_message(line=line, count=count, window=loop_window, fingerprint=fp))
|
|
1125
1145
|
emit({"type": "log", "level": "warn", "text": f"Repeated {line or 'the same action'} {count} times: told the agent to try a different approach"})
|
|
1126
1146
|
return False
|
|
1127
1147
|
loop_break_state.update(
|
|
@@ -1488,6 +1508,9 @@ async def run_browser_task(req: RunRequest, emit: Emit, cancel_event: asyncio.Ev
|
|
|
1488
1508
|
if kept:
|
|
1489
1509
|
emit({"type": "memory", "text": f"Kept the {len(kept['steps'])} steps up to the expected result that did not hold (step {kept['failed_at']}); the next run of this task replays them and checks it again"})
|
|
1490
1510
|
|
|
1511
|
+
# When the run started, and whether the agent was told its time is nearly up (deadline_note).
|
|
1512
|
+
deadline: Dict[str, Any] = {"start": time.monotonic()}
|
|
1513
|
+
|
|
1491
1514
|
async def _on_step_end(agent_ref: Any) -> None:
|
|
1492
1515
|
try:
|
|
1493
1516
|
last = agent_ref.history.history[-1]
|
|
@@ -1549,6 +1572,10 @@ async def run_browser_task(req: RunRequest, emit: Emit, cancel_event: asyncio.Ev
|
|
|
1549
1572
|
pass
|
|
1550
1573
|
if overlay is not None:
|
|
1551
1574
|
await overlay.thinking()
|
|
1575
|
+
note = deadline_note(deadline, time.monotonic(), timeout_sec)
|
|
1576
|
+
if note and loop_break_state.get("can_nudge"):
|
|
1577
|
+
nudges.append(note)
|
|
1578
|
+
emit({"type": "log", "level": "warn", "text": f"{timeout_sec - (time.monotonic() - deadline['start']):.0f}s of the run's time left: told the agent to finish with what it has"})
|
|
1552
1579
|
|
|
1553
1580
|
# Remote grids stop taking input on a tab after a password form is submitted: see FreshTabAfterPassword.
|
|
1554
1581
|
fresh_tab = FreshTabAfterPassword() if browser_provider != "local" else None
|
|
@@ -1575,6 +1602,7 @@ async def run_browser_task(req: RunRequest, emit: Emit, cancel_event: asyncio.Ev
|
|
|
1575
1602
|
return await agent.run(on_step_start=_on_step_start, on_step_end=_on_step_end)
|
|
1576
1603
|
|
|
1577
1604
|
emit({"type": "log", "level": "info", "progress": True, "text": "Opening the browser…"})
|
|
1605
|
+
deadline["start"] = time.monotonic()
|
|
1578
1606
|
run_task = asyncio.create_task(_run_agent())
|
|
1579
1607
|
cancel_wait = asyncio.create_task(cancel_event.wait())
|
|
1580
1608
|
done, _pending = await asyncio.wait({run_task, cancel_wait}, return_when=asyncio.FIRST_COMPLETED, timeout=timeout_sec)
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{A as e,F as t,Ft as n,G as r,It as i,J as a,M as o,N as s,Q as c,U as l,W as u,Y as d,Zt as f,ct as p,en as m,et as h,f as g,j as _,jt as v,q as y,r as b,t as x}from"./status-CL_clA0J.js";import{a as S}from"./markdown-GKy06efH.js";import{t as C}from"./file-chart-column-increasing-b-Yh6rmz.js";import{o as w,p as T,s as E,x as D}from"./index-Bkx3fVbr.js";var O={name:`braces`,size:24,node:[[`path`,{d:`M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1`,key:`ezmyqa`}],[`path`,{d:`M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1`,key:`e1hn23`}]],aliases:[`curly-braces`]};O.node;var k=v(O),A={name:`link-2`,size:24,node:[[`path`,{d:`M9 17H7A5 5 0 0 1 7 7h2`,key:`8i5ue5`}],[`path`,{d:`M15 7h2a5 5 0 1 1 0 10h-2`,key:`1b9ql8`}],[`line`,{x1:`8`,x2:`16`,y1:`12`,y2:`12`,key:`1jonct`}]]};A.node;var j=v(A),M=m(f(),1),N=y(),P={passed:`pass`,failed:`fail`,error:`fail`,skipped:`neutral`},F={GET:`text-pass`,POST:`text-accent`,PUT:`text-warn`,PATCH:`text-warn`,DELETE:`text-fail`},I={run:`a browser run's calls`,openapi:`OpenAPI`,swagger:`Swagger`,wsdl:`WSDL`,graphql:`GraphQL`,proto:`.proto`,case:`a test case`,text:`a description`},L=e=>new Date(e).toLocaleString([],{month:`short`,day:`numeric`,hour:`numeric`,minute:`2-digit`}),R=e=>e?e.failed||e.error?`fail`:e.passed?`pass`:`neutral`:`neutral`;function z({job:e,onStop:t}){let n=(0,M.useRef)(null);(0,M.useEffect)(()=>{n.current&&(n.current.scrollTop=n.current.scrollHeight)},[e.lines.length]);let[i,a]={contracts:[`Making contracts`,`Made contracts`],run:[`Running the contracts`,`Ran the contracts`],scenarios:[`Making scenarios`,`Made scenarios`],"run-scenarios":[`Running the scenarios`,`Ran the scenarios`]}[e.kind]??[`Working`,`Done`],o=(e.kind===`run`||e.kind===`run-scenarios`)&&e.exitCode===1;return(0,N.jsxs)(`section`,{"aria-label":e.running?i:a,className:`flex flex-col gap-2 rounded-[14px] border border-line-soft p-4`,children:[(0,N.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,N.jsx)(`span`,{className:`flex-1 font-medium`,children:e.running?i:a}),(0,N.jsx)(x,{orb:e.kind===`contracts`?`handing`:e.kind===`scenarios`?`writing`:`working`,tone:e.running?`running`:e.exitCode===0?`pass`:o?`warn`:`fail`,children:e.running?`Working`:e.exitCode===0?`Done`:o?`Some failed`:`Did not finish`}),e.running&&(0,N.jsxs)(r,{onClick:t,className:`border-fail text-fail hover:bg-fail-soft`,children:[(0,N.jsx)(h,{size:14,fill:`currentColor`,"aria-hidden":`true`}),`Stop`]})]}),(0,N.jsx)(`code`,{className:`font-mono text-[12px] text-muted [overflow-wrap:anywhere]`,children:e.command}),(0,N.jsxs)(`details`,{open:e.running,children:[(0,N.jsxs)(`summary`,{className:`cursor-pointer text-sm text-muted`,children:[`Output (`,e.lines.length,` lines)`]}),(0,N.jsx)(`pre`,{ref:n,className:`mt-2 max-h-[300px] overflow-auto rounded-[10px] bg-surface-muted p-3 font-mono text-[12px] leading-relaxed whitespace-pre-wrap text-ink-2`,children:e.lines.join(`
|
|
2
|
-
`)||`Starting…`})]})]})}var B=({m:e})=>(0,N.jsx)(`span`,{className:a(`w-[58px] flex-none font-mono text-[12px] font-semibold`,F[e.toUpperCase()]||`text-ink-2`),children:e.toUpperCase()});function V({c:e,r:t}){let n=Array.isArray(e.expect.status)?e.expect.status.join(` or `):e.expect.status;return(0,N.jsxs)(`li`,{className:`flex flex-col gap-1 border-t border-line-soft py-2 text-sm first:border-t-0`,children:[(0,N.jsxs)(`span`,{className:`flex min-w-0 items-center gap-2`,children:[(0,N.jsx)(B,{m:e.method}),(0,N.jsx)(`span`,{className:`min-w-0 flex-1 font-mono text-[13px] [overflow-wrap:anywhere]`,children:e.protocol===`rest`?e.path:e.name}),e.writes&&(0,N.jsx)(x,{tone:`warn`,children:`Changes data`}),e.needsData?.length?(0,N.jsxs)(x,{tone:`neutral`,icon:!1,children:[`Needs real `,e.needsData.join(`, `)]}):null,t&&(0,N.jsx)(x,{tone:P[t.status],children:t.status===`skipped`?`Skipped`:t.httpStatus?`${t.httpStatus}`:t.status})]}),(0,N.jsx)(`span`,{className:`pl-[66px] text-[12px] text-muted`,children:[n?`expects ${n}`:``,e.expect.contentType,e.expect.keyFields?.length?`${e.expect.keyFields.length} key field${e.expect.keyFields.length===1?``:`s`}`:``,e.auth?`${e.auth.type} auth`:``].filter(Boolean).join(` · `)}),t&&t.status!==`passed`&&(t.reason||t.checks.some(e=>!e.ok))&&(0,N.jsx)(`span`,{className:a(`pl-[66px] text-[12px] [overflow-wrap:anywhere]`,t.status===`skipped`?`text-muted`:`text-fail`),children:[t.reason,...t.checks.filter(e=>!e.ok).map(e=>`${e.name}${e.detail?`: ${e.detail}`:``}`)].filter(Boolean).join(` · `)})]})}function H({s:e,r:t}){let[n,r]=(0,M.useState)(!1);return(0,N.jsxs)(`li`,{className:`border-t border-line-soft first:border-t-0`,children:[(0,N.jsxs)(`button`,{type:`button`,"aria-expanded":n,onClick:()=>r(!n),className:`flex min-h-[52px] w-full cursor-pointer items-center gap-3 border-0 bg-transparent px-1 py-2 text-left text-ink hover:bg-surface-muted`,children:[(0,N.jsx)(w,{size:16,"aria-hidden":`true`,className:`flex-none text-muted`}),(0,N.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,N.jsx)(`span`,{className:`text-sm [overflow-wrap:anywhere]`,children:e.name}),(0,N.jsxs)(`span`,{className:`truncate text-[12px] text-muted`,children:[e.kind,` · `,e.steps.length,` step`,e.steps.length===1?``:`s`,e.cleanup?.length?` · ${e.cleanup.length} cleanup`:``,e.caseId?` · ${e.caseId}`:``]})]}),e.writes&&(0,N.jsx)(x,{tone:`warn`,children:`Changes data`}),t&&(0,N.jsx)(x,{tone:P[t.status],children:t.status.charAt(0).toUpperCase()+t.status.slice(1)})]}),n&&(0,N.jsxs)(`ol`,{className:`m-0 mb-3 ml-8 flex list-none flex-col gap-1 border-l border-line pl-3 text-sm`,children:[e.steps.map((e,n)=>{let r=t?.steps[n];return(0,N.jsxs)(`li`,{className:`flex flex-col`,children:[(0,N.jsxs)(`span`,{className:`flex min-w-0 items-center gap-2`,children:[(0,N.jsx)(B,{m:e.method}),(0,N.jsx)(`span`,{className:`min-w-0 flex-1 font-mono text-[12px] [overflow-wrap:anywhere]`,children:e.path}),r&&(0,N.jsx)(x,{tone:P[r.status],children:r.httpStatus?String(r.httpStatus):r.status})]}),(0,N.jsxs)(`span`,{className:`pl-[66px] text-[12px] text-muted`,children:[e.name,e.save&&Object.keys(e.save).length?` · keeps ${Object.keys(e.save).join(`, `)}`:``,e.noAuth?` · without credentials`:``]}),r&&r.checks.some(e=>!e.ok)&&(0,N.jsx)(`span`,{className:`pl-[66px] text-[12px] text-fail`,children:r.checks.filter(e=>!e.ok).map(e=>`${e.name}${e.detail?`: ${e.detail}`:``}`).join(` · `)})]},n)}),e.cleanup?.map((e,t)=>(0,N.jsxs)(`li`,{className:`flex items-center gap-2 text-muted`,children:[(0,N.jsx)(B,{m:e.method}),(0,N.jsx)(`span`,{className:`font-mono text-[12px]`,children:e.path}),(0,N.jsx)(`span`,{className:`text-[12px]`,children:`cleanup`})]},`c${t}`)),t?.reason&&(0,N.jsx)(`li`,{className:`text-[12px] text-fail [overflow-wrap:anywhere]`,children:t.reason})]})]})}function U({what:t,writes:n,defaultUrl:i,onRun:a,onCancel:s,needsData:c}){let[l,u]=(0,M.useState)(i),[d,f]=(0,M.useState)(!1),[m,h]=(0,M.useState)(!1);return(0,N.jsxs)(`div`,{className:`flex flex-col gap-3 rounded-[12px] border border-accent-line bg-accent-soft p-4`,children:[(0,N.jsx)(`span`,{className:`font-medium`,children:t}),(0,N.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm`,children:[`Against`,(0,N.jsx)(o,{value:l,spellCheck:!1,placeholder:`https://qa.example.com/api`,onChange:e=>u(e.target.value),className:`max-w-[480px] bg-bg`})]}),n>0&&(0,N.jsxs)(`span`,{className:`flex items-start gap-3 text-sm`,children:[(0,N.jsx)(e,{checked:d,onChange:f,label:`Also run the calls that change data`}),(0,N.jsxs)(`span`,{className:`flex flex-col`,children:[`Also run the `,n,` call`,n===1?``:`s`,` that change data`,(0,N.jsx)(`span`,{className:`text-[13px] text-warn`,children:`Only on a test environment: they create, change or delete data there.`})]})]}),c?(0,N.jsxs)(`span`,{className:`flex items-center gap-3 text-sm`,children:[(0,N.jsx)(e,{checked:m,onChange:h,label:`Also run calls with made-up values`}),`Also run the `,c,` call`,c===1?``:`s`,` whose values were made up`]}):null,(0,N.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,N.jsxs)(r,{variant:`primary`,onClick:()=>a({...l.trim()?{baseUrl:l.trim()}:{},allowWrites:d,includeNeedsData:m}),children:[(0,N.jsx)(p,{size:16,"aria-hidden":`true`}),`Run`]}),(0,N.jsx)(r,{onClick:s,children:`Don't run`})]})]})}function W({open:t,onClose:n,view:i,set:c,onMake:l,busy:u}){let[f,p]=(0,M.useState)(`spec`),[m,h]=(0,M.useState)(``),[g,v]=(0,M.useState)(``),[y,b]=(0,M.useState)(``),[x,S]=(0,M.useState)(!0);(0,M.useEffect)(()=>{t&&(p(c.source.kind===`run`?`run`:`spec`),h(c.source.kind===`run`?``:i.runs[0]?.id??``))},[t,c.source.kind,i.runs]);let C=(e,t,n)=>(0,N.jsxs)(`label`,{className:a(`flex cursor-pointer items-start gap-3 rounded-[12px] border px-3 py-2.5`,f===e?`border-accent bg-accent-soft`:`border-line-soft hover:bg-surface-muted`),children:[(0,N.jsx)(`input`,{type:`radio`,name:`sc-from`,checked:f===e,onChange:()=>p(e),className:`mt-1 size-4 accent-accent`}),(0,N.jsxs)(`span`,{className:`flex flex-col`,children:[(0,N.jsx)(`span`,{className:`text-sm font-medium`,children:t}),(0,N.jsx)(`span`,{className:`text-[13px] text-muted`,children:n})]})]});return(0,N.jsx)(_,{open:t,onClose:n,title:`Make scenarios`,description:`API calls in order, where later calls use values earlier answers gave, added to ${c.name}. Nothing runs until you run them.`,footer:(0,N.jsxs)(N.Fragment,{children:[(0,N.jsx)(r,{onClick:n,children:`Cancel`}),(0,N.jsx)(r,{variant:`primary`,disabled:u||f===`cases`&&!g.trim()||f===`text`&&!y.trim(),onClick:()=>l({from:f,...f===`run`&&m?{run:m}:{},...f===`cases`?{cases:g}:{},...f===`text`?{text:y}:{},...f===`spec`?{negative:x}:{}}),children:`Make scenarios`})]}),children:(0,N.jsxs)(`div`,{className:`flex flex-col gap-2 pt-3`,children:[C(`spec`,`From the API description`,`Each resource's lifecycle, browsing, the description's links, and negative cases (no model)`),f===`spec`&&(0,N.jsxs)(`span`,{className:`flex items-center gap-2 pl-10 text-sm`,children:[(0,N.jsx)(e,{checked:x,onChange:S,label:`Negative cases`}),` Negative cases: an id that does not exist, no credentials, an empty body`]}),C(`run`,`From a browser run`,`The calls the application made, in order, with the values passed on (no model)`),f===`run`&&(0,N.jsxs)(s,{"aria-label":`Browser run`,value:m,onChange:e=>h(e.target.value),className:`ml-10 max-w-[440px]`,children:[(0,N.jsx)(`option`,{value:``,children:`The latest run`}),i.runs.map(e=>(0,N.jsxs)(`option`,{value:e.id,children:[e.goal,` · `,d(e.calls,`call`),` · `,L(e.at)]},e.id))]}),C(`cases`,`From test cases`,`One scenario per case, planned by the model against the set's operations`),f===`cases`&&(0,N.jsx)(o,{"aria-label":`Test cases`,value:g,spellCheck:!1,placeholder:`ado:2238, jira:SHOP-12`,onChange:e=>v(e.target.value),className:`ml-10 max-w-[440px]`}),C(`text`,`From a description`,`In plain words, planned by the model; a step no operation does is listed, never invented`),f===`text`&&(0,N.jsx)(o,{"aria-label":`The scenario`,value:y,maxLength:1e3,placeholder:`Create a user, log in, add two items, check out, the total is the sum`,onChange:e=>b(e.target.value),className:`ml-10`})]})})}function G({open:e,onClose:t,view:n,onMake:i,busy:a}){let[c,l]=(0,M.useState)(`spec`),[u,f]=(0,M.useState)(``),[p,m]=(0,M.useState)(``),[h,g]=(0,M.useState)(``),[v,y]=(0,M.useState)(``);return(0,M.useEffect)(()=>{e&&(f(n.specs[0]??``),m(``),g(``),y(n.baseUrl??``),l(n.specs.length||!n.runs.length?`spec`:`run`))},[e,n]),(0,N.jsx)(_,{open:e,onClose:t,title:`New contract set`,description:`One contract per operation: an example call and what its answer must have (status, content type, schema, key fields). Making them runs nothing.`,footer:(0,N.jsxs)(N.Fragment,{children:[(0,N.jsx)(r,{onClick:t,children:`Cancel`}),(0,N.jsx)(r,{variant:`primary`,disabled:a||c===`spec`&&!u.trim(),onClick:()=>i({from:c,...c===`spec`?{spec:u.trim()}:p?{run:p}:{},...h.trim()?{name:h.trim()}:{},...v.trim()?{baseUrl:v.trim()}:{}}),children:`Make contracts`})]}),children:(0,N.jsxs)(`div`,{className:`flex flex-col gap-4 pt-3`,children:[(0,N.jsxs)(`fieldset`,{className:`m-0 flex flex-col gap-2 border-0 p-0`,children:[(0,N.jsx)(`legend`,{className:`mb-1.5 text-sm font-medium`,children:`From`}),(0,N.jsxs)(`label`,{className:`flex min-h-9 cursor-pointer items-center gap-2 text-sm`,children:[(0,N.jsx)(`input`,{type:`radio`,name:`set-from`,checked:c===`spec`,onChange:()=>l(`spec`),className:`size-4 accent-accent`}),`An API description: OpenAPI or Swagger, WSDL, GraphQL, .proto, or grpc://`]}),c===`spec`&&(0,N.jsxs)(`div`,{className:`flex flex-col gap-1.5 pl-6`,children:[(0,N.jsx)(o,{"aria-label":`API description`,value:u,spellCheck:!1,list:`spec-files`,placeholder:`https://shop.test/openapi.json, or a file in the project`,onChange:e=>f(e.target.value)}),(0,N.jsx)(`datalist`,{id:`spec-files`,children:n.specs.map(e=>(0,N.jsx)(`option`,{value:e},e))}),n.specs.length>0&&(0,N.jsxs)(`span`,{className:`text-[13px] text-muted`,children:[`In this project: `,n.specs.slice(0,6).join(`, `)]})]}),(0,N.jsxs)(`label`,{className:`flex min-h-9 cursor-pointer items-center gap-2 text-sm`,children:[(0,N.jsx)(`input`,{type:`radio`,name:`set-from`,checked:c===`run`,onChange:()=>l(`run`),className:`size-4 accent-accent`}),`The calls a browser run saw the application make`]}),c===`run`&&(0,N.jsxs)(s,{"aria-label":`Browser run`,value:p,onChange:e=>m(e.target.value),className:`ml-6 max-w-[440px]`,children:[(0,N.jsx)(`option`,{value:``,children:`The latest run`}),n.runs.map(e=>(0,N.jsxs)(`option`,{value:e.id,children:[e.goal,` · `,d(e.calls,`call`),` · `,L(e.at)]},e.id))]})]}),(0,N.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium`,children:[`Name`,(0,N.jsx)(o,{value:h,maxLength:120,placeholder:`Petstore`,onChange:e=>g(e.target.value)})]}),(0,N.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium`,children:[`Base URL`,(0,N.jsx)(o,{value:v,spellCheck:!1,placeholder:`https://qa.shop.test/api`,onChange:e=>y(e.target.value)}),(0,N.jsxs)(`span`,{className:`text-[13px] font-normal text-muted`,children:[`Where the contracts run when not where they were recorded or described.`,n.headers.length?` Each call sends ${n.headers.join(`, `)} from api.headers in forgeqe.yaml.`:` Credentials go in api.headers in forgeqe.yaml, as references to environment variables.`]})]})]})})}function K({id:e,view:a,busy:o,act:s,version:c}){let[l,u]=(0,M.useState)(null),[d,f]=(0,M.useState)(``),[m,h]=(0,M.useState)(null),[_,v]=(0,M.useState)(!1),[y,b]=(0,M.useState)(``);(0,M.useEffect)(()=>{let t=!1;return n.set(e).then(e=>!t&&(u(e),f(``)),e=>!t&&f(e.message)),()=>{t=!0}},[e,c]),(0,M.useEffect)(()=>h(null),[e]);let O=(0,M.useMemo)(()=>new Map((l?.last.contracts?.results??[]).map(e=>[e.id,e])),[l]),A=(0,M.useMemo)(()=>new Map((l?.last.scenarios?.results??[]).map(e=>[e.id,e])),[l]);if(d)return(0,N.jsx)(`p`,{className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail`,children:d});if(!l)return(0,N.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`Loading…`});let j=l.contracts.filter(e=>e.writes).length,P=l.contracts.filter(e=>e.needsData?.length).length,F=l.scenarios??[],z=l.last.contracts,B=l.last.scenarios,G=a.baseUrl||l.baseUrl||``,K=async e=>{try{let{id:n}=await i.start(`Write the API contracts${e?` and scenarios`:``} of set ${l.id} as tests in the repository: call write_api_tests with set_id ${l.id}${e?` and scenarios ["all"]`:``}. Show me the plan before you write.`,{});location.hash=t(`chats`,n)}catch(e){b(e.message)}};return(0,N.jsxs)(`section`,{"aria-labelledby":`set-h`,className:`flex min-w-0 flex-1 flex-col gap-6`,children:[(0,N.jsxs)(`div`,{className:`flex flex-wrap items-start gap-3`,children:[(0,N.jsxs)(`div`,{className:`flex min-w-0 flex-[1_1_260px] flex-col gap-1`,children:[(0,N.jsx)(`h2`,{id:`set-h`,className:`m-0 text-[22px] font-semibold tracking-[-0.01em] [overflow-wrap:anywhere]`,children:l.name}),(0,N.jsxs)(`span`,{className:`text-sm text-muted [overflow-wrap:anywhere]`,children:[`From `,I[l.source.kind]||l.source.kind,` `,(0,N.jsx)(`span`,{className:`font-mono text-[12px]`,children:l.source.ref}),` · `,L(l.createdAt)]}),l.baseUrl&&(0,N.jsx)(`span`,{className:`font-mono text-[12px] text-muted [overflow-wrap:anywhere]`,children:l.baseUrl})]}),(0,N.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,N.jsxs)(r,{variant:`primary`,disabled:o||!l.contracts.length,onClick:()=>h(`contracts`),children:[(0,N.jsx)(p,{size:16,"aria-hidden":`true`}),`Run contracts`]}),(0,N.jsxs)(r,{disabled:o||!F.length,onClick:()=>h(`scenarios`),children:[(0,N.jsx)(p,{size:16,"aria-hidden":`true`}),`Run scenarios`]}),(0,N.jsxs)(r,{disabled:o,onClick:()=>v(!0),children:[(0,N.jsx)(T,{size:16,"aria-hidden":`true`}),`Make scenarios`]})]})]}),m===`contracts`&&(0,N.jsx)(U,{what:`Run ${l.contracts.length} contract${l.contracts.length===1?``:`s`}`,writes:j,needsData:P,defaultUrl:G,onCancel:()=>h(null),onRun:e=>{h(null),s(()=>n.run(l.id,e))}}),m===`scenarios`&&(0,N.jsx)(U,{what:`Run ${F.length} scenario${F.length===1?``:`s`}`,writes:F.filter(e=>e.writes).length,defaultUrl:G,onCancel:()=>h(null),onRun:e=>{h(null),s(()=>n.runScenarios(l.id,{...e.baseUrl?{baseUrl:e.baseUrl}:{},allowWrites:e.allowWrites}))}}),(0,N.jsxs)(`div`,{className:`flex flex-wrap gap-3`,children:[(0,N.jsx)(g,{icon:k,label:`Contracts`,value:String(l.contracts.length),note:[j?`${j} change data`:``,P?`${P} need real values`:``].filter(Boolean).join(` · `)||void 0}),(0,N.jsx)(g,{icon:w,label:`Scenarios`,value:String(F.length)}),(0,N.jsx)(g,{icon:D,label:`Contracts run`,value:z?`${z.counts.passed}/${z.counts.passed+z.counts.failed+z.counts.error}`:`–`,note:z?`passed · ${L(z.at)}`:`Not run yet`}),(0,N.jsx)(g,{icon:D,label:`Scenarios run`,value:B?`${B.counts.passed}/${B.counts.passed+B.counts.failed+B.counts.error}`:`–`,note:B?`passed · ${L(B.at)}`:`Not run yet`})]}),(0,N.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[z?.report&&(0,N.jsxs)(r,{onClick:async()=>b(await S(z.report)),children:[(0,N.jsx)(C,{size:16,"aria-hidden":`true`}),`Contracts report`]}),B?.report&&(0,N.jsxs)(r,{onClick:async()=>b(await S(B.report)),children:[(0,N.jsx)(C,{size:16,"aria-hidden":`true`}),`Scenarios report`]}),(0,N.jsxs)(r,{variant:`ghost`,onClick:()=>void K(F.length>0),children:[(0,N.jsx)(E,{size:16,"aria-hidden":`true`}),`Write them as tests`]}),y&&(0,N.jsx)(`span`,{className:`text-[13px] text-fail`,children:y})]}),(0,N.jsxs)(`section`,{className:`flex flex-col gap-2`,children:[(0,N.jsxs)(`div`,{className:`flex flex-wrap items-baseline gap-2`,children:[(0,N.jsxs)(`h3`,{className:`m-0 flex-1 text-base font-semibold`,children:[`Contracts (`,l.contracts.length,`)`]}),z&&(0,N.jsxs)(x,{tone:R(z.counts),children:[z.counts.passed,` passed · `,z.counts.failed+z.counts.error,` failed · `,z.counts.skipped,` skipped`]})]}),(0,N.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:l.contracts.slice(0,300).map(e=>(0,N.jsx)(V,{c:e,r:O.get(e.id)},e.id))}),l.skipped?.length?(0,N.jsxs)(`p`,{className:`m-0 text-[13px] text-muted`,children:[`Left out: `,l.skipped.slice(0,10).join(`; `)]}):null]}),(0,N.jsxs)(`section`,{className:`flex flex-col gap-2`,children:[(0,N.jsxs)(`div`,{className:`flex flex-wrap items-baseline gap-2`,children:[(0,N.jsxs)(`h3`,{className:`m-0 flex-1 text-base font-semibold`,children:[`Scenarios (`,F.length,`)`]}),B&&(0,N.jsxs)(x,{tone:R(B.counts),children:[B.counts.passed,` passed · `,B.counts.failed+B.counts.error,` failed · `,B.counts.skipped,` skipped`]})]}),F.length?(0,N.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:F.map(e=>(0,N.jsx)(H,{s:e,r:A.get(e.id)},e.id))}):(0,N.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`None yet: Make scenarios chains the calls end to end, from the description, a browser run, test cases, or a few words.`})]}),(0,N.jsx)(W,{open:_,onClose:()=>v(!1),view:a,set:l,busy:o,onMake:e=>{v(!1),s(()=>n.scenarios(l.id,e))}})]})}function q({sub:e}){let[i,o]=(0,M.useState)(null),[s,f]=(0,M.useState)(``),[p,m]=(0,M.useState)(!1),[h,g]=(0,M.useState)(!1),_=(0,M.useCallback)(()=>{n.view().then(e=>{o(e),f(``)},e=>f(e.message))},[]);(0,M.useEffect)(_,[_]);let v=!!i?.job?.running;(0,M.useEffect)(()=>{if(!v)return;let e=setInterval(_,1500);return()=>clearInterval(e)},[v,_]);let y=async e=>{m(!0),f(``);try{await e(),_()}catch(e){f(e.message)}finally{m(!1)}},x=e?decodeURIComponent(e):``,S=i?.sets.find(e=>e.id===x)||i?.sets[0],C=`${i?.job?.endedAt??``}|${S?.at??``}`;return(0,N.jsxs)(`div`,{className:`flex min-h-screen flex-col`,children:[(0,N.jsx)(b,{}),(0,N.jsxs)(`main`,{className:`@container mx-auto box-border flex w-full max-w-[1180px] flex-auto flex-col gap-4 px-6 pt-10 pb-10`,children:[(0,N.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,N.jsx)(u,{icon:c,size:`lg`}),(0,N.jsx)(`h1`,{className:`m-0 flex-1 text-[26px] font-semibold tracking-[-0.01em]`,children:`API tests`}),(0,N.jsxs)(r,{variant:`primary`,disabled:!i||v,onClick:()=>g(!0),children:[(0,N.jsx)(T,{size:16,"aria-hidden":`true`}),`New contract set`]})]}),(0,N.jsx)(`p`,{className:`m-0 max-w-[74ch] text-sm leading-relaxed text-muted`,children:`Test the API directly: REST, GraphQL, SOAP and gRPC. Contracts come from an API description or from the calls a browser run saw the application make; scenarios chain calls end to end. Both run straight against the API, with no browser and no model, in a fraction of a UI test's time.`}),s&&(0,N.jsx)(`p`,{role:`alert`,className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail [overflow-wrap:anywhere]`,children:s}),!i&&!s&&(0,N.jsx)(`p`,{role:`status`,className:`m-0 text-sm text-muted`,children:`Loading…`}),i?.job&&(0,N.jsx)(z,{job:i.job,onStop:()=>y(()=>n.stop())}),i&&(i.sets.length?(0,N.jsxs)(`div`,{className:`flex flex-col gap-6 @2xl:flex-row @2xl:items-start @2xl:gap-8`,children:[(0,N.jsxs)(`nav`,{"aria-label":`Contract sets`,className:`flex min-w-0 flex-col gap-1 @2xl:sticky @2xl:top-6 @2xl:w-[250px] @2xl:flex-none`,children:[(0,N.jsxs)(`span`,{className:`px-3 text-[13px] font-medium text-muted`,children:[`Contract sets (`,i.sets.length,`)`]}),(0,N.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:i.sets.map(e=>{let n=e.last?.contracts;return(0,N.jsx)(`li`,{children:(0,N.jsxs)(`a`,{href:t(`api-tests`,encodeURIComponent(e.id)),"aria-current":S?.id===e.id?`page`:void 0,className:a(`flex min-h-[60px] items-center gap-3 rounded-[12px] px-3 py-2 text-ink no-underline`,S?.id===e.id?`bg-surface-strong hover:text-ink`:`hover:bg-surface-muted hover:text-ink`),children:[i?.job?.running&&i.job.target===e.id?(0,N.jsx)(l,{state:`working`,label:`Working on it`,px:16}):(0,N.jsx)(`span`,{className:a(`size-2.5 flex-none rounded-full`,n?n.counts.failed||n.counts.error?`bg-fail`:`bg-pass`:`bg-line`),"aria-label":n?n.counts.failed||n.counts.error?`failures`:`passed`:`not run`}),(0,N.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,N.jsx)(`span`,{className:`truncate text-[15px] font-medium`,children:e.name}),(0,N.jsxs)(`span`,{className:`truncate text-[13px] text-muted`,children:[d(e.contracts,`contract`),e.scenarios?` · ${d(e.scenarios,`scenario`)}`:``,` · `,L(e.at)]})]})]})},e.id)})})]}),S&&(0,N.jsx)(K,{id:S.id,view:i,busy:p||v,act:y,version:C})]}):(0,N.jsxs)(`div`,{className:`flex flex-col items-start gap-3 rounded-[18px] border border-line p-6`,children:[(0,N.jsx)(j,{size:24,"aria-hidden":`true`,className:`text-muted`}),(0,N.jsxs)(`p`,{className:`m-0 max-w-[62ch] leading-relaxed text-ink-2`,children:[`No API contracts yet. Make a set from your OpenAPI, Swagger, WSDL, GraphQL or .proto description`,i.runs.length?`, or from the ${d(i.runs[0].calls,`call`)} the run "${i.runs[0].goal}" saw`:``,`.`]}),(0,N.jsx)(r,{onClick:()=>g(!0),children:`New contract set`})]}))]}),i&&(0,N.jsx)(G,{open:h,onClose:()=>g(!1),view:i,busy:p||v,onMake:e=>y(async()=>{await n.contracts(e),g(!1)})})]})}export{q as ApiTests};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{F as e,G as t,It as n,J as r,K as i,W as a,Zt as o,a as s,en as c,jt as l,n as u,q as d,r as f,t as p,ut as m}from"./status-CL_clA0J.js";import{n as h,r as g}from"./Response-YL9AIB8R.js";import{a as _,i as v,n as y,r as b,t as x,u as S}from"./index-Bkx3fVbr.js";var C={name:`arrow-down`,size:24,node:[[`path`,{d:`M12 5v14`,key:`s699le`}],[`path`,{d:`m19 12-7 7-7-7`,key:`1idqje`}]]};C.node;var w=l(C),T=c(o(),1),E=d();function D({id:t}){let[a,o]=(0,T.useState)([]),[c,l]=(0,T.useState)(null),[d,f]=(0,T.useState)(``),[p,m]=(0,T.useState)(``),[C,D]=(0,T.useState)(!1),O=(0,T.useRef)(null),k=(0,T.useRef)(null),A=(0,T.useRef)(!0),[j,M]=(0,T.useState)(!0);(0,T.useEffect)(()=>{let e=!1,r=0;return o([]),l(null),f(``),(async()=>{for(;!e;)try{let i=await n.events(t,r,r>0);if(e||(f(``),l({title:i.title,state:i.state,profile:i.profile}),i.events.length&&(r=i.events[i.events.length-1].n,o(e=>[...e,...i.events])),(i.state===`closed`||i.state===`failed`)&&!i.events.length))return}catch(t){if(e||(f(t.message),t.status===404))return;await new Promise(e=>setTimeout(e,2e3))}})(),()=>{e=!0}},[t]);let N=(0,T.useMemo)(()=>g(a),[a]);s(N.context||N.usage?{context:N.context,usage:N.usage,model:N.model?.name}:null);let P=c?.state,F=P===`busy`||P===`starting`,I=P===`closed`||P===`failed`,L=N.turns.at(-1),R=!!L&&!L.outcome&&(L.asks.some(e=>e.answer===void 0)||L.waits.some(e=>e.answer===void 0));(0,T.useEffect)(()=>{let e=document.documentElement,t=()=>window.innerHeight+window.scrollY>=e.scrollHeight-48,n=e=>{A.current=e,M(e)},r=window.scrollY,i=()=>{t()?n(!0):window.scrollY<r-2&&n(!1),r=window.scrollY},a=new ResizeObserver(()=>{A.current&&!t()&&window.scrollTo({top:e.scrollHeight})});return O.current&&a.observe(O.current),k.current&&a.observe(k.current),window.addEventListener(`scroll`,i,{passive:!0}),()=>{a.disconnect(),window.removeEventListener(`scroll`,i)}},[]);let z=()=>{A.current=!0,M(!0),window.scrollTo({top:document.documentElement.scrollHeight,behavior:`smooth`})},B=(0,T.useCallback)(async e=>{try{await e()}catch(e){f(e.message)}},[]),V=v(`chat-${t}`.replace(/[^\w-]/g,``).slice(0,80),e=>{m(e),b(`reply`)}),H=async e=>{if(e.trim()&&!C){D(!0),A.current=!0,M(!0);try{await n.send(t,e.trim()),m(``),f(``),V.close()}catch(e){f(e.message)}finally{D(!1)}}};return(0,E.jsxs)(`div`,{className:`flex min-h-screen flex-col`,children:[(0,E.jsxs)(`header`,{className:`sticky top-0 z-20 flex items-center gap-1 border-b border-line-soft bg-bg py-3 pr-16 pl-5`,children:[(0,E.jsx)(u,{}),(0,E.jsx)(`h1`,{className:`m-0 ml-2 min-w-0 flex-1 truncate text-base font-semibold`,children:c?.title||`Chat`}),N.model&&(0,E.jsxs)(`span`,{className:`hidden min-h-9 items-center gap-1.5 rounded-lg px-2 text-sm text-ink-2 sm:inline-flex`,title:`The model this chat uses`,children:[(0,E.jsx)(S,{size:14,fill:`currentColor`,"aria-hidden":`true`,className:`text-accent`}),N.model.name]})]}),(0,E.jsxs)(`main`,{ref:O,className:`mx-auto box-border flex w-full max-w-[960px] flex-auto flex-col gap-8 px-6 pt-8 pb-6`,children:[!c&&!d&&(0,E.jsx)(`p`,{role:`status`,className:`m-0 text-sm text-muted`,children:`Opening the chat…`}),N.turns.map((e,r)=>(0,E.jsxs)(`section`,{"aria-label":`Message ${r+1}`,className:`flex flex-col gap-5`,children:[(0,E.jsx)(`div`,{className:`ml-auto max-w-[min(85%,640px)] rounded-[18px] bg-surface-strong px-4 py-2.5 text-[15px] leading-relaxed whitespace-pre-wrap text-ink [overflow-wrap:anywhere]`,children:e.user}),(0,E.jsx)(h,{turn:e,live:r===N.turns.length-1&&!I,onAnswer:(e,r)=>B(()=>n.answer(t,e,r)),onProceed:(e,r)=>B(()=>n.proceed(t,e,r))})]},e.id)),L?.outcome===`done`&&L.next.length>0&&!F&&!I&&(0,E.jsx)(`div`,{className:`flex flex-wrap gap-2 pl-12`,children:L.next.map(e=>(0,E.jsx)(`button`,{type:`button`,title:e.request===e.label?void 0:e.request,onClick:()=>void H(e.request),className:`inline-flex min-h-10 cursor-pointer items-center rounded-full border border-line bg-bg px-4 text-sm font-medium text-ink-2 hover:border-accent-line hover:bg-accent-soft hover:text-ink`,children:e.label},e.label))}),(N.fatal||I&&N.ended)&&(0,E.jsxs)(`div`,{role:`alert`,className:r(`flex flex-col gap-2 rounded-[14px] px-4 py-3 text-sm leading-relaxed`,N.fatal||P===`failed`?`bg-fail-soft text-fail`:`bg-surface-muted text-ink-2`),children:[(0,E.jsx)(`span`,{className:`[overflow-wrap:anywhere]`,children:N.fatal||N.ended?.message||(P===`failed`?`ForgeQE stopped unexpectedly.`:`This chat has ended.`)}),N.ended?.stderr&&(0,E.jsx)(`pre`,{className:`m-0 max-h-[200px] overflow-auto rounded-lg bg-bg/60 px-3 py-2 font-mono text-[12px] whitespace-pre-wrap text-ink-2 [overflow-wrap:anywhere]`,children:N.ended.stderr.join(`
|
|
2
|
-
`)}),(0,E.jsxs)(`span`,{className:`flex flex-wrap gap-2`,children:[(0,E.jsx)(`a`,{className:i({size:`sm`}),href:e(),children:`Start a new chat`}),N.fatal&&/model|key|sign/i.test(N.fatal)&&(0,E.jsx)(`a`,{className:i({size:`sm`}),href:e(`settings`,`models`),children:`Models`}),N.fatal&&/runtime|engine|setup/i.test(N.fatal)&&(0,E.jsx)(`a`,{className:i({size:`sm`}),href:e(`settings`,`setup`),children:`Set up`})]})]}),N.notes.map((e,t)=>(0,E.jsx)(`p`,{className:`m-0 text-sm text-muted [overflow-wrap:anywhere]`,children:e},t))]}),(0,E.jsx)(`div`,{ref:k,className:`sticky bottom-0 z-10 bg-gradient-to-t from-bg from-70% to-transparent px-6 pt-4 pb-5`,children:(0,E.jsxs)(`div`,{className:`relative mx-auto flex w-full max-w-[880px] flex-col gap-2`,children:[!j&&F&&(0,E.jsxs)(`button`,{type:`button`,onClick:z,className:`absolute -top-11 left-1/2 inline-flex min-h-8 -translate-x-1/2 cursor-pointer items-center gap-1.5 rounded-full border border-line bg-bg px-3 text-[13px] font-medium text-ink-2 shadow-sm hover:text-ink`,children:[(0,E.jsx)(w,{size:14,"aria-hidden":`true`}),`Latest activity`]}),(0,E.jsx)(`div`,{"aria-live":`polite`,children:d&&(0,E.jsx)(`p`,{role:`alert`,className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail [overflow-wrap:anywhere]`,children:d})}),(0,E.jsx)(_,{id:`reply`,label:`Reply to ForgeQE`,rows:1,value:p,onChange:m,onSend:()=>void H(p),onStop:()=>B(()=>n.stop(t)),busy:F&&!R,disabled:I||C||R||V.running,top:V.state&&(0,E.jsx)(y,{state:V.state,onStop:()=>void V.stop(),onUndo:V.undo,onClose:V.close}),left:(0,E.jsx)(x,{running:V.running,disabled:!p.trim()||I||C||R,onClick:()=>void V.start(p)}),placeholder:I?`This chat has ended`:R?`Answer above to go on`:F?`ForgeQE is working…`:`Reply to ForgeQE…`})]})})]})}var O={starting:{label:`Starting`,tone:`running`},busy:{label:`Working`,tone:`running`},ready:{label:`Your turn`,tone:`pass`},closed:{label:`Ended`,tone:`neutral`},failed:{label:`Failed`,tone:`fail`}};function k(){let[r,o]=(0,T.useState)(null),[s,c]=(0,T.useState)(``);return(0,T.useEffect)(()=>{n.list().then(e=>o(e.chats),e=>c(e.message))},[]),(0,E.jsxs)(`div`,{className:`flex min-h-screen flex-col`,children:[(0,E.jsx)(f,{}),(0,E.jsxs)(`main`,{className:`mx-auto box-border flex w-full max-w-[720px] flex-col gap-4 px-6 py-10`,children:[(0,E.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,E.jsx)(a,{icon:m,size:`lg`}),(0,E.jsx)(`h1`,{className:`m-0 flex-1 text-[26px] font-semibold tracking-[-0.01em]`,children:`Chats`}),(0,E.jsx)(`a`,{className:i({variant:`primary`}),href:e(),children:`New chat`})]}),(0,E.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`The chats since forgeqe web started. Each one is its own ForgeQE agent in your project folder.`}),s&&(0,E.jsx)(`p`,{role:`alert`,className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail`,children:s}),r&&r.length===0&&(0,E.jsxs)(`div`,{className:`flex flex-col items-start gap-3 border-t border-line-soft py-6`,children:[(0,E.jsx)(`p`,{className:`m-0 text-ink-2`,children:`No chats yet.`}),(0,E.jsx)(t,{onClick:()=>location.hash=e(),children:`Ask ForgeQE something`})]}),r&&r.length>0&&(0,E.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:r.map(t=>(0,E.jsx)(`li`,{className:`border-t border-line-soft`,children:(0,E.jsxs)(`a`,{href:e(`chats`,t.id),className:`flex min-h-[60px] items-center gap-3 px-2 text-ink no-underline hover:bg-surface-muted hover:text-ink`,children:[(0,E.jsx)(m,{size:18,strokeWidth:1.8,"aria-hidden":`true`,className:`flex-none text-muted`}),(0,E.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col`,children:[(0,E.jsx)(`span`,{className:`truncate font-medium`,children:t.title}),(0,E.jsxs)(`span`,{className:`text-[13px] text-muted`,children:[new Date(t.started).toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`}),t.profile?` · ${t.profile}`:``]})]}),(0,E.jsx)(p,{tone:O[t.state].tone,orb:t.state===`starting`?`waiting`:`working`,children:O[t.state].label})]})},t.id))})]})]})}export{D as Chat,k as Chats};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{At as e,Ct as t,F as n,G as r,It as i,J as a,M as o,N as s,Rt as c,W as l,Y as u,Zt as d,en as f,f as p,jt as m,q as h,r as g,t as _,ut as v,v as y}from"./status-CL_clA0J.js";import{t as b}from"./circle-dashed-DbpKfamm.js";import{n as x}from"./markdown-GKy06efH.js";import{t as S}from"./search-YLg3uzAx.js";var C={name:`settings-2`,size:24,node:[[`path`,{d:`M14 17H5`,key:`gfn3mx`}],[`path`,{d:`M19 7h-9`,key:`6i9tg`}],[`circle`,{cx:`17`,cy:`17`,r:`3`,key:`18b49y`}],[`circle`,{cx:`7`,cy:`7`,r:`3`,key:`dfmy0x`}]]};C.node;var w=m(C),T=f(d(),1),E=h(),D={ready:{label:`Ready`,tone:`pass`},partial:{label:`Ready, some parts need setup`,tone:`warn`},needs_setup:{label:`Needs setup`,tone:`neutral`}},O={models:`Models`,integrations:`Integrations`,monitor:`Monitor`,notifications:`Notifications`,agents:`Agents`,environments:`Environments`,containers:`Containers`,databases:`Databases`};async function k(e,t){try{let{id:t}=await i.start(e,{});location.hash=n(`chats`,t)}catch(e){t(e.message)}}function A({f:e,onScope:t,busy:i}){let[o,l]=(0,T.useState)(null),[d,f]=(0,T.useState)(``),[p,m]=(0,T.useState)(``);(0,T.useEffect)(()=>{c.detail(e.id).then(l,e=>f(e.message))},[e.id]);let h=e.gaps.filter(e=>!e.optional),g=e.gaps.filter(e=>e.optional);return(0,E.jsxs)(`div`,{className:`flex flex-col gap-4 py-4`,children:[[[h,`Needs`],[g,`Would do more with`]].map(([e,t])=>e.length?(0,E.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[(0,E.jsx)(`span`,{className:`text-sm font-medium`,children:t}),(0,E.jsx)(`ul`,{className:`m-0 flex list-none flex-col gap-2 p-0`,children:e.map((e,r)=>(0,E.jsxs)(`li`,{className:`flex flex-col gap-0.5 rounded-[10px] bg-bg px-3 py-2 text-sm`,children:[(0,E.jsx)(`span`,{className:a(t===`Needs`?`text-ink`:`text-ink-2`),children:e.missing}),(0,E.jsx)(`span`,{className:`text-[13px] text-muted`,children:e.fix}),e.settings&&(0,E.jsxs)(`a`,{href:n(`settings`,e.settings),className:`inline-flex items-center gap-1 self-start text-[13px] font-medium`,children:[(0,E.jsx)(w,{size:13,"aria-hidden":`true`}),`Set it up in Settings, `,O[e.settings]||e.settings]})]},r))})]},t):null),e.inputs.length>0&&(0,E.jsxs)(`p`,{className:`m-0 text-sm text-ink-2`,children:[`From your request: `,e.inputs.join(`; `),`.`]}),d&&(0,E.jsx)(`p`,{className:`m-0 text-sm text-fail`,children:d}),o&&(0,E.jsxs)(E.Fragment,{children:[o.examples.length>0&&(0,E.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[(0,E.jsx)(`span`,{className:`text-sm font-medium`,children:`Ask for it like this`}),(0,E.jsx)(`div`,{className:`flex flex-wrap gap-2`,children:o.examples.slice(0,4).map(t=>(0,E.jsxs)(r,{size:`sm`,disabled:e.state===`needs_setup`,title:e.state===`needs_setup`?`Set it up first`:`Start a chat with this request`,onClick:()=>void k(t,m),className:`h-auto min-h-8 max-w-full py-1.5 text-left whitespace-normal`,children:[(0,E.jsx)(v,{size:14,"aria-hidden":`true`,className:`flex-none`}),t]},t))}),p&&(0,E.jsx)(`span`,{className:`text-[13px] text-fail`,children:p})]}),o.limits.length>0&&(0,E.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,E.jsx)(`span`,{className:`text-sm font-medium`,children:`What it does not do`}),(0,E.jsx)(`ul`,{className:`m-0 list-disc pl-5 text-sm text-ink-2`,children:o.limits.map(e=>(0,E.jsx)(`li`,{children:e},e))})]}),o.docs.length>0&&(0,E.jsxs)(`details`,{children:[(0,E.jsxs)(`summary`,{className:`cursor-pointer text-sm font-medium`,children:[`How it works (`,u(o.docs.length,`section`),`)`]}),(0,E.jsx)(`div`,{className:`mt-2 flex flex-col gap-4`,children:o.docs.map(e=>(0,E.jsxs)(`section`,{className:`rounded-[12px] bg-bg px-4 py-3`,children:[(0,E.jsx)(`h4`,{className:`m-0 mb-1 text-sm font-semibold`,children:e.heading}),(0,E.jsx)(x,{text:e.text,className:`text-sm`})]},e.heading))})]}),(0,E.jsxs)(`span`,{className:`font-mono text-[11.5px] text-muted [overflow-wrap:anywhere]`,children:[`Tools: `,o.tools.join(`, `)]})]}),(0,E.jsxs)(`label`,{className:`flex flex-wrap items-center gap-3 border-t border-line-soft pt-3 text-sm`,children:[(0,E.jsxs)(`span`,{className:`flex-[1_1_240px]`,children:[`The agent gets its tools`,(0,E.jsx)(`span`,{className:`block text-[13px] text-muted`,children:`agent.capabilities in forgeqe.yaml`})]}),(0,E.jsxs)(s,{value:e.scope,disabled:i,onChange:e=>t(e.target.value),className:`w-[240px]`,children:[(0,E.jsx)(`option`,{value:`auto`,children:`When a request is about it`}),(0,E.jsx)(`option`,{value:`always`,children:`Always`}),(0,E.jsx)(`option`,{value:`off`,children:`Never, in this project`})]})]})]})}function j(){let[n,r]=(0,T.useState)(null),[i,s]=(0,T.useState)(``),[u,d]=(0,T.useState)(``),[f,m]=(0,T.useState)(`all`),[h,v]=(0,T.useState)(``),[x,C]=(0,T.useState)(!1),w=(0,T.useCallback)(()=>c.view().then(r,e=>s(e.message)),[]);(0,T.useEffect)(()=>void w(),[w]);let O=(0,T.useMemo)(()=>{let e=h.toLowerCase().split(/\s+/).filter(Boolean);return(n?.areas||[]).map(t=>({...t,features:t.features.filter(n=>(f===`all`||(f===`ready`?n.state!==`needs_setup`:n.state!==`ready`))&&e.every(e=>`${n.title} ${n.summary} ${t.area}`.toLowerCase().includes(e)))})).filter(e=>e.features.length)},[n,f,h]),k=async(e,t)=>{C(!0);try{r(await c.scope(e,t)),s(``)}catch(e){s(e.message)}finally{C(!1)}},j=n?.counts;return(0,E.jsxs)(`div`,{className:`flex min-h-screen flex-col`,children:[(0,E.jsx)(g,{}),(0,E.jsxs)(`main`,{className:`mx-auto box-border flex w-full max-w-[920px] flex-auto flex-col gap-5 px-6 pt-10 pb-10`,children:[(0,E.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,E.jsx)(l,{icon:e,size:`lg`}),(0,E.jsx)(`h1`,{className:`m-0 flex-1 text-[26px] font-semibold tracking-[-0.01em]`,children:`What's ready`})]}),(0,E.jsx)(`p`,{className:`m-0 max-w-[70ch] text-sm leading-relaxed text-muted`,children:`Everything ForgeQE can do, and whether it works here: in this project, with these settings, on this machine. Each one says what it still needs and where to set it up, and how to ask for it.`}),i&&(0,E.jsx)(`p`,{role:`alert`,className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail [overflow-wrap:anywhere]`,children:i}),!n&&!i&&(0,E.jsx)(`p`,{role:`status`,className:`m-0 text-sm text-muted`,children:`Loading…`}),n&&j&&(0,E.jsxs)(E.Fragment,{children:[(0,E.jsxs)(`div`,{className:`flex flex-wrap gap-3`,children:[(0,E.jsx)(p,{icon:e,label:`Ready`,value:String(j.ready)}),(0,E.jsx)(p,{icon:t,label:`Some parts need setup`,value:String(j.partial)}),(0,E.jsx)(p,{icon:b,label:`Need setup`,value:String(j.needs_setup)})]}),(0,E.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,E.jsx)(`div`,{role:`radiogroup`,"aria-label":`Show`,className:`flex gap-1 rounded-full bg-surface-strong p-1`,children:[[`all`,`All`],[`needs`,`Needs setup`],[`ready`,`Ready`]].map(([e,t])=>(0,E.jsx)(`button`,{type:`button`,role:`radio`,"aria-checked":f===e,onClick:()=>m(e),className:a(`min-h-8 cursor-pointer rounded-full border-0 px-3.5 text-sm font-medium`,f===e?`bg-bg text-ink shadow-sm`:`bg-transparent text-muted hover:text-ink`),children:t},e))}),(0,E.jsxs)(`div`,{className:`relative min-w-0 flex-[1_1_220px]`,children:[(0,E.jsx)(S,{size:16,"aria-hidden":`true`,className:`pointer-events-none absolute top-1/2 left-3 -translate-y-1/2 text-muted`}),(0,E.jsx)(o,{type:`search`,"aria-label":`Find a feature`,value:h,placeholder:`Find a feature`,onChange:e=>v(e.target.value),className:`pl-9`})]})]}),O.length===0&&(0,E.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`Nothing matches.`}),O.map(e=>(0,E.jsxs)(`section`,{"aria-labelledby":`area-${e.area}`,children:[(0,E.jsx)(`h2`,{id:`area-${e.area}`,className:`m-0 mb-1 text-base font-semibold`,children:e.area}),e.features.map(e=>(0,E.jsx)(y,{title:e.title,meta:(0,E.jsx)(`span`,{className:a(u!==e.id&&`line-clamp-2`),children:e.summary}),open:u===e.id,onToggle:()=>d(u===e.id?``:e.id),state:(0,E.jsxs)(`span`,{className:`flex flex-wrap items-center justify-end gap-1.5`,children:[e.scope!==`auto`&&(0,E.jsx)(_,{tone:`neutral`,icon:!1,children:e.scope===`always`?`Always given`:`Off here`}),(0,E.jsx)(_,{tone:D[e.state].tone,icon:e.state!==`needs_setup`,children:D[e.state].label})]}),children:(0,E.jsx)(A,{f:e,busy:x,onScope:t=>void k(e.id,t)})},e.id))]},e.area)),n.outOfScope.length>0&&(0,E.jsxs)(`section`,{"aria-labelledby":`oos-h`,className:`flex flex-col gap-2 rounded-[14px] border border-line-soft px-4 py-3`,children:[(0,E.jsx)(`h2`,{id:`oos-h`,className:`m-0 text-base font-semibold`,children:`Not what ForgeQE does`}),(0,E.jsx)(`ul`,{className:`m-0 flex list-none flex-col gap-1 p-0 text-sm`,children:n.outOfScope.map(e=>(0,E.jsxs)(`li`,{children:[e.what,e.instead?(0,E.jsxs)(`span`,{className:`text-muted`,children:[`; instead: `,e.instead]}):null]},e.what))})]})]})]})]})}export{j as Features};
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{A as e,Bt as t,Dt as n,F as r,G as i,J as a,Kt as o,M as s,N as c,U as l,W as u,Zt as d,ct as f,en as p,et as m,f as h,j as g,jt as _,ot as v,q as y,r as b,st as x,t as S}from"./status-CL_clA0J.js";import{n as C,t as w}from"./terminal-BdTFlGZR.js";import{a as T,n as E,o as D,t as O}from"./markdown-GKy06efH.js";import{t as k}from"./external-link-pvVHJFAZ.js";import{t as A}from"./trash-DIUJbRpw.js";import{_ as j,d as M,h as N,p as P,w as F,x as I}from"./index-Bkx3fVbr.js";var L={name:`calendar-plus`,size:24,node:[[`path`,{d:`M16 18h6`,key:`987eiv`}],[`path`,{d:`M16 2v3`,key:`otl347`}],[`path`,{d:`M19 15v6`,key:`10aioa`}],[`path`,{d:`M21 11.5V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h8.3`,key:`jgwkxf`}],[`path`,{d:`M3 9h18`,key:`1pudct`}],[`path`,{d:`M8 2v3`,key:`1ioesn`}]]};L.node;var R=_(L),z={name:`power`,size:24,node:[[`path`,{d:`M12 2v10`,key:`mnfbl`}],[`path`,{d:`M18.4 6.6a9 9 0 1 1-12.77.04`,key:`obofu9`}]]};z.node;var B=_(z),V={name:`search-check`,size:24,node:[[`path`,{d:`m8 11 2 2 4-4`,key:`1sed1v`}],[`circle`,{cx:`11`,cy:`11`,r:`8`,key:`4ej97u`}],[`path`,{d:`m21 21-4.3-4.3`,key:`1qie3q`}]]};V.node;var H=_(V),U={name:`timer-off`,size:24,node:[[`path`,{d:`M10 2h4`,key:`n1abiw`}],[`path`,{d:`M4.6 11a8 8 0 0 0 1.7 8.7 8 8 0 0 0 8.7 1.7`,key:`10he05`}],[`path`,{d:`M7.4 7.4a8 8 0 0 1 10.3 1 8 8 0 0 1 .9 10.2`,key:`15f7sh`}],[`path`,{d:`m2 2 20 20`,key:`1ooewy`}],[`path`,{d:`M12 12v-2`,key:`fwoke6`}]]};U.node;var W=_(U),G=p(d(),1),K=y(),q={monitor:`Monitor`,suite:`Suite`,run:`Browser run`,heal:`Heal`,"pr-tests":`PR tests`,pack:`Pack`,pipeline:`Pipeline watch`},J={running:{label:`Running`,tone:`running`},passed:{label:`Passed`,tone:`pass`},failed:{label:`Failed`,tone:`fail`},stopped:{label:`Stopped`,tone:`neutral`},timed_out:{label:`Timed out`,tone:`fail`},interrupted:{label:`Interrupted`,tone:`warn`}},Y=e=>{if(!e)return``;let t=new Date(e);return Number.isNaN(t.getTime())?e:t.toLocaleString([],{month:`short`,day:`numeric`,...t.getFullYear()===new Date().getFullYear()?{}:{year:`numeric`},hour:`numeric`,minute:`2-digit`})},X=e=>{if(!e.endedAt)return``;let t=Math.max(0,Math.round((Date.parse(e.endedAt)-Date.parse(e.startedAt))/1e3));return t<60?`${t}s`:`${Math.floor(t/60)}m ${t%60}s`};function Z(e){return e.running?J.running:e.enabled?e.last?J[e.last.status]:{label:`Not run yet`,tone:`neutral`}:{label:`Disabled`,tone:`neutral`}}var Q={pass:`bg-pass`,fail:`bg-fail`,warn:`bg-warn`,running:`bg-accent`,neutral:`bg-line`};function $({label:e,icon:t,children:n}){return(0,K.jsxs)(`div`,{className:`flex min-h-[48px] flex-wrap items-baseline gap-x-4 gap-y-1 border-t border-line-soft py-2.5`,children:[(0,K.jsxs)(`span`,{className:`flex w-[170px] flex-none items-center gap-2 text-sm text-muted`,children:[t&&(0,K.jsx)(t,{size:16,"aria-hidden":`true`,className:`flex-none`}),e]}),(0,K.jsx)(`span`,{className:`min-w-0 flex-1 text-sm [overflow-wrap:anywhere]`,children:n})]})}function ee({m:e,rounds:t}){let[n,a]=(0,G.useState)(``);return(0,K.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,K.jsxs)(`h3`,{className:`m-0 mb-1 flex items-center gap-2 text-base font-semibold`,children:[(0,K.jsx)(j,{size:18,"aria-hidden":`true`,className:`text-accent`}),`Monitor`]}),(0,K.jsx)($,{icon:N,label:`Checks`,children:e.checks.length?(0,K.jsx)(E,{text:e.checks.map(e=>`\`${e.replace(/`/g,``)}\``).join(`, `),className:`text-sm [&_p]:my-0`}):`The checks in forgeqe.yaml (monitor.checks)`}),(0,K.jsxs)($,{icon:F,label:`Alerts after`,children:[e.failAfter,` failing round`,e.failAfter===1?``:`s`,` in a row`,e.remind?`, again every ${e.remind} while it keeps failing`:``]}),(0,K.jsx)($,{icon:M,label:`Alerts go to`,children:e.alerts.length?e.alerts.join(` and `):`Nowhere yet: add Teams or email in Settings, Monitor`}),(0,K.jsxs)($,{icon:x,label:`Each round`,children:[e.parallel,` check`,e.parallel===1?``:`s`,` at once; a recorded run `,e.replay===`trust`?`is trusted`:e.replay===`off`?`is not replayed`:`is verified with one model call`]}),(0,K.jsx)($,{icon:D,label:`Rounds kept in`,children:(0,K.jsx)(O,{path:e.outDir})}),(0,K.jsx)(`p`,{className:`m-0 border-t border-line-soft py-2.5 text-sm`,children:(0,K.jsx)(`a`,{href:r(`settings`,`monitor`),children:`Change the monitor in Settings`})}),(0,K.jsx)(`h4`,{className:`m-0 mt-3 mb-1 text-sm font-semibold`,children:`Latest rounds`}),n&&(0,K.jsx)(`p`,{className:`m-0 text-[13px] text-fail`,children:n}),t.length===0?(0,K.jsx)(`p`,{className:`m-0 border-t border-line-soft py-3 text-sm text-muted`,children:`No rounds yet.`}):(0,K.jsx)(`ul`,{className:`m-0 list-none p-0`,children:t.slice(0,8).map(e=>(0,K.jsxs)(`li`,{className:`flex min-h-[48px] items-center gap-3 border-t border-line-soft py-2`,children:[(0,K.jsxs)(S,{tone:e.counts.failed?`fail`:`pass`,children:[e.counts.passed,`/`,e.counts.total,` passed`]}),(0,K.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-sm`,children:Y(e.startedAt)}),e.report&&(0,K.jsx)(i,{variant:`icon`,size:`icon`,"aria-label":`Open the report of the round of ${Y(e.startedAt)}`,onClick:async()=>a(await T(e.report)),children:(0,K.jsx)(k,{size:16,"aria-hidden":`true`})}),(0,K.jsx)(`a`,{href:r(`reports`,encodeURIComponent(e.key)),className:`text-[13px]`,children:`Details`})]},e.key))})]})}function te({jobId:e,run:n}){let[r,i]=(0,G.useState)(null),[a,o]=(0,G.useState)(``);(0,G.useEffect)(()=>{let r=!1;i(null);let a=()=>t.log(e,n.id).then(e=>!r&&i(e.lines),e=>!r&&o(e.message));a();let s=n.status===`running`?setInterval(a,2e3):void 0;return()=>{r=!0,s&&clearInterval(s)}},[e,n.id,n.status]);let s=Math.max(2,...(r||[]).map(e=>Math.max(0,...(e.match(/`+/g)||[]).map(e=>e.length)))),c="`".repeat(s+1),l=[`**${J[n.status].label}**${n.endedAt?` after ${X(n)}`:``}${n.exitCode!==void 0&&n.exitCode!==null?`, exit ${n.exitCode}`:``}. Started ${Y(n.startedAt)}, ${n.reason===`asked`?`asked for`:`on schedule`}.`,...n.investigating?[``,`_Its failures are being investigated._`]:[],...n.findings?.length?[``,`**Investigated:**`,...n.findings.map(e=>`- ${e.verdict}: ${e.tests.map(e=>`\`${e.replace(/`/g,``)}\``).join(`, `)}${e.branch?` on branch \`${e.branch.replace(/`/g,``)}\``:``}`)]:[],...n.investigationNote?[``,n.investigationNote]:[],``,`${c}text`,...r||[],c].join(`
|
|
2
|
-
`);return(0,K.jsxs)(`div`,{className:`flex flex-col gap-2 rounded-[12px] border border-line-soft bg-surface-muted px-4 py-3`,children:[a&&(0,K.jsx)(`p`,{className:`m-0 text-sm text-fail`,children:a}),!r&&!a?(0,K.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`Reading its output…`}):(0,K.jsx)(E,{text:l,className:`text-sm [&_pre]:max-h-[320px] [&_pre]:overflow-auto`})]})}function ne({job:r,rounds:a,busy:o,act:s}){let[c,l]=(0,G.useState)(null),[u,d]=(0,G.useState)(null),[p,g]=(0,G.useState)(!1),_=`${r.running?.id}|${r.last?.id}|${r.last?.status}`;(0,G.useEffect)(()=>{let e=!1;return t.detail(r.id).then(t=>{e||(l(t.runs),d(e=>e??t.runs[0]?.id??null))},()=>{}),()=>{e=!0}},[r.id,_]),(0,G.useEffect)(()=>{d(null),g(!1)},[r.id]);let y=Z(r),b=c?.filter(e=>e.status===`passed`).length??0,x=c?.filter(e=>e.status!==`running`).length??0;return(0,K.jsxs)(`section`,{"aria-labelledby":`job-h`,className:`flex min-w-0 flex-1 flex-col gap-5`,children:[(0,K.jsxs)(`div`,{className:`flex flex-wrap items-start gap-x-3 gap-y-3`,children:[(0,K.jsxs)(`div`,{className:`flex min-w-0 flex-[1_1_260px] flex-col gap-1`,children:[(0,K.jsx)(`h2`,{id:`job-h`,className:`m-0 text-[22px] font-semibold tracking-[-0.01em] [overflow-wrap:anywhere]`,children:r.name}),(0,K.jsxs)(`span`,{className:`text-sm text-muted`,children:[q[r.kind]||r.kind,` · `,r.schedule]})]}),(0,K.jsx)(S,{tone:y.tone,children:y.label}),r.running?(0,K.jsxs)(i,{disabled:o,onClick:()=>s(`stop`),children:[(0,K.jsx)(m,{size:14,fill:`currentColor`,"aria-hidden":`true`}),`Stop`]}):(0,K.jsxs)(i,{variant:`primary`,disabled:o,onClick:()=>s(`run`),children:[(0,K.jsx)(f,{size:16,"aria-hidden":`true`}),`Run now`]})]}),(0,K.jsxs)(`div`,{className:`flex flex-wrap gap-3`,children:[(0,K.jsx)(h,{icon:v,label:`Runs kept`,value:String(c?.length??`–`)}),(0,K.jsx)(h,{icon:I,label:`Passed`,value:x?`${Math.round(b/x*100)}%`:`–`,note:x?`${b} of ${x}`:void 0}),(0,K.jsx)(h,{icon:n,label:`Next run`,value:r.nextAt?new Date(r.nextAt).toLocaleTimeString([],{hour:`numeric`,minute:`2-digit`}):`–`,note:r.nextAt?Y(r.nextAt):r.enabled?void 0:`Disabled`})]}),(0,K.jsxs)(`div`,{className:`flex flex-col`,children:[(0,K.jsx)($,{icon:w,label:`Runs`,children:(0,K.jsx)(E,{text:`\`${r.command.replace(/`/g,``)}\``,className:`text-sm [&_p]:my-0`})}),(0,K.jsx)($,{icon:n,label:`When`,children:r.schedule}),(0,K.jsx)($,{icon:B,label:`Enabled`,children:(0,K.jsx)(e,{checked:r.enabled,disabled:o,onChange:e=>s(e?`enable`:`disable`),label:`${r.name} enabled`})}),(0,K.jsxs)($,{icon:W,label:`Stopped after`,children:[r.timeoutMs?r.timeoutMs%36e5==0?`${r.timeoutMs/36e5} hour${r.timeoutMs===36e5?``:`s`}`:`${Math.round(r.timeoutMs/6e4)} minutes`:`the service's default`,` of running`]}),(0,K.jsx)($,{icon:C,label:`Tells the team`,children:r.notify?`When it fails, recovers or ends (the notify channels that take job events)`:r.kind===`monitor`?`The monitor sends its own alerts`:`No`}),(0,K.jsx)($,{icon:H,label:`Investigates failures`,children:r.investigate?`Yes, on branches of their own`:`No`}),(0,K.jsxs)($,{icon:R,label:`Added`,children:[Y(r.createdAt),` `,r.by===`agent`?`by ForgeQE's agent, after you agreed`:`from the command line or here`]})]}),r.monitor&&(0,K.jsx)(ee,{m:r.monitor,rounds:a}),(0,K.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,K.jsx)(`h3`,{className:`m-0 mb-1 text-base font-semibold`,children:`Runs`}),c?c.length===0?(0,K.jsx)(`p`,{className:`m-0 border-t border-line-soft py-3 text-sm text-muted`,children:`No runs yet. Run now starts one.`}):(0,K.jsx)(`ul`,{className:`m-0 list-none p-0`,children:c.map(e=>(0,K.jsxs)(`li`,{className:`border-t border-line-soft`,children:[(0,K.jsxs)(`button`,{type:`button`,"aria-expanded":u===e.id,onClick:()=>d(u===e.id?null:e.id),className:`flex min-h-[52px] w-full cursor-pointer items-center gap-3 border-0 bg-transparent px-1 py-2 text-left text-ink hover:bg-surface-muted`,children:[(0,K.jsx)(S,{tone:J[e.status].tone,children:J[e.status].label}),(0,K.jsxs)(`span`,{className:`min-w-0 flex-1 truncate text-sm`,children:[Y(e.startedAt),(0,K.jsxs)(`span`,{className:`text-muted`,children:[` `,`· `,e.reason===`asked`?`asked for`:`scheduled`,X(e)?` · ${X(e)}`:``]})]}),e.summary?.length?(0,K.jsx)(`span`,{className:`hidden max-w-[40%] truncate text-[13px] text-muted sm:inline`,children:e.summary.at(-1)}):null]}),u===e.id&&(0,K.jsx)(`div`,{className:`pb-3`,children:(0,K.jsx)(te,{jobId:r.id,run:e})})]},e.id))}):(0,K.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`Loading…`})]}),(0,K.jsx)(`div`,{className:`flex flex-wrap items-center gap-2 border-t border-line-soft pt-4`,children:p?(0,K.jsxs)(K.Fragment,{children:[(0,K.jsxs)(`span`,{className:`text-sm text-ink-2`,children:[`Remove `,r.name,`? Its runs and logs go with it.`]}),(0,K.jsx)(i,{disabled:o,onClick:()=>s(`remove`),className:`border-fail text-fail hover:bg-fail-soft`,children:`Remove`}),(0,K.jsx)(i,{onClick:()=>g(!1),children:`Keep it`})]}):(0,K.jsxs)(i,{variant:`ghost`,onClick:()=>g(!0),className:`text-fail`,children:[(0,K.jsx)(A,{size:16,"aria-hidden":`true`}),`Remove this job`]})})]})}function re({open:n,onClose:r,commands:a,initial:o,onAdded:l}){let[u,d]=(0,G.useState)(``),[f,p]=(0,G.useState)(`suite`),[m,h]=(0,G.useState)(``),[_,v]=(0,G.useState)(`every`),[y,b]=(0,G.useState)(`30m`),[x,S]=(0,G.useState)(`0 2 * * 1-5`),[C,w]=(0,G.useState)(!0),[T,E]=(0,G.useState)(``),[D,O]=(0,G.useState)(!1);(0,G.useEffect)(()=>{n&&(d(o?.name??``),p(o?.command??`suite`),h(o?.args??``),v(`every`),b(o?.every??`30m`),w(o?.command!==`monitor`),E(``))},[n,o]);let k=async()=>{O(!0),E(``);try{l(await t.add({name:u.trim(),command:f,args:m,..._===`every`?{every:y}:_===`cron`?{cron:x}:{now:!0},notify:C}),u.trim())}catch(e){E(e.message)}finally{O(!1)}},A=`flex flex-col gap-1.5 py-3`;return(0,K.jsx)(g,{open:n,onClose:r,title:`Add a job`,description:`A ForgeQE command the jobs service runs on a schedule, or once now, in a process of its own; it keeps going with this page closed.`,footer:(0,K.jsxs)(K.Fragment,{children:[T&&(0,K.jsx)(`span`,{role:`alert`,className:`mr-auto self-center text-sm text-fail [overflow-wrap:anywhere]`,children:T}),(0,K.jsx)(i,{onClick:r,children:`Cancel`}),(0,K.jsx)(i,{variant:`primary`,disabled:D||!u.trim(),onClick:()=>void k(),children:D?`Adding…`:`Add job`})]}),children:(0,K.jsxs)(`div`,{className:`flex flex-col pt-3`,children:[(0,K.jsxs)(`div`,{className:A,children:[(0,K.jsx)(`label`,{htmlFor:`job-name`,className:`font-medium`,children:`Name`}),(0,K.jsx)(s,{id:`job-name`,value:u,spellCheck:!1,placeholder:`nightly-smoke`,onChange:e=>d(e.target.value)}),(0,K.jsx)(`span`,{className:`text-sm text-muted`,children:`Letters, digits and dashes.`})]}),(0,K.jsxs)(`div`,{className:A,children:[(0,K.jsx)(`label`,{htmlFor:`job-command`,className:`font-medium`,children:`Runs`}),(0,K.jsx)(c,{id:`job-command`,value:f,onChange:e=>p(e.target.value),children:a.map(e=>(0,K.jsxs)(`option`,{value:e,children:[`forgeqe `,e]},e))})]}),(0,K.jsxs)(`div`,{className:A,children:[(0,K.jsx)(`label`,{htmlFor:`job-args`,className:`font-medium`,children:`With`}),(0,K.jsx)(s,{id:`job-args`,value:m,spellCheck:!1,placeholder:f===`monitor`?`Empty: the checks in forgeqe.yaml`:`checks/smoke.yaml --parallel 2`,onChange:e=>h(e.target.value),className:`font-mono text-[13px]`}),(0,K.jsx)(`span`,{className:`text-sm text-muted`,children:`Test case sources and flags, as you would type them after the command. "…" keeps spaces in one. Nothing goes through a shell.`})]}),(0,K.jsxs)(`fieldset`,{className:`m-0 flex flex-col gap-2 border-0 p-0 py-3`,children:[(0,K.jsx)(`legend`,{className:`mb-1 font-medium`,children:`When`}),[[`every`,`Every`],[`cron`,`On a schedule (cron, this machine's time)`],[`once`,`Once, now`]].map(([e,t])=>(0,K.jsxs)(`label`,{className:`flex min-h-10 cursor-pointer items-center gap-3`,children:[(0,K.jsx)(`input`,{type:`radio`,name:`job-when`,checked:_===e,onChange:()=>v(e),className:`size-4 accent-accent`}),(0,K.jsx)(`span`,{className:`flex-none`,children:t}),e===`every`&&_===`every`&&(0,K.jsx)(s,{"aria-label":`Every`,value:y,onChange:e=>b(e.target.value),placeholder:`30m`,className:`max-w-[120px] font-mono text-[13px]`}),e===`cron`&&_===`cron`&&(0,K.jsx)(s,{"aria-label":`Cron schedule`,value:x,onChange:e=>S(e.target.value),className:`max-w-[180px] font-mono text-[13px]`})]},e))]}),(0,K.jsxs)(`div`,{className:`flex items-center gap-4 py-3`,children:[(0,K.jsxs)(`span`,{className:`flex flex-1 flex-col gap-0.5`,children:[(0,K.jsx)(`span`,{className:`font-medium`,children:`Tell the team`}),(0,K.jsx)(`span`,{className:`text-sm text-muted`,children:`When it fails, recovers or ends, on the notify channels that take job events. A monitor sends its own alerts.`})]}),(0,K.jsx)(e,{checked:C,onChange:w,label:`Tell the team`})]})]})})}function ie({sub:e}){let[s,c]=(0,G.useState)(null),[d,f]=(0,G.useState)([]),[p,m]=(0,G.useState)(``),[h,g]=(0,G.useState)(!1),[_,v]=(0,G.useState)(null),y=(0,G.useCallback)(()=>{t.list().then(e=>{c(e),m(``)},e=>m(e.message))},[]);(0,G.useEffect)(y,[y]),(0,G.useEffect)(()=>{o.list().then(e=>f(e.rows.filter(e=>e.scope===`monitor`)),()=>{})},[s?.jobs.map(e=>e.last?.id).join()]);let x=s?.jobs.some(e=>e.running);(0,G.useEffect)(()=>{let e=setInterval(y,x?3e3:2e4);return()=>clearInterval(e)},[y,x]);let C=e?decodeURIComponent(e):``,w=s?.jobs.find(e=>e.id===C)||s?.jobs[0],T=(0,G.useMemo)(()=>s?.jobs.filter(e=>e.kind===`monitor`)??[],[s]),D=(0,G.useMemo)(()=>s?.jobs.filter(e=>e.kind!==`monitor`)??[],[s]),O=async e=>{if(w){g(!0),m(``);try{c(e===`remove`?await t.remove(w.id):await t.act(w.id,e)),e===`remove`&&(location.hash=r(`jobs`))}catch(e){m(e.message)}finally{g(!1)}}},k=e=>{let t=Z(e),n=w?.id===e.id;return(0,K.jsx)(`li`,{children:(0,K.jsxs)(`a`,{href:r(`jobs`,encodeURIComponent(e.id)),"aria-current":n?`page`:void 0,className:a(`flex min-h-[60px] items-center gap-3 rounded-[12px] px-3 py-2 text-ink no-underline`,n?`bg-surface-strong hover:text-ink`:`hover:bg-surface-muted hover:text-ink`),children:[e.running?(0,K.jsx)(l,{label:t.label,px:16}):(0,K.jsx)(`span`,{"aria-label":t.label,className:a(`size-2.5 flex-none rounded-full`,Q[t.tone])}),(0,K.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,K.jsx)(`span`,{className:`truncate text-[15px] font-medium`,children:e.name}),(0,K.jsx)(`span`,{className:`truncate text-[13px] text-muted`,children:[t.label,e.schedule,e.nextAt&&e.enabled?`next ${Y(e.nextAt)}`:``].filter(Boolean).join(` · `)})]})]})},e.id)};return(0,K.jsxs)(`div`,{className:`flex min-h-screen flex-col`,children:[(0,K.jsx)(b,{}),(0,K.jsxs)(`main`,{className:`@container mx-auto box-border flex w-full max-w-[1180px] flex-auto flex-col gap-4 px-6 pt-10 pb-10`,children:[(0,K.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-4 gap-y-2`,children:[(0,K.jsx)(u,{icon:n,size:`lg`}),(0,K.jsx)(`h1`,{className:`m-0 text-[26px] font-semibold tracking-[-0.01em]`,children:`Jobs`}),s&&(0,K.jsx)(S,{tone:s.service.running?`pass`:`neutral`,children:s.service.running?`Service running since ${Y(s.service.startedAt)}`:`Service stopped: jobs wait until it starts`}),(0,K.jsx)(`div`,{className:`flex-auto`}),(0,K.jsxs)(i,{variant:`primary`,disabled:!s,onClick:()=>v({}),children:[(0,K.jsx)(P,{size:16,"aria-hidden":`true`}),`Add a job`]})]}),(0,K.jsx)(`p`,{className:`m-0 max-w-[70ch] text-sm leading-relaxed text-muted`,children:`ForgeQE commands that run on a schedule, or once, in a service of their own, so they keep going while you work on something else or with this page closed: monitors, suites, packs and pipeline watches.`}),p&&(0,K.jsx)(`p`,{role:`alert`,className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail [overflow-wrap:anywhere]`,children:p}),!s&&!p&&(0,K.jsx)(`p`,{role:`status`,className:`m-0 text-sm text-muted`,children:`Loading…`}),s?.monitor&&!s.monitor.scheduled&&(0,K.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-4 gap-y-3 rounded-[14px] border border-accent-line bg-accent-soft px-4 py-3`,children:[(0,K.jsx)(j,{size:20,"aria-hidden":`true`,className:`flex-none text-accent`}),(0,K.jsxs)(`span`,{className:`flex min-w-0 flex-[1_1_300px] flex-col gap-0.5 text-sm`,children:[(0,K.jsx)(`span`,{className:`font-medium`,children:`The monitor in forgeqe.yaml is not scheduled`}),(0,K.jsxs)(`span`,{className:`text-ink-2`,children:[s.monitor.checks.length,` check`,s.monitor.checks.length===1?``:`s`,`, every `,s.monitor.every,`. As a job it runs a round each time, with this page closed.`]})]}),(0,K.jsx)(i,{onClick:()=>v({name:`monitor`,command:`monitor`,args:``,every:s.monitor.every}),children:`Schedule it`})]}),s&&(0,K.jsxs)(`div`,{className:`flex flex-col gap-6 @2xl:flex-row @2xl:items-start @2xl:gap-8`,children:[(0,K.jsxs)(`nav`,{"aria-label":`Jobs`,className:`flex min-w-0 flex-col gap-4 @2xl:sticky @2xl:top-6 @2xl:w-[250px] @2xl:flex-none`,children:[(0,K.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,K.jsxs)(`span`,{className:`flex items-center gap-2 px-3 text-[13px] font-medium text-muted`,children:[(0,K.jsx)(j,{size:14,"aria-hidden":`true`}),`Monitoring (`,T.length,`)`]}),T.length?(0,K.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:T.map(k)}):(0,K.jsx)(`p`,{className:`m-0 px-3 py-2 text-sm text-muted`,children:`No monitor jobs.`})]}),(0,K.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,K.jsxs)(`span`,{className:`flex items-center gap-2 px-3 text-[13px] font-medium text-muted`,children:[(0,K.jsx)(n,{size:14,"aria-hidden":`true`}),`Other jobs (`,D.length,`)`]}),D.length?(0,K.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:D.map(k)}):(0,K.jsx)(`p`,{className:`m-0 px-3 py-2 text-sm text-muted`,children:`None.`})]}),s.events.length>0&&(0,K.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,K.jsx)(`span`,{className:`px-3 text-[13px] font-medium text-muted`,children:`Recent activity`}),(0,K.jsx)(E,{text:s.events.slice(0,12).map(e=>`- \`${Y(e.at)}\` ${e.text.replace(/([\\`*_[\]<>#|~!])/g,`\\$1`)}`).join(`
|
|
3
|
-
`),className:`px-3 text-[13px] text-ink-2 [&_li]:my-1 [&_ul]:my-0 [&_ul]:pl-4`})]})]}),w?(0,K.jsx)(ne,{job:w,rounds:d,busy:h,act:O}):(0,K.jsxs)(`div`,{className:`flex min-w-0 flex-1 flex-col items-start gap-3 rounded-[18px] border border-line p-6`,children:[(0,K.jsx)(n,{size:24,"aria-hidden":`true`,className:`text-muted`}),(0,K.jsx)(`p`,{className:`m-0 max-w-[60ch] leading-relaxed text-ink-2`,children:`No jobs yet. Add one to run a suite every night, a monitor every 15 minutes, or watch a CI pipeline.`}),(0,K.jsx)(i,{onClick:()=>v({}),children:`Add a job`})]})]})]}),(0,K.jsx)(re,{open:!!_,onClose:()=>v(null),commands:s?.commands??[],initial:_??void 0,onAdded:(e,t)=>{c(e),v(null);let n=e.jobs.find(e=>e.name===t);n&&(location.hash=r(`jobs`,encodeURIComponent(n.id)))}})]})}export{ie as Jobs};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{F as e,G as t,It as n,J as r,M as i,Ut as a,W as o,Zt as s,en as c,f as l,jt as u,mt as d,p as f,q as p,r as m,t as h,ut as g}from"./status-CL_clA0J.js";import{i as _}from"./markdown-GKy06efH.js";import{t as v}from"./external-link-pvVHJFAZ.js";import{x as y}from"./index-Bkx3fVbr.js";var b={name:`accessibility`,size:24,node:[[`circle`,{cx:`16`,cy:`4`,r:`1`,key:`1grugj`}],[`path`,{d:`m18 19 1-7-6 1`,key:`r0i19z`}],[`path`,{d:`m5 8 3-3 5.5 3-2.36 3.5`,key:`9ptxx2`}],[`path`,{d:`M4.24 14.5a5 5 0 0 0 6.88 6`,key:`10kmtu`}],[`path`,{d:`M13.76 17.5a5 5 0 0 0-6.88-6`,key:`2qq6rc`}]]};b.node;var x=u(b),S={name:`image-off`,size:24,node:[[`line`,{x1:`2`,x2:`22`,y1:`2`,y2:`22`,key:`a6p6uj`}],[`path`,{d:`M10.41 10.41a2 2 0 1 1-2.83-2.83`,key:`1bzlo9`}],[`line`,{x1:`13.5`,x2:`6`,y1:`13.5`,y2:`21`,key:`1q0aeu`}],[`line`,{x1:`18`,x2:`21`,y1:`12`,y2:`15`,key:`5mozeu`}],[`path`,{d:`M3.59 3.59A1.99 1.99 0 0 0 3 5v14a2 2 0 0 0 2 2h14c.55 0 1.052-.22 1.41-.59`,key:`mmje98`}],[`path`,{d:`M21 15V5a2 2 0 0 0-2-2H9`,key:`43el77`}]]};S.node;var C=u(S),w={name:`images`,size:24,node:[[`path`,{d:`m22 11-1.296-1.296a2.4 2.4 0 0 0-3.408 0L11 16`,key:`9kzy35`}],[`path`,{d:`M4 8a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2`,key:`1t0f0t`}],[`circle`,{cx:`13`,cy:`7`,r:`1`,fill:`currentColor`,key:`1obus6`}],[`rect`,{x:`8`,y:`2`,width:`14`,height:`14`,rx:`2`,key:`1gvhby`}]]};w.node;var T=u(w),E={name:`scan-eye`,size:24,node:[[`path`,{d:`M3 7V5a2 2 0 0 1 2-2h2`,key:`aa7l1z`}],[`path`,{d:`M17 3h2a2 2 0 0 1 2 2v2`,key:`4qcy5o`}],[`path`,{d:`M21 17v2a2 2 0 0 1-2 2h-2`,key:`6vwrx8`}],[`path`,{d:`M7 21H5a2 2 0 0 1-2-2v-2`,key:`ioqczr`}],[`circle`,{cx:`12`,cy:`12`,r:`1`,key:`41hilf`}],[`path`,{d:`M18.944 12.33a1 1 0 0 0 0-.66 7.5 7.5 0 0 0-13.888 0 1 1 0 0 0 0 .66 7.5 7.5 0 0 0 13.888 0`,key:`11ak4c`}]]};E.node;var D=u(E),O={name:`trending-down`,size:24,node:[[`path`,{d:`M16 17h6v-6`,key:`t6n2it`}],[`path`,{d:`m22 17-8.5-8.5-5 5L2 7`,key:`x473p`}]]};O.node;var k=u(O),A={name:`trending-up`,size:24,node:[[`path`,{d:`M16 7h6v6`,key:`box55l`}],[`path`,{d:`m22 7-8.5 8.5-5-5L2 17`,key:`1t1m79`}]]};A.node;var j=u(A),M=c(s(),1),N=p(),P=[`critical`,`serious`,`moderate`,`minor`],F={critical:`fail`,serious:`fail`,moderate:`warn`,minor:`neutral`},I=[{key:`lcp`,label:`LCP`,good:2500,poor:4e3,unit:`ms`},{key:`cls`,label:`CLS`,good:.1,poor:.25,unit:``},{key:`inp`,label:`INP`,good:200,poor:500,unit:`ms`},{key:`fcp`,label:`FCP`,good:1800,poor:3e3,unit:`ms`},{key:`ttfb`,label:`TTFB`,good:800,poor:1800,unit:`ms`},{key:`load`,label:`Load`,good:1/0,poor:1/0,unit:`ms`}],L={same:{label:`Same`,tone:`pass`},changed:{label:`Changed`,tone:`warn`},new:{label:`New baseline`,tone:`neutral`},no_baseline:{label:`No baseline`,tone:`neutral`}},R=e=>new Date(e).toLocaleString([],{month:`short`,day:`numeric`,hour:`numeric`,minute:`2-digit`}),z=(e,t)=>t?e>=1e3?`${(e/1e3).toFixed(1)}s`:`${Math.round(e)}ms`:e.toFixed(3).replace(/0+$/,``).replace(/\.$/,``),B=(e,t)=>`${e} ${t}${e===1?``:`s`}`,V=e=>{try{let t=new URL(e);return`${t.host}${t.pathname}${t.search}`}catch{return e}};function H(e){let t=[];return e.a11y&&t.push(e.a11y.failed?`${B(e.a11y.failed,`rule`)} failed`:`WCAG clean`),e.perf.over&&t.push(`${B(e.perf.over,`page`)} slow`),e.visual?.changed&&t.push(`${e.visual.changed} changed`),e.captured&&t.push(`not compared`),t.join(` · `)||`measured`}var U=e=>e.breaches?`bg-fail`:e.a11y?.critical||e.a11y?.serious||e.visual?.changed?`bg-warn`:`bg-pass`;function W({run:e,standard:t}){let n=e.report,[r,i]=(0,M.useState)(null);if(!n?.accessibility.pages)return(0,N.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`This run has no accessibility results: report.accessibility was off, or the run visited no page.`});let a=n.accessibility,o=n.breaches.filter(e=>e.kind===`accessibility`);return(0,N.jsxs)(`div`,{className:`flex flex-col gap-4`,children:[(0,N.jsxs)(`div`,{className:`flex flex-wrap gap-3`,children:[P.map(e=>(0,N.jsx)(l,{label:e.charAt(0).toUpperCase()+e.slice(1),value:String(a.byImpact[e]),note:`elements`},e)),(0,N.jsx)(l,{icon:y,label:`Rules passed`,value:String(a.passed.length),note:`${a.failed.length} failed · ${B(a.pages,`page`)}`})]}),(0,N.jsxs)(`p`,{className:`m-0 text-[13px] text-muted`,children:[t,` with `,n.engine||`axe`,`. `,n.budgets.accessibility?o.length?`${B(o.length,`budget`)} exceeded.`:`Within the budget.`:`No accessibility budget set: nothing fails on it (quality.budgets.accessibility).`]}),o.map((e,t)=>(0,N.jsxs)(`p`,{className:`m-0 rounded-[10px] bg-fail-soft px-3 py-2 text-sm text-fail [overflow-wrap:anywhere]`,children:[V(e.page),`: `,e.text]},t)),a.failed.length?(0,N.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:a.failed.map(t=>(0,N.jsx)(G,{f:t,examples:e.examples[t.rule]??[],open:r===t.rule,onToggle:()=>i(r===t.rule?null:t.rule)},t.rule))}):(0,N.jsx)(`p`,{className:`m-0 text-sm text-pass`,children:`No rule failed on the pages this run visited.`})]})}function G({f:e,examples:t,open:n,onToggle:r}){return(0,N.jsxs)(`li`,{className:`border-t border-line-soft first:border-t-0`,children:[(0,N.jsxs)(`button`,{type:`button`,"aria-expanded":n,onClick:r,className:`flex min-h-[56px] w-full cursor-pointer items-center gap-3 border-0 bg-transparent px-1 py-2 text-left text-ink hover:bg-surface-muted`,children:[(0,N.jsx)(h,{tone:F[e.impact],className:`w-[92px] flex-none justify-center`,children:e.impact}),(0,N.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,N.jsx)(`span`,{className:`text-sm [overflow-wrap:anywhere]`,children:e.help}),(0,N.jsxs)(`span`,{className:`truncate text-[12px] text-muted`,children:[e.rule,e.wcag?` · WCAG ${e.wcag}${e.level?` (${e.level})`:``}`:``,` · `,B(e.count,`element`),` on `,B(e.pages,`page`)]})]})]}),n&&(0,N.jsxs)(`div`,{className:`mb-3 ml-[104px] flex flex-col gap-2 text-sm max-sm:ml-2`,children:[t.length?t.map((e,t)=>(0,N.jsxs)(`div`,{className:`flex flex-col gap-1 rounded-[10px] bg-surface-muted p-2.5`,children:[(0,N.jsxs)(`span`,{className:`text-[12px] text-muted [overflow-wrap:anywhere]`,children:[V(e.page),e.target?` · ${e.target}`:``]}),e.html&&(0,N.jsx)(`code`,{className:`font-mono text-[12px] whitespace-pre-wrap text-ink-2 [overflow-wrap:anywhere]`,children:e.html})]},t)):(0,N.jsx)(`span`,{className:`text-muted`,children:`No elements were kept for this rule.`}),e.helpUrl&&_(e.helpUrl)===`web`&&(0,N.jsxs)(`a`,{href:e.helpUrl,target:`_blank`,rel:`noreferrer noopener`,className:`inline-flex items-center gap-1 self-start text-[13px]`,children:[`How to fix it `,(0,N.jsx)(v,{size:13,"aria-hidden":`true`})]})]})]})}function K({run:e}){let t=e.report;if(!t?.performance.pages.length)return(0,N.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`This run has no timings: the run visited no page.`});let n=(e,t)=>e>t.poor?`poor`:e>t.good?`needs improvement`:`good`,i={poor:`text-fail`,"needs improvement":`text-warn`,good:`text-pass`};return(0,N.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[(0,N.jsxs)(`p`,{className:`m-0 text-[13px] text-muted`,children:[`Core Web Vitals per page against Google's thresholds: `,(0,N.jsx)(`span`,{className:`text-pass`,children:`good`}),`, `,(0,N.jsx)(`span`,{className:`text-warn`,children:`needs improvement`}),`, `,(0,N.jsx)(`span`,{className:`text-fail`,children:`poor`}),`.`,` `,t.budgets.performance?`Pages over the budget in quality.budgets.performance are marked.`:`No performance budget set: nothing fails on timings (quality.budgets.performance).`]}),(0,N.jsx)(`div`,{className:`overflow-x-auto`,children:(0,N.jsxs)(`table`,{className:`w-full min-w-[640px] border-collapse text-sm`,children:[(0,N.jsx)(`thead`,{children:(0,N.jsxs)(`tr`,{className:`text-left text-[12px] text-muted`,children:[(0,N.jsx)(`th`,{className:`py-2 pr-3 font-medium`,children:`Page`}),I.map(e=>(0,N.jsx)(`th`,{className:`px-2 py-2 text-right font-medium`,children:e.label},e.key)),(0,N.jsx)(`th`,{className:`py-2 pl-2 text-right font-medium`,children:`Requests`})]})}),(0,N.jsx)(`tbody`,{children:t.performance.pages.map((e,t)=>(0,N.jsxs)(`tr`,{className:`border-t border-line-soft align-top`,children:[(0,N.jsxs)(`td`,{className:`py-2 pr-3`,children:[(0,N.jsx)(`span`,{className:`block font-mono text-[12px] [overflow-wrap:anywhere]`,children:V(e.url)}),e.over.length>0&&(0,N.jsx)(`span`,{className:`text-[12px] text-fail`,children:e.over.join(`, `)})]}),I.map(t=>{let a=e[t.key];return(0,N.jsxs)(`td`,{className:r(`px-2 py-2 text-right tabular-nums`,typeof a==`number`&&t.good!==1/0?i[n(a,t)]:typeof a==`number`?`text-ink-2`:`text-muted`),children:[typeof a==`number`?z(a,t.unit):`–`,typeof a==`number`&&t.good!==1/0&&(0,N.jsxs)(`span`,{className:`sr-only`,children:[` (`,n(a,t),`)`]})]},t.key)}),(0,N.jsx)(`td`,{className:`py-2 pl-2 text-right text-muted tabular-nums`,children:e.requests?`${e.requests} · ${e.kb} KB`:`–`})]},t))})]})})]})}function q({run:e,index:t,which:n,label:r}){let[i,o]=(0,M.useState)(``),[s,c]=(0,M.useState)(!1);return(0,M.useEffect)(()=>{let r=!1;return o(``),c(!1),a.image(e,t,n).then(e=>!r&&o(e),()=>!r&&c(!0)),()=>{r=!0}},[e,t,n]),(0,N.jsxs)(`figure`,{className:`m-0 flex min-w-0 flex-col gap-1.5`,children:[(0,N.jsx)(`figcaption`,{className:`text-[12px] font-medium text-muted`,children:r}),i?(0,N.jsx)(`img`,{src:i,alt:`${r} of the page`,className:`block h-auto w-full rounded-[10px] border border-line-soft bg-surface-muted`}):(0,N.jsx)(`span`,{className:`flex h-[120px] items-center justify-center gap-2 rounded-[10px] border border-line-soft bg-surface-muted text-[12px] text-muted`,children:s?(0,N.jsxs)(N.Fragment,{children:[(0,N.jsx)(C,{size:14,"aria-hidden":`true`}),` Not available`]}):`Loading…`})]})}function J({run:e,budget:n,onChange:o}){let[s,c]=(0,M.useState)(null),[l,u]=(0,M.useState)(``),[d,f]=(0,M.useState)(!1),[p,m]=(0,M.useState)(``),[g,_]=(0,M.useState)(null),v=e.visual?.pages??[],b=v.find(e=>e.status===`changed`)?.index??null;(0,M.useEffect)(()=>_(b),[e.id,b]);let x=async e=>{f(!0),m(``);try{o(await e()),c(null),u(``)}catch(e){m(e.message)}finally{f(!1)}};if(!e.visual)return e.captured?(0,N.jsxs)(`div`,{className:`flex flex-col items-start gap-3`,children:[(0,N.jsxs)(`p`,{className:`m-0 text-sm text-ink-2`,children:[`This run captured `,B(e.captured,`page`),` but they were not compared with the baselines yet.`]}),(0,N.jsxs)(t,{variant:`primary`,disabled:d,onClick:()=>x(()=>a.compare(e.id)),children:[(0,N.jsx)(D,{size:16,"aria-hidden":`true`}),`Compare with the baselines`]}),p&&(0,N.jsx)(`p`,{className:`m-0 text-sm text-fail`,children:p})]}):(0,N.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`This run has no visual captures: quality.visual.enabled was off, or the run visited no page.`});let S=v.filter(e=>e.status===`changed`);return(0,N.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[(0,N.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,N.jsxs)(`p`,{className:`m-0 flex-1 text-[13px] text-muted`,children:[`Each page captured with animations frozen and masked regions hidden, against its approved baseline. `,n?`A page over quality.budgets.visual fails the run.`:`No visual budget set: a change does not fail the run (quality.budgets.visual).`]}),S.length>1&&(0,N.jsxs)(t,{disabled:d,onClick:()=>c(S.map(e=>e.url)),children:[`Approve all `,S.length,` changes`]})]}),s&&(0,N.jsxs)(`div`,{role:`alertdialog`,"aria-label":`Approve as baseline`,className:`flex flex-col gap-3 rounded-[12px] border border-accent-line bg-accent-soft p-4`,children:[(0,N.jsxs)(`span`,{className:`font-medium`,children:[`Make this run's capture the baseline of `,s.length===1?V(s[0]):B(s.length,`page`),`?`]}),(0,N.jsx)(`span`,{className:`text-sm text-ink-2`,children:`Later runs are compared with it. Approve only a change that was intended; one that was not is a visual defect.`}),(0,N.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm`,children:[`Why it changed (kept with the baseline)`,(0,N.jsx)(i,{value:l,maxLength:300,placeholder:`New checkout banner`,onChange:e=>u(e.target.value),className:`max-w-[480px] bg-bg`})]}),(0,N.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,N.jsxs)(t,{variant:`primary`,disabled:d,onClick:()=>x(()=>a.approve(e.id,s,l.trim()||void 0)),children:[(0,N.jsx)(y,{size:16,"aria-hidden":`true`}),`Approve`]}),(0,N.jsx)(t,{onClick:()=>c(null),children:`Cancel`})]})]}),p&&(0,N.jsx)(`p`,{className:`m-0 text-sm text-fail`,children:p}),(0,N.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:v.map(n=>(0,N.jsxs)(`li`,{className:`border-t border-line-soft first:border-t-0`,children:[(0,N.jsxs)(`button`,{type:`button`,"aria-expanded":g===n.index,onClick:()=>_(g===n.index?null:n.index),className:`flex min-h-[52px] w-full cursor-pointer items-center gap-3 border-0 bg-transparent px-1 py-2 text-left text-ink hover:bg-surface-muted`,children:[(0,N.jsx)(T,{size:16,"aria-hidden":`true`,className:`flex-none text-muted`}),(0,N.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,N.jsx)(`span`,{className:`text-sm [overflow-wrap:anywhere]`,children:n.title||V(n.url)}),(0,N.jsxs)(`span`,{className:`truncate text-[12px] text-muted`,children:[V(n.url),n.size?` · ${n.size}`:``,n.resized?` · resized ${n.resized}`:``]})]}),n.status===`changed`&&(0,N.jsxs)(`span`,{className:r(`text-sm tabular-nums`,n.over?`text-fail`:`text-warn`),children:[n.diffPercent,`%`,n.limit===void 0?``:` / ${n.limit}%`]}),(0,N.jsx)(h,{tone:n.over?`fail`:L[n.status].tone,children:n.over?`Over budget`:L[n.status].label})]}),g===n.index&&(0,N.jsxs)(`div`,{className:`mb-4 flex flex-col gap-3 px-1`,children:[(0,N.jsxs)(`div`,{className:`grid grid-cols-[repeat(auto-fit,minmax(160px,1fr))] gap-3`,children:[n.images.baseline&&(0,N.jsx)(q,{run:e.id,index:n.index,which:`baseline`,label:`Baseline`}),n.images.current&&(0,N.jsx)(q,{run:e.id,index:n.index,which:`current`,label:`This run`}),n.images.diff&&(0,N.jsx)(q,{run:e.id,index:n.index,which:`diff`,label:`Difference (in red)`})]}),n.status===`changed`&&(0,N.jsxs)(t,{className:`self-start`,disabled:d,onClick:()=>c([n.url]),children:[(0,N.jsx)(y,{size:16,"aria-hidden":`true`}),`Approve as the new baseline`]})]})]},n.index))})]})}function Y({values:e}){if(e.length<2)return null;let t=Math.max(1,...e),n=e.map((n,r)=>`${r/(e.length-1)*96+2},${22-n/t*20}`).join(` `);return(0,N.jsx)(`svg`,{width:`100`,height:`24`,viewBox:`0 0 100 24`,"aria-label":`Violations over the last ${e.length} runs: ${e.join(`, `)}`,role:`img`,className:`flex-none text-muted`,children:(0,N.jsx)(`polyline`,{points:n,fill:`none`,stroke:`currentColor`,strokeWidth:`1.5`,strokeLinejoin:`round`})})}function X({icon:e,className:t,lines:n}){return n.length?(0,N.jsxs)(`span`,{className:r(`flex items-start gap-1.5 text-[13px]`,t),children:[(0,N.jsx)(e,{size:14,"aria-hidden":`true`,className:`mt-0.5 flex-none`}),(0,N.jsx)(`span`,{className:`[overflow-wrap:anywhere]`,children:n.join(` · `)})]}):null}function Z({trends:e}){return e.length?(0,N.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:e.map(e=>{let t=e.a11y.latest?P.reduce((t,n)=>t+(e.a11y.latest[n]||0),0):void 0;return(0,N.jsxs)(`li`,{className:`flex flex-col gap-1.5 border-t border-line-soft py-3 first:border-t-0`,children:[(0,N.jsxs)(`span`,{className:`flex min-w-0 flex-wrap items-center gap-3`,children:[(0,N.jsx)(`span`,{className:`min-w-0 flex-[1_1_240px] font-mono text-[13px] [overflow-wrap:anywhere]`,children:e.page}),(0,N.jsx)(Y,{values:e.a11y.trail}),(0,N.jsxs)(`span`,{className:`text-[12px] text-muted`,children:[B(e.runs,`run`),t===void 0?``:` · ${B(t,`violation`)} now`]})]}),(0,N.jsx)(X,{icon:j,className:`text-fail`,lines:[...e.perf.worse.map(e=>`slower: ${e}`),...e.a11y.newRules.map(e=>`newly failing: ${e}`)]}),(0,N.jsx)(X,{icon:k,className:`text-pass`,lines:[...e.perf.better.map(e=>`faster: ${e}`),...e.a11y.fixedRules.map(e=>`fixed: ${e}`)]})]},e.page)})}):(0,N.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`No history yet: every browser run adds each page's accessibility and timings to quality-history.json in the reports folder.`})}function Q({id:r,view:i,onChanged:o}){let[s,c]=(0,M.useState)(null),[l,u]=(0,M.useState)(``),[d,p]=(0,M.useState)(`accessibility`),[m,h]=(0,M.useState)(``);if((0,M.useEffect)(()=>{let e=!1;return c(null),a.run(r).then(t=>{if(e)return;c(t),u(``);let n=t.visual?.pages.some(e=>e.status===`changed`)||!t.visual&&t.captured;p(t.report?.breaches.some(e=>e.kind===`performance`)&&!t.report.accessibility.failed.length?`performance`:n&&!t.report?.accessibility.failed.length?`visual`:t.report?.accessibility.pages?`accessibility`:t.report?.performance.pages.length?`performance`:`visual`)},t=>!e&&u(t.message)),()=>{e=!0}},[r]),l)return(0,N.jsx)(`p`,{className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail`,children:l});if(!s)return(0,N.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`Loading…`});let _=async()=>{try{let{id:t}=await n.start(`Explain the accessibility, performance and visual results of the run in ${s.dir}: call quality_report and visual_compare with run_dir ${s.dir}. Say what matters most to fix first, and why.`,{});location.hash=e(`chats`,t)}catch(e){h(e.message)}},v={accessibility:s.report?.accessibility.failed.length??0,performance:s.report?.performance.pages.filter(e=>e.over.length).length??0,visual:s.visual?.pages.filter(e=>e.status===`changed`).length??0};return(0,N.jsxs)(`section`,{"aria-labelledby":`run-h`,className:`flex min-w-0 flex-1 flex-col gap-4`,children:[(0,N.jsxs)(`div`,{className:`flex flex-wrap items-start gap-3`,children:[(0,N.jsxs)(`div`,{className:`flex min-w-0 flex-[1_1_260px] flex-col gap-1`,children:[(0,N.jsx)(`h2`,{id:`run-h`,className:`m-0 text-[22px] font-semibold tracking-[-0.01em] [overflow-wrap:anywhere]`,children:s.goal}),(0,N.jsxs)(`span`,{className:`text-sm text-muted [overflow-wrap:anywhere]`,children:[R(s.at),` · `,(0,N.jsx)(`span`,{className:`font-mono text-[12px]`,children:s.dir})]})]}),(0,N.jsxs)(t,{variant:`ghost`,onClick:()=>void _(),children:[(0,N.jsx)(g,{size:16,"aria-hidden":`true`}),`Ask in a chat`]})]}),m&&(0,N.jsx)(`p`,{className:`m-0 text-sm text-fail`,children:m}),(0,N.jsxs)(`div`,{children:[(0,N.jsx)(f,{label:`Results`,current:d,onPick:p,tabs:[{id:`accessibility`,label:`Accessibility${v.accessibility?` (${v.accessibility})`:``}`},{id:`performance`,label:`Performance${v.performance?` (${v.performance})`:``}`},{id:`visual`,label:`Visual${v.visual?` (${v.visual})`:``}`}]}),(0,N.jsxs)(`div`,{role:`tabpanel`,id:`panel-${d}`,"aria-labelledby":`tab-${d}`,children:[d===`accessibility`&&(0,N.jsx)(W,{run:s,standard:i.standard}),d===`performance`&&(0,N.jsx)(K,{run:s}),d===`visual`&&(0,N.jsx)(J,{run:s,budget:i.budgets.visual,onChange:e=>{c(e),o()}})]})]}),d===`accessibility`&&s.report?.accessibility.pages?(0,N.jsx)(`p`,{className:`m-0 text-[12px] text-muted`,children:i.disclaimer}):null]})}function $({sub:t}){let[n,i]=(0,M.useState)(null),[s,c]=(0,M.useState)(``),[l,u]=(0,M.useState)(t===`trends`?`trends`:`runs`),p=(0,M.useCallback)(()=>{a.view().then(e=>{i(e),c(``)},e=>c(e.message))},[]);(0,M.useEffect)(p,[p]);let h=t&&t!==`trends`?decodeURIComponent(t):``;(0,M.useEffect)(()=>{t===`trends`?u(`trends`):t&&u(`runs`)},[t]);let g=(0,M.useMemo)(()=>n?.runs.find(e=>e.id===h)||n?.runs[0],[n,h]),_=n?.trends.filter(e=>e.perf.worse.length||e.a11y.newRules.length).length??0;return(0,N.jsxs)(`div`,{className:`flex min-h-screen flex-col`,children:[(0,N.jsx)(m,{}),(0,N.jsxs)(`main`,{className:`@container mx-auto box-border flex w-full max-w-[1180px] flex-auto flex-col gap-4 px-6 pt-10 pb-10`,children:[(0,N.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,N.jsx)(o,{icon:d,size:`lg`}),(0,N.jsx)(`h1`,{className:`m-0 flex-1 text-[26px] font-semibold tracking-[-0.01em]`,children:`Quality`})]}),(0,N.jsxs)(`p`,{className:`m-0 max-w-[74ch] text-sm leading-relaxed text-muted`,children:[`What every browser run measures beside its steps: accessibility against `,n?.standard||`WCAG`,`, Core Web Vitals, and each page against its approved visual baseline. Budgets in forgeqe.yaml (quality.budgets) decide what fails a run.`]}),s&&(0,N.jsx)(`p`,{role:`alert`,className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail [overflow-wrap:anywhere]`,children:s}),!n&&!s&&(0,N.jsx)(`p`,{role:`status`,className:`m-0 text-sm text-muted`,children:`Loading…`}),n&&(0,N.jsx)(f,{label:`Quality`,current:l,onPick:t=>{u(t),t===`trends`?location.hash=e(`quality`,`trends`):location.hash=g?e(`quality`,encodeURIComponent(g.id)):e(`quality`)},tabs:[{id:`runs`,label:`Runs (${n.runs.length})`},{id:`trends`,label:`Trends${_?` (${_} worse)`:``}`}]}),n&&l===`trends`&&(0,N.jsx)(`div`,{role:`tabpanel`,id:`panel-trends`,"aria-labelledby":`tab-trends`,children:(0,N.jsx)(Z,{trends:n.trends})}),n&&l===`runs`&&(n.runs.length?(0,N.jsxs)(`div`,{role:`tabpanel`,id:`panel-runs`,"aria-labelledby":`tab-runs`,className:`flex flex-col gap-6 @2xl:flex-row @2xl:items-start @2xl:gap-8`,children:[(0,N.jsx)(`nav`,{"aria-label":`Runs`,className:`flex min-w-0 flex-col gap-1 @2xl:sticky @2xl:top-6 @2xl:w-[250px] @2xl:flex-none`,children:(0,N.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:n.runs.map(t=>(0,N.jsx)(`li`,{children:(0,N.jsxs)(`a`,{href:e(`quality`,encodeURIComponent(t.id)),"aria-current":g?.id===t.id?`page`:void 0,className:r(`flex min-h-[60px] items-center gap-3 rounded-[12px] px-3 py-2 text-ink no-underline`,g?.id===t.id?`bg-surface-strong hover:text-ink`:`hover:bg-surface-muted hover:text-ink`),children:[(0,N.jsx)(`span`,{className:r(`size-2.5 flex-none rounded-full`,U(t)),"aria-label":t.breaches?`over budget`:`within budgets`}),(0,N.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,N.jsx)(`span`,{className:`truncate text-[15px] font-medium`,children:t.goal}),(0,N.jsxs)(`span`,{className:`truncate text-[13px] text-muted`,children:[R(t.at),` · `,H(t)]})]})]})},t.id))})}),g&&(0,N.jsx)(Q,{id:g.id,view:n,onChanged:p})]}):(0,N.jsxs)(`div`,{className:`flex flex-col items-start gap-3 rounded-[18px] border border-line p-6`,children:[(0,N.jsx)(x,{size:24,"aria-hidden":`true`,className:`text-muted`}),(0,N.jsx)(`p`,{className:`m-0 max-w-[62ch] leading-relaxed text-ink-2`,children:`No run has measured anything yet. Every browser run checks accessibility, timings and visual changes of the pages it visits; run a flow from a chat or the Record page, and its results show here.`})]}))]})]})}export{$ as Quality};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{A as e,F as t,G as n,It as r,J as i,M as a,N as o,U as s,W as c,Wt as l,Y as u,Zt as d,ct as f,en as p,et as m,gt as h,jt as g,q as _,r as v,t as y,wt as b,yt as x}from"./status-CL_clA0J.js";import{n as S,r as C,t as w}from"./rotate-ccw-BL6YOAPy.js";import{a as T,t as E}from"./markdown-GKy06efH.js";import{t as D}from"./file-chart-column-increasing-b-Yh6rmz.js";import{t as O}from"./sticky-note-DIQ2FWMw.js";import{t as k}from"./trash-DIUJbRpw.js";import{c as A,h as j,m as M,s as N,v as P,x as F}from"./index-Bkx3fVbr.js";var I={name:`crosshair`,size:24,node:[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`line`,{x1:`22`,x2:`18`,y1:`12`,y2:`12`,key:`l9bcsi`}],[`line`,{x1:`6`,x2:`2`,y1:`12`,y2:`12`,key:`13hhkx`}],[`line`,{x1:`12`,x2:`12`,y1:`6`,y2:`2`,key:`10w3f3`}],[`line`,{x1:`12`,x2:`12`,y1:`22`,y2:`18`,key:`15g9kq`}]]};I.node;var L=g(I),R={name:`dot`,size:24,node:[[`circle`,{cx:`12`,cy:`12`,r:`1`,key:`41hilf`}]]};R.node;var ee=g(R),z={name:`hourglass`,size:24,node:[[`path`,{d:`M5 22h14`,key:`ehvnwv`}],[`path`,{d:`M5 2h14`,key:`pdyrp9`}],[`path`,{d:`M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22`,key:`1d314k`}],[`path`,{d:`M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2`,key:`1vvvr6`}]]};z.node;var B=g(z),V={name:`keyboard`,size:24,node:[[`path`,{d:`M10 8h.01`,key:`1r9ogq`}],[`path`,{d:`M12 12h.01`,key:`1mp3jc`}],[`path`,{d:`M14 8h.01`,key:`1primd`}],[`path`,{d:`M16 12h.01`,key:`1l6xoz`}],[`path`,{d:`M18 8h.01`,key:`emo2bl`}],[`path`,{d:`M6 8h.01`,key:`x9i8wu`}],[`path`,{d:`M7 16h10`,key:`wp8him`}],[`path`,{d:`M8 12h.01`,key:`czm47f`}],[`rect`,{width:`20`,height:`16`,x:`2`,y:`4`,rx:`2`,key:`18n3k1`}]]};V.node;var H=g(V),U={name:`mouse-pointer-click`,size:24,node:[[`path`,{d:`M14 4.1 12 6`,key:`ita8i4`}],[`path`,{d:`m5.1 8-2.9-.8`,key:`1go3kf`}],[`path`,{d:`m6 12-1.9 2`,key:`mnht97`}],[`path`,{d:`M7.2 2.2 8 5.1`,key:`1cfko1`}],[`path`,{d:`M9.037 9.69a.498.498 0 0 1 .653-.653l11 4.5a.5.5 0 0 1-.074.949l-4.349 1.041a1 1 0 0 0-.74.739l-1.04 4.35a.5.5 0 0 1-.95.074z`,key:`s0h3yz`}]]};U.node;var W=g(U),G={name:`pause`,size:24,node:[[`rect`,{x:`14`,y:`3`,width:`5`,height:`18`,rx:`1`,key:`kaeet6`}],[`rect`,{x:`5`,y:`3`,width:`5`,height:`18`,rx:`1`,key:`1wsw3u`}]]};G.node;var te=g(G),K={name:`upload`,size:24,node:[[`path`,{d:`M12 3v12`,key:`1x0j5s`}],[`path`,{d:`m17 8-5-5-5 5`,key:`7q97r8`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}]]};K.node;var ne=g(K),q={name:`variable`,size:24,node:[[`path`,{d:`M8 21s-4-3-4-9 4-9 4-9`,key:`uto9ud`}],[`path`,{d:`M16 3s4 3 4 9-4 9-4 9`,key:`4w2vsq`}],[`line`,{x1:`15`,x2:`9`,y1:`9`,y2:`15`,key:`f7djnv`}],[`line`,{x1:`9`,x2:`15`,y1:`9`,y2:`15`,key:`1shsy8`}]]};q.node;var J=g(q),Y=p(d(),1),X=_(),re={navigate:P,back:C,click:W,dblclick:W,input:H,press:H,select:j,upload:ne,expect:F,wait_for:B,capture:J,note:O,launch:f,double_click:W,type:H,keys:H},ie=e=>re[e]||(e.startsWith(`expect`)?F:ee),ae=new Set([`starting`,`recording`,`paused`,`stopping`]);function Z(e,t){let n=Math.max(0,Math.round(((t?Date.parse(t):Date.now())-Date.parse(e))/1e3));return n<3600?`${Math.floor(n/60)}:${String(n%60).padStart(2,`0`)}`:`${Math.floor(n/3600)}h ${Math.floor(n%3600/60)}m`}var oe=e=>new Date(e).toLocaleString([],{month:`short`,day:`numeric`,hour:`numeric`,minute:`2-digit`});async function Q(e){let{id:n}=await r.start(`Turn the recording in ${e} into a test: call write_test with run_dir ${e}. Show me the plan before you write.`,{});location.hash=t(`chats`,n)}async function $(e){let{id:n}=await r.start(`Replay the desktop workflow ${e}: call desktop_replay with workflow ${e}, and tell me how each step went.`,{});location.hash=t(`chats`,n)}function se({s:e,live:t,onDelete:r}){let a=ie(e.op);return(0,X.jsxs)(`li`,{className:`group flex min-h-[44px] items-start gap-3 border-t border-line-soft py-2 first:border-t-0`,children:[(0,X.jsx)(`span`,{className:`w-6 flex-none pt-0.5 text-right text-[13px] text-muted tabular-nums`,children:e.n}),(0,X.jsx)(a,{size:16,"aria-hidden":`true`,className:i(`mt-0.5 flex-none`,e.op.startsWith(`expect`)?`text-pass`:`text-muted`)}),(0,X.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,X.jsx)(`span`,{className:`text-sm [overflow-wrap:anywhere]`,children:e.text}),(e.note||e.dynamic||e.inferred||e.case_step)&&(0,X.jsxs)(`span`,{className:`flex flex-wrap gap-x-2 text-[12px] text-muted`,children:[e.case_step?(0,X.jsxs)(`span`,{children:[`case step `,e.case_step]}):null,e.dynamic&&(0,X.jsx)(`span`,{children:`value changes between runs`}),e.inferred&&(0,X.jsx)(`span`,{children:`added by ForgeQE`}),e.note&&(0,X.jsxs)(`span`,{className:`[overflow-wrap:anywhere]`,children:[`note: `,e.note]})]})]}),t&&e.n>1&&(0,X.jsx)(n,{variant:`icon`,size:`icon`,"aria-label":`Delete step ${e.n}`,onClick:r,className:`size-8 flex-none opacity-60 group-hover:opacity-100`,children:(0,X.jsx)(k,{size:15,"aria-hidden":`true`})})]})}function ce({s:e,send:t,busy:r}){let[o,c]=(0,Y.useState)(``),[,l]=(0,Y.useState)(0);(0,Y.useEffect)(()=>{let e=setInterval(()=>l(e=>e+1),1e3);return()=>clearInterval(e)},[]);let u=(0,Y.useRef)(null);(0,Y.useEffect)(()=>{u.current&&(u.current.scrollTop=u.current.scrollHeight)},[e.log.length]);let d=e.kind===`desktop`,p=e.state===`paused`,h=e.log.at(-1)?.level===`warn`&&/press Stop again/i.test(e.log.at(-1).text),g=Math.max(0,...e.steps.map(e=>e.case_step||0)),_=(i,a,o,s)=>(0,X.jsxs)(n,{disabled:r||e.state===`starting`,onClick:()=>t({cmd:i}),title:s,children:[(0,X.jsx)(o,{size:16,"aria-hidden":`true`}),a]},i);return(0,X.jsxs)(`div`,{className:`flex flex-col gap-5`,children:[(0,X.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-4 gap-y-3 rounded-[14px] border border-line-soft px-4 py-3`,children:[p?(0,X.jsx)(`span`,{"aria-hidden":`true`,className:`size-3 flex-none rounded-full bg-warn`}):(0,X.jsx)(s,{state:e.state===`starting`?`waiting`:e.state===`stopping`?`working`:`recording`,px:20}),(0,X.jsxs)(`span`,{className:`flex min-w-0 flex-[1_1_260px] flex-col gap-0.5`,children:[(0,X.jsx)(`span`,{className:`font-medium [overflow-wrap:anywhere]`,children:e.name}),(0,X.jsxs)(`span`,{className:`truncate text-[13px] text-muted`,children:[e.state===`starting`?d?`Starting ${e.app}…`:`Opening the browser…`:e.state===`stopping`?`Saving the workflow…`:p?`Paused: what you do now is not recorded`:`Recording`,` · `,Z(e.startedAt),` ·`,` `,d?`${e.app}, a desktop app`:e.url]})]}),!d&&(0,X.jsxs)(n,{disabled:r||e.state===`starting`,onClick:()=>t({cmd:`pause`}),children:[p?(0,X.jsx)(f,{size:16,"aria-hidden":`true`}):(0,X.jsx)(te,{size:16,"aria-hidden":`true`}),p?`Resume`:`Pause`]}),(0,X.jsxs)(n,{disabled:r||e.state===`stopping`,onClick:()=>t({cmd:`stop`,...h?{force:!0}:{}}),className:`border-fail bg-fail text-white hover:bg-fail hover:text-white hover:opacity-90`,children:[(0,X.jsx)(m,{size:14,fill:`currentColor`,"aria-hidden":`true`}),h?`Stop anyway`:`Stop and save`]})]}),d?(0,X.jsxs)(`p`,{className:`m-0 rounded-[12px] bg-accent-soft px-4 py-3 text-sm leading-relaxed text-ink-2`,children:[e.app,` is open on this computer: do the steps in it. Only clicks and keys in its own windows are recorded; password fields are saved as secret names, never as what you type. In the app, `,(0,X.jsx)(`strong`,{children:`Ctrl+Shift+F11`}),` over a control adds a check of what it shows, and `,(0,X.jsx)(`strong`,{children:`Ctrl+Shift+F12`}),` or closing the app stops.`]}):(0,X.jsxs)(X.Fragment,{children:[(0,X.jsxs)(`p`,{className:`m-0 rounded-[12px] bg-accent-soft px-4 py-3 text-sm leading-relaxed text-ink-2`,children:[`The browser is open on this computer: do the steps there. The Recorder panel in the page does the same as these buttons; in the page, `,e.leader,` then a letter: `,(0,X.jsx)(`strong`,{children:`A`}),` check, `,(0,X.jsx)(`strong`,{children:`V`}),` keep a value, `,(0,X.jsx)(`strong`,{children:`W`}),` wait for,`,` `,(0,X.jsx)(`strong`,{children:`D`}),` test data, `,(0,X.jsx)(`strong`,{children:`N`}),` note, `,(0,X.jsx)(`strong`,{children:`P`}),` pause, `,(0,X.jsx)(`strong`,{children:`Z`}),` undo, `,(0,X.jsx)(`strong`,{children:`S`}),` stop.`]}),(0,X.jsxs)(`div`,{className:`flex flex-wrap gap-2`,role:`toolbar`,"aria-label":`Recorder`,children:[_(`assert`,`Check an element`,L,`Pick an element in the page to check`),_(`variable`,`Keep a value`,J,`Pick an element whose text later steps use`),_(`wait`,`Wait for`,B,`Pick an element the test waits for`),_(`data`,`Test data`,x,`The last value you typed becomes test data`),_(`note`,`Note`,O,`A note for the step, for whoever reads the test`),_(`undo`,`Undo`,A,`Remove the last step`)]}),(0,X.jsxs)(`form`,{className:`flex flex-wrap gap-2`,onSubmit:e=>{e.preventDefault(),o.trim()&&(t({cmd:`chat`,text:o.trim()}),c(``))},children:[(0,X.jsx)(`label`,{htmlFor:`rec-check`,className:`sr-only`,children:`Describe a check`}),(0,X.jsx)(a,{id:`rec-check`,value:o,maxLength:2e3,placeholder:`Describe a check: the total shows "$40.00", or note: …`,onChange:e=>c(e.target.value),className:`min-w-0 flex-[1_1_320px]`}),(0,X.jsxs)(n,{type:`submit`,variant:`primary`,disabled:r||!o.trim()||e.state===`starting`,children:[(0,X.jsx)(N,{size:16,"aria-hidden":`true`}),`Add`]})]})]}),(0,X.jsxs)(`div`,{className:`flex flex-wrap items-start gap-x-6 gap-y-5`,children:[(0,X.jsxs)(`section`,{"aria-labelledby":`rec-steps`,className:`flex min-w-0 flex-[999_1_420px] flex-col gap-2 rounded-[14px] border border-line-soft px-4 py-3`,children:[(0,X.jsxs)(`h2`,{id:`rec-steps`,className:`m-0 flex items-center gap-2 text-base font-semibold`,children:[(0,X.jsx)(j,{size:18,"aria-hidden":`true`,className:`text-accent`}),`Steps`,(0,X.jsx)(`span`,{className:`text-sm font-normal text-muted`,children:e.summary})]}),e.steps.length?(0,X.jsx)(`ol`,{className:`m-0 flex list-none flex-col p-0`,"aria-live":`polite`,children:e.steps.map(e=>(0,X.jsx)(se,{s:e,live:!d,onDelete:()=>t({cmd:`delete`,n:e.n})},`${e.n}-${e.text}`))}):(0,X.jsx)(`p`,{className:`m-0 py-2 text-sm text-muted`,children:e.state===`starting`?d?`Starting ${e.app}…`:`Opening the browser…`:`Nothing yet: do the first step in ${d?e.app:`the browser`}.`})]}),(0,X.jsxs)(`div`,{className:`flex min-w-0 flex-[1_1_300px] flex-col gap-5`,children:[e.case&&(0,X.jsxs)(`section`,{"aria-labelledby":`rec-case`,className:`flex flex-col gap-2 rounded-[14px] border border-line-soft px-4 py-3`,children:[(0,X.jsxs)(`h2`,{id:`rec-case`,className:`m-0 flex items-center gap-2 text-base font-semibold`,children:[(0,X.jsx)(j,{size:18,"aria-hidden":`true`,className:`text-accent`}),e.case.id]}),(0,X.jsxs)(`p`,{className:`m-0 text-[13px] text-muted`,children:[e.case.title,`. Each step you record belongs to the case step you are on: pick one to record it.`]}),(0,X.jsx)(`ol`,{className:`m-0 flex list-none flex-col p-0`,children:e.case.steps.map(n=>{let a=e.steps.some(e=>e.case_step===n.n);return(0,X.jsx)(`li`,{className:`border-t border-line-soft first:border-t-0`,children:(0,X.jsxs)(`button`,{type:`button`,disabled:r,"aria-current":n.n===g?`step`:void 0,onClick:()=>t({cmd:`case_step`,n:n.n}),className:i(`flex w-full cursor-pointer items-start gap-2 rounded-[10px] border-0 bg-transparent px-2 py-2 text-left text-sm text-ink hover:bg-surface-muted`,n.n===g&&`bg-accent-soft`),children:[a?(0,X.jsx)(F,{size:16,"aria-hidden":`true`,className:`mt-0.5 flex-none text-pass`}):(0,X.jsx)(`span`,{className:`mt-0.5 w-4 flex-none text-center text-[12px] text-muted`,children:n.n}),(0,X.jsxs)(`span`,{className:`flex min-w-0 flex-col`,children:[(0,X.jsx)(`span`,{className:`[overflow-wrap:anywhere]`,children:n.action}),n.expected&&(0,X.jsxs)(`span`,{className:`text-[12px] text-muted [overflow-wrap:anywhere]`,children:[`Expect: `,n.expected]})]})]})},n.n)})}),(0,X.jsx)(n,{disabled:r,onClick:()=>t({cmd:`case_next`}),className:`self-start`,children:`Next case step`})]}),(0,X.jsxs)(`section`,{"aria-labelledby":`rec-log`,className:`flex flex-col gap-2 rounded-[14px] border border-line-soft px-4 py-3`,children:[(0,X.jsx)(`h2`,{id:`rec-log`,className:`m-0 text-base font-semibold`,children:`What the recorder says`}),(0,X.jsx)(`div`,{ref:u,className:`flex max-h-[280px] flex-col gap-1 overflow-auto text-[13px]`,"aria-live":`polite`,children:e.log.length?e.log.map((e,t)=>(0,X.jsx)(`p`,{className:i(`m-0 [overflow-wrap:anywhere]`,e.level===`warn`?`text-warn`:e.level===`error`?`text-fail`:`text-ink-2`),children:e.text},t)):(0,X.jsxs)(`p`,{className:`m-0 text-muted`,children:[`Waiting for `,d?`the app`:`the browser`,`…`]})})]})]})]})]})}function le({s:e}){let t=e.result,[r,i]=(0,Y.useState)(``),[a,o]=(0,Y.useState)(!1);if(!t)return null;let s=e.kind===`desktop`;return(0,X.jsxs)(`section`,{"aria-labelledby":`rec-result`,className:`flex flex-col gap-3 rounded-[14px] border border-line-soft p-4`,children:[(0,X.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,X.jsx)(`h2`,{id:`rec-result`,className:`m-0 flex-1 text-base font-semibold [overflow-wrap:anywhere]`,children:e.name}),(0,X.jsx)(y,{tone:t.ok?`pass`:`fail`,children:t.ok?`Recorded`:`Not recorded`})]}),(0,X.jsxs)(`p`,{className:`m-0 text-sm text-ink-2`,children:[t.ok?t.summary||e.summary:t.error||`Nothing was recorded.`,` `,e.endedAt&&(0,X.jsxs)(`span`,{className:`text-muted`,children:[`· `,Z(e.startedAt,e.endedAt)]})]}),t.ok&&(0,X.jsxs)(`div`,{className:`flex flex-wrap gap-2 text-sm`,children:[(0,X.jsx)(y,{tone:`neutral`,icon:!1,children:u(e.steps.length,`step`)}),(0,X.jsxs)(y,{tone:t.checks?`pass`:`warn`,children:[t.checks,` check`,t.checks===1?``:`s`]}),t.variables.length>0&&(0,X.jsxs)(y,{tone:`neutral`,icon:!1,children:[`kept: `,t.variables.join(`, `)]}),t.testData.length>0&&(0,X.jsxs)(y,{tone:`neutral`,icon:!1,children:[`test data: `,t.testData.join(`, `)]})]}),!s&&t.secrets.length>0&&(0,X.jsxs)(`p`,{className:`m-0 text-sm text-ink-2`,children:[`The test reads`,` `,t.secrets.map((e,t)=>(0,X.jsxs)(`span`,{children:[t?`, `:``,(0,X.jsx)(`code`,{className:`font-mono text-[13px]`,children:e})]},e)),` `,`from the environment: what you typed there was not saved. Set `,t.secrets.length===1?`it`:`them`,` before the test runs.`]}),t.caseGaps.length>0&&(0,X.jsx)(`ul`,{className:`m-0 flex flex-col gap-1 pl-5 text-sm text-warn`,children:t.caseGaps.map(e=>(0,X.jsxs)(`li`,{children:[`No check for case step `,e.n,`: expects “`,e.expected,`”`]},e.n))}),(t.notes??[]).length>0&&(0,X.jsx)(`ul`,{className:`m-0 flex flex-col gap-1 pl-5 text-sm text-ink-2`,children:t.notes.map((e,t)=>(0,X.jsx)(`li`,{children:e},t))}),t.skipped.length>0&&(0,X.jsx)(`ul`,{className:`m-0 flex flex-col gap-1 pl-5 text-sm text-muted`,children:t.skipped.map((e,t)=>(0,X.jsxs)(`li`,{children:[`Not recorded: `,e.action,`: `,e.reason]},t))}),(0,X.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[s&&t.workflow&&(0,X.jsxs)(n,{variant:`primary`,disabled:a,onClick:async()=>{o(!0);try{await $(t.workflow)}catch(e){i(e.message),o(!1)}},children:[(0,X.jsx)(w,{size:16,"aria-hidden":`true`}),`Replay it`]}),s&&t.report&&(0,X.jsxs)(n,{onClick:async()=>i(await T(t.report)),children:[(0,X.jsx)(D,{size:16,"aria-hidden":`true`}),`Open the report`]}),!s&&t.ok&&(0,X.jsxs)(n,{variant:`primary`,disabled:a,onClick:async()=>{o(!0);try{await Q(e.runDir)}catch(e){i(e.message),o(!1)}},children:[(0,X.jsx)(N,{size:16,"aria-hidden":`true`}),`Write a test from it`]}),!s&&t.ok&&(0,X.jsxs)(n,{onClick:async()=>i(await T(`${e.runDir}/trace.md`)),children:[(0,X.jsx)(h,{size:16,"aria-hidden":`true`}),`Read the steps`]}),(0,X.jsx)(`span`,{className:`text-sm`,children:(0,X.jsx)(E,{path:s&&t.workflow?t.workflow:e.runDir})}),r&&(0,X.jsx)(`span`,{className:`text-[13px] text-fail`,children:r})]})]})}function ue({view:r,busy:s,onStart:c}){let[l,u]=(0,Y.useState)(r.session?.kind===`desktop`&&r.desktop.available?`desktop`:`web`),[d,f]=(0,Y.useState)(r.desktop.apps[0]??``),[p,m]=(0,Y.useState)(r.apps.length?`app`:`url`),[h,g]=(0,Y.useState)(r.apps[0]?.name??``),[_,v]=(0,Y.useState)(``),[y,x]=(0,Y.useState)(``),[S,C]=(0,Y.useState)(``),[w,T]=(0,Y.useState)(r.environment??``),[E,D]=(0,Y.useState)(!1),O=`flex flex-col gap-1.5`,k=l===`desktop`?r.desktop.available&&!!d:p===`app`?!!h:/^https?:\/\/\S+$/i.test(_.trim()),A=(e,t,n,r,a=!1)=>(0,X.jsxs)(`label`,{className:i(`flex min-h-[64px] flex-[1_1_240px] items-start gap-3 rounded-[12px] border px-3 py-2.5`,l===e?`border-accent bg-accent-soft`:`border-line-soft`,a?`cursor-not-allowed opacity-60`:`cursor-pointer hover:bg-surface-muted`),children:[(0,X.jsx)(`input`,{type:`radio`,name:`rec-kind`,checked:l===e,disabled:a,onChange:()=>u(e),className:`mt-1 size-4 accent-accent`}),(0,X.jsx)(r,{size:18,"aria-hidden":`true`,className:`mt-0.5 flex-none text-accent`}),(0,X.jsxs)(`span`,{className:`flex min-w-0 flex-col gap-0.5`,children:[(0,X.jsx)(`span`,{className:`text-sm font-medium`,children:t}),(0,X.jsx)(`span`,{className:`text-[13px] text-muted`,children:n})]})]});return(0,X.jsxs)(`form`,{className:`flex flex-col gap-4 rounded-[14px] border border-line-soft p-4`,onSubmit:e=>{if(e.preventDefault(),k){if(l===`desktop`)return c({kind:`desktop`,app:d,...y.trim()?{name:y.trim()}:{},...w&&w!==r.environment?{env:w}:{}});c({...p===`app`?{app:h}:{url:_.trim()},...y.trim()?{name:y.trim()}:{},...S.trim()?{case:S.trim()}:{},...w&&w!==r.environment?{env:w}:{},signedIn:E})}},children:[(0,X.jsx)(`h2`,{className:`m-0 text-base font-semibold`,children:`New recording`}),(0,X.jsxs)(`fieldset`,{className:`m-0 flex flex-col gap-2 border-0 p-0`,children:[(0,X.jsx)(`legend`,{className:`mb-1.5 text-sm font-medium`,children:`What to record`}),(0,X.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[A(`web`,`A web page`,`In a browser, with the Recorder panel in the page; a chat turns it into a test.`,P),A(`desktop`,`A desktop app`,r.desktop.available?`A Windows app from your settings; it is saved as a workflow that replays with no model.`:r.desktop.reason||`Windows only.`,M,!r.desktop.available)]})]}),l===`desktop`?(0,X.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,X.jsx)(`label`,{htmlFor:`rec-desktop-app`,className:`text-sm font-medium`,children:`App`}),r.desktop.apps.length?(0,X.jsx)(o,{id:`rec-desktop-app`,value:d,onChange:e=>f(e.target.value),className:`max-w-[420px]`,children:r.desktop.apps.map(e=>(0,X.jsx)(`option`,{value:e,children:e},e))}):(0,X.jsxs)(`p`,{className:`m-0 text-sm text-muted`,children:[`No desktop apps yet: add one in `,(0,X.jsx)(`a`,{href:t(`settings`,`environments`),children:`Settings, Environments`}),`.`]})]}):(0,X.jsxs)(`fieldset`,{className:`m-0 flex flex-col gap-2 border-0 p-0`,children:[(0,X.jsx)(`legend`,{className:`mb-1.5 text-sm font-medium`,children:`Start on`}),(0,X.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[r.apps.length>0&&(0,X.jsxs)(`label`,{className:`flex min-h-10 cursor-pointer items-center gap-2 text-sm`,children:[(0,X.jsx)(`input`,{type:`radio`,name:`rec-from`,checked:p===`app`,onChange:()=>m(`app`),className:`size-4 accent-accent`}),`An app of `,r.environment?`the ${r.environment} environment`:`the environment`]}),(0,X.jsxs)(`label`,{className:`flex min-h-10 cursor-pointer items-center gap-2 text-sm`,children:[(0,X.jsx)(`input`,{type:`radio`,name:`rec-from`,checked:p===`url`,onChange:()=>m(`url`),className:`size-4 accent-accent`}),`A page`]})]}),p===`app`?(0,X.jsx)(o,{"aria-label":`App`,value:h,onChange:e=>g(e.target.value),className:`max-w-[420px]`,children:r.apps.map(e=>(0,X.jsxs)(`option`,{value:e.name,children:[e.name,` · `,e.url]},e.name))}):(0,X.jsx)(a,{"aria-label":`Page address`,type:`url`,value:_,spellCheck:!1,placeholder:`https://shop.example.com/`,onChange:e=>v(e.target.value),className:`max-w-[520px]`})]}),(0,X.jsxs)(`div`,{className:`grid grid-cols-[repeat(auto-fit,minmax(240px,1fr))] gap-4`,children:[(0,X.jsxs)(`div`,{className:O,children:[(0,X.jsx)(`label`,{htmlFor:`rec-name`,className:`text-sm font-medium`,children:`What the test does`}),(0,X.jsx)(a,{id:`rec-name`,value:y,maxLength:200,placeholder:`Checkout with a saved card`,onChange:e=>x(e.target.value)})]}),l===`web`&&(0,X.jsxs)(`div`,{className:O,children:[(0,X.jsxs)(`label`,{htmlFor:`rec-case`,className:`text-sm font-medium`,children:[`Test case `,(0,X.jsx)(`span`,{className:`font-normal text-muted`,children:`(optional)`})]}),(0,X.jsx)(a,{id:`rec-case`,value:S,spellCheck:!1,placeholder:`SHOP-12, ado:1234 or a file`,onChange:e=>C(e.target.value)})]}),r.environments.length>1&&(l===`desktop`||p===`app`)&&(0,X.jsxs)(`div`,{className:O,children:[(0,X.jsx)(`label`,{htmlFor:`rec-env`,className:`text-sm font-medium`,children:`Environment`}),(0,X.jsx)(o,{id:`rec-env`,value:w,onChange:e=>T(e.target.value),children:r.environments.map(e=>(0,X.jsx)(`option`,{value:e,children:e},e))})]})]}),l===`web`&&r.logins.length>0&&(0,X.jsxs)(`div`,{className:`flex items-center gap-4`,children:[(0,X.jsxs)(`span`,{className:`flex flex-1 flex-col gap-0.5`,children:[(0,X.jsx)(`span`,{className:`text-sm font-medium`,children:`Start signed in`}),(0,X.jsxs)(`span`,{className:`text-[13px] text-muted`,children:[`With the saved sign-ins (`,r.logins.join(`, `),`). Off: record the sign-in too; passwords are saved as variable names, never as typed.`]})]}),(0,X.jsx)(e,{checked:E,onChange:D,label:`Start signed in`})]}),(0,X.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,X.jsxs)(n,{type:`submit`,variant:`primary`,disabled:s||!k,children:[(0,X.jsx)(b,{size:16,"aria-hidden":`true`}),s?l===`desktop`?`Starting the app…`:`Opening the browser…`:`Start recording`]}),(0,X.jsx)(`span`,{className:`text-[13px] text-muted`,children:l===`desktop`?`The app starts on this computer; do the steps in it.`:`A browser opens on this computer, with the Recorder panel in the page.`})]})]})}function de({rows:e}){let[t,r]=(0,Y.useState)(``);return e.length?(0,X.jsxs)(`section`,{"aria-labelledby":`rec-list`,className:`flex flex-col gap-2`,children:[(0,X.jsx)(`h2`,{id:`rec-list`,className:`m-0 text-base font-semibold`,children:`Recordings`}),t&&(0,X.jsx)(`p`,{className:`m-0 text-[13px] text-fail`,children:t}),(0,X.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:e.map(e=>(0,X.jsxs)(`li`,{className:`flex min-h-[60px] flex-wrap items-center gap-x-3 gap-y-2 border-t border-line-soft py-2 first:border-t-0`,children:[(0,X.jsx)(`span`,{"aria-hidden":`true`,className:i(`size-2.5 flex-none rounded-full`,e.ok?`bg-pass`:`bg-fail`)}),e.kind===`desktop`?(0,X.jsx)(M,{size:16,"aria-label":`Desktop app`,className:`flex-none text-muted`}):(0,X.jsx)(P,{size:16,"aria-label":`Web page`,className:`flex-none text-muted`}),(0,X.jsxs)(`span`,{className:`flex min-w-0 flex-[1_1_280px] flex-col gap-0.5`,children:[(0,X.jsx)(`span`,{className:`truncate text-[15px] font-medium`,children:e.name}),(0,X.jsxs)(`span`,{className:`flex flex-wrap items-center gap-x-2 text-[13px] text-muted`,children:[(0,X.jsx)(S,{size:13,"aria-hidden":`true`}),oe(e.startedAt),` · `,e.steps,` step`,e.steps===1?``:`s`,` · `,e.checks,` check`,e.checks===1?``:`s`,e.caseId?` · ${e.caseId}`:``,e.app?` · ${e.app}`:``]})]}),e.trace&&(0,X.jsxs)(n,{size:`sm`,onClick:async()=>r(await T(e.trace)),children:[(0,X.jsx)(h,{size:15,"aria-hidden":`true`}),`Steps`]}),e.report&&(0,X.jsxs)(n,{size:`sm`,onClick:async()=>r(await T(e.report)),children:[(0,X.jsx)(D,{size:15,"aria-hidden":`true`}),`Report`]}),e.kind===`desktop`&&e.workflow&&(0,X.jsxs)(n,{size:`sm`,onClick:()=>$(e.workflow).catch(e=>r(e.message)),children:[(0,X.jsx)(w,{size:15,"aria-hidden":`true`}),`Replay`]}),e.kind===`web`&&e.ok&&(0,X.jsxs)(n,{size:`sm`,onClick:()=>Q(e.runDir).catch(e=>r(e.message)),children:[(0,X.jsx)(N,{size:15,"aria-hidden":`true`}),`Write a test`]})]},e.runDir))})]}):null}function fe(){let[e,t]=(0,Y.useState)(null),[n,r]=(0,Y.useState)(``),[i,a]=(0,Y.useState)(!1),o=(0,Y.useCallback)(()=>{l.get().then(e=>{t(e),r(``)},e=>r(e.message))},[]);(0,Y.useEffect)(o,[o]);let s=!!e?.session&&ae.has(e.session.state);(0,Y.useEffect)(()=>{if(!s)return;let e=setInterval(o,800);return()=>clearInterval(e)},[s,o]);let u=async e=>{a(!0),r(``);try{t(await e())}catch(e){r(e.message)}finally{a(!1)}},d=e?.session;return(0,X.jsxs)(`div`,{className:`flex min-h-screen flex-col`,children:[(0,X.jsx)(v,{}),(0,X.jsxs)(`main`,{className:`mx-auto box-border flex w-full max-w-[1100px] flex-auto flex-col gap-5 px-6 pt-10 pb-10`,children:[(0,X.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,X.jsx)(c,{icon:b,size:`lg`}),(0,X.jsx)(`h1`,{className:`m-0 text-[26px] font-semibold tracking-[-0.01em]`,children:`Record`})]}),(0,X.jsx)(`p`,{className:`m-0 max-w-[70ch] text-sm leading-relaxed text-muted`,children:`Do the steps of a test yourself, in a browser or a Windows desktop app ForgeQE opens. Each click, entry and choice is recorded in order, with the checks you add. A web recording becomes a test in your repository through a chat; a desktop one is a workflow that replays with no model.`}),n&&(0,X.jsx)(`p`,{role:`alert`,className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail [overflow-wrap:anywhere]`,children:n}),!e&&!n&&(0,X.jsx)(`p`,{role:`status`,className:`m-0 text-sm text-muted`,children:`Loading…`}),e&&d&&s&&(0,X.jsx)(ce,{s:d,busy:i,send:e=>void u(()=>l.command(e))}),e&&!s&&(0,X.jsxs)(X.Fragment,{children:[d?.result&&(0,X.jsx)(le,{s:d}),(0,X.jsx)(ue,{view:e,busy:i,onStart:e=>void u(()=>l.start(e))}),(0,X.jsx)(de,{rows:e.recordings})]})]})]})}export{fe as Record};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{A as e,F as t,G as n,Ht as r,J as i,M as a,U as o,W as s,Y as c,Zt as l,ct as u,en as d,et as f,f as p,j as m,jt as h,lt as g,q as _,r as v,t as y}from"./status-CL_clA0J.js";import{t as b}from"./external-link-pvVHJFAZ.js";import{t as x}from"./sticky-note-DIQ2FWMw.js";import{l as S}from"./index-Bkx3fVbr.js";var C={name:`bandage`,size:24,node:[[`path`,{d:`M10 10.01h.01`,key:`1e9xi7`}],[`path`,{d:`M10 14.01h.01`,key:`ac23bv`}],[`path`,{d:`M14 10.01h.01`,key:`2wfrvf`}],[`path`,{d:`M14 14.01h.01`,key:`8tw8yn`}],[`path`,{d:`M18 6v12`,key:`1bcixs`}],[`path`,{d:`M6 6v12`,key:`vkc79e`}],[`rect`,{x:`2`,y:`6`,width:`20`,height:`12`,rx:`2`,key:`1wpnh2`}]]};C.node;var w=h(C),T={name:`flame`,size:24,node:[[`path`,{d:`M12 3q1 4 4 6.5t3 5.5a1 1 0 0 1-14 0 5 5 0 0 1 1-3 1 1 0 0 0 5 0c0-2-1.5-3-1.5-5q0-2 2.5-4`,key:`1slcih`}]]};T.node;var E=h(T),D={name:`git-commit-horizontal`,size:24,node:[[`circle`,{cx:`12`,cy:`12`,r:`3`,key:`1v7zrd`}],[`line`,{x1:`3`,x2:`9`,y1:`12`,y2:`12`,key:`1dyftd`}],[`line`,{x1:`15`,x2:`21`,y1:`12`,y2:`12`,key:`oup4p8`}]],aliases:[`git-commit`]};D.node;var O=h(D),k={name:`git-pull-request`,size:24,node:[[`circle`,{cx:`18`,cy:`18`,r:`3`,key:`1xkwt0`}],[`circle`,{cx:`6`,cy:`6`,r:`3`,key:`1lh9wr`}],[`path`,{d:`M13 6h3a2 2 0 0 1 2 2v7`,key:`1yeb86`}],[`line`,{x1:`6`,x2:`6`,y1:`9`,y2:`21`,key:`rroup`}]]};k.node;var A=h(k),j={name:`hammer`,size:24,node:[[`path`,{d:`m15 12-9.373 9.373a1 1 0 0 1-3.001-3L12 9`,key:`1hayfq`}],[`path`,{d:`m18 15 4-4`,key:`16gjal`}],[`path`,{d:`m21.5 11.5-1.914-1.914A2 2 0 0 1 19 8.172v-.344a2 2 0 0 0-.586-1.414l-1.657-1.657A6 6 0 0 0 12.516 3H9l1.243 1.243A6 6 0 0 1 12 8.485V10l2 2h1.172a2 2 0 0 1 1.414.586L18.5 14.5`,key:`15ts47`}]]};j.node;var M=h(j),N=d(l(),1),P=_(),F={linked:{label:`Linked test case`,tone:`pass`,help:`It automates a test case that tests a changed work item`},changed:{label:`Test code changed`,tone:`running`,help:`The change is in test code it uses`},covers:{label:`Matches a change`,tone:`neutral`,help:`The change's words name what it does`},risk:{label:`Risk area`,tone:`warn`,help:`The release touches an area with defects or failures before`},smoke:{label:`Smoke`,tone:`neutral`,help:`Tagged smoke: in every pack`}},I={heal:{label:`Heal`,tone:`warn`,does:`An element it uses is gone: the healing agent re-runs the flow and updates the test.`},check:{label:`AI re-run decides`,tone:`warn`,does:`The healing agent re-runs the flow: it heals the test when the flow works, and reports an app bug when it fails at the same step.`},flaky:{label:`Flaky`,tone:`neutral`,does:`Passed when run again: quarantine it, or fix the cause it flakes on. Not healed.`},environment:{label:`Environment`,tone:`neutral`,does:`Network, a server error, sign-in: run again once the environment works. No heal, no bug.`},test_code:{label:`Test code broken`,tone:`fail`,does:`A missing module, a compile error, a missing fixture: fix the test's code.`}},L={work_item:S,pr:A,commit:O,note:x},R=e=>new Date(e).toLocaleString([],{month:`short`,day:`numeric`,hour:`numeric`,minute:`2-digit`}),z=e=>e?e.status===`passed`?`pass`:e.status===`failed`?`fail`:`warn`:`neutral`,B=e=>e<60?`${Math.round(e)}s`:`${Math.floor(e/60)}m ${Math.round(e%60)}s`,V=e=>e&&/^https?:\/\//i.test(e)?e:void 0;function H({job:e,onStop:t}){let r=(0,N.useRef)(null),[,i]=(0,N.useState)(0);(0,N.useEffect)(()=>{if(!e.running)return;let t=setInterval(()=>i(e=>e+1),1e3);return()=>clearInterval(t)},[e.running]),(0,N.useEffect)(()=>{r.current&&(r.current.scrollTop=r.current.scrollHeight)},[e.lines.length]);let a=e.running?e.kind===`build`?`Building the pack`:e.kind===`run`?`Running ${e.pack}`:`Healing ${e.pack}'s failures`:e.kind===`build`?`Built ${e.pack||`the pack`}`:e.kind===`run`?`Ran ${e.pack}`:`Healed ${e.pack}'s failures`,o=Math.round(((e.endedAt?Date.parse(e.endedAt):Date.now())-Date.parse(e.startedAt))/1e3);return(0,P.jsxs)(`section`,{"aria-label":a,className:`flex flex-col gap-2 rounded-[14px] border border-line-soft p-4`,children:[(0,P.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,P.jsx)(`span`,{className:`flex-1 font-medium`,children:a}),(0,P.jsx)(y,{orb:e.kind===`build`?`handing`:e.kind===`heal`?`retrying`:`working`,tone:e.running?`running`:e.exitCode===0?`pass`:e.kind===`run`&&e.exitCode===1?`warn`:`fail`,children:e.running?`Working`:e.exitCode===0?`Done`:e.kind===`run`&&e.exitCode===1?`Tests failed`:`Did not finish`}),(0,P.jsx)(`span`,{className:`text-[13px] text-muted tabular-nums`,children:B(o)}),e.running&&(0,P.jsxs)(n,{onClick:t,className:`border-fail text-fail hover:bg-fail-soft`,children:[(0,P.jsx)(f,{size:14,fill:`currentColor`,"aria-hidden":`true`}),`Stop`]})]}),(0,P.jsx)(`code`,{className:`font-mono text-[12px] text-muted [overflow-wrap:anywhere]`,children:e.command}),(0,P.jsxs)(`details`,{open:e.running,children:[(0,P.jsxs)(`summary`,{className:`cursor-pointer text-sm text-muted`,children:[`Output (`,e.lines.length,` lines)`]}),(0,P.jsx)(`pre`,{ref:r,className:`mt-2 max-h-[300px] overflow-auto rounded-[10px] bg-surface-muted p-3 font-mono text-[12px] leading-relaxed whitespace-pre-wrap text-ink-2`,children:e.lines.join(`
|
|
2
|
-
`)||`Starting…`})]})]})}function U({title:e,aside:t,children:n}){return(0,P.jsxs)(`section`,{className:`flex flex-col gap-2`,children:[(0,P.jsxs)(`div`,{className:`flex flex-wrap items-baseline gap-2`,children:[(0,P.jsx)(`h3`,{className:`m-0 flex-1 text-base font-semibold`,children:e}),t]}),n]})}function W(e){let[t,n]=e.slice(-2).length===2?e.slice(-2):[void 0,e.at(-1)];return!n||!t?{fresh:[],fixed:[]}:{fresh:n.failed.filter(e=>!t.failed.includes(e)),fixed:t.failed.filter(e=>!n.failed.includes(e))}}function G({name:e,busy:t,act:a,version:o}){let[s,c]=(0,N.useState)(null),[l,d]=(0,N.useState)(``),[f,m]=(0,N.useState)(`all`),[h,g]=(0,N.useState)(null),[_,v]=(0,N.useState)(new Set);if((0,N.useEffect)(()=>{let t=!1;return r.detail(e).then(e=>!t&&(c(e),d(``)),e=>!t&&d(e.message)),()=>{t=!0}},[e,o]),(0,N.useEffect)(()=>{m(`all`),g(null),v(new Set)},[e]),l)return(0,P.jsx)(`p`,{className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail`,children:l});if(!s)return(0,P.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`Loading…`});let S=s.runs.at(-1),{fresh:C,fixed:T}=W(s.runs),D=s.tests.filter(e=>f===`all`||e.reasons.some(e=>e.kind===f)),O=(s.triage?.items??[]).filter(e=>e.route===`heal`||e.route===`check`),k=Object.keys(I).filter(e=>s.triage?.items.some(t=>t.route===e)),A=Object.keys(F).filter(e=>s.tests.some(t=>t.reasons.some(t=>t.kind===e)));return(0,P.jsxs)(`section`,{"aria-labelledby":`pack-h`,className:`flex min-w-0 flex-1 flex-col gap-6`,children:[(0,P.jsxs)(`div`,{className:`flex flex-wrap items-start gap-3`,children:[(0,P.jsxs)(`div`,{className:`flex min-w-0 flex-[1_1_260px] flex-col gap-1`,children:[(0,P.jsx)(`h2`,{id:`pack-h`,className:`m-0 text-[22px] font-semibold tracking-[-0.01em] [overflow-wrap:anywhere]`,children:s.name}),(0,P.jsxs)(`span`,{className:`text-sm text-muted`,children:[`Built `,R(s.createdAt)]}),s.scope.length>0&&(0,P.jsx)(`span`,{className:`flex flex-wrap gap-1.5 pt-1`,children:s.scope.map(e=>(0,P.jsx)(`span`,{className:`rounded-full bg-surface-strong px-2.5 py-0.5 text-[12px] text-ink-2`,children:e},e))})]}),h?(0,P.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2 rounded-[12px] bg-accent-soft px-3 py-2`,children:[(0,P.jsxs)(`span`,{className:`text-sm`,children:[`Run `,s.tests.length,` test`,s.tests.length===1?``:`s`,` `,h===`local`?`on this machine`:`on TestMu AI`,`?`]}),(0,P.jsx)(n,{variant:`primary`,disabled:t,onClick:()=>{g(null),a(()=>r.run(s.name,h))},children:`Run`}),(0,P.jsx)(n,{onClick:()=>g(null),children:`Don't run`})]}):(0,P.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,P.jsxs)(n,{variant:`primary`,disabled:t||!s.tests.length,onClick:()=>g(`local`),children:[(0,P.jsx)(u,{size:16,"aria-hidden":`true`}),`Run here`]}),(0,P.jsx)(n,{disabled:t||!s.tests.length,onClick:()=>g(`testmu`),children:`Run on TestMu AI`})]})]}),(0,P.jsxs)(`div`,{className:`flex flex-wrap gap-3`,children:[(0,P.jsx)(p,{label:`Tests`,value:String(s.tests.length),note:A.map(e=>`${s.tests.filter(t=>t.reasons.some(t=>t.kind===e)).length} ${F[e].label.toLowerCase()}`).join(` · `)}),(0,P.jsx)(p,{label:`Not covered`,value:String(s.uncovered.length),note:`changes no test is in for`}),(0,P.jsx)(p,{label:`Risk areas`,value:String(s.risk.length)}),(0,P.jsx)(p,{label:`Last run`,value:S?S.status===`passed`?`Passed`:`${S.counts.failed} failed`:`–`,note:S?`${R(S.at)} · ${S.on===`testmu`?`TestMu AI`:S.on===`pipeline`?`pipeline`:`this machine`}`:`Not run yet`})]}),s.triage&&(0,P.jsxs)(U,{title:`Triage of the last run`,aside:(0,P.jsxs)(`span`,{className:`text-[13px] text-muted`,children:[s.triage.items.length,` failed or flaky`]}),children:[s.triage.environmentDown&&(0,P.jsx)(`p`,{className:`m-0 rounded-[12px] bg-warn-soft px-3 py-2 text-sm text-warn`,children:`Most failures were the environment: run the pack again before healing anything.`}),k.map(e=>(0,P.jsxs)(`div`,{className:`flex flex-col gap-1 rounded-[12px] border border-line-soft px-4 py-3`,children:[(0,P.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,P.jsx)(y,{tone:I[e].tone,children:I[e].label}),(0,P.jsx)(`span`,{className:`text-[13px] text-muted`,children:I[e].does})]}),(0,P.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:s.triage.items.filter(t=>t.route===e).map(t=>(0,P.jsxs)(`li`,{className:`flex items-start gap-3 border-t border-line-soft py-2 text-sm first:border-t-0`,children:[(e===`heal`||e===`check`)&&(0,P.jsx)(`input`,{type:`checkbox`,"aria-label":`Heal ${t.name}`,checked:_.has(t.name),onChange:e=>{let n=new Set(_);e.target.checked?n.add(t.name):n.delete(t.name),v(n)},className:`mt-1 size-5 accent-accent`}),(0,P.jsxs)(`span`,{className:`flex min-w-0 flex-col gap-0.5`,children:[(0,P.jsx)(`span`,{className:`[overflow-wrap:anywhere]`,children:t.name}),(0,P.jsxs)(`span`,{className:`text-[13px] text-muted [overflow-wrap:anywhere]`,children:[t.why,` · `,t.confidence,` confidence`,t.file?` · ${t.file}`:``,t.history?` · ${t.history}`:``]})]})]},t.name))})]},e)),O.length>0&&(0,P.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,P.jsxs)(n,{disabled:t||!_.size,onClick:()=>a(()=>r.heal(s.name,[..._])),children:[(0,P.jsx)(w,{size:16,"aria-hidden":`true`}),`Heal `,_.size||``,` selected`]}),(0,P.jsxs)(n,{variant:`ghost`,disabled:t,onClick:()=>v(new Set(O.map(e=>e.name))),children:[`Select all `,O.length]}),(0,P.jsx)(`span`,{className:`text-[13px] text-muted`,children:`The healing agent re-runs each flow in the browser before it changes anything.`})]})]}),s.runs.length>0&&(0,P.jsxs)(U,{title:`Runs`,aside:C.length||T.length?(0,P.jsxs)(`span`,{className:`text-[13px] text-muted`,children:[[C.length?`${C.length} newly failing`:``,T.length?`${T.length} fixed`:``].filter(Boolean).join(` · `),` since the run before`]}):void 0,children:[(0,P.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:[...s.runs].reverse().map((e,t)=>(0,P.jsxs)(`li`,{className:`flex flex-wrap items-center gap-x-3 gap-y-1 border-t border-line-soft py-2 text-sm first:border-t-0`,children:[(0,P.jsx)(y,{tone:z(e),children:e.status===`passed`?`Passed`:e.status===`failed`?`Failed`:e.status}),(0,P.jsxs)(`span`,{className:`flex-1`,children:[R(e.at),` · `,e.on===`testmu`?`TestMu AI`:e.on===`pipeline`?`pipeline`:`this machine`]}),(0,P.jsxs)(`span`,{className:`text-muted tabular-nums`,children:[e.counts.passed,` passed · `,e.counts.failed,` failed`,e.counts.skipped?` · ${e.counts.skipped} skipped`:``,` · `,B(e.seconds)]}),V(e.url)&&(0,P.jsxs)(`a`,{href:e.url,target:`_blank`,rel:`noopener noreferrer`,className:`inline-flex items-center gap-1`,children:[`Pipeline `,(0,P.jsx)(b,{size:13,"aria-hidden":`true`})]})]},`${e.at}-${t}`))}),C.length>0&&(0,P.jsxs)(`p`,{className:`m-0 text-[13px] text-fail`,children:[`Newly failing: `,C.join(`, `)]}),T.length>0&&(0,P.jsxs)(`p`,{className:`m-0 text-[13px] text-pass`,children:[`Fixed: `,T.join(`, `)]})]}),(0,P.jsxs)(U,{title:`Tests (${s.tests.length})`,aside:(0,P.jsx)(`div`,{role:`group`,"aria-label":`Why in`,className:`flex flex-wrap gap-1`,children:[`all`,...A].map(e=>(0,P.jsx)(`button`,{type:`button`,"aria-pressed":f===e,title:F[e]?.help,onClick:()=>m(e),className:i(`min-h-8 cursor-pointer rounded-full border px-3 text-[13px]`,f===e?`border-ink bg-ink text-bg`:`border-line-soft bg-bg text-ink hover:bg-surface-muted`),children:e===`all`?`All`:F[e].label},e))}),children:[(0,P.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:D.slice(0,300).map(e=>(0,P.jsxs)(`li`,{className:`flex flex-col gap-1 border-t border-line-soft py-2 text-sm first:border-t-0`,children:[(0,P.jsxs)(`span`,{className:`flex flex-wrap items-center gap-2`,children:[(0,P.jsx)(`span`,{className:`font-medium [overflow-wrap:anywhere]`,children:e.test}),e.flaky&&(0,P.jsx)(y,{tone:`warn`,children:`Flaky`})]}),(0,P.jsxs)(`span`,{className:`font-mono text-[12px] text-muted [overflow-wrap:anywhere]`,children:[e.file,e.line?`:${e.line}`:``]}),(0,P.jsx)(`span`,{className:`flex flex-wrap gap-1.5`,children:e.reasons.map((e,t)=>(0,P.jsxs)(`span`,{className:`inline-flex items-center gap-1.5 text-[13px] text-ink-2`,children:[(0,P.jsx)(y,{tone:F[e.kind]?.tone??`neutral`,icon:!1,children:F[e.kind]?.label??e.kind}),e.text]},t))})]},`${e.file}:${e.test}`))}),D.length>300&&(0,P.jsxs)(`p`,{className:`m-0 text-[13px] text-muted`,children:[`The first 300 of `,D.length,`.`]})]}),s.uncovered.length>0&&(0,P.jsx)(U,{title:`Not covered (${s.uncovered.length})`,aside:(0,P.jsx)(`span`,{className:`text-[13px] text-muted`,children:`Extend the closest test rather than write a new one`}),children:(0,P.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:s.uncovered.map(e=>(0,P.jsxs)(`li`,{className:`flex flex-col gap-0.5 border-t border-line-soft py-2 text-sm first:border-t-0`,children:[(0,P.jsxs)(`span`,{className:`[overflow-wrap:anywhere]`,children:[(0,P.jsx)(`span`,{className:`mr-1.5 font-mono text-[12px] text-muted`,children:e.item}),e.title]}),(0,P.jsx)(`span`,{className:`text-[13px] text-muted`,children:e.why}),e.nearest&&(0,P.jsxs)(`span`,{className:`text-[13px] text-ink-2 [overflow-wrap:anywhere]`,children:[`Closest: `,e.nearest.test,` `,(0,P.jsxs)(`span`,{className:`font-mono text-[12px] text-muted`,children:[`(`,e.nearest.file,`)`]}),e.nearest.lacks.length?`, lacks ${e.nearest.lacks.join(`, `)}`:``]})]},e.item))})}),s.risk.length>0&&(0,P.jsx)(U,{title:`Risk areas`,children:(0,P.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:s.risk.map(e=>(0,P.jsxs)(`li`,{className:`flex items-start gap-3 border-t border-line-soft py-2 text-sm first:border-t-0`,children:[(0,P.jsx)(E,{size:16,"aria-hidden":`true`,className:i(`mt-0.5 flex-none`,e.level===`high`?`text-fail`:`text-warn`)}),(0,P.jsxs)(`span`,{className:`flex flex-col`,children:[(0,P.jsxs)(`span`,{children:[e.area,` `,(0,P.jsxs)(`span`,{className:`text-muted`,children:[`· `,e.level]})]}),(0,P.jsx)(`span`,{className:`text-[13px] text-muted`,children:e.reasons.join(`; `)})]})]},e.area))})}),(0,P.jsx)(U,{title:`Changes (${s.items.length})`,children:(0,P.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:s.items.slice(0,200).map(e=>{let t=L[e.kind]||x;return(0,P.jsxs)(`li`,{className:`flex items-start gap-3 border-t border-line-soft py-2 text-sm first:border-t-0`,children:[(0,P.jsx)(t,{size:16,"aria-hidden":`true`,className:`mt-0.5 flex-none text-muted`}),(0,P.jsxs)(`span`,{className:`min-w-0 flex-1 [overflow-wrap:anywhere]`,children:[(0,P.jsxs)(`span`,{className:`mr-1.5 font-mono text-[12px] text-muted`,children:[e.type?`${e.type} `:``,e.id]}),e.title]}),V(e.url)&&(0,P.jsx)(`a`,{href:e.url,target:`_blank`,rel:`noopener noreferrer`,"aria-label":`Open ${e.id}`,className:`flex-none`,children:(0,P.jsx)(b,{size:14,"aria-hidden":`true`})})]},`${e.kind}-${e.id}`)})})}),s.notes.length>0&&(0,P.jsx)(`ul`,{className:`m-0 flex flex-col gap-1 pl-5 text-[13px] text-muted`,children:s.notes.map((e,t)=>(0,P.jsx)(`li`,{children:e},t))})]})}function K({open:r,onClose:o,view:s,onBuild:c,busy:l}){let[u,d]=(0,N.useState)({rangeRepo:`product`,prRepo:`product`,risk:!0,smoke:!0});(0,N.useEffect)(()=>{r&&d({rangeRepo:s.productRepo?`product`:`tests`,prRepo:s.productRepo?`product`:`tests`,risk:!0,smoke:!0})},[r,s.productRepo]);let f=(e,t)=>d({...u,[e]:t}),p=(e,t,n,r)=>(0,P.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,P.jsx)(`label`,{htmlFor:`pk-${e}`,className:`text-sm font-medium`,children:t}),(0,P.jsx)(a,{id:`pk-${e}`,value:String(u[e]??``),spellCheck:!1,maxLength:200,placeholder:n,onChange:t=>f(e,t.target.value)}),r&&(0,P.jsx)(`span`,{className:`text-[13px] text-muted`,children:r})]}),h=(e,t)=>(0,P.jsxs)(`fieldset`,{className:`m-0 flex flex-wrap items-center gap-x-4 gap-y-1 border-0 p-0`,children:[(0,P.jsx)(`legend`,{className:`mb-1 text-[13px] text-muted`,children:t}),(0,P.jsxs)(`label`,{className:`flex min-h-9 cursor-pointer items-center gap-2 text-sm`,children:[(0,P.jsx)(`input`,{type:`radio`,name:e,checked:u[e]===`product`,disabled:!s.productRepo,onChange:()=>f(e,`product`),className:`size-4 accent-accent`}),`The product's `,s.productRepo?(0,P.jsx)(`code`,{className:`font-mono text-[12px]`,children:s.productRepo}):(0,P.jsx)(`span`,{className:`text-muted`,children:`(set defects.product_repo)`})]}),(0,P.jsxs)(`label`,{className:`flex min-h-9 cursor-pointer items-center gap-2 text-sm`,children:[(0,P.jsx)(`input`,{type:`radio`,name:e,checked:u[e]===`tests`,onChange:()=>f(e,`tests`),className:`size-4 accent-accent`}),`The tests' `,s.repo===`.`?`(this project)`:(0,P.jsx)(`code`,{className:`font-mono text-[12px]`,children:s.repo})]})]}),g=`flex flex-col gap-3 rounded-[12px] border border-line-soft p-3`;return(0,P.jsx)(m,{open:r,onClose:o,wide:!0,title:`Build a regression pack`,description:`The tests a release's changes can break, from any mix of the changes below. Building runs nothing; it takes seconds.`,footer:(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(n,{onClick:o,children:`Cancel`}),(0,P.jsxs)(n,{variant:`primary`,disabled:l,onClick:()=>c(u),children:[(0,P.jsx)(M,{size:16,"aria-hidden":`true`}),`Build the pack`]})]}),children:(0,P.jsxs)(`div`,{className:`flex flex-col gap-4 pt-3`,children:[p(`name`,`Name`,`release-1.5`,`Lower-case letters, digits, dots and dashes. Empty: release-<date>.`),(0,P.jsxs)(`div`,{className:g,children:[(0,P.jsxs)(`h3`,{className:`m-0 flex items-center gap-2 text-sm font-semibold`,children:[(0,P.jsx)(O,{size:16,"aria-hidden":`true`,className:`text-accent`}),`Code changes`]}),(0,P.jsxs)(`div`,{className:`grid grid-cols-[repeat(auto-fit,minmax(200px,1fr))] gap-3`,children:[p(`from`,`Commits after`,`release/1.4, a tag or a commit`),p(`to`,`Up to`,`HEAD`)]}),h(`rangeRepo`,`In the repository`),(0,P.jsxs)(`div`,{className:`grid grid-cols-[repeat(auto-fit,minmax(200px,1fr))] gap-3`,children:[p(`prs`,`Pull requests`,`57, 58 or their links`),(0,P.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,P.jsx)(`label`,{htmlFor:`pk-days`,className:`text-sm font-medium`,children:`Or merged in the last`}),(0,P.jsxs)(`span`,{className:`flex items-center gap-2 text-sm`,children:[(0,P.jsx)(a,{id:`pk-days`,type:`number`,min:1,max:365,value:u.mergedDays??``,onChange:e=>f(`mergedDays`,e.target.value?Number(e.target.value):void 0),className:`w-24`}),` days`]})]})]}),(u.prs||u.mergedDays)&&h(`prRepo`,`Pull requests of`)]}),(0,P.jsxs)(`div`,{className:i(g,!s.azureDevops&&`opacity-70`),children:[(0,P.jsxs)(`h3`,{className:`m-0 flex items-center gap-2 text-sm font-semibold`,children:[(0,P.jsx)(S,{size:16,"aria-hidden":`true`,className:`text-accent`}),`Azure DevOps work items and their linked test cases`]}),!s.azureDevops&&(0,P.jsxs)(`p`,{className:`m-0 text-[13px] text-muted`,children:[`Not set up: `,(0,P.jsx)(`a`,{href:t(`settings`,`integrations`),children:`Settings, Integrations`}),`.`]}),(0,P.jsxs)(`div`,{className:`grid grid-cols-[repeat(auto-fit,minmax(200px,1fr))] gap-3`,children:[p(`sprint`,`Sprint`,`current, or Sprint 41`),p(`iteration`,`Iteration path`,`Shop\\Release 2026.09`),p(`area`,`Area path`,`Shop\\Checkout`),p(`parent`,`Epic or feature`,`1200`),p(`workItems`,`Work items`,`2211, 2212`)]}),(0,P.jsx)(`p`,{className:`m-0 text-[13px] text-muted`,children:`Their stories and bugs, the test cases each is tested by, and the files their linked pull requests changed.`})]}),(0,P.jsxs)(`div`,{className:`flex flex-wrap gap-x-6 gap-y-2`,children:[(0,P.jsxs)(`span`,{className:`flex items-center gap-2 text-sm`,children:[(0,P.jsx)(e,{checked:u.risk!==!1,onChange:e=>f(`risk`,e),label:`Add tests for risk areas`}),`Add tests for risk areas the release touches`]}),(0,P.jsxs)(`span`,{className:`flex items-center gap-2 text-sm`,children:[(0,P.jsx)(e,{checked:u.smoke!==!1,onChange:e=>f(`smoke`,e),label:`Add smoke tests`}),`Add smoke-tagged tests`]})]})]})})}function q({sub:e}){let[a,l]=(0,N.useState)(null),[u,d]=(0,N.useState)(``),[f,p]=(0,N.useState)(!1),[m,h]=(0,N.useState)(!1),_=(0,N.useCallback)(()=>{r.list().then(e=>{l(e),d(``)},e=>d(e.message))},[]);(0,N.useEffect)(_,[_]);let y=!!a?.job?.running;(0,N.useEffect)(()=>{if(!y)return;let e=setInterval(_,1500);return()=>clearInterval(e)},[y,_]);let b=async e=>{p(!0),d(``);try{await e(),_()}catch(e){d(e.message)}finally{p(!1)}},x=e?decodeURIComponent(e):``,S=a?.packs.find(e=>e.name===x)||a?.packs[0],C=(0,N.useMemo)(()=>`${a?.job?.endedAt??``}|${S?.runs??0}|${S?.createdAt??``}`,[a,S]);return(0,P.jsxs)(`div`,{className:`flex min-h-screen flex-col`,children:[(0,P.jsx)(v,{}),(0,P.jsxs)(`main`,{className:`@container mx-auto box-border flex w-full max-w-[1180px] flex-auto flex-col gap-4 px-6 pt-10 pb-10`,children:[(0,P.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,P.jsx)(s,{icon:g,size:`lg`}),(0,P.jsx)(`h1`,{className:`m-0 flex-1 text-[26px] font-semibold tracking-[-0.01em]`,children:`Regression`}),(0,P.jsxs)(n,{variant:`primary`,disabled:!a||y,onClick:()=>h(!0),children:[(0,P.jsx)(M,{size:16,"aria-hidden":`true`}),`Build a pack`]})]}),(0,P.jsxs)(`p`,{className:`m-0 max-w-[74ch] text-sm leading-relaxed text-muted`,children:[`A regression pack is the set of tests in `,a&&a.repo!==`.`?(0,P.jsx)(`code`,{className:`font-mono text-[13px]`,children:a.repo}):`this project`,` that a release's changes can break: from the commits and pull requests of the product or development repositories, and the Azure DevOps sprint, epic or work items with their linked test cases. Each test says why it is in; the changes no test covers are listed. Run it here or on TestMu AI, and each run is triaged into what to heal, re-run or fix.`]}),u&&(0,P.jsx)(`p`,{role:`alert`,className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail [overflow-wrap:anywhere]`,children:u}),!a&&!u&&(0,P.jsx)(`p`,{role:`status`,className:`m-0 text-sm text-muted`,children:`Loading…`}),a?.job&&(0,P.jsx)(H,{job:a.job,onStop:()=>b(()=>r.stop())}),a&&(a.packs.length?(0,P.jsxs)(`div`,{className:`flex flex-col gap-6 @2xl:flex-row @2xl:items-start @2xl:gap-8`,children:[(0,P.jsxs)(`nav`,{"aria-label":`Packs`,className:`flex min-w-0 flex-col gap-1 @2xl:sticky @2xl:top-6 @2xl:w-[250px] @2xl:flex-none`,children:[(0,P.jsxs)(`span`,{className:`px-3 text-[13px] font-medium text-muted`,children:[`Packs (`,a.packs.length,`)`]}),(0,P.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:a.packs.map(e=>(0,P.jsx)(`li`,{children:(0,P.jsxs)(`a`,{href:t(`regression`,encodeURIComponent(e.name)),"aria-current":S?.name===e.name?`page`:void 0,className:i(`flex min-h-[60px] items-center gap-3 rounded-[12px] px-3 py-2 text-ink no-underline`,S?.name===e.name?`bg-surface-strong hover:text-ink`:`hover:bg-surface-muted hover:text-ink`),children:[a?.job?.running&&a.job.pack===e.name?(0,P.jsx)(o,{state:a.job.kind===`heal`?`retrying`:`working`,label:`Working on it`,px:16}):(0,P.jsx)(`span`,{className:i(`size-2.5 flex-none rounded-full`,e.lastRun?e.lastRun.status===`passed`?`bg-pass`:`bg-fail`:`bg-line`),"aria-label":e.lastRun?e.lastRun.status:`not run`}),(0,P.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,P.jsx)(`span`,{className:`truncate text-[15px] font-medium`,children:e.name}),(0,P.jsxs)(`span`,{className:`truncate text-[13px] text-muted`,children:[c(e.tests,`test`),` · `,e.uncovered,` not covered · `,R(e.createdAt)]})]})]})},e.name))})]}),S&&(0,P.jsx)(G,{name:S.name,busy:f||y,act:b,version:C})]}):(0,P.jsxs)(`div`,{className:`flex flex-col items-start gap-3 rounded-[18px] border border-line p-6`,children:[(0,P.jsx)(g,{size:24,"aria-hidden":`true`,className:`text-muted`}),(0,P.jsx)(`p`,{className:`m-0 max-w-[62ch] leading-relaxed text-ink-2`,children:`No regression packs yet. Build one from a sprint, the commits since the last release, or the pull requests merged this week.`}),(0,P.jsx)(n,{onClick:()=>h(!0),children:`Build a pack`})]}))]}),a&&(0,P.jsx)(K,{open:m,onClose:()=>h(!1),view:a,busy:f||y,onBuild:e=>b(async()=>{await r.build(e),h(!1)})})]})}export{q as Regression};
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{F as e,G as t,It as n,J as r,Kt as i,M as a,N as o,St as s,Tt as c,U as l,W as u,Y as d,Zt as f,_ as p,bt as m,ct as h,en as g,f as _,g as v,jt as y,p as b,q as x,r as S,t as C,vt as w}from"./status-CL_clA0J.js";import{t as T}from"./circle-dashed-DbpKfamm.js";import{a as E,n as D,t as O}from"./markdown-GKy06efH.js";import{t as k}from"./external-link-pvVHJFAZ.js";import{t as A}from"./search-YLg3uzAx.js";import{t as j}from"./timer-NLZ0XmVl.js";import{T as M,h as N,x as P}from"./index-Bkx3fVbr.js";var F={name:`percent`,size:24,node:[[`line`,{x1:`19`,x2:`5`,y1:`5`,y2:`19`,key:`1x9vlm`}],[`circle`,{cx:`6.5`,cy:`6.5`,r:`2.5`,key:`4mh3h7`}],[`circle`,{cx:`17.5`,cy:`17.5`,r:`2.5`,key:`1mdrzq`}]]};F.node;var I=y(F),L=g(f(),1),R=x(),z={suite:`Suite`,monitor:`Monitor round`,run:`Browser run`,explore:`Exploration`,api:`API run`,desktop:`Desktop run`,mobile:`Mobile run`,page:`Report page`},B=e=>{if(!e)return``;let t=new Date(e);return Number.isNaN(t.getTime())?e:t.toLocaleString([],{month:`short`,day:`numeric`,...t.getFullYear()===new Date().getFullYear()?{}:{year:`numeric`},hour:`numeric`,minute:`2-digit`})},V=e=>{if(!e)return``;let t=Math.round(e/1e3);return t<60?`${t}s`:t<3600?`${Math.floor(t/60)}m ${t%60}s`:`${Math.floor(t/3600)}h ${Math.floor(t%3600/60)}m`},H=e=>e===`passed`,U=e=>e===`failed`||e===`error`;function W(e){return e.scope===`page`?{tone:`neutral`,label:`Report`}:e.counts.total?e.counts.failed?{tone:`fail`,label:`${e.counts.failed} failed`}:e.stopped||e.counts.other?{tone:`warn`,label:e.stopped?`Stopped`:`${e.counts.other} not run`}:{tone:`pass`,label:`Passed`}:{tone:`neutral`,label:`No results`}}var G={pass:`bg-pass`,fail:`bg-fail`,warn:`bg-warn`,neutral:`bg-line`};function K({counts:e}){let t=[{n:e.passed,label:`passed`,cls:`bg-pass`},{n:e.failed,label:`failed`,cls:`bg-fail`},{n:e.other,label:`other`,cls:`bg-line`}].filter(e=>e.n>0);return(0,R.jsxs)(`div`,{role:`img`,"aria-label":`${e.passed} passed, ${e.failed} failed, ${e.other} other, of ${e.total}`,className:`flex flex-col gap-1.5`,children:[(0,R.jsx)(`div`,{className:`flex h-3 w-full overflow-hidden rounded-full bg-surface-strong`,children:t.map(t=>(0,R.jsx)(`span`,{className:t.cls,style:{width:`${t.n/Math.max(1,e.total)*100}%`}},t.label))}),(0,R.jsx)(`div`,{"aria-hidden":`true`,className:`flex flex-wrap gap-x-4 text-[13px] text-muted`,children:t.map(e=>(0,R.jsxs)(`span`,{className:`inline-flex items-center gap-1.5`,children:[(0,R.jsx)(`span`,{className:r(`size-2 rounded-full`,e.cls)}),e.n,` `,e.label]},e.label))})]})}function q({runs:e}){let t=e.map(e=>({at:e.startedAt,rate:e.counts.total?Math.round(e.counts.passed/e.counts.total*100):0}));return t.length<2?(0,R.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`A trend needs two runs of the same suite. This one has run once.`}):(0,R.jsx)(`div`,{role:`img`,"aria-label":`Pass rate of the last ${t.length} runs, oldest first: ${t.map(e=>`${e.rate}%`).join(`, `)}`,className:`flex h-[180px] items-end gap-2 border-b border-line pb-1`,children:t.map((e,n)=>(0,R.jsxs)(`div`,{className:`flex h-full min-w-0 flex-1 flex-col items-center justify-end gap-1`,title:`${B(e.at)}: ${e.rate}%`,children:[(0,R.jsxs)(`span`,{className:`text-[12px] text-ink-2 tabular-nums`,children:[e.rate,`%`]}),(0,R.jsx)(`span`,{className:r(`w-full max-w-[44px] rounded-t-[6px]`,n===t.length-1?`bg-accent`:`bg-[#dbe1fe]`),style:{height:`${Math.max(2,e.rate)}%`}})]},n))})}function J({c:e,open:t,onToggle:n}){let i=H(e.status)?`pass`:U(e.status)?`fail`:`neutral`;return(0,R.jsxs)(`li`,{className:`border-t border-line-soft`,children:[(0,R.jsxs)(`button`,{type:`button`,"aria-expanded":t,onClick:n,className:`flex min-h-[56px] w-full cursor-pointer items-center gap-3 border-0 bg-transparent px-2 py-2 text-left text-ink hover:bg-surface-muted`,children:[(0,R.jsx)(C,{tone:i,children:e.status}),(0,R.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,R.jsxs)(`span`,{className:`truncate font-medium`,children:[(0,R.jsx)(`span`,{className:`font-mono text-[13px] text-ink-2`,children:e.id}),` `,e.title]}),(0,R.jsx)(`span`,{className:`text-[13px] text-muted`,children:[e.device,V(e.ms),e.tokens?`${p(e.tokens)} tokens`:``].filter(Boolean).join(` · `)})]}),(0,R.jsx)(c,{size:16,"aria-hidden":`true`,className:r(`flex-none text-muted transition-transform`,t&&`rotate-180`)})]}),t&&(0,R.jsxs)(`div`,{className:`flex flex-col gap-2 px-2 pb-3 pl-[84px]`,children:[e.summary&&(0,R.jsx)(D,{text:e.summary,className:`text-sm`}),(0,R.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-3 gap-y-1 text-[13px] text-muted`,children:[e.defect&&(0,R.jsxs)(`span`,{children:[`Defect: `,e.defect]}),e.bug&&(0,R.jsx)(D,{text:e.bug.url?`Bug: [${e.bug.label.replace(/[[\]]/g,``)}](${e.bug.url})`:`Bug: ${e.bug.label}`,className:`text-[13px] text-muted`}),e.testFile&&(0,R.jsxs)(`span`,{children:[`Test: `,(0,R.jsx)(O,{path:e.testFile})]}),e.runDir&&(0,R.jsxs)(`span`,{children:[`Run: `,(0,R.jsx)(O,{path:e.runDir})]}),e.url&&(0,R.jsx)(D,{text:`[Open the case](${e.url})`,className:`text-[13px]`})]})]})]})}function Y({run:i,history:a}){let[o,c]=(0,L.useState)(`overview`),[l,u]=(0,L.useState)(null),[d,f]=(0,L.useState)(``),[m,h]=(0,L.useState)(``),[g,y]=(0,L.useState)(!1);(0,L.useEffect)(()=>{c(`overview`),u(null),f(``)},[i.key]);let x=i.cases.filter(e=>!H(e.status)),S=W(i),w=i.scope===`page`,A=async()=>{if(m.trim()&&!g){y(!0);try{let{id:t}=await n.start(`About the ${z[i.scope].toLowerCase()} report "${i.title}" in \`${i.folder}\` (${B(i.startedAt)}): ${m.trim()}`,{});location.hash=e(`chats`,t)}catch(e){f(e.message),y(!1)}}};return(0,R.jsxs)(`section`,{"aria-labelledby":`report-h`,className:`flex min-w-0 flex-1 flex-col gap-5`,children:[(0,R.jsxs)(`div`,{className:`flex flex-wrap items-start gap-x-4 gap-y-3`,children:[(0,R.jsxs)(`div`,{className:`flex min-w-0 flex-[1_1_300px] flex-col gap-1`,children:[(0,R.jsx)(`h2`,{id:`report-h`,className:`m-0 text-[22px] font-semibold tracking-[-0.01em] [overflow-wrap:anywhere]`,children:i.title}),(0,R.jsx)(`span`,{className:`text-sm text-muted`,children:[z[i.scope],B(i.startedAt),V(i.ms),i.tokens?`${p(i.tokens)} tokens`:``,i.cost?v(i.cost.amount,i.cost.currency):``].filter(Boolean).join(` · `)})]}),(0,R.jsx)(C,{tone:S.tone===`warn`?`warn`:S.tone,children:S.label}),i.report?(0,R.jsxs)(t,{variant:`primary`,onClick:async()=>f(await E(i.report)),children:[(0,R.jsx)(k,{size:16,"aria-hidden":`true`}),`Open full report`]}):(0,R.jsx)(O,{path:i.folder})]}),d&&(0,R.jsx)(`p`,{role:`alert`,className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail`,children:d}),w&&(0,R.jsx)(`p`,{className:`m-0 max-w-[60ch] text-sm leading-relaxed text-ink-2`,children:`A report page ForgeQE wrote (a readiness review, results imported from a test framework, …). Its details are in the page itself: Open full report.`}),(0,R.jsxs)(`div`,{className:r(w&&`hidden`),children:[(0,R.jsx)(b,{label:`Report`,current:o,onPick:c,tabs:[{id:`overview`,label:`Overview`},{id:`cases`,label:`Cases (${i.counts.total})`},{id:`trend`,label:`Trend`}]}),(0,R.jsxs)(`div`,{role:`tabpanel`,id:`panel-${o}`,"aria-labelledby":`tab-${o}`,className:`flex flex-col gap-5`,children:[o===`overview`&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsxs)(`div`,{className:`flex flex-wrap gap-3`,children:[(0,R.jsx)(_,{icon:P,label:`Passed`,value:String(i.counts.passed)}),(0,R.jsx)(_,{icon:s,label:`Failed`,value:String(i.counts.failed)}),(0,R.jsx)(_,{icon:T,label:`Other`,value:String(i.counts.other),note:i.stopped?`Stopped before the end`:void 0}),(0,R.jsx)(_,{icon:j,label:`Time`,value:V(i.ms)||`–`})]}),(0,R.jsx)(K,{counts:i.counts}),(0,R.jsxs)(`div`,{className:`flex flex-col`,children:[(0,R.jsx)(`h3`,{className:`m-0 mb-1 text-base font-semibold`,children:`Needs attention`}),x.length===0?(0,R.jsx)(`p`,{className:`m-0 border-t border-line-soft py-4 text-sm text-muted`,children:`Nothing: every case passed.`}):(0,R.jsx)(`ul`,{className:`m-0 list-none p-0`,children:x.map((e,t)=>(0,R.jsx)(J,{c:e,open:l===`a${t}`,onToggle:()=>u(l===`a${t}`?null:`a${t}`)},`${e.id}-${t}`))})]}),i.writeBack.length>0&&(0,R.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,R.jsx)(`h3`,{className:`m-0 text-base font-semibold`,children:`Posted back`}),(0,R.jsx)(D,{text:i.writeBack.map(e=>`- ${e}`).join(`
|
|
2
|
-
`),className:`text-sm`})]})]}),o===`cases`&&(0,R.jsxs)(`ul`,{className:`m-0 list-none p-0`,children:[i.cases.map((e,t)=>(0,R.jsx)(J,{c:e,open:l===`c${t}`,onToggle:()=>u(l===`c${t}`?null:`c${t}`)},`${e.id}-${t}`)),i.cases.length<i.counts.total&&(0,R.jsxs)(`li`,{className:`border-t border-line-soft py-3 text-sm text-muted`,children:[`The first `,i.cases.length,` of `,i.counts.total,`; the full report has them all.`]})]}),o===`trend`&&(0,R.jsx)(q,{runs:a})]})]}),(0,R.jsxs)(`form`,{onSubmit:e=>{e.preventDefault(),A()},className:`flex items-center gap-2 rounded-full border border-line bg-bg py-1.5 pr-1.5 pl-5 shadow-composer @2xl:sticky @2xl:bottom-4`,children:[(0,R.jsx)(`label`,{htmlFor:`ask-report`,className:`sr-only`,children:`Ask about this report`}),(0,R.jsx)(`input`,{id:`ask-report`,value:m,onChange:e=>h(e.target.value),placeholder:`Ask about this report…`,className:`min-h-10 min-w-0 flex-1 border-0 bg-transparent text-[15px] text-ink outline-none placeholder:text-muted`}),(0,R.jsx)(`button`,{type:`submit`,"aria-label":`Ask`,disabled:!m.trim()||g,className:r(`inline-flex size-10 flex-none cursor-pointer items-center justify-center rounded-full border-0 disabled:cursor-not-allowed`,m.trim()?`bg-accent text-white hover:bg-accent-hover`:`bg-line text-muted`),children:(0,R.jsx)(M,{size:18,strokeWidth:2.2,"aria-hidden":`true`})})]})]})}function X({days:e}){if(!e.length)return(0,R.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`No runs with results yet.`});let t=e.slice(-14),n=e=>e.cases?Math.round(e.passed/e.cases*100):0;return(0,R.jsx)(`div`,{role:`img`,"aria-label":`Pass rate by day, oldest first: ${t.map(e=>`${e.day} ${n(e)}%`).join(`, `)}`,className:`flex h-[180px] items-end gap-1.5 border-b border-line pb-1`,children:t.map((e,i)=>(0,R.jsxs)(`div`,{className:`flex h-full min-w-0 flex-1 flex-col items-center justify-end gap-1`,title:`${e.day}: ${e.passed} of ${e.cases} passed in ${e.runs} run${e.runs===1?``:`s`}`,children:[(0,R.jsxs)(`span`,{className:`text-[11px] text-ink-2 tabular-nums`,children:[n(e),`%`]}),(0,R.jsx)(`span`,{className:r(`w-full max-w-[36px] rounded-t-[6px]`,i===t.length-1?`bg-accent`:`bg-[#dbe1fe]`),style:{height:`${Math.max(2,n(e))}%`}}),(0,R.jsx)(`span`,{className:`text-[11px] text-muted tabular-nums`,children:e.day.slice(5)})]},e.day))})}function Z({project:e,build:n,onBuilt:a}){let[o,s]=(0,L.useState)(n),[c,u]=(0,L.useState)(``),f=!!o?.running;(0,L.useEffect)(()=>{if(!f)return;let e=setInterval(async()=>{try{let e=await i.buildStatus();s(e),e&&!e.running&&a()}catch{}},1200);return()=>clearInterval(e)},[f,a]);let g=e.cases?Math.round(e.passed/e.cases*100):0;return(0,R.jsxs)(`section`,{"aria-labelledby":`project-h`,className:`flex min-w-0 flex-1 flex-col gap-6`,children:[(0,R.jsxs)(`div`,{className:`flex flex-wrap items-start gap-x-4 gap-y-3`,children:[(0,R.jsxs)(`div`,{className:`flex min-w-0 flex-[1_1_300px] flex-col gap-1`,children:[(0,R.jsx)(`h2`,{id:`project-h`,className:`m-0 text-[22px] font-semibold tracking-[-0.01em]`,children:`Project report`}),(0,R.jsx)(`span`,{className:`text-sm text-muted`,children:e.runs?`Every run from ${B(e.from)} to ${B(e.to)}, taken together.`:`Every run, taken together, once there are some.`})]}),e.page&&(0,R.jsxs)(t,{variant:`primary`,onClick:async()=>u(await E(e.page.file)),children:[(0,R.jsx)(k,{size:16,"aria-hidden":`true`}),`Open project report`]}),(0,R.jsx)(t,{disabled:f,onClick:async()=>{u(``);try{s(await i.build())}catch(e){u(e.message)}},children:f?(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(l,{state:`working`,px:16}),`Building…`]}):e.page?`Rebuild`:`Build project report`})]}),(0,R.jsx)(`p`,{className:`-mt-3 m-0 text-[13px] text-muted`,children:e.page?`The page was built ${B(e.page.builtAt)}, by forgeqe bi build.`:`The project report page is built by forgeqe bi build from every run; it has not been built yet.`}),c&&(0,R.jsx)(`p`,{role:`alert`,className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail`,children:c}),o&&(o.running||o.ok===!1||o.lines.length>0)&&(0,R.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[!o.running&&(0,R.jsx)(`p`,{className:r(`m-0 rounded-[12px] px-3 py-2 text-sm`,o.ok?`bg-pass-soft text-pass`:`bg-fail-soft text-fail`),children:o.ok?`The project report is built.`:`forgeqe bi build stopped with a problem; its output is below.`}),(0,R.jsx)(`pre`,{"aria-label":`forgeqe bi build output`,"aria-live":`polite`,className:`m-0 max-h-[200px] overflow-auto rounded-[12px] bg-surface-muted px-4 py-3 font-mono text-[12px] leading-relaxed whitespace-pre-wrap text-ink-2 [overflow-wrap:anywhere]`,children:o.lines.length?o.lines.join(`
|
|
3
|
-
`):`Starting…`})]}),(0,R.jsxs)(`div`,{className:`flex flex-wrap gap-3`,children:[(0,R.jsx)(_,{icon:h,label:`Runs`,value:String(e.runs)}),(0,R.jsx)(_,{icon:N,label:`Cases run`,value:String(e.cases)}),(0,R.jsx)(_,{icon:I,label:`Pass rate`,value:e.cases?`${g}%`:`–`,note:`${e.passed} passed, ${e.failed} failed`}),(0,R.jsx)(_,{icon:m,label:`Tokens`,value:p(e.tokens),note:e.cost?v(e.cost.amount,e.cost.currency):void 0})]}),(0,R.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[(0,R.jsx)(`h3`,{className:`m-0 text-base font-semibold`,children:`Pass rate by day`}),(0,R.jsx)(X,{days:e.days})]}),(0,R.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,R.jsx)(`h3`,{className:`m-0 text-base font-semibold`,children:`Fails most`}),e.failing.length===0?(0,R.jsx)(`p`,{className:`m-0 border-t border-line-soft py-4 text-sm text-muted`,children:`No case has failed.`}):(0,R.jsx)(`ul`,{className:`m-0 list-none p-0`,children:e.failing.map(e=>(0,R.jsxs)(`li`,{className:`flex min-h-[56px] flex-wrap items-center gap-x-3 gap-y-1 border-t border-line-soft py-2`,children:[(0,R.jsxs)(`span`,{className:`flex min-w-0 flex-[1_1_260px] flex-col gap-0.5`,children:[(0,R.jsxs)(`span`,{className:`font-medium`,children:[(0,R.jsx)(`span`,{className:`font-mono text-[13px] text-ink-2`,children:e.id}),` `,e.title]}),e.summary&&(0,R.jsx)(D,{text:e.summary,className:`text-[13px] text-muted [&_p]:my-0`})]}),(0,R.jsxs)(C,{tone:`fail`,children:[e.failed,` of `,e.runs,` failed`]}),(0,R.jsxs)(`span`,{className:`text-[13px] text-muted`,children:[`last `,B(e.lastFailed)]})]},e.id||e.title))})]}),(0,R.jsxs)(`div`,{className:`flex flex-wrap gap-x-10 gap-y-6`,children:[(0,R.jsxs)(`div`,{className:`flex min-w-0 flex-[1_1_280px] flex-col gap-1`,children:[(0,R.jsx)(`h3`,{className:`m-0 text-base font-semibold`,children:`Flaky`}),(0,R.jsx)(`p`,{className:`m-0 mb-1 text-[13px] text-muted`,children:`Cases that both passed and failed.`}),e.flaky.length===0?(0,R.jsx)(`p`,{className:`m-0 border-t border-line-soft py-3 text-sm text-muted`,children:`None.`}):e.flaky.map(e=>(0,R.jsxs)(`div`,{className:`flex items-center gap-3 border-t border-line-soft py-2 text-sm`,children:[(0,R.jsxs)(`span`,{className:`min-w-0 flex-1 truncate`,children:[(0,R.jsx)(`span`,{className:`font-mono text-[13px] text-ink-2`,children:e.id}),` `,e.title]}),(0,R.jsxs)(`span`,{className:`text-[13px] text-muted tabular-nums`,children:[e.failed,`/`,e.runs,` failed`]})]},e.id||e.title))]}),(0,R.jsxs)(`div`,{className:`flex min-w-0 flex-[1_1_280px] flex-col gap-1`,children:[(0,R.jsx)(`h3`,{className:`m-0 text-base font-semibold`,children:`Defects`}),(0,R.jsx)(`p`,{className:`m-0 mb-1 text-[13px] text-muted`,children:`Failures grouped by cause.`}),e.defects.length===0?(0,R.jsx)(`p`,{className:`m-0 border-t border-line-soft py-3 text-sm text-muted`,children:`None found.`}):e.defects.map(e=>(0,R.jsxs)(`div`,{className:`flex items-center gap-3 border-t border-line-soft py-2 text-sm`,children:[(0,R.jsx)(`span`,{className:`min-w-0 flex-1 [overflow-wrap:anywhere]`,children:e.title}),(0,R.jsxs)(`span`,{className:`text-[13px] text-muted tabular-nums`,children:[e.cases,` case`,e.cases===1?``:`s`,`, `,e.runs,` time`,e.runs===1?``:`s`]})]},e.title))]})]}),e.kinds.length>1&&(0,R.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,R.jsx)(`h3`,{className:`m-0 text-base font-semibold`,children:`By kind`}),e.kinds.map(e=>(0,R.jsxs)(`div`,{className:`flex items-center gap-3 border-t border-line-soft py-2 text-sm`,children:[(0,R.jsx)(`span`,{className:`flex-1`,children:z[e.scope]||e.scope}),(0,R.jsxs)(`span`,{className:`text-[13px] text-muted tabular-nums`,children:[d(e.runs,`run`),` · `,e.cases?Math.round(e.passed/e.cases*100):0,`% passed`]})]},e.scope))]})]})}function Q({sub:n}){let[s,c]=(0,L.useState)(null),[l,d]=(0,L.useState)(``),[f,p]=(0,L.useState)(``),[m,h]=(0,L.useState)(``),[g,_]=(0,L.useState)(``),v=(0,L.useCallback)(()=>{i.list().then(c,e=>d(e.message))},[]);(0,L.useEffect)(v,[v]);let y=n===`project`?`project`:`execution`,b=s?.rows??null,x=(0,L.useMemo)(()=>(b||[]).filter(e=>(!f||e.scope===f)&&(!m.trim()||`${e.title} ${e.id}`.toLowerCase().includes(m.trim().toLowerCase()))),[b,f,m]),C=n&&y===`execution`?decodeURIComponent(n):``,T=b?.find(e=>e.key===C)||x[0],D=(0,L.useMemo)(()=>T&&b?b.filter(e=>e.scope===T.scope&&e.title===T.title&&String(e.startedAt||``)<=String(T.startedAt||``)).slice(0,10).reverse():[],[T,b]),O=[...new Set((b||[]).map(e=>e.scope))];return(0,L.useEffect)(()=>{n&&y===`execution`&&window.innerWidth<1024&&document.getElementById(`report-h`)?.scrollIntoView({block:`start`})},[n,y]),(0,R.jsxs)(`div`,{className:`flex min-h-screen flex-col`,children:[(0,R.jsx)(S,{}),(0,R.jsxs)(`main`,{className:`@container mx-auto box-border flex w-full max-w-[1180px] flex-auto flex-col gap-4 px-6 pt-10 pb-6`,children:[(0,R.jsxs)(`div`,{className:`flex flex-wrap items-end gap-x-6 gap-y-2`,children:[(0,R.jsx)(u,{icon:w,size:`lg`}),(0,R.jsx)(`h1`,{className:`m-0 text-[26px] font-semibold tracking-[-0.01em]`,children:`Reports`}),(0,R.jsx)(`nav`,{"aria-label":`Reports`,className:`flex gap-1 rounded-full bg-surface-strong p-1`,children:[[`execution`,`Execution reports`,e(`reports`)],[`project`,`Project report`,e(`reports`,`project`)]].map(([e,t,n])=>(0,R.jsx)(`a`,{href:n,"aria-current":y===e?`page`:void 0,className:r(`inline-flex min-h-9 items-center rounded-full px-4 text-sm font-medium no-underline`,y===e?`bg-bg text-ink shadow-composer hover:text-ink`:`text-ink-2 hover:text-ink`),children:t},e))})]}),l&&(0,R.jsx)(`p`,{role:`alert`,className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail`,children:l}),!s&&!l&&(0,R.jsx)(`p`,{role:`status`,className:`m-0 text-sm text-muted`,children:`Loading…`}),s&&y===`project`&&(0,R.jsx)(Z,{project:s.project,build:s.build,onBuilt:v}),s&&y===`execution`&&(0,R.jsxs)(`div`,{className:`flex flex-col gap-6 @2xl:flex-row @2xl:items-start @2xl:gap-8`,children:[(0,R.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-3 @2xl:sticky @2xl:top-6 @2xl:w-[250px] @2xl:flex-none`,children:[(0,R.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[(0,R.jsx)(`label`,{htmlFor:`report-kind`,className:`sr-only`,children:`Kind`}),(0,R.jsxs)(o,{id:`report-kind`,value:f,onChange:e=>p(e.target.value),children:[(0,R.jsxs)(`option`,{value:``,children:[`All kinds (`,s.total,`)`]}),O.map(e=>(0,R.jsx)(`option`,{value:e,children:z[e]},e))]}),(0,R.jsxs)(`div`,{className:`relative min-w-0`,children:[(0,R.jsx)(A,{size:16,"aria-hidden":`true`,className:`pointer-events-none absolute top-1/2 left-3 -translate-y-1/2 text-muted`}),(0,R.jsx)(`label`,{htmlFor:`report-search`,className:`sr-only`,children:`Search reports`}),(0,R.jsx)(a,{id:`report-search`,value:m,onChange:e=>h(e.target.value),placeholder:`Search`,className:`pl-9`})]})]}),g&&(0,R.jsx)(`p`,{role:`alert`,className:`m-0 text-[13px] text-fail`,children:g}),(0,R.jsxs)(`ul`,{"aria-label":`Execution reports`,className:`m-0 flex max-h-[42vh] list-none flex-col overflow-y-auto p-0 @2xl:max-h-[calc(100vh-260px)]`,children:[x.length===0&&(0,R.jsx)(`li`,{className:`py-4 text-sm text-muted`,children:s.rows.length?`Nothing matches.`:`No reports yet. Runs, suites and monitor rounds show here once they have run.`}),x.map(n=>{let i=W(n),a=T?.key===n.key;return(0,R.jsxs)(`li`,{className:r(`flex items-center rounded-[12px]`,a?`bg-surface-strong`:`hover:bg-surface-muted`),children:[(0,R.jsxs)(`a`,{href:e(`reports`,encodeURIComponent(n.key)),"aria-current":a?`page`:void 0,className:`flex min-h-[60px] min-w-0 flex-1 items-center gap-3 py-2 pl-3 text-ink no-underline hover:text-ink`,children:[(0,R.jsx)(`span`,{"aria-label":i.label,className:r(`size-2.5 flex-none rounded-full`,G[i.tone])}),(0,R.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,R.jsx)(`span`,{className:`truncate text-[15px] font-medium`,children:n.title}),(0,R.jsx)(`span`,{className:`truncate text-[13px] text-muted`,children:[n.scope===`page`?``:`${n.counts.passed}/${n.counts.total} passed`,B(n.startedAt),z[n.scope]].filter(Boolean).join(` · `)})]})]}),n.report&&(0,R.jsx)(t,{variant:`icon`,size:`icon`,"aria-label":`Open the report of ${n.title} in a new tab`,title:`Open the HTML report in a new tab`,onClick:async()=>_(await E(n.report)),className:`mr-1 flex-none`,children:(0,R.jsx)(k,{size:16,"aria-hidden":`true`})})]},n.key)}),s.total>s.rows.length&&(0,R.jsxs)(`li`,{className:`py-3 text-[13px] text-muted`,children:[`The newest `,s.rows.length,` of `,s.total,`.`]})]})]}),T?(0,R.jsx)(Y,{run:T,history:D}):(0,R.jsxs)(`div`,{className:`flex min-w-0 flex-1 flex-col items-start gap-3 rounded-[18px] border border-line p-6`,children:[(0,R.jsx)(w,{size:24,"aria-hidden":`true`,className:`text-muted`}),(0,R.jsx)(`p`,{className:`m-0 max-w-[60ch] leading-relaxed text-ink-2`,children:`Run something from a chat, and its report shows here.`}),(0,R.jsx)(`a`,{href:e(),className:`text-accent`,children:`New chat`})]})]})]})]})}export{Q as Reports};
|