@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.
Files changed (62) hide show
  1. package/HOW_TO_USE.md +28 -7
  2. package/README.md +4 -2
  3. package/dist/forgeqe.js +1041 -1007
  4. package/dist/web/assets/ApiTests-B-0nk720.js +2 -0
  5. package/dist/web/assets/Chat-Be6J6POH.js +3 -0
  6. package/dist/web/assets/{Data-Qolfhsd7.js → Data-BtcD7_T2.js} +4 -4
  7. package/dist/web/assets/Features-DHIGL43K.js +1 -0
  8. package/dist/web/assets/Jobs-C-cEyrrH.js +3 -0
  9. package/dist/web/assets/Quality-C6hKykoO.js +1 -0
  10. package/dist/web/assets/Record-B5395d_b.js +1 -0
  11. package/dist/web/assets/Regression-BS5g4T3v.js +2 -0
  12. package/dist/web/assets/Reports-CHqfhNR8.js +2 -0
  13. package/dist/web/assets/Response-P2oku3_B.js +8 -0
  14. package/dist/web/assets/Runs-DPmd5QPL.js +2 -0
  15. package/dist/web/assets/chevron-right-Cwpv0kJi.js +1 -0
  16. package/dist/web/assets/circle-dashed-COka5oNi.js +1 -0
  17. package/dist/web/assets/external-link-B9T-hxHX.js +1 -0
  18. package/dist/web/assets/file-chart-column-increasing-Ch7H145u.js +1 -0
  19. package/dist/web/assets/filters-C5e4Y0To.js +1 -0
  20. package/dist/web/assets/index-BI_5kNwE.css +2 -0
  21. package/dist/web/assets/index-KUNDUjo0.js +26 -0
  22. package/dist/web/assets/{markdown-GKy06efH.js → markdown-DclrGj2e.js} +7 -7
  23. package/dist/web/assets/rotate-ccw-BizUsxuR.js +1 -0
  24. package/dist/web/assets/sticky-note-CFIxnO7K.js +1 -0
  25. package/dist/web/assets/terminal-BnhGgg6d.js +1 -0
  26. package/dist/web/assets/trash-CtWVl2Xk.js +1 -0
  27. package/dist/web/assets/trending-up-BnzWZEhg.js +1 -0
  28. package/dist/web/assets/when-CHnpLdYw.js +1 -0
  29. package/dist/web/index.html +2 -3
  30. package/docs/ARCHITECTURE.md +5 -2
  31. package/docs/user-guide.html +32 -13
  32. package/evals/prompts/brain/browser-test-intent.yaml +30 -0
  33. package/forgeqe.example.yaml +4 -3
  34. package/package.json +1 -1
  35. package/prompts/brain.md +4 -1
  36. package/py/forgeqe_engine/flowmatch.py +72 -1
  37. package/py/forgeqe_engine/logins.py +17 -5
  38. package/py/forgeqe_engine/loops.py +26 -3
  39. package/py/forgeqe_engine/memory.py +11 -0
  40. package/py/forgeqe_engine/service.py +29 -1
  41. package/dist/web/assets/ApiTests-CSSed9hU.js +0 -2
  42. package/dist/web/assets/Chat-CFfiv7wI.js +0 -2
  43. package/dist/web/assets/Features-BM48eq6x.js +0 -1
  44. package/dist/web/assets/Jobs-BjVNQUaK.js +0 -3
  45. package/dist/web/assets/Quality-F0sKErYg.js +0 -1
  46. package/dist/web/assets/Record-CkwV9gN8.js +0 -1
  47. package/dist/web/assets/Regression-DKaNfFu1.js +0 -2
  48. package/dist/web/assets/Reports-o_82URX6.js +0 -3
  49. package/dist/web/assets/Response-YL9AIB8R.js +0 -8
  50. package/dist/web/assets/Runs-Dp-DzyW1.js +0 -2
  51. package/dist/web/assets/circle-dashed-DbpKfamm.js +0 -1
  52. package/dist/web/assets/external-link-pvVHJFAZ.js +0 -1
  53. package/dist/web/assets/file-chart-column-increasing-b-Yh6rmz.js +0 -1
  54. package/dist/web/assets/index-Bkx3fVbr.js +0 -21
  55. package/dist/web/assets/index-DSEYuBEd.css +0 -2
  56. package/dist/web/assets/rotate-ccw-BL6YOAPy.js +0 -1
  57. package/dist/web/assets/search-YLg3uzAx.js +0 -1
  58. package/dist/web/assets/status-CL_clA0J.js +0 -3
  59. package/dist/web/assets/sticky-note-DIQ2FWMw.js +0 -1
  60. package/dist/web/assets/terminal-BdTFlGZR.js +0 -1
  61. package/dist/web/assets/timer-NLZ0XmVl.js +0 -1
  62. package/dist/web/assets/trash-DIUJbRpw.js +0 -1
@@ -0,0 +1,2 @@
1
+ import{t as e}from"./circle-dashed-COka5oNi.js";import"./markdown-DclrGj2e.js";import{A as t,Ct as n,Et as r,It as i,L as a,Lt as o,M as s,N as c,O as l,P as u,Pt as d,R as f,S as p,U as m,Xt as h,_ as g,b as _,g as v,i as y,m as b,nt as x,o as S,p as C,qt as w,rt as T,x as E,z as D}from"./index-KUNDUjo0.js";import{n as O,r as k,t as A}from"./filters-C5e4Y0To.js";import{t as j}from"./when-CHnpLdYw.js";var M={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`}]]};M.node;var N=d(M),P=h(w(),1),F=s(),I={passed:`pass`,failed:`fail`,error:`fail`,skipped:`neutral`},L={GET:`text-pass`,POST:`text-accent`,PUT:`text-warn`,PATCH:`text-warn`,DELETE:`text-fail`},ee={run:`a browser run's calls`,openapi:`OpenAPI`,swagger:`Swagger`,wsdl:`WSDL`,graphql:`GraphQL`,proto:`.proto`,case:`a test case`,text:`a description`},R=e=>new Date(e).toLocaleString([],{month:`short`,day:`numeric`,hour:`numeric`,minute:`2-digit`});function z({job:e,onStop:n}){let r=(0,P.useRef)(null);(0,P.useEffect)(()=>{r.current&&(r.current.scrollTop=r.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,F.jsxs)(`section`,{"aria-label":e.running?i:a,className:`flex flex-col gap-2 rounded-[14px] border border-line-soft p-4`,children:[(0,F.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,F.jsx)(`span`,{className:`flex-1 font-medium`,children:e.running?i:a}),(0,F.jsx)(y,{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,F.jsxs)(t,{onClick:n,className:`border-fail text-fail hover:bg-fail-soft`,children:[(0,F.jsx)(m,{size:14,fill:`currentColor`,"aria-hidden":`true`}),`Stop`]})]}),(0,F.jsx)(`code`,{className:`font-mono text-[12px] text-muted [overflow-wrap:anywhere]`,children:e.command}),(0,F.jsxs)(`details`,{open:e.running,children:[(0,F.jsxs)(`summary`,{className:`cursor-pointer text-sm text-muted`,children:[`Output (`,e.lines.length,` lines)`]}),(0,F.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…`})]})]})}var B=({m:e})=>(0,F.jsx)(`span`,{className:c(`w-[58px] flex-none font-mono text-[12px] font-semibold`,L[e.toUpperCase()]||`text-ink-2`),children:e.toUpperCase()});function te({c:e,r:t}){let n=Array.isArray(e.expect.status)?e.expect.status.join(` or `):e.expect.status,r=t&&t.status!==`passed`?[t.reason,...t.checks.filter(e=>!e.ok).map(e=>`${e.name}${e.detail?`: ${e.detail}`:``}`)].filter(Boolean).join(` · `):``;return(0,F.jsxs)(`li`,{className:`grid min-h-11 grid-cols-[58px_minmax(0,1fr)_auto] items-center gap-x-3 rounded-[10px] px-3 py-1.5 hover:bg-surface-muted @3xl:grid-cols-[58px_minmax(0,1fr)_200px_auto]`,children:[(0,F.jsx)(B,{m:e.method}),(0,F.jsxs)(`span`,{className:`flex min-w-0 flex-col`,children:[(0,F.jsx)(`span`,{className:`truncate font-mono text-[13px]`,title:e.protocol===`rest`?e.path:e.name,children:e.protocol===`rest`?e.path:e.name}),r?(0,F.jsx)(`span`,{className:c(`truncate text-[12px]`,t?.status===`skipped`?`text-muted`:`text-fail`),title:r,children:r}):(e.writes||e.needsData?.length)&&(0,F.jsx)(`span`,{className:`truncate text-[12px] text-warn`,children:[e.writes?`Changes data`:``,e.needsData?.length?`Needs real ${e.needsData.join(`, `)}`:``].filter(Boolean).join(` · `)})]}),(0,F.jsx)(`span`,{className:`hidden truncate text-[12px] text-muted @3xl:block`,children:[n?`expects ${n}`:``,e.expect.contentType,e.auth?`${e.auth.type} auth`:``].filter(Boolean).join(` · `)}),(0,F.jsx)(`span`,{className:`flex justify-end`,children:t?(0,F.jsx)(y,{tone:I[t.status],children:t.status===`skipped`?`Skipped`:t.httpStatus?`${t.httpStatus}`:t.status}):(0,F.jsx)(`span`,{className:`text-[12px] text-muted`,children:`Not run`})})]})}function ne({s:e,r:t}){let[n,r]=(0,P.useState)(!1);return(0,F.jsxs)(`li`,{className:`border-t border-line-soft first:border-t-0`,children:[(0,F.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,F.jsx)(a,{size:16,"aria-hidden":`true`,className:`flex-none text-muted`}),(0,F.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,F.jsx)(`span`,{className:`text-sm [overflow-wrap:anywhere]`,children:e.name}),(0,F.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,F.jsx)(y,{tone:`warn`,children:`Changes data`}),t&&(0,F.jsx)(y,{tone:I[t.status],children:t.status.charAt(0).toUpperCase()+t.status.slice(1)})]}),n&&(0,F.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,F.jsxs)(`li`,{className:`flex flex-col`,children:[(0,F.jsxs)(`span`,{className:`flex min-w-0 items-center gap-2`,children:[(0,F.jsx)(B,{m:e.method}),(0,F.jsx)(`span`,{className:`min-w-0 flex-1 font-mono text-[12px] [overflow-wrap:anywhere]`,children:e.path}),r&&(0,F.jsx)(y,{tone:I[r.status],children:r.httpStatus?String(r.httpStatus):r.status})]}),(0,F.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,F.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,F.jsxs)(`li`,{className:`flex items-center gap-2 text-muted`,children:[(0,F.jsx)(B,{m:e.method}),(0,F.jsx)(`span`,{className:`font-mono text-[12px]`,children:e.path}),(0,F.jsx)(`span`,{className:`text-[12px]`,children:`cleanup`})]},`c${t}`)),t?.reason&&(0,F.jsx)(`li`,{className:`text-[12px] text-fail [overflow-wrap:anywhere]`,children:t.reason})]})]})}function V({what:e,writes:n,defaultUrl:r,onRun:i,onCancel:a,needsData:o}){let[s,c]=(0,P.useState)(r),[l,u]=(0,P.useState)(!1),[d,f]=(0,P.useState)(!1);return(0,F.jsxs)(`div`,{className:`flex flex-col gap-3 rounded-[12px] border border-accent-line bg-accent-soft p-4`,children:[(0,F.jsx)(`span`,{className:`font-medium`,children:e}),(0,F.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm`,children:[`Against`,(0,F.jsx)(C,{value:s,spellCheck:!1,placeholder:`https://qa.example.com/api`,onChange:e=>c(e.target.value),className:`max-w-[480px] bg-bg`})]}),n>0&&(0,F.jsxs)(`span`,{className:`flex items-start gap-3 text-sm`,children:[(0,F.jsx)(E,{checked:l,onChange:u,label:`Also run the calls that change data`}),(0,F.jsxs)(`span`,{className:`flex flex-col`,children:[`Also run the `,n,` call`,n===1?``:`s`,` that change data`,(0,F.jsx)(`span`,{className:`text-[13px] text-warn`,children:`Only on a test environment: they create, change or delete data there.`})]})]}),o?(0,F.jsxs)(`span`,{className:`flex items-center gap-3 text-sm`,children:[(0,F.jsx)(E,{checked:d,onChange:f,label:`Also run calls with made-up values`}),`Also run the `,o,` call`,o===1?``:`s`,` whose values were made up`]}):null,(0,F.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,F.jsxs)(t,{variant:`primary`,onClick:()=>i({...s.trim()?{baseUrl:s.trim()}:{},allowWrites:l,includeNeedsData:d}),children:[(0,F.jsx)(T,{size:16,"aria-hidden":`true`}),`Run`]}),(0,F.jsx)(t,{onClick:a,children:`Don't run`})]})]})}function re({open:e,onClose:n,view:r,set:i,onMake:a,busy:o}){let[s,l]=(0,P.useState)(`spec`),[d,f]=(0,P.useState)(``),[p,m]=(0,P.useState)(``),[h,g]=(0,P.useState)(``),[_,y]=(0,P.useState)(!0);(0,P.useEffect)(()=>{e&&(l(i.source.kind===`run`?`run`:`spec`),f(i.source.kind===`run`?``:r.runs[0]?.id??``))},[e,i.source.kind,r.runs]);let x=(e,t,n)=>(0,F.jsxs)(`label`,{className:c(`flex cursor-pointer items-start gap-3 rounded-[12px] border px-3 py-2.5`,s===e?`border-accent bg-accent-soft`:`border-line-soft hover:bg-surface-muted`),children:[(0,F.jsx)(`input`,{type:`radio`,name:`sc-from`,checked:s===e,onChange:()=>l(e),className:`mt-1 size-4 accent-accent`}),(0,F.jsxs)(`span`,{className:`flex flex-col`,children:[(0,F.jsx)(`span`,{className:`text-sm font-medium`,children:t}),(0,F.jsx)(`span`,{className:`text-[13px] text-muted`,children:n})]})]});return(0,F.jsx)(v,{open:e,onClose:n,title:`Make scenarios`,description:`API calls in order, where later calls use values earlier answers gave, added to ${i.name}. Nothing runs until you run them.`,footer:(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(t,{onClick:n,children:`Cancel`}),(0,F.jsx)(t,{variant:`primary`,disabled:o||s===`cases`&&!p.trim()||s===`text`&&!h.trim(),onClick:()=>a({from:s,...s===`run`&&d?{run:d}:{},...s===`cases`?{cases:p}:{},...s===`text`?{text:h}:{},...s===`spec`?{negative:_}:{}}),children:`Make scenarios`})]}),children:(0,F.jsxs)(`div`,{className:`flex flex-col gap-2 pt-3`,children:[x(`spec`,`From the API description`,`Each resource's lifecycle, browsing, the description's links, and negative cases (no model)`),s===`spec`&&(0,F.jsxs)(`span`,{className:`flex items-center gap-2 pl-10 text-sm`,children:[(0,F.jsx)(E,{checked:_,onChange:y,label:`Negative cases`}),` Negative cases: an id that does not exist, no credentials, an empty body`]}),x(`run`,`From a browser run`,`The calls the application made, in order, with the values passed on (no model)`),s===`run`&&(0,F.jsxs)(b,{"aria-label":`Browser run`,value:d,onChange:e=>f(e.target.value),className:`ml-10 max-w-[440px]`,children:[(0,F.jsx)(`option`,{value:``,children:`The latest run`}),r.runs.map(e=>(0,F.jsxs)(`option`,{value:e.id,children:[e.goal,` · `,u(e.calls,`call`),` · `,R(e.at)]},e.id))]}),x(`cases`,`From test cases`,`One scenario per case, planned by the model against the set's operations`),s===`cases`&&(0,F.jsx)(C,{"aria-label":`Test cases`,value:p,spellCheck:!1,placeholder:`ado:2238, jira:SHOP-12`,onChange:e=>m(e.target.value),className:`ml-10 max-w-[440px]`}),x(`text`,`From a description`,`In plain words, planned by the model; a step no operation does is listed, never invented`),s===`text`&&(0,F.jsx)(C,{"aria-label":`The scenario`,value:h,maxLength:1e3,placeholder:`Create a user, log in, add two items, check out, the total is the sum`,onChange:e=>g(e.target.value),className:`ml-10`})]})})}function H({open:e,onClose:n,view:r,onMake:i,busy:a}){let[o,s]=(0,P.useState)(`spec`),[c,l]=(0,P.useState)(``),[d,f]=(0,P.useState)(``),[p,m]=(0,P.useState)(``),[h,g]=(0,P.useState)(``);return(0,P.useEffect)(()=>{e&&(l(r.specs[0]??``),f(``),m(``),g(r.baseUrl??``),s(r.specs.length||!r.runs.length?`spec`:`run`))},[e,r]),(0,F.jsx)(v,{open:e,onClose:n,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,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(t,{onClick:n,children:`Cancel`}),(0,F.jsx)(t,{variant:`primary`,disabled:a||o===`spec`&&!c.trim(),onClick:()=>i({from:o,...o===`spec`?{spec:c.trim()}:d?{run:d}:{},...p.trim()?{name:p.trim()}:{},...h.trim()?{baseUrl:h.trim()}:{}}),children:`Make contracts`})]}),children:(0,F.jsxs)(`div`,{className:`flex flex-col gap-4 pt-3`,children:[(0,F.jsxs)(`fieldset`,{className:`m-0 flex flex-col gap-2 border-0 p-0`,children:[(0,F.jsx)(`legend`,{className:`mb-1.5 text-sm font-medium`,children:`From`}),(0,F.jsxs)(`label`,{className:`flex min-h-9 cursor-pointer items-center gap-2 text-sm`,children:[(0,F.jsx)(`input`,{type:`radio`,name:`set-from`,checked:o===`spec`,onChange:()=>s(`spec`),className:`size-4 accent-accent`}),`An API description: OpenAPI or Swagger, WSDL, GraphQL, .proto, or grpc://`]}),o===`spec`&&(0,F.jsxs)(`div`,{className:`flex flex-col gap-1.5 pl-6`,children:[(0,F.jsx)(C,{"aria-label":`API description`,value:c,spellCheck:!1,list:`spec-files`,placeholder:`https://shop.test/openapi.json, or a file in the project`,onChange:e=>l(e.target.value)}),(0,F.jsx)(`datalist`,{id:`spec-files`,children:r.specs.map(e=>(0,F.jsx)(`option`,{value:e},e))}),r.specs.length>0&&(0,F.jsxs)(`span`,{className:`text-[13px] text-muted`,children:[`In this project: `,r.specs.slice(0,6).join(`, `)]})]}),(0,F.jsxs)(`label`,{className:`flex min-h-9 cursor-pointer items-center gap-2 text-sm`,children:[(0,F.jsx)(`input`,{type:`radio`,name:`set-from`,checked:o===`run`,onChange:()=>s(`run`),className:`size-4 accent-accent`}),`The calls a browser run saw the application make`]}),o===`run`&&(0,F.jsxs)(b,{"aria-label":`Browser run`,value:d,onChange:e=>f(e.target.value),className:`ml-6 max-w-[440px]`,children:[(0,F.jsx)(`option`,{value:``,children:`The latest run`}),r.runs.map(e=>(0,F.jsxs)(`option`,{value:e.id,children:[e.goal,` · `,u(e.calls,`call`),` · `,R(e.at)]},e.id))]})]}),(0,F.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium`,children:[`Name`,(0,F.jsx)(C,{value:p,maxLength:120,placeholder:`Petstore`,onChange:e=>m(e.target.value)})]}),(0,F.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium`,children:[`Base URL`,(0,F.jsx)(C,{value:h,spellCheck:!1,placeholder:`https://qa.shop.test/api`,onChange:e=>g(e.target.value)}),(0,F.jsxs)(`span`,{className:`text-[13px] font-normal text-muted`,children:[`Where the contracts run when not where they were recorded or described.`,r.headers.length?` Each call sends ${r.headers.join(`, `)} from api.headers in forgeqe.yaml.`:` Credentials go in api.headers in forgeqe.yaml, as references to environment variables.`]})]})]})})}var U=50,ie=e=>e.length>48?`…/${e.split(/[\\/]/).filter(Boolean).slice(-2).join(`/`)}`:e;function W({label:t,last:i,onOpen:a}){if(!i)return(0,F.jsxs)(`span`,{className:`inline-flex items-center gap-1.5 text-[13px] text-muted`,children:[(0,F.jsx)(e,{size:14,"aria-hidden":`true`}),t,`: not run yet`]});let o=i.counts.failed+i.counts.error,s=i.counts.passed+o;return(0,F.jsxs)(`span`,{className:`inline-flex flex-wrap items-center gap-x-1.5 text-[13px] text-ink-2`,children:[o?(0,F.jsx)(n,{size:14,"aria-label":`Failures`,className:`text-fail`}):(0,F.jsx)(r,{size:14,"aria-label":`Passed`,className:`text-pass`}),t,`:`,` `,(0,F.jsxs)(`b`,{className:c(`tabular-nums`,o?`text-fail`:`text-ink`),children:[i.counts.passed,`/`,s,` passed`]}),i.counts.skipped?(0,F.jsxs)(`span`,{className:`text-muted`,children:[`· `,i.counts.skipped,` skipped`]}):null,(0,F.jsxs)(`span`,{className:`text-muted`,children:[`· `,j(i.at)]}),i.report&&(0,F.jsx)(`button`,{type:`button`,onClick:()=>a(i.report),className:`cursor-pointer border-0 bg-transparent p-0 text-[13px] text-accent hover:underline`,children:`· Report`})]})}function G({id:e,view:n,busy:r,act:a,version:s}){let[l,d]=(0,P.useState)(null),[f,p]=(0,P.useState)(``),[m,h]=(0,P.useState)(null),[v,y]=(0,P.useState)(!1),[b,S]=(0,P.useState)(``),[C,w]=(0,P.useState)(`contracts`),[E,j]=(0,P.useState)(``),[M,N]=(0,P.useState)(`all`),[I,L]=(0,P.useState)(0);(0,P.useEffect)(()=>{let t=!1;return i.set(e).then(e=>!t&&(d(e),p(``)),e=>!t&&p(e.message)),()=>{t=!0}},[e,s]),(0,P.useEffect)(()=>{h(null),w(`contracts`),j(``),N(`all`)},[e]),(0,P.useEffect)(()=>L(0),[e,C,E,M]);let z=(0,P.useMemo)(()=>new Map((l?.last.contracts?.results??[]).map(e=>[e.id,e])),[l]),B=(0,P.useMemo)(()=>new Map((l?.last.scenarios?.results??[]).map(e=>[e.id,e])),[l]);if(f)return(0,F.jsx)(`p`,{className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail`,children:f});if(!l)return(0,F.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`Loading…`});let H=l.contracts.filter(e=>e.writes).length,G=l.contracts.filter(e=>e.needsData?.length).length,K=l.scenarios??[],q=l.last.contracts,ae=l.last.scenarios,J=n.baseUrl||l.baseUrl||``,Y=async e=>S(await g(e)),oe=async e=>{try{let{id:t}=await o.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=_(`chats`,t)}catch(e){S(e.message)}},X=e=>{let t=z.get(e.id);return!!t&&(t.status===`failed`||t.status===`error`)},Z=E.toLowerCase().split(/\s+/).filter(Boolean),Q=l.contracts.filter(e=>Z.every(t=>`${e.method} ${e.path} ${e.name}`.toLowerCase().includes(t))),se=[{id:`all`,label:`All`,n:Q.length},{id:`failed`,label:`Failed`,n:Q.filter(X).length,alert:!0},...H?[{id:`writes`,label:`Changes data`,n:Q.filter(e=>e.writes).length}]:[],...q?[{id:`unrun`,label:`Not run`,n:Q.filter(e=>!z.has(e.id)).length}]:[]],$=[...Q.filter(e=>M===`failed`?X(e):M===`writes`?e.writes:M!==`unrun`||!z.has(e.id))].sort((e,t)=>Number(X(t))-Number(X(e))),ce=K.filter(e=>Z.every(t=>e.name.toLowerCase().includes(t)));return(0,F.jsxs)(`section`,{"aria-labelledby":`set-h`,className:`flex min-w-0 flex-1 flex-col gap-4`,children:[(0,F.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,F.jsx)(`h2`,{id:`set-h`,className:`m-0 text-[22px] font-semibold tracking-[-0.01em] [overflow-wrap:anywhere]`,children:l.name}),(0,F.jsxs)(`span`,{className:`flex flex-wrap items-center gap-x-2 text-[13px] text-muted`,children:[(0,F.jsxs)(`span`,{children:[`From `,ee[l.source.kind]||l.source.kind,` `,(0,F.jsx)(`span`,{className:`font-mono text-[12px]`,title:l.source.ref,children:ie(l.source.ref)})]}),`· `,(0,F.jsx)(`span`,{children:R(l.createdAt)}),l.baseUrl&&(0,F.jsxs)(F.Fragment,{children:[`· `,(0,F.jsx)(`span`,{className:`font-mono text-[12px] [overflow-wrap:anywhere]`,children:l.baseUrl})]})]})]}),(0,F.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,F.jsxs)(t,{variant:`primary`,size:`sm`,disabled:r||!l.contracts.length,onClick:()=>h(`contracts`),children:[(0,F.jsx)(T,{size:15,"aria-hidden":`true`}),`Run contracts`]}),(0,F.jsxs)(t,{size:`sm`,disabled:r||!K.length,onClick:()=>h(`scenarios`),children:[(0,F.jsx)(T,{size:15,"aria-hidden":`true`}),`Run scenarios`]}),(0,F.jsxs)(t,{size:`sm`,disabled:r,onClick:()=>y(!0),children:[(0,F.jsx)(x,{size:15,"aria-hidden":`true`}),`Make scenarios`]}),(0,F.jsxs)(t,{size:`sm`,variant:`ghost`,onClick:()=>void oe(K.length>0),children:[(0,F.jsx)(D,{size:15,"aria-hidden":`true`}),`Write them as tests`]})]}),b&&(0,F.jsx)(`p`,{className:`m-0 text-[13px] text-fail`,children:b}),m===`contracts`&&(0,F.jsx)(V,{what:`Run ${l.contracts.length} contract${l.contracts.length===1?``:`s`}`,writes:H,needsData:G,defaultUrl:J,onCancel:()=>h(null),onRun:e=>{h(null),a(()=>i.run(l.id,e))}}),m===`scenarios`&&(0,F.jsx)(V,{what:`Run ${K.length} scenario${K.length===1?``:`s`}`,writes:K.filter(e=>e.writes).length,defaultUrl:J,onCancel:()=>h(null),onRun:e=>{h(null),a(()=>i.runScenarios(l.id,{...e.baseUrl?{baseUrl:e.baseUrl}:{},allowWrites:e.allowWrites}))}}),(0,F.jsxs)(`div`,{className:`flex flex-col gap-1.5 rounded-[12px] bg-surface-muted px-4 py-2.5`,children:[(0,F.jsx)(W,{label:`Contracts`,last:q,onOpen:e=>void Y(e)}),K.length>0&&(0,F.jsx)(W,{label:`Scenarios`,last:ae,onOpen:e=>void Y(e)})]}),(0,F.jsxs)(`div`,{className:`flex min-w-0 flex-col rounded-[14px] border border-line-soft bg-bg`,children:[(0,F.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2 border-b border-line-soft px-3 py-2.5`,children:[(0,F.jsx)(`div`,{role:`tablist`,"aria-label":`In this set`,className:`flex gap-1 rounded-full bg-surface-strong p-1`,children:[[`contracts`,`Contracts`,l.contracts.length],[`scenarios`,`Scenarios`,K.length]].map(([e,t,n])=>(0,F.jsxs)(`button`,{type:`button`,role:`tab`,"aria-selected":C===e,onClick:()=>w(e),className:c(`inline-flex min-h-8 cursor-pointer items-center gap-1.5 rounded-full border-0 px-3 text-sm font-medium`,C===e?`bg-bg text-ink shadow-sm`:`bg-transparent text-muted hover:text-ink`),children:[t,(0,F.jsx)(`span`,{className:`text-[12px] text-muted tabular-nums`,children:n})]},e))}),(0,F.jsx)(k,{id:`api-search`,label:C===`contracts`?`Find a call`:`Find a scenario`,placeholder:C===`contracts`?`Find a call by its path`:`Find a scenario`,value:E,onChange:j})]}),C===`contracts`?(0,F.jsxs)(F.Fragment,{children:[se.length>1&&(0,F.jsx)(`div`,{className:`flex flex-wrap gap-1.5 px-3 pt-2.5`,children:(0,F.jsx)(A,{items:se,value:M,onChange:N})}),(0,F.jsxs)(`ul`,{"aria-label":`Contracts`,className:`@container m-0 flex list-none flex-col p-1.5`,children:[$.length===0&&(0,F.jsx)(`li`,{className:`px-3 py-4 text-sm text-muted`,children:l.contracts.length?`Nothing matches.`:`This set has no contracts.`}),$.slice(I*U,I*U+U).map(e=>(0,F.jsx)(te,{c:e,r:z.get(e.id)},e.id))]}),(0,F.jsx)(O,{page:I,size:U,total:$.length,onPage:L,label:`Pages of contracts`,children:l.skipped?.length?(0,F.jsxs)(`span`,{title:l.skipped.join(`; `),children:[` · `,u(l.skipped.length,`operation`),` left out`]}):null})]}):K.length?(0,F.jsxs)(`ul`,{"aria-label":`Scenarios`,className:`m-0 flex list-none flex-col px-3 py-1.5`,children:[ce.length===0&&(0,F.jsx)(`li`,{className:`py-4 text-sm text-muted`,children:`Nothing matches.`}),ce.map(e=>(0,F.jsx)(ne,{s:e,r:B.get(e.id)},e.id))]}):(0,F.jsxs)(`div`,{className:`flex flex-col items-start gap-2 px-4 py-5`,children:[(0,F.jsx)(`span`,{className:`text-sm text-muted`,children:`No scenarios yet. A scenario chains calls end to end, each using what the one before answered.`}),(0,F.jsxs)(t,{size:`sm`,disabled:r,onClick:()=>y(!0),children:[(0,F.jsx)(x,{size:15,"aria-hidden":`true`}),`Make scenarios`]})]})]}),(0,F.jsx)(re,{open:v,onClose:()=>y(!1),view:n,set:l,busy:r,onMake:e=>{y(!1),a(()=>i.scenarios(l.id,e))}})]})}function K({sub:a}){let[o,s]=(0,P.useState)(null),[d,m]=(0,P.useState)(``),[h,g]=(0,P.useState)(!1),[v,y]=(0,P.useState)(!1),b=(0,P.useCallback)(()=>{i.view().then(e=>{s(e),m(``)},e=>m(e.message))},[]);(0,P.useEffect)(b,[b]);let C=!!o?.job?.running;(0,P.useEffect)(()=>{if(!C)return;let e=setInterval(b,1500);return()=>clearInterval(e)},[C,b]);let w=async e=>{g(!0),m(``);try{await e(),b()}catch(e){m(e.message)}finally{g(!1)}},T=a?decodeURIComponent(a):``,E=o?.sets.find(e=>e.id===T)||o?.sets[0],D=`${o?.job?.endedAt??``}|${E?.at??``}`,[O,A]=(0,P.useState)(``),M=(o?.sets??[]).filter(e=>O.toLowerCase().split(/\s+/).filter(Boolean).every(t=>e.name.toLowerCase().includes(t)));return(0,F.jsxs)(`div`,{className:`flex min-h-screen flex-col`,children:[(0,F.jsx)(S,{}),(0,F.jsxs)(`main`,{className:`@container mx-auto box-border flex w-full max-w-[1180px] flex-auto flex-col gap-4 px-6 pt-8 pb-10`,children:[(0,F.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,F.jsx)(p,{icon:f,size:`lg`}),(0,F.jsx)(`h1`,{className:`m-0 flex-1 text-[26px] font-semibold tracking-[-0.01em]`,children:`API tests`}),(0,F.jsxs)(t,{variant:`primary`,disabled:!o||C,onClick:()=>y(!0),children:[(0,F.jsx)(x,{size:16,"aria-hidden":`true`}),`New contract set`]})]}),(0,F.jsx)(`p`,{className:`m-0 max-w-[74ch] text-sm leading-relaxed text-muted`,children:`REST, GraphQL, SOAP and gRPC, tested straight against the API with no browser and no model. A contract is one call and what its answer must have; a scenario chains calls end to end.`}),d&&(0,F.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}),!o&&!d&&(0,F.jsx)(`p`,{role:`status`,className:`m-0 text-sm text-muted`,children:`Loading…`}),o?.job&&(0,F.jsx)(z,{job:o.job,onStop:()=>w(()=>i.stop())}),o&&(o.sets.length?(0,F.jsxs)(`div`,{className:`flex flex-col gap-6 @2xl:flex-row @2xl:items-start @2xl:gap-8`,children:[(0,F.jsxs)(`nav`,{"aria-label":`Contract sets`,className:`flex min-w-0 flex-col gap-2 @2xl:sticky @2xl:top-6 @2xl:w-[280px] @2xl:flex-none`,children:[(0,F.jsxs)(`span`,{className:`px-1 text-[13px] font-medium text-muted`,children:[`Contract sets (`,o.sets.length,`)`]}),o.sets.length>6&&(0,F.jsx)(k,{id:`set-search`,label:`Find a set`,value:O,onChange:A,className:`flex-none`}),(0,F.jsxs)(`ul`,{className:`m-0 flex max-h-[60vh] list-none flex-col overflow-y-auto p-0 @2xl:max-h-[calc(100vh-240px)]`,children:[M.length===0&&(0,F.jsx)(`li`,{className:`px-3 py-3 text-sm text-muted`,children:`Nothing matches.`}),M.map(t=>{let i=t.last?.contracts,a=!!i&&i.counts.failed+i.counts.error>0;return(0,F.jsx)(`li`,{children:(0,F.jsxs)(`a`,{href:_(`api-tests`,encodeURIComponent(t.id)),"aria-current":E?.id===t.id?`page`:void 0,className:c(`flex min-h-12 items-center gap-2.5 rounded-[10px] px-3 py-1.5 text-ink no-underline`,E?.id===t.id?`bg-surface-strong hover:text-ink`:`hover:bg-surface-muted hover:text-ink`),children:[o?.job?.running&&o.job.target===t.id?(0,F.jsx)(l,{state:`working`,label:`Working on it`,px:16}):i?a?(0,F.jsx)(n,{size:16,"aria-label":`Failures`,className:`flex-none text-fail`}):(0,F.jsx)(r,{size:16,"aria-label":`Passed`,className:`flex-none text-pass`}):(0,F.jsx)(e,{size:16,"aria-label":`Not run`,className:`flex-none text-muted`}),(0,F.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col`,children:[(0,F.jsx)(`span`,{className:`truncate text-[14px] font-medium`,children:t.name}),(0,F.jsxs)(`span`,{className:`truncate text-[12px] text-muted`,children:[u(t.contracts,`contract`),t.scenarios?` · ${u(t.scenarios,`scenario`)}`:``,i?` · ${i.counts.passed}/${i.counts.passed+i.counts.failed+i.counts.error} passed`:``]})]}),(0,F.jsx)(`span`,{className:`flex-none text-[12px] text-muted tabular-nums`,children:j(t.at)})]})},t.id)})]})]}),E&&(0,F.jsx)(G,{id:E.id,view:o,busy:h||C,act:w,version:D})]}):(0,F.jsxs)(`div`,{className:`flex flex-col items-start gap-3 rounded-[18px] border border-line p-6`,children:[(0,F.jsx)(N,{size:24,"aria-hidden":`true`,className:`text-muted`}),(0,F.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`,o.runs.length?`, or from the ${u(o.runs[0].calls,`call`)} the run "${o.runs[0].goal}" saw`:``,`.`]}),(0,F.jsx)(t,{onClick:()=>y(!0),children:`New contract set`})]}))]}),o&&(0,F.jsx)(H,{open:v,onClose:()=>y(!1),view:o,busy:h||C,onMake:e=>w(async()=>{await i.contracts(e),y(!1)})})]})}export{K as ApiTests};
@@ -0,0 +1,3 @@
1
+ import{n as e,r as t}from"./Response-P2oku3_B.js";import{A as n,C as r,D as i,E as a,Ft as o,K as s,Lt as c,M as l,N as u,P as d,Pt as f,S as p,T as m,W as h,X as g,Xt as _,a as v,b as y,i as b,j as x,k as ee,o as S,ot as C,p as w,q as te,qt as T,s as ne,v as E,w as D,y as re}from"./index-KUNDUjo0.js";import{n as O,t as k}from"./when-CHnpLdYw.js";var A={name:`arrow-down`,size:24,node:[[`path`,{d:`M12 5v14`,key:`s699le`}],[`path`,{d:`m19 12-7 7-7-7`,key:`1idqje`}]]};A.node;var j=f(A),M=_(T(),1),N=l();function P({id:l}){let[d,f]=(0,M.useState)([]),[p,h]=(0,M.useState)(null),[g,_]=(0,M.useState)(``),[b,S]=(0,M.useState)(``),[C,w]=(0,M.useState)(!1),T=(0,M.useRef)(null),O=(0,M.useRef)(null),k=(0,M.useRef)(!0),[A,P]=(0,M.useState)(!0);(0,M.useEffect)(()=>{let e=!1,t=0;return f([]),h(null),_(``),(async()=>{for(;!e;)try{let n=await c.events(l,t,t>0);if(e||(_(``),h({title:n.title,state:n.state,profile:n.profile}),n.events.length&&(t=n.events[n.events.length-1].n,f(e=>[...e,...n.events])),(n.state===`closed`||n.state===`failed`)&&!n.events.length))return}catch(t){if(e||(_(t.message),t.status===404))return;await new Promise(e=>setTimeout(e,2e3))}})(),()=>{e=!0}},[l]);let F=(0,M.useMemo)(()=>t(d),[d]),I=(0,M.useMemo)(()=>{let e=new Map;for(let t of F.turns.flatMap(e=>e.changes)){let n=e.get(t.path);e.set(t.path,n?{...n,added:n.added+t.added,removed:n.removed+t.removed}:{path:t.path,added:t.added,removed:t.removed})}return[...e.values()]},[F]),L=(0,M.useMemo)(()=>{let e=[];for(let t of F.turns){for(let n of t.tests)n.testFile&&e.push({label:n.ok?`Test`:`Test (failed)`,path:n.testFile,kind:`test`});for(let n of t.reports)e.push({label:/\.html?$/i.test(n.path)?`Report`:`Run report`,path:n.path,kind:`report`})}return e.filter((t,n)=>e.findIndex(e=>e.path===t.path)===n)},[F]),R=(0,M.useRef)(()=>{});ne(F.context||F.usage||I.length||L.length?{context:F.context,usage:F.usage,model:F.model?.name,changes:I,outputs:L,busy:p?.state===`busy`||p?.state===`starting`,...I.length?{onCreatePr:()=>R.current()}:{}}:null);let z=p?.state,B=z===`busy`||z===`starting`,V=z===`closed`||z===`failed`,H=F.turns.at(-1),U=!!H&&!H.outcome&&(H.asks.some(e=>e.answer===void 0)||H.waits.some(e=>e.answer===void 0));(0,M.useEffect)(()=>{let e=document.documentElement,t=()=>window.innerHeight+window.scrollY>=e.scrollHeight-48,n=e=>{k.current=e,P(e)},r=window.scrollY,i=()=>{t()?n(!0):window.scrollY<r-2&&n(!1),r=window.scrollY},a=new ResizeObserver(()=>{k.current&&!t()&&window.scrollTo({top:e.scrollHeight})});return T.current&&a.observe(T.current),O.current&&a.observe(O.current),window.addEventListener(`scroll`,i,{passive:!0}),()=>{a.disconnect(),window.removeEventListener(`scroll`,i)}},[]);let ie=()=>{k.current=!0,P(!0),window.scrollTo({top:document.documentElement.scrollHeight,behavior:`smooth`})},[W,G]=(0,M.useState)(null);(0,M.useEffect)(()=>void o.settings().then(G,()=>{}),[]);let[K,q]=(0,M.useState)(!1),[J,Y]=(0,M.useState)({});(0,M.useEffect)(()=>Y({}),[F.headed,F.writeTests]);let X={...re(W),headed:J.headed??F.headed??!1,code:J.code??F.writeTests??!0},Z=(0,M.useCallback)(async e=>{try{await e()}catch(e){_(e.message)}},[]),Q=a(`chat-${l}`.replace(/[^\w-]/g,``).slice(0,80),e=>{S(e),m(`reply`)}),$=async e=>{if(e.trim()&&!C){w(!0),k.current=!0,P(!0);try{await c.send(l,e.trim()),S(``),_(``),Q.close()}catch(e){_(e.message)}finally{w(!1)}}};return R.current=()=>{if(p?.state===`busy`||p?.state===`starting`||C)return;let e=I.map(e=>`- ${e.path}`).join(`
2
+ `);$(`Open a pull request with the changes you made in this chat:\n${e}\n\nUse a new branch, a clear title and description, and tell me its link.`)},(0,N.jsxs)(`div`,{className:`flex min-h-screen flex-col`,children:[(0,N.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,N.jsx)(v,{}),(0,N.jsx)(`h1`,{className:`m-0 ml-2 min-w-0 flex-1 truncate text-base font-semibold`,children:p?.title||`Chat`}),F.model&&(0,N.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,N.jsx)(s,{size:14,fill:`currentColor`,"aria-hidden":`true`,className:`text-accent`}),F.model.name]})]}),(0,N.jsxs)(`main`,{ref:T,className:`mx-auto box-border flex w-full max-w-[960px] flex-auto flex-col gap-8 px-6 pt-8 pb-6`,children:[!p&&!g&&(0,N.jsx)(`p`,{role:`status`,className:`m-0 text-sm text-muted`,children:`Opening the chat…`}),F.turns.map((t,n)=>(0,N.jsxs)(`section`,{"aria-label":`Message ${n+1}`,className:`flex flex-col gap-5`,children:[(0,N.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:t.user}),(0,N.jsx)(e,{turn:t,live:n===F.turns.length-1&&!V,onAnswer:(e,t)=>Z(()=>c.answer(l,e,t)),onProceed:(e,t)=>Z(()=>c.proceed(l,e,t))})]},t.id)),H?.outcome===`done`&&H.next.length>0&&!B&&!V&&(0,N.jsx)(`div`,{className:`flex flex-wrap gap-2 pl-12`,children:H.next.map(e=>(0,N.jsx)(`button`,{type:`button`,title:e.request===e.label?void 0:e.request,onClick:()=>void $(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))}),(F.fatal||V&&F.ended)&&(0,N.jsxs)(`div`,{role:`alert`,className:u(`flex flex-col gap-2 rounded-[14px] px-4 py-3 text-sm leading-relaxed`,F.fatal||z===`failed`?`bg-fail-soft text-fail`:`bg-surface-muted text-ink-2`),children:[(0,N.jsx)(`span`,{className:`[overflow-wrap:anywhere]`,children:F.fatal||F.ended?.message||(z===`failed`?`ForgeQE stopped unexpectedly.`:`This chat has ended.`)}),F.ended?.stderr&&(0,N.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:F.ended.stderr.join(`
3
+ `)}),(0,N.jsxs)(`span`,{className:`flex flex-wrap gap-2`,children:[(0,N.jsx)(`a`,{className:x({size:`sm`}),href:y(),children:`Start a new chat`}),F.fatal&&/model|key|sign/i.test(F.fatal)&&(0,N.jsx)(`a`,{className:x({size:`sm`}),href:y(`settings`,`models`),children:`Models`}),F.fatal&&/runtime|engine|setup/i.test(F.fatal)&&(0,N.jsx)(`a`,{className:x({size:`sm`}),href:y(`settings`,`setup`),children:`Set up`})]})]}),F.notes.map((e,t)=>(0,N.jsx)(`p`,{className:`m-0 text-sm text-muted [overflow-wrap:anywhere]`,children:e},t))]}),(0,N.jsx)(`div`,{ref:O,className:`sticky bottom-0 z-10 bg-gradient-to-t from-bg from-70% to-transparent px-6 pt-4 pb-5`,children:(0,N.jsxs)(`div`,{className:`relative mx-auto flex w-full max-w-[880px] flex-col gap-2`,children:[!A&&B&&(0,N.jsxs)(`button`,{type:`button`,onClick:ie,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,N.jsx)(j,{size:14,"aria-hidden":`true`}),`Latest activity`]}),(0,N.jsx)(`div`,{"aria-live":`polite`,children:g&&(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:g})}),(0,N.jsx)(i,{id:`reply`,label:`Reply to ForgeQE`,rows:1,value:b,onChange:S,onSend:()=>void $(b),onStop:()=>Z(()=>c.stop(l)),busy:B&&!U,disabled:V||C||U||Q.running,top:Q.state&&(0,N.jsx)(D,{state:Q.state,onStop:()=>void Q.stop(),onUndo:Q.undo,onClose:Q.close}),left:(0,N.jsxs)(N.Fragment,{children:[(0,N.jsxs)(`div`,{className:`relative`,children:[(0,N.jsxs)(n,{variant:`ghost`,className:`px-2.5 text-[15px]`,disabled:V,"aria-expanded":K,"aria-haspopup":`dialog`,onClick:()=>q(!K),children:[(0,N.jsx)(te,{size:18,strokeWidth:1.8,"aria-hidden":`true`}),`Tools`,(X.headed||!X.code)&&(0,N.jsx)(`span`,{"aria-hidden":`true`,className:`size-1.5 rounded-full bg-accent`})]}),(0,N.jsx)(ee,{open:K,onClose:()=>q(!1),label:`Tools for this chat`,role:`dialog`,side:`auto`,className:`left-0 w-[min(380px,calc(100vw-48px))]`,children:(0,N.jsx)(E,{tools:X,chat:{model:F.model?.name,provider:F.model?.provider},onChange:e=>{let t={};e.headed!==X.headed&&(t.headed=e.headed),e.code!==X.code&&(t.writeTests=e.code),Object.keys(t).length&&(Y({headed:e.headed,code:e.code}),Z(()=>c.settings(l,t)))}})})]}),(0,N.jsx)(r,{running:Q.running,disabled:!b.trim()||V||C||U,onClick:()=>void Q.start(b)})]}),placeholder:V?`This chat has ended`:U?`Answer above to go on`:B?`ForgeQE is working…`:`Reply to ForgeQE…`})]})})]})}function F(e){return e.waiting||e.state===`ready`&&e.outcome===`needs_input`?`waiting`:e.state===`busy`||e.state===`starting`?`working`:e.state===`closed`||e.state===`failed`?`ended`:`done`}var I={working:{label:`Working`,tone:`running`},waiting:{label:`Needs you`,tone:`warn`},done:{label:`Your turn`,tone:`pass`},ended:{label:`Ended`,tone:`neutral`}},L=[{id:`all`,label:`All`},{id:`working`,label:`Working`},{id:`waiting`,label:`Needs you`},{id:`done`,label:`Your turn`},{id:`ended`,label:`Ended`}];function R({c:e}){let t=F(e),n=I[t];return(0,N.jsxs)(`a`,{href:y(`chats`,e.id),className:`group grid min-h-14 grid-cols-[minmax(0,1fr)_auto] items-center gap-x-4 rounded-[10px] px-3 py-2 text-ink no-underline hover:bg-surface-muted hover:text-ink @3xl:grid-cols-[minmax(0,1fr)_120px_90px_150px_96px]`,children:[(0,N.jsxs)(`span`,{className:`flex min-w-0 flex-col`,children:[(0,N.jsxs)(`span`,{className:`flex min-w-0 items-center gap-2`,children:[t===`waiting`&&(0,N.jsx)(`span`,{"aria-hidden":`true`,className:`size-2 flex-none rounded-full bg-warn`}),(0,N.jsx)(`span`,{className:`truncate text-[14px] font-medium`,children:e.title})]}),(0,N.jsx)(`span`,{className:u(`truncate text-[12.5px]`,e.lastReply?`text-muted`:`text-muted italic`),children:e.lastReply||(t===`working`?`Working on the first answer…`:`No answer yet`)})]}),(0,N.jsx)(`span`,{className:`justify-self-end @3xl:justify-self-start`,children:(0,N.jsx)(b,{tone:n.tone,orb:e.state===`starting`?`waiting`:`working`,children:n.label})}),(0,N.jsx)(`span`,{className:`hidden text-[13px] text-muted tabular-nums @3xl:block`,children:e.turns?d(e.turns,`message`):`—`}),(0,N.jsx)(`span`,{className:`hidden min-w-0 items-center gap-1 truncate text-[13px] text-muted @3xl:flex`,children:e.profile?(0,N.jsxs)(N.Fragment,{children:[(0,N.jsx)(s,{size:11,fill:`currentColor`,"aria-hidden":`true`,className:`flex-none text-accent`}),(0,N.jsx)(`span`,{className:`truncate`,children:e.profile})]}):`Default model`}),(0,N.jsx)(`span`,{className:`hidden text-right text-[13px] text-muted tabular-nums @3xl:block`,children:k(e.lastAt||e.started)})]})}var z=50;function B(){let[e,t]=(0,M.useState)(null),[r,i]=(0,M.useState)(``),[a,o]=(0,M.useState)(`all`),[s,l]=(0,M.useState)(``),[d,f]=(0,M.useState)(z);(0,M.useEffect)(()=>f(z),[a,s]),(0,M.useEffect)(()=>{let e=!1,n=()=>c.list().then(n=>!e&&t(n.chats),t=>!e&&i(t.message));n();let r=setInterval(n,5e3);return()=>{e=!0,clearInterval(r)}},[]);let m=(0,M.useMemo)(()=>{let t={all:0,working:0,waiting:0,done:0,ended:0};for(let n of e||[])t.all++,t[F(n)]++;return t},[e]),_=(0,M.useMemo)(()=>{let t=s.toLowerCase().split(/\s+/).filter(Boolean),n=e=>Date.parse(e.lastAt||e.started)||0;return(e||[]).filter(e=>(a===`all`||F(e)===a)&&t.every(t=>`${e.title} ${e.lastReply||``} ${e.profile||``}`.toLowerCase().includes(t))).sort((e,t)=>n(t)-n(e))},[e,a,s]),v=(0,M.useMemo)(()=>{let e=[];for(let t of _.slice(0,d)){let n=O(t.lastAt||t.started),r=e.find(e=>e.day===n);r?r.chats.push(t):e.push({day:n,chats:[t]})}return e},[_,d]);return(0,N.jsxs)(`div`,{className:`flex min-h-screen flex-col`,children:[(0,N.jsx)(S,{}),(0,N.jsxs)(`main`,{className:`mx-auto box-border flex w-full max-w-[1180px] flex-col gap-5 px-6 pt-8 pb-10`,children:[(0,N.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,N.jsx)(p,{icon:C,size:`lg`}),(0,N.jsxs)(`div`,{className:`flex min-w-0 flex-1 flex-col`,children:[(0,N.jsx)(`h1`,{className:`m-0 text-[26px] font-semibold tracking-[-0.01em]`,children:`Chats`}),(0,N.jsx)(`span`,{className:`text-[13px] text-muted`,children:`Each one is its own ForgeQE agent in your project folder, for as long as forgeqe web runs.`})]}),(0,N.jsxs)(`a`,{className:u(x({variant:`primary`}),`gap-2`),href:y(),children:[(0,N.jsx)(h,{size:16,"aria-hidden":`true`}),`New chat`]})]}),e&&e.length>0&&(0,N.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,N.jsx)(`div`,{role:`radiogroup`,"aria-label":`Show`,className:`flex flex-wrap gap-1 rounded-full bg-surface-strong p-1`,children:L.map(e=>(0,N.jsxs)(`button`,{type:`button`,role:`radio`,"aria-checked":a===e.id,onClick:()=>o(e.id),className:u(`inline-flex min-h-8 cursor-pointer items-center gap-1.5 rounded-full border-0 px-3 text-sm font-medium`,a===e.id?`bg-bg text-ink shadow-sm`:`bg-transparent text-muted hover:text-ink`),children:[e.label,(0,N.jsx)(`span`,{className:u(`tabular-nums text-[12px]`,e.id===`waiting`&&m.waiting?`text-warn`:`text-muted`),children:m[e.id]})]},e.id))}),(0,N.jsxs)(`div`,{className:`relative min-w-0 flex-[1_1_240px]`,children:[(0,N.jsx)(g,{size:16,"aria-hidden":`true`,className:`pointer-events-none absolute top-1/2 left-3 -translate-y-1/2 text-muted`}),(0,N.jsx)(w,{type:`search`,"aria-label":`Find a chat`,value:s,placeholder:`Find a chat`,onChange:e=>l(e.target.value),className:`pl-9`})]})]}),r&&(0,N.jsx)(`p`,{role:`alert`,className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail`,children:r}),!e&&!r&&(0,N.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`Loading…`}),e&&e.length===0&&(0,N.jsxs)(`div`,{className:`flex flex-col items-center gap-4 rounded-[18px] border border-dashed border-line px-6 py-14 text-center`,children:[(0,N.jsx)(p,{icon:C,size:`lg`}),(0,N.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,N.jsx)(`span`,{className:`text-lg font-semibold`,children:`No chats yet`}),(0,N.jsx)(`span`,{className:`max-w-[48ch] text-sm text-muted`,children:`Ask ForgeQE to run test cases, explore a journey, write a test or review requirements. Each chat shows up here while forgeqe web runs.`})]}),(0,N.jsx)(`a`,{className:x({variant:`primary`}),href:y(),children:`Start a chat`})]}),e&&e.length>0&&_.length===0&&(0,N.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`No chat matches.`}),_.length>0&&(0,N.jsxs)(`div`,{className:`@container rounded-[14px] border border-line-soft bg-bg`,children:[(0,N.jsxs)(`div`,{"aria-hidden":`true`,className:`hidden grid-cols-[minmax(0,1fr)_120px_90px_150px_96px] gap-x-4 border-b border-line-soft px-6 py-2 text-[12px] font-medium text-muted @3xl:grid`,children:[(0,N.jsx)(`span`,{children:`Chat`}),(0,N.jsx)(`span`,{children:`Status`}),(0,N.jsx)(`span`,{children:`Messages`}),(0,N.jsx)(`span`,{children:`Model`}),(0,N.jsx)(`span`,{className:`text-right`,children:`Last activity`})]}),v.map(e=>(0,N.jsxs)(`section`,{"aria-labelledby":`day-${e.day}`,children:[(0,N.jsxs)(`h2`,{id:`day-${e.day}`,className:`sticky top-0 z-[1] m-0 border-b border-line-soft bg-surface-muted/95 px-6 py-1.5 text-[12px] font-semibold tracking-wide text-muted uppercase backdrop-blur`,children:[e.day,` `,(0,N.jsxs)(`span`,{className:`font-normal tabular-nums`,children:[`· `,e.chats.length]})]}),(0,N.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-1.5`,children:e.chats.map(e=>(0,N.jsx)(`li`,{children:(0,N.jsx)(R,{c:e})},e.id))})]},e.day)),_.length>d&&(0,N.jsxs)(`div`,{className:`flex items-center justify-between gap-3 border-t border-line-soft px-6 py-3 text-[13px] text-muted`,children:[(0,N.jsxs)(`span`,{children:[`Showing `,d,` of `,_.length]}),(0,N.jsxs)(n,{size:`sm`,onClick:()=>f(d+z),children:[`Show `,Math.min(z,_.length-d),` more`]})]})]})]})]})}export{P as Chat,B as Chats};
@@ -1,4 +1,4 @@
1
- import{F as e,G as t,It as n,J as r,Lt as i,M as a,N as o,P as s,W as c,Y as l,Zt as u,ct as d,en as f,et as p,jt as m,p as h,q as g,r as _,t as v,ut as y,yt as b}from"./status-CL_clA0J.js";import{a as x}from"./markdown-GKy06efH.js";import{t as S}from"./file-chart-column-increasing-b-Yh6rmz.js";import{t as C}from"./search-YLg3uzAx.js";import{t as w}from"./trash-DIUJbRpw.js";import{f as T,x as E,y as D}from"./index-Bkx3fVbr.js";var O={name:`plug-zap`,size:24,node:[[`path`,{d:`M6.3 20.3a2.4 2.4 0 0 0 3.4 0L12 18l-6-6-2.3 2.3a2.4 2.4 0 0 0 0 3.4Z`,key:`goz73y`}],[`path`,{d:`m2 22 3-3`,key:`19mgm9`}],[`path`,{d:`M7.5 13.5 10 11`,key:`7xgeeb`}],[`path`,{d:`M10.5 16.5 13 14`,key:`10btkg`}],[`path`,{d:`m18 3-4 4h6l-4 4`,key:`16psg9`}]],aliases:[`plug-zap-2`]};O.node;var k=m(O),A={name:`sprout`,size:24,node:[[`path`,{d:`M14 9.536V7a4 4 0 0 1 4-4h1.5a.5.5 0 0 1 .5.5V5a4 4 0 0 1-4 4 4 4 0 0 0-4 4c0 2 1 3 1 5a5 5 0 0 1-1 3`,key:`139s4v`}],[`path`,{d:`M4 9a5 5 0 0 1 8 4 5 5 0 0 1-8-4`,key:`1dlkgp`}],[`path`,{d:`M5 21h14`,key:`11awu3`}]]};A.node;var j=m(A),M={name:`table-2`,size:24,node:[[`path`,{d:`M3 9h18`,key:`1pudct`}],[`path`,{d:`M9 3v18`,key:`fh3hqa`}],[`rect`,{x:`3`,y:`3`,width:`18`,height:`18`,rx:`2`,key:`h1oib`}]]};M.node;var N=m(M),P={name:`waypoints`,size:24,node:[[`path`,{d:`m10.586 5.414-5.172 5.172`,key:`4mc350`}],[`path`,{d:`m18.586 13.414-5.172 5.172`,key:`8c96vv`}],[`path`,{d:`M6 12h12`,key:`8npq4p`}],[`circle`,{cx:`12`,cy:`20`,r:`2`,key:`144qzu`}],[`circle`,{cx:`12`,cy:`4`,r:`2`,key:`muu5ef`}],[`circle`,{cx:`20`,cy:`12`,r:`2`,key:`1xzzfp`}],[`circle`,{cx:`4`,cy:`12`,r:`2`,key:`1hvhnz`}]]};P.node;var F=m(P),I=f(u(),1),L=g(),R=[`databases`,`testdata`,`flows`],z={test:`databases`,tables:`databases`,query:`databases`,gen:`testdata`,files:`testdata`,insert:`testdata`,batches:`testdata`,clean:`testdata`,check:`flows`,trace:`flows`},B={test:[`Testing the connection`,`Tested the connection`,`waiting`],tables:[`Reading the tables`,`Read the tables`,`searching`],query:[`Running the query`,`Ran the query`,`working`],gen:[`Making a preview`,`Made a preview`,`writing`],files:[`Writing the files`,`Wrote the files`,`writing`],insert:[`Inserting test data`,`Inserted test data`,`working`],batches:[`Listing the batches`,`Listed the batches`,`searching`],clean:[`Removing a batch`,`Removed a batch`,`working`],check:[`Checking the flows`,`Checked the flows`,`working`],trace:[`Following the record`,`Followed the record`,`searching`]},V=e=>e==null?`NULL`:typeof e==`object`?JSON.stringify(e):String(e),H=e=>(e?.keys||[]).map(e=>typeof e==`object`&&e?`${e.key}×${e.times}`:String(e)).join(`, `);function U({job:e,onStop:n}){let[r,i,a]=B[e.kind]??[`Working`,`Done`,`working`],o=(0,I.useRef)(null);(0,I.useEffect)(()=>{o.current&&(o.current.scrollTop=o.current.scrollHeight)},[e.lines.length]);let s=e.exitCode===0,c=!s&&(e.kind===`check`||e.kind===`trace`)&&e.result!==void 0&&!e.result.error;return(0,L.jsxs)(`section`,{"aria-label":e.running?r:i,className:`flex flex-col gap-2 rounded-[14px] border border-line-soft p-4`,children:[(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,L.jsxs)(`span`,{className:`min-w-0 flex-1 font-medium [overflow-wrap:anywhere]`,children:[e.running?r:i,e.target?(0,L.jsxs)(`span`,{className:`font-normal text-muted`,children:[` · `,e.target]}):null]}),(0,L.jsx)(v,{tone:e.running?`running`:s?`pass`:e.exitCode===2||c?`warn`:`fail`,orb:a,children:e.running?`Working`:s?`Done`:c?`Some broken`:e.exitCode===2?`Not done`:`Failed`}),e.running&&(0,L.jsxs)(t,{onClick:n,className:`border-fail text-fail hover:bg-fail-soft`,children:[(0,L.jsx)(p,{size:14,fill:`currentColor`,"aria-hidden":`true`}),`Stop`]})]}),(0,L.jsxs)(`details`,{open:e.running||!s&&!e.result,children:[(0,L.jsxs)(`summary`,{className:`cursor-pointer text-sm text-muted`,children:[`Output (`,l(e.lines.length,`line`),`)`]}),(0,L.jsx)(`code`,{className:`mt-2 block font-mono text-[12px] text-muted [overflow-wrap:anywhere]`,children:e.command}),(0,L.jsx)(`pre`,{ref:o,className:`mt-2 max-h-[240px] 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 W({text:e}){return(0,L.jsx)(`p`,{className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail [overflow-wrap:anywhere]`,children:e})}function G({columns:e,rows:t}){return(0,L.jsx)(`div`,{className:`max-h-[420px] overflow-auto rounded-[12px] border border-line-soft`,children:(0,L.jsxs)(`table`,{className:`w-full border-collapse text-[13px]`,children:[(0,L.jsx)(`thead`,{className:`sticky top-0 bg-surface-muted`,children:(0,L.jsx)(`tr`,{children:e.map(e=>(0,L.jsx)(`th`,{className:`px-3 py-2 text-left font-medium whitespace-nowrap text-muted`,children:e},e))})}),(0,L.jsx)(`tbody`,{children:t.map((e,t)=>(0,L.jsx)(`tr`,{className:`border-t border-line-soft`,children:e.map((e,t)=>(0,L.jsx)(`td`,{className:r(`max-w-[320px] px-3 py-1.5 align-top font-mono text-[12px] [overflow-wrap:anywhere]`,e===null&&`text-muted`),children:V(e)},t))},t))})]})})}function K({db:r,job:i,busy:c,act:u}){let[f,p]=(0,I.useState)(``),[m,h]=(0,I.useState)(``),[g,_]=(0,I.useState)(`200`),[b,x]=(0,I.useState)(``),[S,C]=(0,I.useState)(``),w=i&&!i.running&&i.target===r.name?i:null,T=w?.kind===`query`?w.result:void 0,D=async()=>{try{let{id:t}=await n.start(`On the ${r.name} database: ${b.trim()} Read the schema first, check the SQL with run_sql dry_run: true, then run it and show the rows. End with the final SQL in one sql code block.`,{});location.hash=e(`chats`,t)}catch(e){C(e.message)}};return(0,L.jsxs)(`section`,{"aria-labelledby":`db-h`,className:`flex min-w-0 flex-1 flex-col gap-5`,children:[(0,L.jsxs)(`div`,{className:`flex flex-wrap items-start gap-3`,children:[(0,L.jsxs)(`div`,{className:`flex min-w-0 flex-[1_1_240px] flex-col gap-1`,children:[(0,L.jsx)(`h2`,{id:`db-h`,className:`m-0 text-[22px] font-semibold tracking-[-0.01em]`,children:r.name}),(0,L.jsx)(`span`,{className:`text-sm text-muted [overflow-wrap:anywhere]`,children:[r.engine||`from its connection string`,r.where,r.auth===`password`?``:r.auth].filter(Boolean).join(` · `)}),r.description&&(0,L.jsx)(`span`,{className:`text-sm text-ink-2`,children:r.description})]}),(0,L.jsx)(v,{tone:r.writable?`warn`:`neutral`,icon:r.writable,children:r.writable?`Writable, after asking`:`Read only`})]}),(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,L.jsxs)(t,{disabled:c,onClick:()=>u(`test`,{db:r.name}),children:[(0,L.jsx)(k,{size:16,"aria-hidden":`true`}),`Test the connection`]}),(0,L.jsxs)(`form`,{className:`flex min-w-0 flex-[1_1_280px] items-center gap-2`,onSubmit:e=>{e.preventDefault(),u(`tables`,{db:r.name,...f.trim()?{search:f.trim()}:{}})},children:[(0,L.jsx)(a,{"aria-label":`Tables matching`,value:f,placeholder:`Tables matching…`,onChange:e=>p(e.target.value),className:`min-w-0 flex-1`}),(0,L.jsxs)(t,{type:`submit`,disabled:c,children:[(0,L.jsx)(N,{size:16,"aria-hidden":`true`}),`Show tables`]})]})]}),w&&(w.kind===`test`||w.kind===`tables`)&&(0,L.jsx)(`pre`,{className:`m-0 max-h-[320px] overflow-auto rounded-[12px] bg-surface-muted p-3 font-mono text-[12px] leading-relaxed whitespace-pre-wrap text-ink-2`,children:w.lines.filter(e=>!/^args |^token /.test(e)).join(`
3
- `)}),(0,L.jsxs)(`section`,{"aria-labelledby":`query-h`,className:`flex flex-col gap-3`,children:[(0,L.jsx)(`h3`,{id:`query-h`,className:`m-0 text-base font-semibold`,children:`Query`}),(0,L.jsx)(`p`,{className:`m-0 text-[13px] text-muted`,children:`One read. A change to the data goes through a chat, which asks before it writes.`}),(0,L.jsx)(`label`,{className:`sr-only`,htmlFor:`sql`,children:`SQL`}),(0,L.jsx)(s,{id:`sql`,rows:5,value:m,spellCheck:!1,placeholder:r.engine===`mongodb`?`{"find": "orders", "filter": {"status": "open"}}`:`select * from orders where created_at > now() - interval '1 day'`,onChange:e=>h(e.target.value),className:`font-mono text-[13px]`}),(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,L.jsxs)(t,{variant:`primary`,disabled:c||!m.trim(),onClick:()=>u(`query`,{db:r.name,sql:m,maxRows:Number(g)}),children:[(0,L.jsx)(d,{size:16,"aria-hidden":`true`}),`Run`]}),(0,L.jsxs)(t,{disabled:c||!m.trim(),onClick:()=>u(`query`,{db:r.name,sql:m,dryRun:!0}),children:[(0,L.jsx)(E,{size:16,"aria-hidden":`true`}),`Check only`]}),(0,L.jsxs)(`label`,{className:`flex items-center gap-2 text-sm text-muted`,children:[`Rows`,(0,L.jsx)(o,{value:g,onChange:e=>_(e.target.value),className:`w-[96px]`,children:[`50`,`200`,`1000`].map(e=>(0,L.jsx)(`option`,{children:e},e))})]})]}),T&&!T.ok&&(0,L.jsx)(W,{text:T.needs_confirmation?`Only reads run here. Ask in a chat to change data: it shows the statement and asks first.`:T.error||`The query failed.`}),T?.ok&&T.dry_run&&(0,L.jsxs)(`p`,{className:`m-0 rounded-[12px] bg-pass-soft px-3 py-2 text-sm text-pass`,children:[T.note||`The database accepts it.`,T.columns?.length?` Columns: ${T.columns.join(`, `)}`:``]}),T?.ok&&!T.dry_run&&T.columns&&(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(G,{columns:T.columns,rows:T.rows||[]}),(0,L.jsxs)(`span`,{className:`text-[13px] text-muted`,children:[l(T.row_count??T.rows?.length??0,`row`),T.truncated?` (more not shown)`:``,T.ms===void 0?``:` in ${T.ms} ms`]})]})]}),(0,L.jsxs)(`section`,{"aria-labelledby":`ask-h`,className:`flex flex-col gap-2`,children:[(0,L.jsx)(`h3`,{id:`ask-h`,className:`m-0 text-base font-semibold`,children:`Ask in your words`}),(0,L.jsxs)(`form`,{className:`flex flex-wrap items-center gap-2`,onSubmit:e=>{e.preventDefault(),b.trim()&&D()},children:[(0,L.jsx)(a,{"aria-label":`Your question`,value:b,maxLength:500,placeholder:`Which customers ordered more than twice last week?`,onChange:e=>x(e.target.value),className:`min-w-0 flex-[1_1_280px]`}),(0,L.jsxs)(t,{type:`submit`,disabled:!b.trim(),children:[(0,L.jsx)(y,{size:16,"aria-hidden":`true`}),`Ask in a chat`]})]}),S&&(0,L.jsx)(W,{text:S})]})]})}function q({view:t,job:n,busy:i,act:a,sub:o}){let s=t.databases.find(e=>e.name===o)||t.databases[0];return t.databases.length?(0,L.jsxs)(`div`,{className:`flex flex-col gap-6 @2xl:flex-row @2xl:items-start @2xl:gap-8`,children:[(0,L.jsxs)(`nav`,{"aria-label":`Databases`,className:`flex min-w-0 flex-col gap-1 @2xl:sticky @2xl:top-6 @2xl:w-[250px] @2xl:flex-none`,children:[(0,L.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:t.databases.map(t=>(0,L.jsx)(`li`,{children:(0,L.jsxs)(`a`,{href:e(`data`,`databases/${encodeURIComponent(t.name)}`),"aria-current":s?.name===t.name?`page`:void 0,className:r(`flex min-h-[60px] items-center gap-3 rounded-[12px] px-3 py-2 text-ink no-underline`,s?.name===t.name?`bg-surface-strong hover:text-ink`:`hover:bg-surface-muted hover:text-ink`),children:[(0,L.jsx)(b,{size:18,"aria-hidden":`true`,className:`flex-none text-muted`}),(0,L.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,L.jsx)(`span`,{className:`truncate text-[15px] font-medium`,children:t.name}),(0,L.jsx)(`span`,{className:`truncate text-[13px] text-muted`,children:[t.engine||`connection string`,t.writable?`writable`:`read only`].join(` · `)})]})]})},t.name))}),(0,L.jsx)(`a`,{href:e(`settings`,`databases`),className:`px-3 pt-1 text-[13px]`,children:`Add or change in Settings`})]}),s&&(0,L.jsx)(K,{db:s,job:n,busy:i,act:a},s.name)]}):(0,L.jsxs)(`div`,{className:`flex flex-col items-start gap-3 rounded-[18px] border border-line p-6`,children:[(0,L.jsx)(b,{size:24,"aria-hidden":`true`,className:`text-muted`}),(0,L.jsx)(`p`,{className:`m-0 max-w-[62ch] leading-relaxed text-ink-2`,children:`No databases yet. Add one in Settings: a connection string, or its host, user and password, kept in your credential store.`}),(0,L.jsx)(`a`,{href:e(`settings`,`databases`),className:`text-sm font-medium`,children:`Settings, Databases`})]})}function J({r:e}){return(0,L.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[(e.order||[]).map(t=>{let n=e.preview?.[t],r=n?.sample||[],i=r.length?Object.keys(r[0]):[];return(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsxs)(`span`,{className:`text-sm`,children:[(0,L.jsx)(`span`,{className:`font-medium`,children:t}),` · `,l(e.rows?.[t]??0,`row`),n?.database_fills?.length?(0,L.jsxs)(`span`,{className:`text-muted`,children:[` · the database fills `,n.database_fills.join(`, `)]}):null,e.masked?.[t]?.length?(0,L.jsxs)(`span`,{className:`text-muted`,children:[` · masked `,e.masked[t].join(`, `)]}):null]}),i.length>0&&(0,L.jsx)(G,{columns:i,rows:r.slice(0,3).map(e=>i.map(t=>e[t]))})]},t)}),(e.warnings||[]).map((e,t)=>(0,L.jsx)(`p`,{className:`m-0 text-[13px] text-warn`,children:e},t))]})}function Y({view:r,job:i,busy:s,act:c}){let l=r.databases.filter(e=>e.writable),[u,d]=(0,I.useState)(r.specs[0]??``),[f,p]=(0,I.useState)(``),[m,h]=(0,I.useState)(``),[g,_]=(0,I.useState)(`csv`),[v,b]=(0,I.useState)(!1),[S,C]=(0,I.useState)(l[0]?.name??``),[T,E]=(0,I.useState)(``),[O,k]=(0,I.useState)(``),A={spec:u,...f?{db:f}:{},...m?{seed:m}:{}},M=i&&!i.running?i:null,N=M&&[`gen`,`files`,`insert`].includes(M.kind)&&M.target===u?M.result:void 0,P=M?.kind===`batches`?(Array.isArray(M.result)?M.result.flatMap(e=>e.batches||[]):M.result?.batches)??[]:null,F=r.databases.find(e=>e.name===f),R=async()=>{try{let{id:t}=await n.start(`Write a test data spec for this project (a YAML file with tables: under test-data/), from the database's schema. Show me the spec and a preview before you save it.`,{});location.hash=e(`chats`,t)}catch(e){k(e.message)}};return(0,L.jsxs)(`div`,{className:`flex flex-col gap-8`,children:[(0,L.jsxs)(`section`,{"aria-labelledby":`spec-h`,className:`flex flex-col gap-4`,children:[(0,L.jsxs)(`div`,{className:`flex flex-wrap items-baseline gap-2`,children:[(0,L.jsx)(`h2`,{id:`spec-h`,className:`m-0 flex-1 text-lg font-semibold`,children:`Make test data`}),(0,L.jsxs)(t,{variant:`ghost`,onClick:()=>void R(),children:[(0,L.jsx)(y,{size:16,"aria-hidden":`true`}),`Write a spec in a chat`]})]}),(0,L.jsx)(`p`,{className:`m-0 max-w-[70ch] text-sm leading-relaxed text-muted`,children:`A spec is a YAML file with tables: and what goes in each column. The same spec and seed make the same data again, so a test or a pipeline can rely on it. No model is used.`}),O&&(0,L.jsx)(W,{text:O}),r.specs.length===0?(0,L.jsx)(`p`,{className:`m-0 rounded-[12px] border border-line-soft px-4 py-3 text-sm text-muted`,children:`No specs in this project yet: a YAML file with tables: in test-data/, data/ or specs/. Write one in a chat, from a database's schema.`}):(0,L.jsxs)(`div`,{className:`flex flex-col gap-3 rounded-[14px] border border-line-soft p-4`,children:[(0,L.jsxs)(`div`,{className:`flex flex-wrap gap-3`,children:[(0,L.jsxs)(`label`,{className:`flex min-w-0 flex-[2_1_260px] flex-col gap-1.5 text-sm font-medium`,children:[`Spec`,(0,L.jsx)(o,{value:u,onChange:e=>d(e.target.value),children:r.specs.map(e=>(0,L.jsx)(`option`,{children:e},e))})]}),(0,L.jsxs)(`label`,{className:`flex min-w-0 flex-[1_1_180px] flex-col gap-1.5 text-sm font-medium`,children:[`Database`,(0,L.jsxs)(o,{value:f,onChange:e=>p(e.target.value),children:[(0,L.jsx)(`option`,{value:``,children:`The spec's own`}),r.databases.map(e=>(0,L.jsxs)(`option`,{value:e.name,children:[e.name,e.writable?``:` (read only)`]},e.name))]})]}),(0,L.jsxs)(`label`,{className:`flex w-[120px] flex-col gap-1.5 text-sm font-medium`,children:[`Seed`,(0,L.jsx)(a,{value:m,inputMode:`numeric`,placeholder:`Random`,onChange:e=>h(e.target.value.replace(/\D/g,``).slice(0,9))})]})]}),(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,L.jsxs)(t,{variant:`primary`,disabled:s||!u,onClick:()=>c(`gen`,A),children:[(0,L.jsx)(D,{size:16,"aria-hidden":`true`}),`Preview`]}),(0,L.jsxs)(`span`,{className:`inline-flex items-center gap-1`,children:[(0,L.jsx)(t,{disabled:s||!u,onClick:()=>c(`gen`,{...A,mode:`files`,format:g}),children:`Write files`}),(0,L.jsx)(o,{"aria-label":`File format`,value:g,onChange:e=>_(e.target.value),className:`w-[88px]`,children:[`csv`,`json`,`sql`].map(e=>(0,L.jsx)(`option`,{children:e},e))})]}),(0,L.jsxs)(t,{disabled:s||!u||!F?.writable,title:F?.writable?void 0:`Pick a writable database to insert into`,onClick:()=>b(!0),children:[(0,L.jsx)(j,{size:16,"aria-hidden":`true`}),`Insert into `,F?.writable?F.name:`a database`]})]}),v&&F&&(0,L.jsxs)(`div`,{role:`alertdialog`,"aria-label":`Insert test data`,className:`flex flex-col gap-3 rounded-[12px] border border-warn bg-warn-soft p-4`,children:[(0,L.jsxs)(`span`,{className:`font-medium`,children:[`Insert the rows of `,u,` into `,F.name,`?`]}),(0,L.jsx)(`span`,{className:`text-sm text-ink-2`,children:`They go in as one batch, which you can remove again below. Only on a test database.`}),(0,L.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,L.jsx)(t,{variant:`primary`,onClick:()=>{b(!1),c(`gen`,{...A,mode:`insert`,confirmed:!0})},children:`Insert`}),(0,L.jsx)(t,{onClick:()=>b(!1),children:`Don't insert`})]})]}),N&&!N.ok&&(0,L.jsx)(W,{text:N.error||`It did not make the data.`}),N?.ok&&(0,L.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[N.batch&&(0,L.jsxs)(`p`,{className:`m-0 rounded-[12px] bg-pass-soft px-3 py-2 text-sm text-pass`,children:[`Inserted as batch `,(0,L.jsx)(`span`,{className:`font-mono text-[13px]`,children:N.batch}),`.`]}),N.files?.length?(0,L.jsxs)(`p`,{className:`m-0 flex flex-wrap items-center gap-2 text-sm`,children:[`Wrote`,N.files.map(e=>(0,L.jsx)(`button`,{type:`button`,onClick:async()=>k(await x(e)),className:`cursor-pointer rounded-md border border-line-soft bg-surface-muted px-1.5 py-0.5 font-mono text-[12px] text-ink-2 hover:bg-surface-strong`,children:e},e))]}):null,(0,L.jsx)(J,{r:N})]})]})]}),(0,L.jsxs)(`section`,{"aria-labelledby":`batch-h`,className:`flex flex-col gap-3`,children:[(0,L.jsx)(`h2`,{id:`batch-h`,className:`m-0 text-lg font-semibold`,children:`Inserted batches`}),l.length===0?(0,L.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`No writable database: test data is inserted only into one marked writable in Settings, Databases.`}):(0,L.jsxs)(L.Fragment,{children:[(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,L.jsx)(o,{"aria-label":`Database`,value:S,onChange:e=>C(e.target.value),className:`w-[220px]`,children:l.map(e=>(0,L.jsx)(`option`,{children:e.name},e.name))}),(0,L.jsx)(t,{disabled:s||!S,onClick:()=>c(`batches`,{db:S}),children:`List batches`})]}),P&&P.length===0&&(0,L.jsxs)(`p`,{className:`m-0 text-sm text-muted`,children:[`No batches in `,M?.target,`.`]}),P&&P.length>0&&(0,L.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:P.map(e=>(0,L.jsxs)(`li`,{className:`flex flex-wrap items-center gap-3 border-t border-line-soft py-2.5 text-sm first:border-t-0`,children:[(0,L.jsxs)(`span`,{className:`flex min-w-0 flex-[1_1_240px] flex-col gap-0.5`,children:[(0,L.jsx)(`span`,{className:`font-mono text-[13px]`,children:e.batch}),(0,L.jsxs)(`span`,{className:`text-[13px] text-muted`,children:[new Date(e.created).toLocaleString([],{month:`short`,day:`numeric`,hour:`numeric`,minute:`2-digit`}),e.spec?` · ${e.spec}`:``,` ·`,` `,Object.entries(e.rows).map(([e,t])=>`${e} ${t}`).join(`, `)]})]}),T===e.batch?(0,L.jsxs)(`span`,{className:`flex items-center gap-2`,children:[(0,L.jsxs)(`span`,{className:`text-[13px] text-warn`,children:[`Remove its rows from `,M?.target,`?`]}),(0,L.jsx)(t,{size:`sm`,className:`border-fail text-fail hover:bg-fail-soft`,onClick:()=>{E(``),c(`clean`,{db:M?.target,batch:e.batch,confirmed:!0})},children:`Remove`}),(0,L.jsx)(t,{size:`sm`,onClick:()=>E(``),children:`Keep`})]}):(0,L.jsxs)(t,{size:`sm`,disabled:s,onClick:()=>E(e.batch),children:[(0,L.jsx)(w,{size:14,"aria-hidden":`true`}),`Remove`]})]},e.batch))}),M?.kind===`clean`&&M.result?.ok&&(0,L.jsxs)(`p`,{className:`m-0 rounded-[12px] bg-pass-soft px-3 py-2 text-sm text-pass`,children:[`Removed:`,` `,Object.entries(M.result.removed||{}).map(([e,t])=>`${e} ${t}`).join(`, `)]})]})]})]})}function X({c:e}){let t=e=>e?.count??0;return(0,L.jsxs)(`li`,{className:`flex flex-col gap-1 border-t border-line-soft py-3 text-sm first:border-t-0`,children:[(0,L.jsxs)(`span`,{className:`flex flex-wrap items-center gap-2`,children:[(0,L.jsx)(v,{tone:e.ok?`pass`:`fail`,children:e.ok?`Holds`:`Broken`}),(0,L.jsx)(`span`,{className:`font-medium`,children:e.flow}),(0,L.jsxs)(`span`,{className:`font-mono text-[12px] text-muted [overflow-wrap:anywhere]`,children:[e.from,` → `,e.to]})]}),e.error?(0,L.jsx)(`span`,{className:`text-fail [overflow-wrap:anywhere]`,children:e.error}):(0,L.jsxs)(`span`,{className:`text-ink-2`,children:[e.matched??0,` of `,e.source_rows??0,` arrived · `,e.late?`${t(e.late)} late (${t(e.in_flight)} in flight)`:`${t(e.missing)} missing`,` · `,e.wrong?.rows??0,` wrong · `,t(e.duplicates),` duplicated`,e.extra?` · ${t(e.extra)} with no source`:``]}),e.note&&(0,L.jsx)(`span`,{className:`text-[13px] text-muted`,children:e.note}),H(e.late??e.missing)&&(0,L.jsxs)(`span`,{className:`text-[13px] text-fail [overflow-wrap:anywhere]`,children:[`Missing: `,H(e.late??e.missing)]}),(e.wrong?.examples||[]).map((e,t)=>(0,L.jsxs)(`span`,{className:`font-mono text-[12px] text-fail [overflow-wrap:anywhere]`,children:[String(e.key),`: `,e.column,` expected `,V(e.expected),`, found `,V(e.target)]},t))]})}function Z({view:r,job:s,busy:c,act:u}){let[d,f]=(0,I.useState)([]),p=(0,I.useMemo)(()=>[...new Set(r.flows.map(e=>e.from).filter(e=>!e.startsWith(`api:`)))],[r.flows]),[m,h]=(0,I.useState)(p[0]??r.flows[0]?.name??``),[g,_]=(0,I.useState)(``),[b,w]=(0,I.useState)(``),[D,O]=(0,I.useState)(``),[k,A]=(0,I.useState)(null),[j,M]=(0,I.useState)(``),N=s&&!s.running?s:null,P=N?.kind===`check`?N.result??null:null,R=N?.kind===`trace`?N.result??null:null;return r.flowsError?(0,L.jsx)(W,{text:`${r.flowsFile} does not read: ${r.flowsError}`}):r.flows.length?(0,L.jsxs)(`div`,{className:`flex flex-col gap-8`,children:[(0,L.jsxs)(`section`,{"aria-labelledby":`flows-h`,className:`flex flex-col gap-3`,children:[(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,L.jsxs)(`h2`,{id:`flows-h`,className:`m-0 flex-1 text-lg font-semibold`,children:[`Flows `,(0,L.jsxs)(`span`,{className:`text-sm font-normal text-muted`,children:[`(`,r.flowsFile,`)`]})]}),(0,L.jsxs)(t,{variant:`primary`,disabled:c,onClick:()=>u(`check`,{flows:d}),children:[(0,L.jsx)(E,{size:16,"aria-hidden":`true`}),d.length?`Check ${l(d.length,`flow`)}`:`Check all`]}),N?.report&&(N.kind===`check`||N.kind===`trace`)&&(0,L.jsxs)(t,{onClick:async()=>M(await x(N.report)),children:[(0,L.jsx)(S,{size:16,"aria-hidden":`true`}),`Report`]})]}),(0,L.jsx)(`p`,{className:`m-0 text-[13px] text-muted`,children:`Each hop: did every record arrive, complete, correct, on time.`}),j&&(0,L.jsx)(W,{text:j}),(0,L.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:r.flows.map(e=>(0,L.jsxs)(`li`,{className:`flex items-start gap-3 border-t border-line-soft py-2.5 first:border-t-0`,children:[(0,L.jsx)(`input`,{type:`checkbox`,"aria-label":`Check ${e.name}`,checked:d.includes(e.name),onChange:t=>f(t.target.checked?[...d,e.name]:d.filter(t=>t!==e.name)),className:`mt-1 size-5 flex-none accent-accent`}),(0,L.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5 text-sm`,children:[(0,L.jsx)(`span`,{className:`font-medium`,children:e.name}),(0,L.jsxs)(`span`,{className:`font-mono text-[12px] text-ink-2 [overflow-wrap:anywhere]`,children:[e.from,` → `,e.to]}),(0,L.jsxs)(`span`,{className:`text-[13px] text-muted`,children:[[e.mode,e.sla?`SLA ${e.sla}`:``,l(e.columns,`column`)].filter(Boolean).join(` · `),e.description?` · ${e.description}`:``]})]})]},e.name))}),P?.error&&(0,L.jsx)(W,{text:P.error}),P?.flows&&(0,L.jsx)(`ul`,{"aria-label":`Check results`,className:`m-0 flex list-none flex-col rounded-[14px] border border-line-soft px-4 py-1`,children:P.flows.map(e=>(0,L.jsx)(X,{c:e},e.flow))})]}),(0,L.jsxs)(`section`,{"aria-labelledby":`trace-h`,className:`flex flex-col gap-3`,children:[(0,L.jsx)(`h2`,{id:`trace-h`,className:`m-0 text-lg font-semibold`,children:`Follow one record`}),(0,L.jsxs)(`form`,{className:`flex flex-wrap items-end gap-3`,onSubmit:e=>{e.preventDefault(),m&&g.trim()&&u(`trace`,{start:m,key:g.trim(),...b?{wait:Number(b)}:{}})},children:[(0,L.jsxs)(`label`,{className:`flex min-w-0 flex-[1_1_220px] flex-col gap-1.5 text-sm font-medium`,children:[`From`,(0,L.jsxs)(o,{value:m,onChange:e=>h(e.target.value),children:[(0,L.jsx)(`optgroup`,{label:`A table`,children:p.map(e=>(0,L.jsx)(`option`,{children:e},e))}),(0,L.jsx)(`optgroup`,{label:`A flow`,children:r.flows.map(e=>(0,L.jsx)(`option`,{children:e.name},e.name))})]})]}),(0,L.jsxs)(`label`,{className:`flex w-[160px] flex-col gap-1.5 text-sm font-medium`,children:[`Its key`,(0,L.jsx)(a,{value:g,maxLength:200,placeholder:`1042`,onChange:e=>_(e.target.value)})]}),(0,L.jsxs)(`label`,{className:`flex w-[130px] flex-col gap-1.5 text-sm font-medium`,children:[`Wait up to (s)`,(0,L.jsx)(a,{value:b,inputMode:`numeric`,placeholder:`0`,onChange:e=>w(e.target.value.replace(/\D/g,``).slice(0,3))})]}),(0,L.jsxs)(t,{type:`submit`,disabled:c||!m||!g.trim(),children:[(0,L.jsx)(T,{size:16,"aria-hidden":`true`}),`Follow it`]})]}),R?.error&&(0,L.jsx)(W,{text:R.error}),R?.hops&&(0,L.jsx)(`ol`,{className:`m-0 flex list-none flex-col gap-2 border-l border-line pl-4`,children:R.hops.map((e,t)=>(0,L.jsxs)(`li`,{className:`flex flex-col gap-0.5 text-sm`,children:[(0,L.jsxs)(`span`,{className:`flex flex-wrap items-center gap-2`,children:[(0,L.jsx)(v,{tone:e.ok?`pass`:`fail`,children:e.ok?`Arrived`:e.found_in_source===!1?`Not in the source`:e.arrived===!1?`Did not arrive`:`Differs`}),(0,L.jsx)(`span`,{className:`font-medium`,children:e.flow}),(0,L.jsxs)(`span`,{className:`font-mono text-[12px] text-muted`,children:[e.from,` → `,e.to,` · key `,V(e.key)]})]}),(e.differences||[]).map((e,t)=>(0,L.jsxs)(`span`,{className:`font-mono text-[12px] text-fail [overflow-wrap:anywhere]`,children:[e.column,`: expected `,V(e.expected),`, found `,V(e.target)]},t)),Number(e.copies)>1&&(0,L.jsxs)(`span`,{className:`text-[13px] text-warn`,children:[e.copies,` copies`]})]},t))})]}),(0,L.jsxs)(`section`,{"aria-labelledby":`impact-h`,className:`flex flex-col gap-3`,children:[(0,L.jsx)(`h2`,{id:`impact-h`,className:`m-0 text-lg font-semibold`,children:`What a change reaches`}),(0,L.jsxs)(`form`,{className:`flex flex-wrap items-center gap-2`,onSubmit:async e=>{e.preventDefault();try{A(await i.impact(D.trim())),M(``)}catch(e){M(e.message)}},children:[(0,L.jsx)(a,{"aria-label":`A table or column`,value:D,spellCheck:!1,placeholder:`shop.orders.total`,onChange:e=>O(e.target.value),className:`min-w-0 flex-[1_1_260px] font-mono text-[13px]`}),(0,L.jsxs)(t,{type:`submit`,disabled:!D.trim(),children:[(0,L.jsx)(C,{size:16,"aria-hidden":`true`}),`Show what reads it`]})]}),k&&k.length===0&&(0,L.jsxs)(`p`,{className:`m-0 text-sm text-muted`,children:[`Nothing in the flows reads `,D,`.`]}),k&&k.length>0&&(0,L.jsx)(`ul`,{className:`m-0 flex list-none flex-col gap-1 p-0`,children:k.map((e,t)=>(0,L.jsxs)(`li`,{className:`font-mono text-[12px] text-ink-2 [overflow-wrap:anywhere]`,style:{paddingLeft:`${(e.depth-1)*16}px`},children:[e.from,` → `,e.to,e.columns.length?` (${e.columns.join(`, `)})`:``,` `,(0,L.jsxs)(`span`,{className:`text-muted`,children:[`[`,e.flow,`]`]})]},t))}),r.map.length>0&&(0,L.jsxs)(`details`,{children:[(0,L.jsx)(`summary`,{className:`cursor-pointer text-sm text-muted`,children:`The whole map`}),(0,L.jsx)(`pre`,{className:`mt-2 overflow-auto rounded-[12px] bg-surface-muted p-3 font-mono text-[12px] leading-relaxed text-ink-2`,children:r.map.join(`
4
- `)})]})]})]}):(0,L.jsxs)(`div`,{className:`flex flex-col items-start gap-3 rounded-[18px] border border-line p-6`,children:[(0,L.jsx)(F,{size:24,"aria-hidden":`true`,className:`text-muted`}),(0,L.jsxs)(`p`,{className:`m-0 max-w-[62ch] leading-relaxed text-ink-2`,children:[`No data flows yet. A flow says how records move from one system to another (a table, a topic or an API), by which key, and what each column becomes; `,(0,L.jsx)(`span`,{className:`font-mono text-[13px]`,children:r.flowsFile}),` holds them. ForgeQE can write them from your mapping documents.`]}),(0,L.jsxs)(t,{onClick:async()=>{try{let{id:t}=await n.start(`Write the data flows of this project into flows.yaml from our mapping documents and the databases' schemas. Show me each flow before you save it.`,{});location.hash=e(`chats`,t)}catch(e){M(e.message)}},children:[(0,L.jsx)(y,{size:16,"aria-hidden":`true`}),`Write flows in a chat`]}),j&&(0,L.jsx)(W,{text:j})]})}function Q({sub:t}){let[n,r]=(0,I.useState)(null),[a,o]=(0,I.useState)(``),[s,l]=(0,I.useState)(!1),[u,...d]=(t||``).split(`/`),f=R.includes(u)?u:`databases`,p=(0,I.useCallback)(()=>{i.view().then(e=>{r(e),o(``)},e=>o(e.message))},[]);(0,I.useEffect)(p,[p]);let m=!!n?.job?.running;(0,I.useEffect)(()=>{if(!m)return;let e=setInterval(p,1e3);return()=>clearInterval(e)},[m,p]);let g=async(e,t)=>{l(!0),o(``);try{await i.run(e,t),p()}catch(e){o(e.message)}finally{l(!1)}},v=n?.job??null,y={databases:`Databases${n?` (${n.databases.length})`:``}`,testdata:`Test data${n?` (${n.specs.length})`:``}`,flows:`Data flows${n?` (${n.flows.length})`:``}`};return(0,L.jsxs)(`div`,{className:`flex min-h-screen flex-col`,children:[(0,L.jsx)(_,{}),(0,L.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,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,L.jsx)(c,{icon:b,size:`lg`}),(0,L.jsx)(`h1`,{className:`m-0 flex-1 text-[26px] font-semibold tracking-[-0.01em]`,children:`Data`})]}),(0,L.jsx)(`p`,{className:`m-0 max-w-[74ch] text-sm leading-relaxed text-muted`,children:`The databases the agents may read, test data made from specs and inserted as batches you can remove again, and the data flows between systems: did every record arrive, complete and correct.`}),a&&(0,L.jsx)(`p`,{role:`alert`,className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail [overflow-wrap:anywhere]`,children:a}),!n&&!a&&(0,L.jsx)(`p`,{role:`status`,className:`m-0 text-sm text-muted`,children:`Loading…`}),n&&(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(h,{label:`Data`,current:f,onPick:t=>location.hash=e(`data`,t),tabs:R.map(e=>({id:e,label:y[e]}))}),v&&(v.running||z[v.kind]===f)&&(0,L.jsx)(U,{job:v,onStop:()=>void i.stop().then(p)}),(0,L.jsxs)(`div`,{role:`tabpanel`,id:`panel-${f}`,"aria-labelledby":`tab-${f}`,children:[f===`databases`&&(0,L.jsx)(q,{view:n,job:v,busy:s||m,act:g,sub:d[0]?decodeURIComponent(d[0]):void 0}),f===`testdata`&&(0,L.jsx)(Y,{view:n,job:v,busy:s||m,act:g}),f===`flows`&&(0,L.jsx)(Z,{view:n,job:v,busy:s||m,act:g})]})]})]})]})}export{Q as Data};
1
+ import"./markdown-DclrGj2e.js";import{t as e}from"./file-chart-column-increasing-Ch7H145u.js";import{t}from"./trash-CtWVl2Xk.js";import{A as n,Et as r,Lt as i,M as a,N as o,P as s,Pt as c,Q as l,Rt as u,S as d,St as f,U as p,X as m,Xt as h,_ as g,b as _,gt as v,h as y,i as b,l as x,m as S,o as C,ot as w,p as T,qt as E,rt as D}from"./index-KUNDUjo0.js";var O={name:`plug-zap`,size:24,node:[[`path`,{d:`M6.3 20.3a2.4 2.4 0 0 0 3.4 0L12 18l-6-6-2.3 2.3a2.4 2.4 0 0 0 0 3.4Z`,key:`goz73y`}],[`path`,{d:`m2 22 3-3`,key:`19mgm9`}],[`path`,{d:`M7.5 13.5 10 11`,key:`7xgeeb`}],[`path`,{d:`M10.5 16.5 13 14`,key:`10btkg`}],[`path`,{d:`m18 3-4 4h6l-4 4`,key:`16psg9`}]],aliases:[`plug-zap-2`]};O.node;var k=c(O),A={name:`sprout`,size:24,node:[[`path`,{d:`M14 9.536V7a4 4 0 0 1 4-4h1.5a.5.5 0 0 1 .5.5V5a4 4 0 0 1-4 4 4 4 0 0 0-4 4c0 2 1 3 1 5a5 5 0 0 1-1 3`,key:`139s4v`}],[`path`,{d:`M4 9a5 5 0 0 1 8 4 5 5 0 0 1-8-4`,key:`1dlkgp`}],[`path`,{d:`M5 21h14`,key:`11awu3`}]]};A.node;var j=c(A),M={name:`table-2`,size:24,node:[[`path`,{d:`M3 9h18`,key:`1pudct`}],[`path`,{d:`M9 3v18`,key:`fh3hqa`}],[`rect`,{x:`3`,y:`3`,width:`18`,height:`18`,rx:`2`,key:`h1oib`}]]};M.node;var N=c(M),P={name:`waypoints`,size:24,node:[[`path`,{d:`m10.586 5.414-5.172 5.172`,key:`4mc350`}],[`path`,{d:`m18.586 13.414-5.172 5.172`,key:`8c96vv`}],[`path`,{d:`M6 12h12`,key:`8npq4p`}],[`circle`,{cx:`12`,cy:`20`,r:`2`,key:`144qzu`}],[`circle`,{cx:`12`,cy:`4`,r:`2`,key:`muu5ef`}],[`circle`,{cx:`20`,cy:`12`,r:`2`,key:`1xzzfp`}],[`circle`,{cx:`4`,cy:`12`,r:`2`,key:`1hvhnz`}]]};P.node;var F=c(P),I=h(E(),1),L=a(),R=[`databases`,`testdata`,`flows`],z={test:`databases`,tables:`databases`,query:`databases`,gen:`testdata`,files:`testdata`,insert:`testdata`,batches:`testdata`,clean:`testdata`,check:`flows`,trace:`flows`},B={test:[`Testing the connection`,`Tested the connection`,`waiting`],tables:[`Reading the tables`,`Read the tables`,`searching`],query:[`Running the query`,`Ran the query`,`working`],gen:[`Making a preview`,`Made a preview`,`writing`],files:[`Writing the files`,`Wrote the files`,`writing`],insert:[`Inserting test data`,`Inserted test data`,`working`],batches:[`Listing the batches`,`Listed the batches`,`searching`],clean:[`Removing a batch`,`Removed a batch`,`working`],check:[`Checking the flows`,`Checked the flows`,`working`],trace:[`Following the record`,`Followed the record`,`searching`]},V=e=>e==null?`NULL`:typeof e==`object`?JSON.stringify(e):String(e),H=e=>(e?.keys||[]).map(e=>typeof e==`object`&&e?`${e.key}×${e.times}`:String(e)).join(`, `);function U({job:e,onStop:t}){let[r,i,a]=B[e.kind]??[`Working`,`Done`,`working`],o=(0,I.useRef)(null);(0,I.useEffect)(()=>{o.current&&(o.current.scrollTop=o.current.scrollHeight)},[e.lines.length]);let c=e.exitCode===0,l=!c&&(e.kind===`check`||e.kind===`trace`)&&e.result!==void 0&&!e.result.error;return(0,L.jsxs)(`section`,{"aria-label":e.running?r:i,className:`flex flex-col gap-2 rounded-[14px] border border-line-soft p-4`,children:[(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,L.jsxs)(`span`,{className:`min-w-0 flex-1 font-medium [overflow-wrap:anywhere]`,children:[e.running?r:i,e.target?(0,L.jsxs)(`span`,{className:`font-normal text-muted`,children:[` · `,e.target]}):null]}),(0,L.jsx)(b,{tone:e.running?`running`:c?`pass`:e.exitCode===2||l?`warn`:`fail`,orb:a,children:e.running?`Working`:c?`Done`:l?`Some broken`:e.exitCode===2?`Not done`:`Failed`}),e.running&&(0,L.jsxs)(n,{onClick:t,className:`border-fail text-fail hover:bg-fail-soft`,children:[(0,L.jsx)(p,{size:14,fill:`currentColor`,"aria-hidden":`true`}),`Stop`]})]}),(0,L.jsxs)(`details`,{open:e.running||!c&&!e.result,children:[(0,L.jsxs)(`summary`,{className:`cursor-pointer text-sm text-muted`,children:[`Output (`,s(e.lines.length,`line`),`)`]}),(0,L.jsx)(`code`,{className:`mt-2 block font-mono text-[12px] text-muted [overflow-wrap:anywhere]`,children:e.command}),(0,L.jsx)(`pre`,{ref:o,className:`mt-2 max-h-[240px] 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 W({text:e}){return(0,L.jsx)(`p`,{className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail [overflow-wrap:anywhere]`,children:e})}function G({columns:e,rows:t}){return(0,L.jsx)(`div`,{className:`max-h-[420px] overflow-auto rounded-[12px] border border-line-soft`,children:(0,L.jsxs)(`table`,{className:`w-full border-collapse text-[13px]`,children:[(0,L.jsx)(`thead`,{className:`sticky top-0 bg-surface-muted`,children:(0,L.jsx)(`tr`,{children:e.map(e=>(0,L.jsx)(`th`,{className:`px-3 py-2 text-left font-medium whitespace-nowrap text-muted`,children:e},e))})}),(0,L.jsx)(`tbody`,{children:t.map((e,t)=>(0,L.jsx)(`tr`,{className:`border-t border-line-soft`,children:e.map((e,t)=>(0,L.jsx)(`td`,{className:o(`max-w-[320px] px-3 py-1.5 align-top font-mono text-[12px] [overflow-wrap:anywhere]`,e===null&&`text-muted`),children:V(e)},t))},t))})]})})}function K({db:e,job:t,busy:a,act:o}){let[c,l]=(0,I.useState)(``),[u,d]=(0,I.useState)(``),[f,p]=(0,I.useState)(`200`),[m,h]=(0,I.useState)(``),[g,v]=(0,I.useState)(``),x=t&&!t.running&&t.target===e.name?t:null,C=x?.kind===`query`?x.result:void 0,E=async()=>{try{let{id:t}=await i.start(`On the ${e.name} database: ${m.trim()} Read the schema first, check the SQL with run_sql dry_run: true, then run it and show the rows. End with the final SQL in one sql code block.`,{});location.hash=_(`chats`,t)}catch(e){v(e.message)}};return(0,L.jsxs)(`section`,{"aria-labelledby":`db-h`,className:`flex min-w-0 flex-1 flex-col gap-5`,children:[(0,L.jsxs)(`div`,{className:`flex flex-wrap items-start gap-3`,children:[(0,L.jsxs)(`div`,{className:`flex min-w-0 flex-[1_1_240px] flex-col gap-1`,children:[(0,L.jsx)(`h2`,{id:`db-h`,className:`m-0 text-[22px] font-semibold tracking-[-0.01em]`,children:e.name}),(0,L.jsx)(`span`,{className:`text-sm text-muted [overflow-wrap:anywhere]`,children:[e.engine||`from its connection string`,e.where,e.auth===`password`?``:e.auth].filter(Boolean).join(` · `)}),e.description&&(0,L.jsx)(`span`,{className:`text-sm text-ink-2`,children:e.description})]}),(0,L.jsx)(b,{tone:e.writable?`warn`:`neutral`,icon:e.writable,children:e.writable?`Writable, after asking`:`Read only`})]}),(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,L.jsxs)(n,{disabled:a,onClick:()=>o(`test`,{db:e.name}),children:[(0,L.jsx)(k,{size:16,"aria-hidden":`true`}),`Test the connection`]}),(0,L.jsxs)(`form`,{className:`flex min-w-0 flex-[1_1_280px] items-center gap-2`,onSubmit:t=>{t.preventDefault(),o(`tables`,{db:e.name,...c.trim()?{search:c.trim()}:{}})},children:[(0,L.jsx)(T,{"aria-label":`Tables matching`,value:c,placeholder:`Tables matching…`,onChange:e=>l(e.target.value),className:`min-w-0 flex-1`}),(0,L.jsxs)(n,{type:`submit`,disabled:a,children:[(0,L.jsx)(N,{size:16,"aria-hidden":`true`}),`Show tables`]})]})]}),x&&(x.kind===`test`||x.kind===`tables`)&&(0,L.jsx)(`pre`,{className:`m-0 max-h-[320px] overflow-auto rounded-[12px] bg-surface-muted p-3 font-mono text-[12px] leading-relaxed whitespace-pre-wrap text-ink-2`,children:x.lines.filter(e=>!/^args |^token /.test(e)).join(`
3
+ `)}),(0,L.jsxs)(`section`,{"aria-labelledby":`query-h`,className:`flex flex-col gap-3`,children:[(0,L.jsx)(`h3`,{id:`query-h`,className:`m-0 text-base font-semibold`,children:`Query`}),(0,L.jsx)(`p`,{className:`m-0 text-[13px] text-muted`,children:`One read. A change to the data goes through a chat, which asks before it writes.`}),(0,L.jsx)(`label`,{className:`sr-only`,htmlFor:`sql`,children:`SQL`}),(0,L.jsx)(y,{id:`sql`,rows:5,value:u,spellCheck:!1,placeholder:e.engine===`mongodb`?`{"find": "orders", "filter": {"status": "open"}}`:`select * from orders where created_at > now() - interval '1 day'`,onChange:e=>d(e.target.value),className:`font-mono text-[13px]`}),(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,L.jsxs)(n,{variant:`primary`,disabled:a||!u.trim(),onClick:()=>o(`query`,{db:e.name,sql:u,maxRows:Number(f)}),children:[(0,L.jsx)(D,{size:16,"aria-hidden":`true`}),`Run`]}),(0,L.jsxs)(n,{disabled:a||!u.trim(),onClick:()=>o(`query`,{db:e.name,sql:u,dryRun:!0}),children:[(0,L.jsx)(r,{size:16,"aria-hidden":`true`}),`Check only`]}),(0,L.jsxs)(`label`,{className:`flex items-center gap-2 text-sm text-muted`,children:[`Rows`,(0,L.jsx)(S,{value:f,onChange:e=>p(e.target.value),className:`w-[96px]`,children:[`50`,`200`,`1000`].map(e=>(0,L.jsx)(`option`,{children:e},e))})]})]}),C&&!C.ok&&(0,L.jsx)(W,{text:C.needs_confirmation?`Only reads run here. Ask in a chat to change data: it shows the statement and asks first.`:C.error||`The query failed.`}),C?.ok&&C.dry_run&&(0,L.jsxs)(`p`,{className:`m-0 rounded-[12px] bg-pass-soft px-3 py-2 text-sm text-pass`,children:[C.note||`The database accepts it.`,C.columns?.length?` Columns: ${C.columns.join(`, `)}`:``]}),C?.ok&&!C.dry_run&&C.columns&&(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(G,{columns:C.columns,rows:C.rows||[]}),(0,L.jsxs)(`span`,{className:`text-[13px] text-muted`,children:[s(C.row_count??C.rows?.length??0,`row`),C.truncated?` (more not shown)`:``,C.ms===void 0?``:` in ${C.ms} ms`]})]})]}),(0,L.jsxs)(`section`,{"aria-labelledby":`ask-h`,className:`flex flex-col gap-2`,children:[(0,L.jsx)(`h3`,{id:`ask-h`,className:`m-0 text-base font-semibold`,children:`Ask in your words`}),(0,L.jsxs)(`form`,{className:`flex flex-wrap items-center gap-2`,onSubmit:e=>{e.preventDefault(),m.trim()&&E()},children:[(0,L.jsx)(T,{"aria-label":`Your question`,value:m,maxLength:500,placeholder:`Which customers ordered more than twice last week?`,onChange:e=>h(e.target.value),className:`min-w-0 flex-[1_1_280px]`}),(0,L.jsxs)(n,{type:`submit`,disabled:!m.trim(),children:[(0,L.jsx)(w,{size:16,"aria-hidden":`true`}),`Ask in a chat`]})]}),g&&(0,L.jsx)(W,{text:g})]})]})}function q({view:e,job:t,busy:n,act:r,sub:i}){let a=e.databases.find(e=>e.name===i)||e.databases[0];return e.databases.length?(0,L.jsxs)(`div`,{className:`flex flex-col gap-6 @2xl:flex-row @2xl:items-start @2xl:gap-8`,children:[(0,L.jsxs)(`nav`,{"aria-label":`Databases`,className:`flex min-w-0 flex-col gap-1 @2xl:sticky @2xl:top-6 @2xl:w-[250px] @2xl:flex-none`,children:[(0,L.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:e.databases.map(e=>(0,L.jsx)(`li`,{children:(0,L.jsxs)(`a`,{href:_(`data`,`databases/${encodeURIComponent(e.name)}`),"aria-current":a?.name===e.name?`page`:void 0,className:o(`flex min-h-[60px] items-center gap-3 rounded-[12px] px-3 py-2 text-ink no-underline`,a?.name===e.name?`bg-surface-strong hover:text-ink`:`hover:bg-surface-muted hover:text-ink`),children:[(0,L.jsx)(f,{size:18,"aria-hidden":`true`,className:`flex-none text-muted`}),(0,L.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,L.jsx)(`span`,{className:`truncate text-[15px] font-medium`,children:e.name}),(0,L.jsx)(`span`,{className:`truncate text-[13px] text-muted`,children:[e.engine||`connection string`,e.writable?`writable`:`read only`].join(` · `)})]})]})},e.name))}),(0,L.jsx)(`a`,{href:_(`settings`,`databases`),className:`px-3 pt-1 text-[13px]`,children:`Add or change in Settings`})]}),a&&(0,L.jsx)(K,{db:a,job:t,busy:n,act:r},a.name)]}):(0,L.jsxs)(`div`,{className:`flex flex-col items-start gap-3 rounded-[18px] border border-line p-6`,children:[(0,L.jsx)(f,{size:24,"aria-hidden":`true`,className:`text-muted`}),(0,L.jsx)(`p`,{className:`m-0 max-w-[62ch] leading-relaxed text-ink-2`,children:`No databases yet. Add one in Settings: a connection string, or its host, user and password, kept in your credential store.`}),(0,L.jsx)(`a`,{href:_(`settings`,`databases`),className:`text-sm font-medium`,children:`Settings, Databases`})]})}function J({r:e}){return(0,L.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[(e.order||[]).map(t=>{let n=e.preview?.[t],r=n?.sample||[],i=r.length?Object.keys(r[0]):[];return(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsxs)(`span`,{className:`text-sm`,children:[(0,L.jsx)(`span`,{className:`font-medium`,children:t}),` · `,s(e.rows?.[t]??0,`row`),n?.database_fills?.length?(0,L.jsxs)(`span`,{className:`text-muted`,children:[` · the database fills `,n.database_fills.join(`, `)]}):null,e.masked?.[t]?.length?(0,L.jsxs)(`span`,{className:`text-muted`,children:[` · masked `,e.masked[t].join(`, `)]}):null]}),i.length>0&&(0,L.jsx)(G,{columns:i,rows:r.slice(0,3).map(e=>i.map(t=>e[t]))})]},t)}),(e.warnings||[]).map((e,t)=>(0,L.jsx)(`p`,{className:`m-0 text-[13px] text-warn`,children:e},t))]})}function Y({view:e,job:r,busy:a,act:o}){let s=e.databases.filter(e=>e.writable),[c,l]=(0,I.useState)(e.specs[0]??``),[u,d]=(0,I.useState)(``),[f,p]=(0,I.useState)(``),[m,h]=(0,I.useState)(`csv`),[y,b]=(0,I.useState)(!1),[x,C]=(0,I.useState)(s[0]?.name??``),[E,D]=(0,I.useState)(``),[O,k]=(0,I.useState)(``),A={spec:c,...u?{db:u}:{},...f?{seed:f}:{}},M=r&&!r.running?r:null,N=M&&[`gen`,`files`,`insert`].includes(M.kind)&&M.target===c?M.result:void 0,P=M?.kind===`batches`?(Array.isArray(M.result)?M.result.flatMap(e=>e.batches||[]):M.result?.batches)??[]:null,F=e.databases.find(e=>e.name===u),R=async()=>{try{let{id:e}=await i.start(`Write a test data spec for this project (a YAML file with tables: under test-data/), from the database's schema. Show me the spec and a preview before you save it.`,{});location.hash=_(`chats`,e)}catch(e){k(e.message)}};return(0,L.jsxs)(`div`,{className:`flex flex-col gap-8`,children:[(0,L.jsxs)(`section`,{"aria-labelledby":`spec-h`,className:`flex flex-col gap-4`,children:[(0,L.jsxs)(`div`,{className:`flex flex-wrap items-baseline gap-2`,children:[(0,L.jsx)(`h2`,{id:`spec-h`,className:`m-0 flex-1 text-lg font-semibold`,children:`Make test data`}),(0,L.jsxs)(n,{variant:`ghost`,onClick:()=>void R(),children:[(0,L.jsx)(w,{size:16,"aria-hidden":`true`}),`Write a spec in a chat`]})]}),(0,L.jsx)(`p`,{className:`m-0 max-w-[70ch] text-sm leading-relaxed text-muted`,children:`A spec is a YAML file with tables: and what goes in each column. The same spec and seed make the same data again, so a test or a pipeline can rely on it. No model is used.`}),O&&(0,L.jsx)(W,{text:O}),e.specs.length===0?(0,L.jsx)(`p`,{className:`m-0 rounded-[12px] border border-line-soft px-4 py-3 text-sm text-muted`,children:`No specs in this project yet: a YAML file with tables: in test-data/, data/ or specs/. Write one in a chat, from a database's schema.`}):(0,L.jsxs)(`div`,{className:`flex flex-col gap-3 rounded-[14px] border border-line-soft p-4`,children:[(0,L.jsxs)(`div`,{className:`flex flex-wrap gap-3`,children:[(0,L.jsxs)(`label`,{className:`flex min-w-0 flex-[2_1_260px] flex-col gap-1.5 text-sm font-medium`,children:[`Spec`,(0,L.jsx)(S,{value:c,onChange:e=>l(e.target.value),children:e.specs.map(e=>(0,L.jsx)(`option`,{children:e},e))})]}),(0,L.jsxs)(`label`,{className:`flex min-w-0 flex-[1_1_180px] flex-col gap-1.5 text-sm font-medium`,children:[`Database`,(0,L.jsxs)(S,{value:u,onChange:e=>d(e.target.value),children:[(0,L.jsx)(`option`,{value:``,children:`The spec's own`}),e.databases.map(e=>(0,L.jsxs)(`option`,{value:e.name,children:[e.name,e.writable?``:` (read only)`]},e.name))]})]}),(0,L.jsxs)(`label`,{className:`flex w-[120px] flex-col gap-1.5 text-sm font-medium`,children:[`Seed`,(0,L.jsx)(T,{value:f,inputMode:`numeric`,placeholder:`Random`,onChange:e=>p(e.target.value.replace(/\D/g,``).slice(0,9))})]})]}),(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,L.jsxs)(n,{variant:`primary`,disabled:a||!c,onClick:()=>o(`gen`,A),children:[(0,L.jsx)(v,{size:16,"aria-hidden":`true`}),`Preview`]}),(0,L.jsxs)(`span`,{className:`inline-flex items-center gap-1`,children:[(0,L.jsx)(n,{disabled:a||!c,onClick:()=>o(`gen`,{...A,mode:`files`,format:m}),children:`Write files`}),(0,L.jsx)(S,{"aria-label":`File format`,value:m,onChange:e=>h(e.target.value),className:`w-[88px]`,children:[`csv`,`json`,`sql`].map(e=>(0,L.jsx)(`option`,{children:e},e))})]}),(0,L.jsxs)(n,{disabled:a||!c||!F?.writable,title:F?.writable?void 0:`Pick a writable database to insert into`,onClick:()=>b(!0),children:[(0,L.jsx)(j,{size:16,"aria-hidden":`true`}),`Insert into `,F?.writable?F.name:`a database`]})]}),y&&F&&(0,L.jsxs)(`div`,{role:`alertdialog`,"aria-label":`Insert test data`,className:`flex flex-col gap-3 rounded-[12px] border border-warn bg-warn-soft p-4`,children:[(0,L.jsxs)(`span`,{className:`font-medium`,children:[`Insert the rows of `,c,` into `,F.name,`?`]}),(0,L.jsx)(`span`,{className:`text-sm text-ink-2`,children:`They go in as one batch, which you can remove again below. Only on a test database.`}),(0,L.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,L.jsx)(n,{variant:`primary`,onClick:()=>{b(!1),o(`gen`,{...A,mode:`insert`,confirmed:!0})},children:`Insert`}),(0,L.jsx)(n,{onClick:()=>b(!1),children:`Don't insert`})]})]}),N&&!N.ok&&(0,L.jsx)(W,{text:N.error||`It did not make the data.`}),N?.ok&&(0,L.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[N.batch&&(0,L.jsxs)(`p`,{className:`m-0 rounded-[12px] bg-pass-soft px-3 py-2 text-sm text-pass`,children:[`Inserted as batch `,(0,L.jsx)(`span`,{className:`font-mono text-[13px]`,children:N.batch}),`.`]}),N.files?.length?(0,L.jsxs)(`p`,{className:`m-0 flex flex-wrap items-center gap-2 text-sm`,children:[`Wrote`,N.files.map(e=>(0,L.jsx)(`button`,{type:`button`,onClick:async()=>k(await g(e)),className:`cursor-pointer rounded-md border border-line-soft bg-surface-muted px-1.5 py-0.5 font-mono text-[12px] text-ink-2 hover:bg-surface-strong`,children:e},e))]}):null,(0,L.jsx)(J,{r:N})]})]})]}),(0,L.jsxs)(`section`,{"aria-labelledby":`batch-h`,className:`flex flex-col gap-3`,children:[(0,L.jsx)(`h2`,{id:`batch-h`,className:`m-0 text-lg font-semibold`,children:`Inserted batches`}),s.length===0?(0,L.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`No writable database: test data is inserted only into one marked writable in Settings, Databases.`}):(0,L.jsxs)(L.Fragment,{children:[(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,L.jsx)(S,{"aria-label":`Database`,value:x,onChange:e=>C(e.target.value),className:`w-[220px]`,children:s.map(e=>(0,L.jsx)(`option`,{children:e.name},e.name))}),(0,L.jsx)(n,{disabled:a||!x,onClick:()=>o(`batches`,{db:x}),children:`List batches`})]}),P&&P.length===0&&(0,L.jsxs)(`p`,{className:`m-0 text-sm text-muted`,children:[`No batches in `,M?.target,`.`]}),P&&P.length>0&&(0,L.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:P.map(e=>(0,L.jsxs)(`li`,{className:`flex flex-wrap items-center gap-3 border-t border-line-soft py-2.5 text-sm first:border-t-0`,children:[(0,L.jsxs)(`span`,{className:`flex min-w-0 flex-[1_1_240px] flex-col gap-0.5`,children:[(0,L.jsx)(`span`,{className:`font-mono text-[13px]`,children:e.batch}),(0,L.jsxs)(`span`,{className:`text-[13px] text-muted`,children:[new Date(e.created).toLocaleString([],{month:`short`,day:`numeric`,hour:`numeric`,minute:`2-digit`}),e.spec?` · ${e.spec}`:``,` ·`,` `,Object.entries(e.rows).map(([e,t])=>`${e} ${t}`).join(`, `)]})]}),E===e.batch?(0,L.jsxs)(`span`,{className:`flex items-center gap-2`,children:[(0,L.jsxs)(`span`,{className:`text-[13px] text-warn`,children:[`Remove its rows from `,M?.target,`?`]}),(0,L.jsx)(n,{size:`sm`,className:`border-fail text-fail hover:bg-fail-soft`,onClick:()=>{D(``),o(`clean`,{db:M?.target,batch:e.batch,confirmed:!0})},children:`Remove`}),(0,L.jsx)(n,{size:`sm`,onClick:()=>D(``),children:`Keep`})]}):(0,L.jsxs)(n,{size:`sm`,disabled:a,onClick:()=>D(e.batch),children:[(0,L.jsx)(t,{size:14,"aria-hidden":`true`}),`Remove`]})]},e.batch))}),M?.kind===`clean`&&M.result?.ok&&(0,L.jsxs)(`p`,{className:`m-0 rounded-[12px] bg-pass-soft px-3 py-2 text-sm text-pass`,children:[`Removed:`,` `,Object.entries(M.result.removed||{}).map(([e,t])=>`${e} ${t}`).join(`, `)]})]})]})]})}function X({c:e}){let t=e=>e?.count??0;return(0,L.jsxs)(`li`,{className:`flex flex-col gap-1 border-t border-line-soft py-3 text-sm first:border-t-0`,children:[(0,L.jsxs)(`span`,{className:`flex flex-wrap items-center gap-2`,children:[(0,L.jsx)(b,{tone:e.ok?`pass`:`fail`,children:e.ok?`Holds`:`Broken`}),(0,L.jsx)(`span`,{className:`font-medium`,children:e.flow}),(0,L.jsxs)(`span`,{className:`font-mono text-[12px] text-muted [overflow-wrap:anywhere]`,children:[e.from,` → `,e.to]})]}),e.error?(0,L.jsx)(`span`,{className:`text-fail [overflow-wrap:anywhere]`,children:e.error}):(0,L.jsxs)(`span`,{className:`text-ink-2`,children:[e.matched??0,` of `,e.source_rows??0,` arrived · `,e.late?`${t(e.late)} late (${t(e.in_flight)} in flight)`:`${t(e.missing)} missing`,` · `,e.wrong?.rows??0,` wrong · `,t(e.duplicates),` duplicated`,e.extra?` · ${t(e.extra)} with no source`:``]}),e.note&&(0,L.jsx)(`span`,{className:`text-[13px] text-muted`,children:e.note}),H(e.late??e.missing)&&(0,L.jsxs)(`span`,{className:`text-[13px] text-fail [overflow-wrap:anywhere]`,children:[`Missing: `,H(e.late??e.missing)]}),(e.wrong?.examples||[]).map((e,t)=>(0,L.jsxs)(`span`,{className:`font-mono text-[12px] text-fail [overflow-wrap:anywhere]`,children:[String(e.key),`: `,e.column,` expected `,V(e.expected),`, found `,V(e.target)]},t))]})}function Z({view:t,job:a,busy:o,act:c}){let[d,f]=(0,I.useState)([]),p=(0,I.useMemo)(()=>[...new Set(t.flows.map(e=>e.from).filter(e=>!e.startsWith(`api:`)))],[t.flows]),[h,v]=(0,I.useState)(p[0]??t.flows[0]?.name??``),[y,x]=(0,I.useState)(``),[C,E]=(0,I.useState)(``),[D,O]=(0,I.useState)(``),[k,A]=(0,I.useState)(null),[j,M]=(0,I.useState)(``),N=a&&!a.running?a:null,P=N?.kind===`check`?N.result??null:null,R=N?.kind===`trace`?N.result??null:null;return t.flowsError?(0,L.jsx)(W,{text:`${t.flowsFile} does not read: ${t.flowsError}`}):t.flows.length?(0,L.jsxs)(`div`,{className:`flex flex-col gap-8`,children:[(0,L.jsxs)(`section`,{"aria-labelledby":`flows-h`,className:`flex flex-col gap-3`,children:[(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,L.jsxs)(`h2`,{id:`flows-h`,className:`m-0 flex-1 text-lg font-semibold`,children:[`Flows `,(0,L.jsxs)(`span`,{className:`text-sm font-normal text-muted`,children:[`(`,t.flowsFile,`)`]})]}),(0,L.jsxs)(n,{variant:`primary`,disabled:o,onClick:()=>c(`check`,{flows:d}),children:[(0,L.jsx)(r,{size:16,"aria-hidden":`true`}),d.length?`Check ${s(d.length,`flow`)}`:`Check all`]}),N?.report&&(N.kind===`check`||N.kind===`trace`)&&(0,L.jsxs)(n,{onClick:async()=>M(await g(N.report)),children:[(0,L.jsx)(e,{size:16,"aria-hidden":`true`}),`Report`]})]}),(0,L.jsx)(`p`,{className:`m-0 text-[13px] text-muted`,children:`Each hop: did every record arrive, complete, correct, on time.`}),j&&(0,L.jsx)(W,{text:j}),(0,L.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:t.flows.map(e=>(0,L.jsxs)(`li`,{className:`flex items-start gap-3 border-t border-line-soft py-2.5 first:border-t-0`,children:[(0,L.jsx)(`input`,{type:`checkbox`,"aria-label":`Check ${e.name}`,checked:d.includes(e.name),onChange:t=>f(t.target.checked?[...d,e.name]:d.filter(t=>t!==e.name)),className:`mt-1 size-5 flex-none accent-accent`}),(0,L.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5 text-sm`,children:[(0,L.jsx)(`span`,{className:`font-medium`,children:e.name}),(0,L.jsxs)(`span`,{className:`font-mono text-[12px] text-ink-2 [overflow-wrap:anywhere]`,children:[e.from,` → `,e.to]}),(0,L.jsxs)(`span`,{className:`text-[13px] text-muted`,children:[[e.mode,e.sla?`SLA ${e.sla}`:``,s(e.columns,`column`)].filter(Boolean).join(` · `),e.description?` · ${e.description}`:``]})]})]},e.name))}),P?.error&&(0,L.jsx)(W,{text:P.error}),P?.flows&&(0,L.jsx)(`ul`,{"aria-label":`Check results`,className:`m-0 flex list-none flex-col rounded-[14px] border border-line-soft px-4 py-1`,children:P.flows.map(e=>(0,L.jsx)(X,{c:e},e.flow))})]}),(0,L.jsxs)(`section`,{"aria-labelledby":`trace-h`,className:`flex flex-col gap-3`,children:[(0,L.jsx)(`h2`,{id:`trace-h`,className:`m-0 text-lg font-semibold`,children:`Follow one record`}),(0,L.jsxs)(`form`,{className:`flex flex-wrap items-end gap-3`,onSubmit:e=>{e.preventDefault(),h&&y.trim()&&c(`trace`,{start:h,key:y.trim(),...C?{wait:Number(C)}:{}})},children:[(0,L.jsxs)(`label`,{className:`flex min-w-0 flex-[1_1_220px] flex-col gap-1.5 text-sm font-medium`,children:[`From`,(0,L.jsxs)(S,{value:h,onChange:e=>v(e.target.value),children:[(0,L.jsx)(`optgroup`,{label:`A table`,children:p.map(e=>(0,L.jsx)(`option`,{children:e},e))}),(0,L.jsx)(`optgroup`,{label:`A flow`,children:t.flows.map(e=>(0,L.jsx)(`option`,{children:e.name},e.name))})]})]}),(0,L.jsxs)(`label`,{className:`flex w-[160px] flex-col gap-1.5 text-sm font-medium`,children:[`Its key`,(0,L.jsx)(T,{value:y,maxLength:200,placeholder:`1042`,onChange:e=>x(e.target.value)})]}),(0,L.jsxs)(`label`,{className:`flex w-[130px] flex-col gap-1.5 text-sm font-medium`,children:[`Wait up to (s)`,(0,L.jsx)(T,{value:C,inputMode:`numeric`,placeholder:`0`,onChange:e=>E(e.target.value.replace(/\D/g,``).slice(0,3))})]}),(0,L.jsxs)(n,{type:`submit`,disabled:o||!h||!y.trim(),children:[(0,L.jsx)(l,{size:16,"aria-hidden":`true`}),`Follow it`]})]}),R?.error&&(0,L.jsx)(W,{text:R.error}),R?.hops&&(0,L.jsx)(`ol`,{className:`m-0 flex list-none flex-col gap-2 border-l border-line pl-4`,children:R.hops.map((e,t)=>(0,L.jsxs)(`li`,{className:`flex flex-col gap-0.5 text-sm`,children:[(0,L.jsxs)(`span`,{className:`flex flex-wrap items-center gap-2`,children:[(0,L.jsx)(b,{tone:e.ok?`pass`:`fail`,children:e.ok?`Arrived`:e.found_in_source===!1?`Not in the source`:e.arrived===!1?`Did not arrive`:`Differs`}),(0,L.jsx)(`span`,{className:`font-medium`,children:e.flow}),(0,L.jsxs)(`span`,{className:`font-mono text-[12px] text-muted`,children:[e.from,` → `,e.to,` · key `,V(e.key)]})]}),(e.differences||[]).map((e,t)=>(0,L.jsxs)(`span`,{className:`font-mono text-[12px] text-fail [overflow-wrap:anywhere]`,children:[e.column,`: expected `,V(e.expected),`, found `,V(e.target)]},t)),Number(e.copies)>1&&(0,L.jsxs)(`span`,{className:`text-[13px] text-warn`,children:[e.copies,` copies`]})]},t))})]}),(0,L.jsxs)(`section`,{"aria-labelledby":`impact-h`,className:`flex flex-col gap-3`,children:[(0,L.jsx)(`h2`,{id:`impact-h`,className:`m-0 text-lg font-semibold`,children:`What a change reaches`}),(0,L.jsxs)(`form`,{className:`flex flex-wrap items-center gap-2`,onSubmit:async e=>{e.preventDefault();try{A(await u.impact(D.trim())),M(``)}catch(e){M(e.message)}},children:[(0,L.jsx)(T,{"aria-label":`A table or column`,value:D,spellCheck:!1,placeholder:`shop.orders.total`,onChange:e=>O(e.target.value),className:`min-w-0 flex-[1_1_260px] font-mono text-[13px]`}),(0,L.jsxs)(n,{type:`submit`,disabled:!D.trim(),children:[(0,L.jsx)(m,{size:16,"aria-hidden":`true`}),`Show what reads it`]})]}),k&&k.length===0&&(0,L.jsxs)(`p`,{className:`m-0 text-sm text-muted`,children:[`Nothing in the flows reads `,D,`.`]}),k&&k.length>0&&(0,L.jsx)(`ul`,{className:`m-0 flex list-none flex-col gap-1 p-0`,children:k.map((e,t)=>(0,L.jsxs)(`li`,{className:`font-mono text-[12px] text-ink-2 [overflow-wrap:anywhere]`,style:{paddingLeft:`${(e.depth-1)*16}px`},children:[e.from,` → `,e.to,e.columns.length?` (${e.columns.join(`, `)})`:``,` `,(0,L.jsxs)(`span`,{className:`text-muted`,children:[`[`,e.flow,`]`]})]},t))}),t.map.length>0&&(0,L.jsxs)(`details`,{children:[(0,L.jsx)(`summary`,{className:`cursor-pointer text-sm text-muted`,children:`The whole map`}),(0,L.jsx)(`pre`,{className:`mt-2 overflow-auto rounded-[12px] bg-surface-muted p-3 font-mono text-[12px] leading-relaxed text-ink-2`,children:t.map.join(`
4
+ `)})]})]})]}):(0,L.jsxs)(`div`,{className:`flex flex-col items-start gap-3 rounded-[18px] border border-line p-6`,children:[(0,L.jsx)(F,{size:24,"aria-hidden":`true`,className:`text-muted`}),(0,L.jsxs)(`p`,{className:`m-0 max-w-[62ch] leading-relaxed text-ink-2`,children:[`No data flows yet. A flow says how records move from one system to another (a table, a topic or an API), by which key, and what each column becomes; `,(0,L.jsx)(`span`,{className:`font-mono text-[13px]`,children:t.flowsFile}),` holds them. ForgeQE can write them from your mapping documents.`]}),(0,L.jsxs)(n,{onClick:async()=>{try{let{id:e}=await i.start(`Write the data flows of this project into flows.yaml from our mapping documents and the databases' schemas. Show me each flow before you save it.`,{});location.hash=_(`chats`,e)}catch(e){M(e.message)}},children:[(0,L.jsx)(w,{size:16,"aria-hidden":`true`}),`Write flows in a chat`]}),j&&(0,L.jsx)(W,{text:j})]})}function Q({sub:e}){let[t,n]=(0,I.useState)(null),[r,i]=(0,I.useState)(``),[a,o]=(0,I.useState)(!1),[s,...c]=(e||``).split(`/`),l=R.includes(s)?s:`databases`,p=(0,I.useCallback)(()=>{u.view().then(e=>{n(e),i(``)},e=>i(e.message))},[]);(0,I.useEffect)(p,[p]);let m=!!t?.job?.running;(0,I.useEffect)(()=>{if(!m)return;let e=setInterval(p,1e3);return()=>clearInterval(e)},[m,p]);let h=async(e,t)=>{o(!0),i(``);try{await u.run(e,t),p()}catch(e){i(e.message)}finally{o(!1)}},g=t?.job??null,v={databases:`Databases${t?` (${t.databases.length})`:``}`,testdata:`Test data${t?` (${t.specs.length})`:``}`,flows:`Data flows${t?` (${t.flows.length})`:``}`};return(0,L.jsxs)(`div`,{className:`flex min-h-screen flex-col`,children:[(0,L.jsx)(C,{}),(0,L.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,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,L.jsx)(d,{icon:f,size:`lg`}),(0,L.jsx)(`h1`,{className:`m-0 flex-1 text-[26px] font-semibold tracking-[-0.01em]`,children:`Data`})]}),(0,L.jsx)(`p`,{className:`m-0 max-w-[74ch] text-sm leading-relaxed text-muted`,children:`The databases the agents may read, test data made from specs and inserted as batches you can remove again, and the data flows between systems: did every record arrive, complete and correct.`}),r&&(0,L.jsx)(`p`,{role:`alert`,className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail [overflow-wrap:anywhere]`,children:r}),!t&&!r&&(0,L.jsx)(`p`,{role:`status`,className:`m-0 text-sm text-muted`,children:`Loading…`}),t&&(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(x,{label:`Data`,current:l,onPick:e=>location.hash=_(`data`,e),tabs:R.map(e=>({id:e,label:v[e]}))}),g&&(g.running||z[g.kind]===l)&&(0,L.jsx)(U,{job:g,onStop:()=>void u.stop().then(p)}),(0,L.jsxs)(`div`,{role:`tabpanel`,id:`panel-${l}`,"aria-labelledby":`tab-${l}`,children:[l===`databases`&&(0,L.jsx)(q,{view:t,job:g,busy:a||m,act:h,sub:c[0]?decodeURIComponent(c[0]):void 0}),l===`testdata`&&(0,L.jsx)(Y,{view:t,job:g,busy:a||m,act:h}),l===`flows`&&(0,L.jsx)(Z,{view:t,job:g,busy:a||m,act:h})]})]})]})]})}export{Q as Data};
@@ -0,0 +1 @@
1
+ import{t as e}from"./circle-dashed-COka5oNi.js";import{n as t}from"./markdown-DclrGj2e.js";import{A as n,J as r,Lt as i,M as a,N as o,Nt as s,P as c,S as l,X as u,Xt as d,b as f,c as p,f as m,i as h,m as g,o as _,ot as v,p as y,qt as b,wt as x,zt as S}from"./index-KUNDUjo0.js";var C=d(b(),1),w=a(),T={ready:{label:`Ready`,tone:`pass`},partial:{label:`Ready, some parts need setup`,tone:`warn`},needs_setup:{label:`Needs setup`,tone:`neutral`}},E={models:`Models`,integrations:`Integrations`,monitor:`Monitor`,notifications:`Notifications`,agents:`Agents`,environments:`Environments`,containers:`Containers`,databases:`Databases`};async function D(e,t){try{let{id:t}=await i.start(e,{});location.hash=f(`chats`,t)}catch(e){t(e.message)}}function O({f:e,onScope:i,busy:a}){let[s,l]=(0,C.useState)(null),[u,d]=(0,C.useState)(``),[p,m]=(0,C.useState)(``);(0,C.useEffect)(()=>{S.detail(e.id).then(l,e=>d(e.message))},[e.id]);let h=e.gaps.filter(e=>!e.optional),_=e.gaps.filter(e=>e.optional);return(0,w.jsxs)(`div`,{className:`flex flex-col gap-4 py-4`,children:[[[h,`Needs`],[_,`Would do more with`]].map(([e,t])=>e.length?(0,w.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[(0,w.jsx)(`span`,{className:`text-sm font-medium`,children:t}),(0,w.jsx)(`ul`,{className:`m-0 flex list-none flex-col gap-2 p-0`,children:e.map((e,n)=>(0,w.jsxs)(`li`,{className:`flex flex-col gap-0.5 rounded-[10px] bg-bg px-3 py-2 text-sm`,children:[(0,w.jsx)(`span`,{className:o(t===`Needs`?`text-ink`:`text-ink-2`),children:e.missing}),(0,w.jsx)(`span`,{className:`text-[13px] text-muted`,children:e.fix}),e.settings&&(0,w.jsxs)(`a`,{href:f(`settings`,e.settings),className:`inline-flex items-center gap-1 self-start text-[13px] font-medium`,children:[(0,w.jsx)(r,{size:13,"aria-hidden":`true`}),`Set it up in Settings, `,E[e.settings]||e.settings]})]},n))})]},t):null),e.inputs.length>0&&(0,w.jsxs)(`p`,{className:`m-0 text-sm text-ink-2`,children:[`From your request: `,e.inputs.join(`; `),`.`]}),u&&(0,w.jsx)(`p`,{className:`m-0 text-sm text-fail`,children:u}),s&&(0,w.jsxs)(w.Fragment,{children:[s.examples.length>0&&(0,w.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[(0,w.jsx)(`span`,{className:`text-sm font-medium`,children:`Ask for it like this`}),(0,w.jsx)(`div`,{className:`flex flex-wrap gap-2`,children:s.examples.slice(0,4).map(t=>(0,w.jsxs)(n,{size:`sm`,disabled:e.state===`needs_setup`,title:e.state===`needs_setup`?`Set it up first`:`Start a chat with this request`,onClick:()=>void D(t,m),className:`h-auto min-h-8 max-w-full py-1.5 text-left whitespace-normal`,children:[(0,w.jsx)(v,{size:14,"aria-hidden":`true`,className:`flex-none`}),t]},t))}),p&&(0,w.jsx)(`span`,{className:`text-[13px] text-fail`,children:p})]}),s.limits.length>0&&(0,w.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,w.jsx)(`span`,{className:`text-sm font-medium`,children:`What it does not do`}),(0,w.jsx)(`ul`,{className:`m-0 list-disc pl-5 text-sm text-ink-2`,children:s.limits.map(e=>(0,w.jsx)(`li`,{children:e},e))})]}),s.docs.length>0&&(0,w.jsxs)(`details`,{children:[(0,w.jsxs)(`summary`,{className:`cursor-pointer text-sm font-medium`,children:[`How it works (`,c(s.docs.length,`section`),`)`]}),(0,w.jsx)(`div`,{className:`mt-2 flex flex-col gap-4`,children:s.docs.map(e=>(0,w.jsxs)(`section`,{className:`rounded-[12px] bg-bg px-4 py-3`,children:[(0,w.jsx)(`h4`,{className:`m-0 mb-1 text-sm font-semibold`,children:e.heading}),(0,w.jsx)(t,{text:e.text,className:`text-sm`})]},e.heading))})]}),(0,w.jsxs)(`span`,{className:`font-mono text-[11.5px] text-muted [overflow-wrap:anywhere]`,children:[`Tools: `,s.tools.join(`, `)]})]}),(0,w.jsxs)(`label`,{className:`flex flex-wrap items-center gap-3 border-t border-line-soft pt-3 text-sm`,children:[(0,w.jsxs)(`span`,{className:`flex-[1_1_240px]`,children:[`The agent gets its tools`,(0,w.jsx)(`span`,{className:`block text-[13px] text-muted`,children:`agent.capabilities in forgeqe.yaml`})]}),(0,w.jsxs)(g,{value:e.scope,disabled:a,onChange:e=>i(e.target.value),className:`w-[240px]`,children:[(0,w.jsx)(`option`,{value:`auto`,children:`When a request is about it`}),(0,w.jsx)(`option`,{value:`always`,children:`Always`}),(0,w.jsx)(`option`,{value:`off`,children:`Never, in this project`})]})]})]})}function k(){let[t,n]=(0,C.useState)(null),[r,i]=(0,C.useState)(``),[a,c]=(0,C.useState)(``),[d,f]=(0,C.useState)(`all`),[g,v]=(0,C.useState)(``),[b,E]=(0,C.useState)(!1),D=(0,C.useCallback)(()=>S.view().then(n,e=>i(e.message)),[]);(0,C.useEffect)(()=>void D(),[D]);let k=(0,C.useMemo)(()=>{let e=g.toLowerCase().split(/\s+/).filter(Boolean);return(t?.areas||[]).map(t=>({...t,features:t.features.filter(n=>(d===`all`||(d===`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)},[t,d,g]),A=async(e,t)=>{E(!0);try{n(await S.scope(e,t)),i(``)}catch(e){i(e.message)}finally{E(!1)}},j=t?.counts;return(0,w.jsxs)(`div`,{className:`flex min-h-screen flex-col`,children:[(0,w.jsx)(_,{}),(0,w.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,w.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,w.jsx)(l,{icon:s,size:`lg`}),(0,w.jsx)(`h1`,{className:`m-0 flex-1 text-[26px] font-semibold tracking-[-0.01em]`,children:`What's ready`})]}),(0,w.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.`}),r&&(0,w.jsx)(`p`,{role:`alert`,className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail [overflow-wrap:anywhere]`,children:r}),!t&&!r&&(0,w.jsx)(`p`,{role:`status`,className:`m-0 text-sm text-muted`,children:`Loading…`}),t&&j&&(0,w.jsxs)(w.Fragment,{children:[(0,w.jsxs)(`div`,{className:`flex flex-wrap gap-3`,children:[(0,w.jsx)(p,{icon:s,label:`Ready`,value:String(j.ready)}),(0,w.jsx)(p,{icon:x,label:`Some parts need setup`,value:String(j.partial)}),(0,w.jsx)(p,{icon:e,label:`Need setup`,value:String(j.needs_setup)})]}),(0,w.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,w.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,w.jsx)(`button`,{type:`button`,role:`radio`,"aria-checked":d===e,onClick:()=>f(e),className:o(`min-h-8 cursor-pointer rounded-full border-0 px-3.5 text-sm font-medium`,d===e?`bg-bg text-ink shadow-sm`:`bg-transparent text-muted hover:text-ink`),children:t},e))}),(0,w.jsxs)(`div`,{className:`relative min-w-0 flex-[1_1_220px]`,children:[(0,w.jsx)(u,{size:16,"aria-hidden":`true`,className:`pointer-events-none absolute top-1/2 left-3 -translate-y-1/2 text-muted`}),(0,w.jsx)(y,{type:`search`,"aria-label":`Find a feature`,value:g,placeholder:`Find a feature`,onChange:e=>v(e.target.value),className:`pl-9`})]})]}),k.length===0&&(0,w.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`Nothing matches.`}),k.map(e=>(0,w.jsxs)(`section`,{"aria-labelledby":`area-${e.area}`,children:[(0,w.jsx)(`h2`,{id:`area-${e.area}`,className:`m-0 mb-1 text-base font-semibold`,children:e.area}),e.features.map(e=>(0,w.jsx)(m,{title:e.title,meta:(0,w.jsx)(`span`,{className:o(a!==e.id&&`line-clamp-2`),children:e.summary}),open:a===e.id,onToggle:()=>c(a===e.id?``:e.id),state:(0,w.jsxs)(`span`,{className:`flex flex-wrap items-center justify-end gap-1.5`,children:[e.scope!==`auto`&&(0,w.jsx)(h,{tone:`neutral`,icon:!1,children:e.scope===`always`?`Always given`:`Off here`}),(0,w.jsx)(h,{tone:T[e.state].tone,icon:e.state!==`needs_setup`,children:T[e.state].label})]}),children:(0,w.jsx)(O,{f:e,busy:b,onScope:t=>void A(e.id,t)})},e.id))]},e.area)),t.outOfScope.length>0&&(0,w.jsxs)(`section`,{"aria-labelledby":`oos-h`,className:`flex flex-col gap-2 rounded-[14px] border border-line-soft px-4 py-3`,children:[(0,w.jsx)(`h2`,{id:`oos-h`,className:`m-0 text-base font-semibold`,children:`Not what ForgeQE does`}),(0,w.jsx)(`ul`,{className:`m-0 flex list-none flex-col gap-1 p-0 text-sm`,children:t.outOfScope.map(e=>(0,w.jsxs)(`li`,{children:[e.what,e.instead?(0,w.jsxs)(`span`,{className:`text-muted`,children:[`; instead: `,e.instead]}):null]},e.what))})]})]})]})]})}export{k as Features};
@@ -0,0 +1,3 @@
1
+ import{n as e,t}from"./markdown-DclrGj2e.js";import{t as n}from"./external-link-B9T-hxHX.js";import{t as r}from"./terminal-BnhGgg6d.js";import{t as i}from"./trash-CtWVl2Xk.js";import{$ as a,A as o,At as s,Et as c,Gt as l,M as u,Mt as d,N as f,O as p,Pt as m,S as h,U as g,Vt as _,Xt as v,Y as y,_ as b,b as x,c as S,ct as C,et as w,g as T,ht as E,i as D,lt as O,m as k,nt as A,o as j,p as M,qt as N,rt as P,x as F}from"./index-KUNDUjo0.js";var I={name:`bell`,size:24,node:[[`path`,{d:`M10.268 21a2 2 0 0 0 3.464 0`,key:`vwvbt9`}],[`path`,{d:`M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326`,key:`11g9vi`}]]};I.node;var L=m(I),R={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`}]]};R.node;var z=m(R),B={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`}]]};B.node;var V=m(B),H={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`}]]};H.node;var U=m(H),W={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`}]]};W.node;var G=m(W),K=v(N(),1),q=u(),J={monitor:`Monitor`,suite:`Suite`,run:`Browser run`,heal:`Heal`,"pr-tests":`PR tests`,pack:`Pack`,pipeline:`Pipeline watch`},Y={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`}},X=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`})},Z=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 Q(e){return e.running?Y.running:e.enabled?e.last?Y[e.last.status]:{label:`Not run yet`,tone:`neutral`}:{label:`Disabled`,tone:`neutral`}}var ee={pass:`bg-pass`,fail:`bg-fail`,warn:`bg-warn`,running:`bg-accent`,neutral:`bg-line`};function $({label:e,icon:t,children:n}){return(0,q.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,q.jsxs)(`span`,{className:`flex w-[170px] flex-none items-center gap-2 text-sm text-muted`,children:[t&&(0,q.jsx)(t,{size:16,"aria-hidden":`true`,className:`flex-none`}),e]}),(0,q.jsx)(`span`,{className:`min-w-0 flex-1 text-sm [overflow-wrap:anywhere]`,children:n})]})}function te({m:r,rounds:i}){let[a,s]=(0,K.useState)(``);return(0,q.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,q.jsxs)(`h3`,{className:`m-0 mb-1 flex items-center gap-2 text-base font-semibold`,children:[(0,q.jsx)(O,{size:18,"aria-hidden":`true`,className:`text-accent`}),`Monitor`]}),(0,q.jsx)($,{icon:C,label:`Checks`,children:r.checks.length?(0,q.jsx)(e,{text:r.checks.map(e=>`\`${e.replace(/`/g,``)}\``).join(`, `),className:`text-sm [&_p]:my-0`}):`The checks in forgeqe.yaml (monitor.checks)`}),(0,q.jsxs)($,{icon:d,label:`Alerts after`,children:[r.failAfter,` failing round`,r.failAfter===1?``:`s`,` in a row`,r.remind?`, again every ${r.remind} while it keeps failing`:``]}),(0,q.jsx)($,{icon:y,label:`Alerts go to`,children:r.alerts.length?r.alerts.join(` and `):`Nowhere yet: add Teams or email in Settings, Monitor`}),(0,q.jsxs)($,{icon:w,label:`Each round`,children:[r.parallel,` check`,r.parallel===1?``:`s`,` at once; a recorded run `,r.replay===`trust`?`is trusted`:r.replay===`off`?`is not replayed`:`is verified with one model call`]}),(0,q.jsx)($,{icon:E,label:`Rounds kept in`,children:(0,q.jsx)(t,{path:r.outDir})}),(0,q.jsx)(`p`,{className:`m-0 border-t border-line-soft py-2.5 text-sm`,children:(0,q.jsx)(`a`,{href:x(`settings`,`monitor`),children:`Change the monitor in Settings`})}),(0,q.jsx)(`h4`,{className:`m-0 mt-3 mb-1 text-sm font-semibold`,children:`Latest rounds`}),a&&(0,q.jsx)(`p`,{className:`m-0 text-[13px] text-fail`,children:a}),i.length===0?(0,q.jsx)(`p`,{className:`m-0 border-t border-line-soft py-3 text-sm text-muted`,children:`No rounds yet.`}):(0,q.jsx)(`ul`,{className:`m-0 list-none p-0`,children:i.slice(0,8).map(e=>(0,q.jsxs)(`li`,{className:`flex min-h-[48px] items-center gap-3 border-t border-line-soft py-2`,children:[(0,q.jsxs)(D,{tone:e.counts.failed?`fail`:`pass`,children:[e.counts.passed,`/`,e.counts.total,` passed`]}),(0,q.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-sm`,children:X(e.startedAt)}),e.report?(0,q.jsxs)(o,{size:`sm`,"aria-label":`Open the report of the round of ${X(e.startedAt)} in a new tab`,onClick:async()=>s(await b(e.report)),children:[(0,q.jsx)(n,{size:14,"aria-hidden":`true`}),`Open report`]}):(0,q.jsx)(t,{path:e.folder})]},e.key))})]})}function ne({jobId:t,run:n}){let[r,i]=(0,K.useState)(null),[a,o]=(0,K.useState)(``);(0,K.useEffect)(()=>{let e=!1;i(null);let r=()=>_.log(t,n.id).then(t=>!e&&i(t.lines),t=>!e&&o(t.message));r();let a=n.status===`running`?setInterval(r,2e3):void 0;return()=>{e=!0,a&&clearInterval(a)}},[t,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=[`**${Y[n.status].label}**${n.endedAt?` after ${Z(n)}`:``}${n.exitCode!==void 0&&n.exitCode!==null?`, exit ${n.exitCode}`:``}. Started ${X(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,q.jsxs)(`div`,{className:`flex flex-col gap-2 rounded-[12px] border border-line-soft bg-surface-muted px-4 py-3`,children:[a&&(0,q.jsx)(`p`,{className:`m-0 text-sm text-fail`,children:a}),!r&&!a?(0,q.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`Reading its output…`}):(0,q.jsx)(e,{text:l,className:`text-sm [&_pre]:max-h-[320px] [&_pre]:overflow-auto`})]})}function re({job:t,rounds:n,busy:l,act:u}){let[d,f]=(0,K.useState)(null),[p,m]=(0,K.useState)(null),[h,v]=(0,K.useState)(!1),y=`${t.running?.id}|${t.last?.id}|${t.last?.status}`;(0,K.useEffect)(()=>{let e=!1;return _.detail(t.id).then(t=>{e||(f(t.runs),m(e=>e??t.runs[0]?.id??null))},()=>{}),()=>{e=!0}},[t.id,y]),(0,K.useEffect)(()=>{m(null),v(!1)},[t.id]);let b=Q(t),x=d?.filter(e=>e.status===`passed`).length??0,C=d?.filter(e=>e.status!==`running`).length??0;return(0,q.jsxs)(`section`,{"aria-labelledby":`job-h`,className:`flex min-w-0 flex-1 flex-col gap-5`,children:[(0,q.jsxs)(`div`,{className:`flex flex-wrap items-start gap-x-3 gap-y-3`,children:[(0,q.jsxs)(`div`,{className:`flex min-w-0 flex-[1_1_260px] flex-col gap-1`,children:[(0,q.jsx)(`h2`,{id:`job-h`,className:`m-0 text-[22px] font-semibold tracking-[-0.01em] [overflow-wrap:anywhere]`,children:t.name}),(0,q.jsxs)(`span`,{className:`text-sm text-muted`,children:[J[t.kind]||t.kind,` · `,t.schedule]})]}),(0,q.jsx)(D,{tone:b.tone,children:b.label}),t.running?(0,q.jsxs)(o,{disabled:l,onClick:()=>u(`stop`),children:[(0,q.jsx)(g,{size:14,fill:`currentColor`,"aria-hidden":`true`}),`Stop`]}):(0,q.jsxs)(o,{variant:`primary`,disabled:l,onClick:()=>u(`run`),children:[(0,q.jsx)(P,{size:16,"aria-hidden":`true`}),`Run now`]})]}),(0,q.jsxs)(`div`,{className:`flex flex-wrap gap-3`,children:[(0,q.jsx)(S,{icon:a,label:`Runs kept`,value:String(d?.length??`–`)}),(0,q.jsx)(S,{icon:c,label:`Passed`,value:C?`${Math.round(x/C*100)}%`:`–`,note:C?`${x} of ${C}`:void 0}),(0,q.jsx)(S,{icon:s,label:`Next run`,value:t.nextAt?new Date(t.nextAt).toLocaleTimeString([],{hour:`numeric`,minute:`2-digit`}):`–`,note:t.nextAt?X(t.nextAt):t.enabled?void 0:`Disabled`})]}),(0,q.jsxs)(`div`,{className:`flex flex-col`,children:[(0,q.jsx)($,{icon:r,label:`Runs`,children:(0,q.jsx)(e,{text:`\`${t.command.replace(/`/g,``)}\``,className:`text-sm [&_p]:my-0`})}),(0,q.jsx)($,{icon:s,label:`When`,children:t.schedule}),(0,q.jsx)($,{icon:V,label:`Enabled`,children:(0,q.jsx)(F,{checked:t.enabled,disabled:l,onChange:e=>u(e?`enable`:`disable`),label:`${t.name} enabled`})}),(0,q.jsxs)($,{icon:G,label:`Stopped after`,children:[t.timeoutMs?t.timeoutMs%36e5==0?`${t.timeoutMs/36e5} hour${t.timeoutMs===36e5?``:`s`}`:`${Math.round(t.timeoutMs/6e4)} minutes`:`the service's default`,` of running`]}),(0,q.jsx)($,{icon:L,label:`Tells the team`,children:t.notify?`When it fails, recovers or ends (the notify channels that take job events)`:t.kind===`monitor`?`The monitor sends its own alerts`:`No`}),(0,q.jsx)($,{icon:U,label:`Investigates failures`,children:t.investigate?`Yes, on branches of their own`:`No`}),(0,q.jsxs)($,{icon:z,label:`Added`,children:[X(t.createdAt),` `,t.by===`agent`?`by ForgeQE's agent, after you agreed`:`from the command line or here`]})]}),t.monitor&&(0,q.jsx)(te,{m:t.monitor,rounds:n}),(0,q.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,q.jsx)(`h3`,{className:`m-0 mb-1 text-base font-semibold`,children:`Runs`}),d?d.length===0?(0,q.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,q.jsx)(`ul`,{className:`m-0 list-none p-0`,children:d.map(e=>(0,q.jsxs)(`li`,{className:`border-t border-line-soft`,children:[(0,q.jsxs)(`button`,{type:`button`,"aria-expanded":p===e.id,onClick:()=>m(p===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,q.jsx)(D,{tone:Y[e.status].tone,children:Y[e.status].label}),(0,q.jsxs)(`span`,{className:`min-w-0 flex-1 truncate text-sm`,children:[X(e.startedAt),(0,q.jsxs)(`span`,{className:`text-muted`,children:[` `,`· `,e.reason===`asked`?`asked for`:`scheduled`,Z(e)?` · ${Z(e)}`:``]})]}),e.summary?.length?(0,q.jsx)(`span`,{className:`hidden max-w-[40%] truncate text-[13px] text-muted sm:inline`,children:e.summary.at(-1)}):null]}),p===e.id&&(0,q.jsx)(`div`,{className:`pb-3`,children:(0,q.jsx)(ne,{jobId:t.id,run:e})})]},e.id))}):(0,q.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`Loading…`})]}),(0,q.jsx)(`div`,{className:`flex flex-wrap items-center gap-2 border-t border-line-soft pt-4`,children:h?(0,q.jsxs)(q.Fragment,{children:[(0,q.jsxs)(`span`,{className:`text-sm text-ink-2`,children:[`Remove `,t.name,`? Its runs and logs go with it.`]}),(0,q.jsx)(o,{disabled:l,onClick:()=>u(`remove`),className:`border-fail text-fail hover:bg-fail-soft`,children:`Remove`}),(0,q.jsx)(o,{onClick:()=>v(!1),children:`Keep it`})]}):(0,q.jsxs)(o,{variant:`ghost`,onClick:()=>v(!0),className:`text-fail`,children:[(0,q.jsx)(i,{size:16,"aria-hidden":`true`}),`Remove this job`]})})]})}function ie({open:e,onClose:t,commands:n,initial:r,onAdded:i}){let[a,s]=(0,K.useState)(``),[c,l]=(0,K.useState)(`suite`),[u,d]=(0,K.useState)(``),[f,p]=(0,K.useState)(`every`),[m,h]=(0,K.useState)(`30m`),[g,v]=(0,K.useState)(`0 2 * * 1-5`),[y,b]=(0,K.useState)(!0),[x,S]=(0,K.useState)(``),[C,w]=(0,K.useState)(!1);(0,K.useEffect)(()=>{e&&(s(r?.name??``),l(r?.command??`suite`),d(r?.args??``),p(`every`),h(r?.every??`30m`),b(r?.command!==`monitor`),S(``))},[e,r]);let E=async()=>{w(!0),S(``);try{i(await _.add({name:a.trim(),command:c,args:u,...f===`every`?{every:m}:f===`cron`?{cron:g}:{now:!0},notify:y}),a.trim())}catch(e){S(e.message)}finally{w(!1)}},D=`flex flex-col gap-1.5 py-3`;return(0,q.jsx)(T,{open:e,onClose:t,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,q.jsxs)(q.Fragment,{children:[x&&(0,q.jsx)(`span`,{role:`alert`,className:`mr-auto self-center text-sm text-fail [overflow-wrap:anywhere]`,children:x}),(0,q.jsx)(o,{onClick:t,children:`Cancel`}),(0,q.jsx)(o,{variant:`primary`,disabled:C||!a.trim(),onClick:()=>void E(),children:C?`Adding…`:`Add job`})]}),children:(0,q.jsxs)(`div`,{className:`flex flex-col pt-3`,children:[(0,q.jsxs)(`div`,{className:D,children:[(0,q.jsx)(`label`,{htmlFor:`job-name`,className:`font-medium`,children:`Name`}),(0,q.jsx)(M,{id:`job-name`,value:a,spellCheck:!1,placeholder:`nightly-smoke`,onChange:e=>s(e.target.value)}),(0,q.jsx)(`span`,{className:`text-sm text-muted`,children:`Letters, digits and dashes.`})]}),(0,q.jsxs)(`div`,{className:D,children:[(0,q.jsx)(`label`,{htmlFor:`job-command`,className:`font-medium`,children:`Runs`}),(0,q.jsx)(k,{id:`job-command`,value:c,onChange:e=>l(e.target.value),children:n.map(e=>(0,q.jsxs)(`option`,{value:e,children:[`forgeqe `,e]},e))})]}),(0,q.jsxs)(`div`,{className:D,children:[(0,q.jsx)(`label`,{htmlFor:`job-args`,className:`font-medium`,children:`With`}),(0,q.jsx)(M,{id:`job-args`,value:u,spellCheck:!1,placeholder:c===`monitor`?`Empty: the checks in forgeqe.yaml`:`checks/smoke.yaml --parallel 2`,onChange:e=>d(e.target.value),className:`font-mono text-[13px]`}),(0,q.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,q.jsxs)(`fieldset`,{className:`m-0 flex flex-col gap-2 border-0 p-0 py-3`,children:[(0,q.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,q.jsxs)(`label`,{className:`flex min-h-10 cursor-pointer items-center gap-3`,children:[(0,q.jsx)(`input`,{type:`radio`,name:`job-when`,checked:f===e,onChange:()=>p(e),className:`size-4 accent-accent`}),(0,q.jsx)(`span`,{className:`flex-none`,children:t}),e===`every`&&f===`every`&&(0,q.jsx)(M,{"aria-label":`Every`,value:m,onChange:e=>h(e.target.value),placeholder:`30m`,className:`max-w-[120px] font-mono text-[13px]`}),e===`cron`&&f===`cron`&&(0,q.jsx)(M,{"aria-label":`Cron schedule`,value:g,onChange:e=>v(e.target.value),className:`max-w-[180px] font-mono text-[13px]`})]},e))]}),(0,q.jsxs)(`div`,{className:`flex items-center gap-4 py-3`,children:[(0,q.jsxs)(`span`,{className:`flex flex-1 flex-col gap-0.5`,children:[(0,q.jsx)(`span`,{className:`font-medium`,children:`Tell the team`}),(0,q.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,q.jsx)(F,{checked:y,onChange:b,label:`Tell the team`})]})]})})}function ae({sub:t}){let[n,r]=(0,K.useState)(null),[i,a]=(0,K.useState)([]),[c,u]=(0,K.useState)(``),[d,m]=(0,K.useState)(!1),[g,v]=(0,K.useState)(null),y=(0,K.useCallback)(()=>{_.list().then(e=>{r(e),u(``)},e=>u(e.message))},[]);(0,K.useEffect)(y,[y]),(0,K.useEffect)(()=>{l.list().then(e=>a(e.rows.filter(e=>e.scope===`monitor`)),()=>{})},[n?.jobs.map(e=>e.last?.id).join()]);let b=n?.jobs.some(e=>e.running);(0,K.useEffect)(()=>{let e=setInterval(y,b?3e3:2e4);return()=>clearInterval(e)},[y,b]);let S=t?decodeURIComponent(t):``,C=n?.jobs.find(e=>e.id===S)||n?.jobs[0],w=(0,K.useMemo)(()=>n?.jobs.filter(e=>e.kind===`monitor`)??[],[n]),T=(0,K.useMemo)(()=>n?.jobs.filter(e=>e.kind!==`monitor`)??[],[n]),E=async e=>{if(C){m(!0),u(``);try{r(e===`remove`?await _.remove(C.id):await _.act(C.id,e)),e===`remove`&&(location.hash=x(`jobs`))}catch(e){u(e.message)}finally{m(!1)}}},k=e=>{let t=Q(e),n=C?.id===e.id;return(0,q.jsx)(`li`,{children:(0,q.jsxs)(`a`,{href:x(`jobs`,encodeURIComponent(e.id)),"aria-current":n?`page`:void 0,className:f(`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,q.jsx)(p,{label:t.label,px:16}):(0,q.jsx)(`span`,{"aria-label":t.label,className:f(`size-2.5 flex-none rounded-full`,ee[t.tone])}),(0,q.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,q.jsx)(`span`,{className:`truncate text-[15px] font-medium`,children:e.name}),(0,q.jsx)(`span`,{className:`truncate text-[13px] text-muted`,children:[t.label,e.schedule,e.nextAt&&e.enabled?`next ${X(e.nextAt)}`:``].filter(Boolean).join(` · `)})]})]})},e.id)};return(0,q.jsxs)(`div`,{className:`flex min-h-screen flex-col`,children:[(0,q.jsx)(j,{}),(0,q.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,q.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-4 gap-y-2`,children:[(0,q.jsx)(h,{icon:s,size:`lg`}),(0,q.jsx)(`h1`,{className:`m-0 text-[26px] font-semibold tracking-[-0.01em]`,children:`Jobs`}),n&&(0,q.jsx)(D,{tone:n.service.running?`pass`:`neutral`,children:n.service.running?`Service running since ${X(n.service.startedAt)}`:`Service stopped: jobs wait until it starts`}),(0,q.jsx)(`div`,{className:`flex-auto`}),(0,q.jsxs)(o,{variant:`primary`,disabled:!n,onClick:()=>v({}),children:[(0,q.jsx)(A,{size:16,"aria-hidden":`true`}),`Add a job`]})]}),(0,q.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.`}),c&&(0,q.jsx)(`p`,{role:`alert`,className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail [overflow-wrap:anywhere]`,children:c}),!n&&!c&&(0,q.jsx)(`p`,{role:`status`,className:`m-0 text-sm text-muted`,children:`Loading…`}),n?.monitor&&!n.monitor.scheduled&&(0,q.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,q.jsx)(O,{size:20,"aria-hidden":`true`,className:`flex-none text-accent`}),(0,q.jsxs)(`span`,{className:`flex min-w-0 flex-[1_1_300px] flex-col gap-0.5 text-sm`,children:[(0,q.jsx)(`span`,{className:`font-medium`,children:`The monitor in forgeqe.yaml is not scheduled`}),(0,q.jsxs)(`span`,{className:`text-ink-2`,children:[n.monitor.checks.length,` check`,n.monitor.checks.length===1?``:`s`,`, every `,n.monitor.every,`. As a job it runs a round each time, with this page closed.`]})]}),(0,q.jsx)(o,{onClick:()=>v({name:`monitor`,command:`monitor`,args:``,every:n.monitor.every}),children:`Schedule it`})]}),n&&(0,q.jsxs)(`div`,{className:`flex flex-col gap-6 @2xl:flex-row @2xl:items-start @2xl:gap-8`,children:[(0,q.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,q.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,q.jsxs)(`span`,{className:`flex items-center gap-2 px-3 text-[13px] font-medium text-muted`,children:[(0,q.jsx)(O,{size:14,"aria-hidden":`true`}),`Monitoring (`,w.length,`)`]}),w.length?(0,q.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:w.map(k)}):(0,q.jsx)(`p`,{className:`m-0 px-3 py-2 text-sm text-muted`,children:`No monitor jobs.`})]}),(0,q.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,q.jsxs)(`span`,{className:`flex items-center gap-2 px-3 text-[13px] font-medium text-muted`,children:[(0,q.jsx)(s,{size:14,"aria-hidden":`true`}),`Other jobs (`,T.length,`)`]}),T.length?(0,q.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:T.map(k)}):(0,q.jsx)(`p`,{className:`m-0 px-3 py-2 text-sm text-muted`,children:`None.`})]}),n.events.length>0&&(0,q.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,q.jsx)(`span`,{className:`px-3 text-[13px] font-medium text-muted`,children:`Recent activity`}),(0,q.jsx)(e,{text:n.events.slice(0,12).map(e=>`- \`${X(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`})]})]}),C?(0,q.jsx)(re,{job:C,rounds:i,busy:d,act:E}):(0,q.jsxs)(`div`,{className:`flex min-w-0 flex-1 flex-col items-start gap-3 rounded-[18px] border border-line p-6`,children:[(0,q.jsx)(s,{size:24,"aria-hidden":`true`,className:`text-muted`}),(0,q.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,q.jsx)(o,{onClick:()=>v({}),children:`Add a job`})]})]})]}),(0,q.jsx)(ie,{open:!!g,onClose:()=>v(null),commands:n?.commands??[],initial:g??void 0,onAdded:(e,t)=>{r(e),v(null);let n=e.jobs.find(e=>e.name===t);n&&(location.hash=x(`jobs`,encodeURIComponent(n.id)))}})]})}export{ae as Jobs};
@@ -0,0 +1 @@
1
+ import{t as e}from"./circle-dashed-COka5oNi.js";import{i as t,t as n}from"./markdown-DclrGj2e.js";import{t as r}from"./external-link-B9T-hxHX.js";import{n as i,t as a}from"./trending-up-BnzWZEhg.js";import{A as o,Ct as s,Et as c,Lt as l,M as u,N as d,Pt as f,S as p,Ut as m,V as h,Xt as g,b as _,i as v,l as y,mt as b,o as x,ot as S,p as C,qt as w}from"./index-KUNDUjo0.js";import{n as T,r as E,t as D}from"./filters-C5e4Y0To.js";import{n as O,t as k}from"./when-CHnpLdYw.js";var A={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`}]]};A.node;var j=f(A),M={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`}]]};M.node;var N=f(M),P={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`}]]};P.node;var F=f(P),I={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`}]]};I.node;var L=f(I),R=g(w(),1),z=u(),B=[`critical`,`serious`,`moderate`,`minor`],ee={critical:`fail`,serious:`fail`,moderate:`warn`,minor:`neutral`},V=[{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`}],H={same:{label:`Same`,tone:`pass`},changed:{label:`Changed`,tone:`warn`},new:{label:`New baseline`,tone:`neutral`},no_baseline:{label:`No baseline`,tone:`neutral`}},U=e=>new Date(e).toLocaleString([],{month:`short`,day:`numeric`,hour:`numeric`,minute:`2-digit`}),W=(e,t)=>t?e>=1e3?`${(e/1e3).toFixed(1)}s`:`${Math.round(e)}ms`:e.toFixed(3).replace(/0+$/,``).replace(/\.$/,``),G=(e,t)=>`${e} ${t}${e===1?``:`s`}`,K=e=>{try{let t=new URL(e);return`${t.host}${t.pathname}${t.search}`}catch{return e}};function te(e){let t=[];return e.a11y&&t.push(e.a11y.failed?`${G(e.a11y.failed,`rule`)} failed`:`WCAG clean`),e.perf.over&&t.push(`${G(e.perf.over,`page`)} slow`),e.visual?.changed&&t.push(`${e.visual.changed} changed`),e.captured&&t.push(`not compared`),t.join(` · `)||`measured`}var q=e=>!!(e.breaches||e.a11y?.failed||e.perf.over||e.visual?.changed),J=25;function Y({run:e,standard:t}){let n=e.report,[r,i]=(0,R.useState)(null),[a,o]=(0,R.useState)(`all`);if(!n?.accessibility.pages)return(0,z.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 s=n.accessibility,c=n.breaches.filter(e=>e.kind===`accessibility`),l=a===`all`?s.failed:s.failed.filter(e=>e.impact===a),u=[{id:`all`,label:`All`,n:s.failed.length},...B.filter(e=>s.failed.some(t=>t.impact===e)).map(e=>({id:e,label:e.charAt(0).toUpperCase()+e.slice(1),n:s.failed.filter(t=>t.impact===e).length,alert:e===`critical`||e===`serious`}))];return(0,z.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[(0,z.jsxs)(`p`,{className:`m-0 text-[13px] text-muted`,children:[t,` with `,n.engine||`axe`,`: `,G(s.passed.length,`rule`),` passed, `,s.failed.length,` failed, on `,G(s.pages,`page`),`. `,n.budgets.accessibility?c.length?`${G(c.length,`budget`)} exceeded.`:`Within the budget.`:`No budget set, so nothing here fails the run.`]}),c.map((e,t)=>(0,z.jsxs)(`p`,{className:`m-0 rounded-[10px] bg-fail-soft px-3 py-2 text-sm text-fail [overflow-wrap:anywhere]`,children:[K(e.page),`: `,e.text]},t)),s.failed.length?(0,z.jsxs)(z.Fragment,{children:[u.length>2&&(0,z.jsx)(`div`,{className:`flex flex-wrap gap-1.5`,children:(0,z.jsx)(D,{items:u,value:a,onChange:o})}),(0,z.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:l.map(t=>(0,z.jsx)(X,{f:t,examples:e.examples[t.rule]??[],open:r===t.rule,onToggle:()=>i(r===t.rule?null:t.rule)},t.rule))})]}):(0,z.jsx)(`p`,{className:`m-0 text-sm text-pass`,children:`No rule failed on the pages this run visited.`})]})}function X({f:e,examples:n,open:i,onToggle:a}){return(0,z.jsxs)(`li`,{className:`border-t border-line-soft first:border-t-0`,children:[(0,z.jsxs)(`button`,{type:`button`,"aria-expanded":i,onClick:a,className:`flex min-h-12 w-full cursor-pointer items-center gap-3 border-0 bg-transparent px-1 py-1.5 text-left text-ink hover:bg-surface-muted`,children:[(0,z.jsx)(v,{tone:ee[e.impact],className:`w-[88px] flex-none justify-center`,children:e.impact}),(0,z.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,z.jsx)(`span`,{className:`text-sm [overflow-wrap:anywhere]`,children:e.help}),(0,z.jsxs)(`span`,{className:`truncate text-[12px] text-muted`,children:[e.rule,e.wcag?` · WCAG ${e.wcag}${e.level?` (${e.level})`:``}`:``,` · `,G(e.count,`element`),` on `,G(e.pages,`page`)]})]})]}),i&&(0,z.jsxs)(`div`,{className:`mb-3 ml-[104px] flex flex-col gap-2 text-sm max-sm:ml-2`,children:[n.length?n.map((e,t)=>(0,z.jsxs)(`div`,{className:`flex flex-col gap-1 rounded-[10px] bg-surface-muted p-2.5`,children:[(0,z.jsxs)(`span`,{className:`text-[12px] text-muted [overflow-wrap:anywhere]`,children:[K(e.page),e.target?` · ${e.target}`:``]}),e.html&&(0,z.jsx)(`code`,{className:`font-mono text-[12px] whitespace-pre-wrap text-ink-2 [overflow-wrap:anywhere]`,children:e.html})]},t)):(0,z.jsx)(`span`,{className:`text-muted`,children:`No elements were kept for this rule.`}),e.helpUrl&&t(e.helpUrl)===`web`&&(0,z.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,z.jsx)(r,{size:13,"aria-hidden":`true`})]})]})]})}function Z({run:e}){let t=e.report;if(!t?.performance.pages.length)return(0,z.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`,r={poor:`text-fail`,"needs improvement":`text-warn`,good:`text-pass`};return(0,z.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[(0,z.jsxs)(`p`,{className:`m-0 text-[13px] text-muted`,children:[`Core Web Vitals per page against Google's thresholds: `,(0,z.jsx)(`span`,{className:`text-pass`,children:`good`}),`, `,(0,z.jsx)(`span`,{className:`text-warn`,children:`needs improvement`}),`, `,(0,z.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,z.jsx)(`div`,{className:`overflow-x-auto`,children:(0,z.jsxs)(`table`,{className:`w-full min-w-[640px] border-collapse text-sm`,children:[(0,z.jsx)(`thead`,{children:(0,z.jsxs)(`tr`,{className:`text-left text-[12px] text-muted`,children:[(0,z.jsx)(`th`,{className:`py-2 pr-3 font-medium`,children:`Page`}),V.map(e=>(0,z.jsx)(`th`,{className:`px-2 py-2 text-right font-medium`,children:e.label},e.key)),(0,z.jsx)(`th`,{className:`py-2 pl-2 text-right font-medium`,children:`Requests`})]})}),(0,z.jsx)(`tbody`,{children:t.performance.pages.map((e,t)=>(0,z.jsxs)(`tr`,{className:`border-t border-line-soft align-top`,children:[(0,z.jsxs)(`td`,{className:`py-2 pr-3`,children:[(0,z.jsx)(`span`,{className:`block font-mono text-[12px] [overflow-wrap:anywhere]`,children:K(e.url)}),e.over.length>0&&(0,z.jsx)(`span`,{className:`text-[12px] text-fail`,children:e.over.join(`, `)})]}),V.map(t=>{let i=e[t.key];return(0,z.jsxs)(`td`,{className:d(`px-2 py-2 text-right tabular-nums`,typeof i==`number`&&t.good!==1/0?r[n(i,t)]:typeof i==`number`?`text-ink-2`:`text-muted`),children:[typeof i==`number`?W(i,t.unit):`–`,typeof i==`number`&&t.good!==1/0&&(0,z.jsxs)(`span`,{className:`sr-only`,children:[` (`,n(i,t),`)`]})]},t.key)}),(0,z.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,a]=(0,R.useState)(``),[o,s]=(0,R.useState)(!1);return(0,R.useEffect)(()=>{let r=!1;return a(``),s(!1),m.image(e,t,n).then(e=>!r&&a(e),()=>!r&&s(!0)),()=>{r=!0}},[e,t,n]),(0,z.jsxs)(`figure`,{className:`m-0 flex min-w-0 flex-col gap-1.5`,children:[(0,z.jsx)(`figcaption`,{className:`text-[12px] font-medium text-muted`,children:r}),i?(0,z.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,z.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:o?(0,z.jsxs)(z.Fragment,{children:[(0,z.jsx)(N,{size:14,"aria-hidden":`true`}),` Not available`]}):`Loading…`})]})}function ne({run:e,budget:t,onChange:n}){let[r,i]=(0,R.useState)(null),[a,s]=(0,R.useState)(``),[l,u]=(0,R.useState)(!1),[f,p]=(0,R.useState)(``),[h,g]=(0,R.useState)(null),_=e.visual?.pages??[],y=_.find(e=>e.status===`changed`)?.index??null;(0,R.useEffect)(()=>g(y),[e.id,y]);let b=async e=>{u(!0),p(``);try{n(await e()),i(null),s(``)}catch(e){p(e.message)}finally{u(!1)}};if(!e.visual)return e.captured?(0,z.jsxs)(`div`,{className:`flex flex-col items-start gap-3`,children:[(0,z.jsxs)(`p`,{className:`m-0 text-sm text-ink-2`,children:[`This run captured `,G(e.captured,`page`),` but they were not compared with the baselines yet.`]}),(0,z.jsxs)(o,{variant:`primary`,disabled:l,onClick:()=>b(()=>m.compare(e.id)),children:[(0,z.jsx)(L,{size:16,"aria-hidden":`true`}),`Compare with the baselines`]}),f&&(0,z.jsx)(`p`,{className:`m-0 text-sm text-fail`,children:f})]}):(0,z.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 x=_.filter(e=>e.status===`changed`);return(0,z.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[(0,z.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,z.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. `,t?`A page over quality.budgets.visual fails the run.`:`No visual budget set: a change does not fail the run (quality.budgets.visual).`]}),x.length>1&&(0,z.jsxs)(o,{disabled:l,onClick:()=>i(x.map(e=>e.url)),children:[`Approve all `,x.length,` changes`]})]}),r&&(0,z.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,z.jsxs)(`span`,{className:`font-medium`,children:[`Make this run's capture the baseline of `,r.length===1?K(r[0]):G(r.length,`page`),`?`]}),(0,z.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,z.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm`,children:[`Why it changed (kept with the baseline)`,(0,z.jsx)(C,{value:a,maxLength:300,placeholder:`New checkout banner`,onChange:e=>s(e.target.value),className:`max-w-[480px] bg-bg`})]}),(0,z.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,z.jsxs)(o,{variant:`primary`,disabled:l,onClick:()=>b(()=>m.approve(e.id,r,a.trim()||void 0)),children:[(0,z.jsx)(c,{size:16,"aria-hidden":`true`}),`Approve`]}),(0,z.jsx)(o,{onClick:()=>i(null),children:`Cancel`})]})]}),f&&(0,z.jsx)(`p`,{className:`m-0 text-sm text-fail`,children:f}),(0,z.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:_.map(t=>(0,z.jsxs)(`li`,{className:`border-t border-line-soft first:border-t-0`,children:[(0,z.jsxs)(`button`,{type:`button`,"aria-expanded":h===t.index,onClick:()=>g(h===t.index?null:t.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,z.jsx)(F,{size:16,"aria-hidden":`true`,className:`flex-none text-muted`}),(0,z.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,z.jsx)(`span`,{className:`text-sm [overflow-wrap:anywhere]`,children:t.title||K(t.url)}),(0,z.jsxs)(`span`,{className:`truncate text-[12px] text-muted`,children:[K(t.url),t.size?` · ${t.size}`:``,t.resized?` · resized ${t.resized}`:``]})]}),t.status===`changed`&&(0,z.jsxs)(`span`,{className:d(`text-sm tabular-nums`,t.over?`text-fail`:`text-warn`),children:[t.diffPercent,`%`,t.limit===void 0?``:` / ${t.limit}%`]}),(0,z.jsx)(v,{tone:t.over?`fail`:H[t.status].tone,children:t.over?`Over budget`:H[t.status].label})]}),h===t.index&&(0,z.jsxs)(`div`,{className:`mb-4 flex flex-col gap-3 px-1`,children:[(0,z.jsxs)(`div`,{className:`grid grid-cols-[repeat(auto-fit,minmax(160px,1fr))] gap-3`,children:[t.images.baseline&&(0,z.jsx)(Q,{run:e.id,index:t.index,which:`baseline`,label:`Baseline`}),t.images.current&&(0,z.jsx)(Q,{run:e.id,index:t.index,which:`current`,label:`This run`}),t.images.diff&&(0,z.jsx)(Q,{run:e.id,index:t.index,which:`diff`,label:`Difference (in red)`})]}),t.status===`changed`&&(0,z.jsxs)(o,{className:`self-start`,disabled:l,onClick:()=>i([t.url]),children:[(0,z.jsx)(c,{size:16,"aria-hidden":`true`}),`Approve as the new baseline`]})]})]},t.index))})]})}function re({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,z.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,z.jsx)(`polyline`,{points:n,fill:`none`,stroke:`currentColor`,strokeWidth:`1.5`,strokeLinejoin:`round`})})}function $({icon:e,className:t,lines:n}){return n.length?(0,z.jsxs)(`span`,{className:d(`flex items-start gap-1.5 text-[13px]`,t),children:[(0,z.jsx)(e,{size:14,"aria-hidden":`true`,className:`mt-0.5 flex-none`}),(0,z.jsx)(`span`,{className:`[overflow-wrap:anywhere]`,children:n.join(` · `)})]}):null}function ie({trends:e}){return e.length?(0,z.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:e.map(e=>{let t=e.a11y.latest?B.reduce((t,n)=>t+(e.a11y.latest[n]||0),0):void 0;return(0,z.jsxs)(`li`,{className:`flex flex-col gap-1.5 border-t border-line-soft py-3 first:border-t-0`,children:[(0,z.jsxs)(`span`,{className:`flex min-w-0 flex-wrap items-center gap-3`,children:[(0,z.jsx)(`span`,{className:`min-w-0 flex-[1_1_240px] font-mono text-[13px] [overflow-wrap:anywhere]`,children:e.page}),(0,z.jsx)(re,{values:e.a11y.trail}),(0,z.jsxs)(`span`,{className:`text-[12px] text-muted`,children:[G(e.runs,`run`),t===void 0?``:` · ${G(t,`violation`)} now`]})]}),(0,z.jsx)($,{icon:a,className:`text-fail`,lines:[...e.perf.worse.map(e=>`slower: ${e}`),...e.a11y.newRules.map(e=>`newly failing: ${e}`)]}),(0,z.jsx)($,{icon:i,className:`text-pass`,lines:[...e.perf.better.map(e=>`faster: ${e}`),...e.a11y.fixedRules.map(e=>`fixed: ${e}`)]})]},e.page)})}):(0,z.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 ae({id:t,view:r,onChanged:i}){let[a,u]=(0,R.useState)(null),[f,p]=(0,R.useState)(``),[g,v]=(0,R.useState)(`accessibility`),[y,x]=(0,R.useState)(``);if((0,R.useEffect)(()=>{let e=!1;return u(null),m.run(t).then(t=>{if(e)return;u(t),p(``);let n=t.visual?.pages.some(e=>e.status===`changed`)||!t.visual&&t.captured;v(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&&p(t.message)),()=>{e=!0}},[t]),f)return(0,z.jsx)(`p`,{className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail`,children:f});if(!a)return(0,z.jsx)(`p`,{className:`m-0 text-sm text-muted`,children:`Loading…`});let C=async()=>{try{let{id:e}=await l.start(`Explain the accessibility, performance and visual results of the run in ${a.dir}: call quality_report and visual_compare with run_dir ${a.dir}. Say what matters most to fix first, and why.`,{});location.hash=_(`chats`,e)}catch(e){x(e.message)}},w=a.report,T=w?.accessibility.failed??[],E=B.find(e=>T.some(t=>t.impact===e)),D=w?.performance.pages.filter(e=>e.over.length).length??0,O=a.visual?.pages.filter(e=>e.status===`changed`).length??0,k=e=>!!w?.breaches.some(t=>t.kind===e),A=[{id:`accessibility`,label:`Accessibility`,icon:j,state:w?.accessibility.pages?k(`accessibility`)||E===`critical`?`fail`:T.length?`warn`:`pass`:`none`,headline:w?.accessibility.pages?T.length?`${G(T.length,`rule`)} failed`:`No rule failed`:`Not measured`,note:w?.accessibility.pages?B.filter(e=>w.accessibility.byImpact[e]).map(e=>`${w.accessibility.byImpact[e]} ${e}`).join(` · `)||`${G(w.accessibility.pages,`page`)} checked`:``},{id:`performance`,label:`Performance`,icon:b,state:w?.performance.pages.length?k(`performance`)?`fail`:D?`warn`:`pass`:`none`,headline:w?.performance.pages.length?D?`${G(D,`page`)} over budget`:`Within budget`:`Not measured`,note:w?.performance.pages.length?`${G(w.performance.pages.length,`page`)} timed`:``},{id:`visual`,label:`Visual`,icon:F,state:a.visual?k(`visual`)?`fail`:O?`warn`:`pass`:`none`,headline:a.visual?O?`${G(O,`page`)} changed`:`No change`:a.captured?`Not compared yet`:`Not captured`,note:a.visual?`${G(a.visual.pages.length,`page`)} against the baseline`:a.captured?`${G(a.captured,`page`)} captured`:``}],M={pass:c,warn:h,fail:s,none:e},N={pass:`text-pass`,warn:`text-warn`,fail:`text-fail`,none:`text-muted`};return(0,z.jsxs)(`section`,{"aria-labelledby":`run-h`,className:`flex min-w-0 flex-1 flex-col gap-4`,children:[(0,z.jsxs)(`div`,{className:`flex flex-wrap items-start gap-3`,children:[(0,z.jsxs)(`div`,{className:`flex min-w-0 flex-[1_1_260px] flex-col gap-1`,children:[(0,z.jsx)(`h2`,{id:`run-h`,title:a.goal,className:`m-0 line-clamp-2 text-[20px] leading-snug font-semibold tracking-[-0.01em] [overflow-wrap:anywhere]`,children:a.goal}),(0,z.jsxs)(`span`,{className:`flex flex-wrap items-center gap-x-2 text-[13px] text-muted`,children:[U(a.at),` · `,(0,z.jsx)(n,{path:a.dir})]})]}),(0,z.jsxs)(o,{size:`sm`,variant:`ghost`,onClick:()=>void C(),children:[(0,z.jsx)(S,{size:15,"aria-hidden":`true`}),`Ask in a chat`]})]}),y&&(0,z.jsx)(`p`,{className:`m-0 text-sm text-fail`,children:y}),(0,z.jsx)(`div`,{role:`tablist`,"aria-label":`Results`,className:`grid gap-2 @xl:grid-cols-3`,children:A.map(e=>{let t=M[e.state];return(0,z.jsxs)(`button`,{type:`button`,role:`tab`,id:`tab-${e.id}`,"aria-selected":g===e.id,"aria-controls":`panel-${e.id}`,onClick:()=>v(e.id),className:d(`flex min-w-0 cursor-pointer flex-col gap-1 rounded-[14px] border px-4 py-3 text-left`,g===e.id?`border-accent bg-accent-soft`:`border-line-soft bg-bg hover:bg-surface-muted`),children:[(0,z.jsxs)(`span`,{className:`flex items-center gap-1.5 text-[13px] text-muted`,children:[(0,z.jsx)(e.icon,{size:14,"aria-hidden":`true`}),e.label]}),(0,z.jsxs)(`span`,{className:`flex items-center gap-1.5 text-[16px] font-semibold text-ink`,children:[(0,z.jsx)(t,{size:16,"aria-hidden":`true`,className:d(`flex-none`,N[e.state])}),e.headline]}),e.note&&(0,z.jsx)(`span`,{className:`truncate text-[12.5px] text-muted`,children:e.note})]},e.id)})}),(0,z.jsxs)(`div`,{role:`tabpanel`,id:`panel-${g}`,"aria-labelledby":`tab-${g}`,className:`flex min-w-0 flex-col`,children:[g===`accessibility`&&(0,z.jsx)(Y,{run:a,standard:r.standard}),g===`performance`&&(0,z.jsx)(Z,{run:a}),g===`visual`&&(0,z.jsx)(ne,{run:a,budget:r.budgets.visual,onChange:e=>{u(e),i()}})]}),g===`accessibility`&&a.report?.accessibility.pages?(0,z.jsx)(`p`,{className:`m-0 text-[12px] text-muted`,children:r.disclaimer}):null]})}function oe({sub:e}){let[t,n]=(0,R.useState)(null),[r,i]=(0,R.useState)(``),[a,o]=(0,R.useState)(e===`trends`?`trends`:`runs`),l=(0,R.useCallback)(()=>{m.view().then(e=>{n(e),i(``)},e=>i(e.message))},[]);(0,R.useEffect)(l,[l]);let u=e&&e!==`trends`?decodeURIComponent(e):``;(0,R.useEffect)(()=>{e===`trends`?o(`trends`):e&&o(`runs`)},[e]);let f=(0,R.useMemo)(()=>t?.runs.find(e=>e.id===u)||t?.runs[0],[t,u]),g=t?.trends.filter(e=>e.perf.worse.length||e.a11y.newRules.length).length??0,[v,S]=(0,R.useState)(``),[C,w]=(0,R.useState)(`all`),[A,M]=(0,R.useState)(0);(0,R.useEffect)(()=>M(0),[v,C]);let N=v.toLowerCase().split(/\s+/).filter(Boolean),P=(t?.runs??[]).filter(e=>N.every(t=>e.goal.toLowerCase().includes(t))),F=C===`attention`?P.filter(q):P;return(0,z.jsxs)(`div`,{className:`flex min-h-screen flex-col`,children:[(0,z.jsx)(x,{}),(0,z.jsxs)(`main`,{className:`@container mx-auto box-border flex w-full max-w-[1180px] flex-auto flex-col gap-4 px-6 pt-8 pb-10`,children:[(0,z.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,z.jsx)(p,{icon:b,size:`lg`}),(0,z.jsx)(`h1`,{className:`m-0 flex-1 text-[26px] font-semibold tracking-[-0.01em]`,children:`Quality`})]}),(0,z.jsxs)(`p`,{className:`m-0 max-w-[74ch] text-sm leading-relaxed text-muted`,children:[`What each browser run measured on the pages it visited: accessibility (`,t?.standard||`WCAG`,`), Core Web Vitals, and visual changes against approved baselines. Budgets (quality.budgets) decide what fails a run.`]}),r&&(0,z.jsx)(`p`,{role:`alert`,className:`m-0 rounded-[12px] bg-fail-soft px-3 py-2 text-sm text-fail [overflow-wrap:anywhere]`,children:r}),!t&&!r&&(0,z.jsx)(`p`,{role:`status`,className:`m-0 text-sm text-muted`,children:`Loading…`}),t&&(0,z.jsx)(y,{label:`Quality`,current:a,onPick:e=>{o(e),e===`trends`?location.hash=_(`quality`,`trends`):location.hash=f?_(`quality`,encodeURIComponent(f.id)):_(`quality`)},tabs:[{id:`runs`,label:`Runs (${t.runs.length})`},{id:`trends`,label:`Trends${g?` (${g} worse)`:``}`}]}),t&&a===`trends`&&(0,z.jsx)(`div`,{role:`tabpanel`,id:`panel-trends`,"aria-labelledby":`tab-trends`,children:(0,z.jsx)(ie,{trends:t.trends})}),t&&a===`runs`&&(t.runs.length?(0,z.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,z.jsxs)(`nav`,{"aria-label":`Runs`,className:`flex min-w-0 flex-col gap-2 @2xl:sticky @2xl:top-6 @2xl:w-[290px] @2xl:flex-none`,children:[(0,z.jsx)(E,{id:`quality-search`,label:`Find a run`,value:v,onChange:S,className:`flex-none`}),(0,z.jsx)(`div`,{className:`flex flex-wrap gap-1.5`,children:(0,z.jsx)(D,{items:[{id:`all`,label:`All`,n:P.length},{id:`attention`,label:`Needs a look`,n:P.filter(q).length,alert:!0}],value:C,onChange:w})}),(0,z.jsxs)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:[F.length===0&&(0,z.jsx)(`li`,{className:`px-3 py-3 text-sm text-muted`,children:`Nothing matches.`}),F.slice(A*J,A*J+J).map((e,t,n)=>{let r=O(e.at),i=e.breaches?s:q(e)?h:c;return(0,z.jsxs)(`li`,{className:`contents`,children:[(t===0||O(n[t-1].at)!==r)&&(0,z.jsx)(`span`,{className:`block px-3 pt-2 pb-1 text-[12px] font-semibold text-muted`,children:r}),(0,z.jsxs)(`a`,{href:_(`quality`,encodeURIComponent(e.id)),"aria-current":f?.id===e.id?`page`:void 0,className:d(`flex min-h-12 items-center gap-2.5 rounded-[10px] px-3 py-1.5 text-ink no-underline`,f?.id===e.id?`bg-surface-strong hover:text-ink`:`hover:bg-surface-muted hover:text-ink`),children:[(0,z.jsx)(i,{size:16,"aria-label":e.breaches?`Over a budget`:q(e)?`Needs a look`:`Within budgets`,className:d(`flex-none`,e.breaches?`text-fail`:q(e)?`text-warn`:`text-pass`)}),(0,z.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col`,children:[(0,z.jsx)(`span`,{className:`truncate text-[14px] font-medium`,title:e.goal,children:e.goal}),(0,z.jsx)(`span`,{className:`truncate text-[12px] text-muted`,children:te(e)})]}),(0,z.jsx)(`span`,{className:`flex-none text-[12px] text-muted tabular-nums`,children:k(e.at)})]})]},e.id)})]}),F.length>J&&(0,z.jsx)(T,{page:A,size:J,total:F.length,onPage:M,label:`Pages of runs`})]}),f&&(0,z.jsx)(ae,{id:f.id,view:t,onChanged:l})]}):(0,z.jsxs)(`div`,{className:`flex flex-col items-start gap-3 rounded-[18px] border border-line p-6`,children:[(0,z.jsx)(j,{size:24,"aria-hidden":`true`,className:`text-muted`}),(0,z.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{oe as Quality};
@@ -0,0 +1 @@
1
+ import{n as e,t}from"./rotate-ccw-BizUsxuR.js";import{t as n}from"./markdown-DclrGj2e.js";import{t as r}from"./file-chart-column-increasing-Ch7H145u.js";import{t as i}from"./sticky-note-CFIxnO7K.js";import{t as a}from"./trash-CtWVl2Xk.js";import{A as o,B as s,Ct as c,Et as l,Lt as u,M as d,N as f,O as p,Ot as m,P as h,Pt as g,S as _,St as v,Tt as y,U as b,Wt as x,Xt as S,_ as C,_t as w,at as T,b as E,ct as D,dt as O,i as k,m as A,o as ee,p as j,qt as te,rt as M,x as ne,z as N}from"./index-KUNDUjo0.js";import{n as P,r as re,t as ie}from"./filters-C5e4Y0To.js";import{n as F,t as ae}from"./when-CHnpLdYw.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 oe=g(I),L={name:`dot`,size:24,node:[[`circle`,{cx:`12`,cy:`12`,r:`1`,key:`41hilf`}]]};L.node;var se=g(L),R={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`}]]};R.node;var z=g(R),B={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`}]]};B.node;var V=g(B),H={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`}]]};H.node;var U=g(H),W={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`}]]};W.node;var ce=g(W),G={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`}]]};G.node;var le=g(G),K={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`}]]};K.node;var q=g(K),J=S(te(),1),Y=d(),ue={navigate:O,back:e,click:U,dblclick:U,input:V,press:V,select:D,upload:le,expect:l,wait_for:z,capture:q,note:i,launch:M,double_click:U,type:V,keys:V},de=e=>ue[e]||(e.startsWith(`expect`)?l:se),fe=new Set([`starting`,`recording`,`paused`,`stopping`]);function X(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`}async function Z(e){let{id:t}=await u.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=E(`chats`,t)}async function Q(e){let{id:t}=await u.start(`Replay the desktop workflow ${e}: call desktop_replay with workflow ${e}, and tell me how each step went.`,{});location.hash=E(`chats`,t)}function pe({s:e,live:t,onDelete:n}){let r=de(e.op);return(0,Y.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,Y.jsx)(`span`,{className:`w-6 flex-none pt-0.5 text-right text-[13px] text-muted tabular-nums`,children:e.n}),(0,Y.jsx)(r,{size:16,"aria-hidden":`true`,className:f(`mt-0.5 flex-none`,e.op.startsWith(`expect`)?`text-pass`:`text-muted`)}),(0,Y.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,Y.jsx)(`span`,{className:`text-sm [overflow-wrap:anywhere]`,children:e.text}),(e.note||e.dynamic||e.inferred||e.case_step)&&(0,Y.jsxs)(`span`,{className:`flex flex-wrap gap-x-2 text-[12px] text-muted`,children:[e.case_step?(0,Y.jsxs)(`span`,{children:[`case step `,e.case_step]}):null,e.dynamic&&(0,Y.jsx)(`span`,{children:`value changes between runs`}),e.inferred&&(0,Y.jsx)(`span`,{children:`added by ForgeQE`}),e.note&&(0,Y.jsxs)(`span`,{className:`[overflow-wrap:anywhere]`,children:[`note: `,e.note]})]})]}),t&&e.n>1&&(0,Y.jsx)(o,{variant:`icon`,size:`icon`,"aria-label":`Delete step ${e.n}`,onClick:n,className:`size-8 flex-none opacity-60 group-hover:opacity-100`,children:(0,Y.jsx)(a,{size:15,"aria-hidden":`true`})})]})}function me({s:e,send:t,busy:n}){let[r,a]=(0,J.useState)(``),[,c]=(0,J.useState)(0);(0,J.useEffect)(()=>{let e=setInterval(()=>c(e=>e+1),1e3);return()=>clearInterval(e)},[]);let u=(0,J.useRef)(null);(0,J.useEffect)(()=>{u.current&&(u.current.scrollTop=u.current.scrollHeight)},[e.log.length]);let d=e.kind===`desktop`,m=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)),_=(r,i,a,s)=>(0,Y.jsxs)(o,{disabled:n||e.state===`starting`,onClick:()=>t({cmd:r}),title:s,children:[(0,Y.jsx)(a,{size:16,"aria-hidden":`true`}),i]},r);return(0,Y.jsxs)(`div`,{className:`flex flex-col gap-5`,children:[(0,Y.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:[m?(0,Y.jsx)(`span`,{"aria-hidden":`true`,className:`size-3 flex-none rounded-full bg-warn`}):(0,Y.jsx)(p,{state:e.state===`starting`?`waiting`:e.state===`stopping`?`working`:`recording`,px:20}),(0,Y.jsxs)(`span`,{className:`flex min-w-0 flex-[1_1_260px] flex-col gap-0.5`,children:[(0,Y.jsx)(`span`,{className:`font-medium [overflow-wrap:anywhere]`,children:e.name}),(0,Y.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…`:m?`Paused: what you do now is not recorded`:`Recording`,` · `,X(e.startedAt),` ·`,` `,d?`${e.app}, a desktop app`:e.url]})]}),!d&&(0,Y.jsxs)(o,{disabled:n||e.state===`starting`,onClick:()=>t({cmd:`pause`}),children:[m?(0,Y.jsx)(M,{size:16,"aria-hidden":`true`}):(0,Y.jsx)(ce,{size:16,"aria-hidden":`true`}),m?`Resume`:`Pause`]}),(0,Y.jsxs)(o,{disabled:n||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,Y.jsx)(b,{size:14,fill:`currentColor`,"aria-hidden":`true`}),h?`Stop anyway`:`Stop and save`]})]}),d?(0,Y.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,Y.jsx)(`strong`,{children:`Ctrl+Shift+F11`}),` over a control adds a check of what it shows, and `,(0,Y.jsx)(`strong`,{children:`Ctrl+Shift+F12`}),` or closing the app stops.`]}):(0,Y.jsxs)(Y.Fragment,{children:[(0,Y.jsxs)(`details`,{className:`rounded-[12px] bg-accent-soft px-4 py-2.5 text-sm text-ink-2`,children:[(0,Y.jsx)(`summary`,{className:`cursor-pointer`,children:`Do the steps in the browser on this computer. Keys in the page…`}),(0,Y.jsxs)(`p`,{className:`m-0 mt-2 leading-relaxed`,children:[`The Recorder panel in the page does the same as these buttons; in the page, `,e.leader,` then a letter: `,(0,Y.jsx)(`strong`,{children:`A`}),` check, `,(0,Y.jsx)(`strong`,{children:`V`}),` keep a value, `,(0,Y.jsx)(`strong`,{children:`W`}),` wait for, `,(0,Y.jsx)(`strong`,{children:`D`}),` test data, `,(0,Y.jsx)(`strong`,{children:`N`}),` note,`,` `,(0,Y.jsx)(`strong`,{children:`P`}),` pause, `,(0,Y.jsx)(`strong`,{children:`Z`}),` undo, `,(0,Y.jsx)(`strong`,{children:`S`}),` stop.`]})]}),(0,Y.jsxs)(`div`,{className:`flex flex-wrap gap-2`,role:`toolbar`,"aria-label":`Recorder`,children:[_(`assert`,`Check an element`,oe,`Pick an element in the page to check`),_(`variable`,`Keep a value`,q,`Pick an element whose text later steps use`),_(`wait`,`Wait for`,z,`Pick an element the test waits for`),_(`data`,`Test data`,v,`The last value you typed becomes test data`),_(`note`,`Note`,i,`A note for the step, for whoever reads the test`),_(`undo`,`Undo`,s,`Remove the last step`)]}),(0,Y.jsxs)(`form`,{className:`flex flex-wrap gap-2`,onSubmit:e=>{e.preventDefault(),r.trim()&&(t({cmd:`chat`,text:r.trim()}),a(``))},children:[(0,Y.jsx)(`label`,{htmlFor:`rec-check`,className:`sr-only`,children:`Describe a check`}),(0,Y.jsx)(j,{id:`rec-check`,value:r,maxLength:2e3,placeholder:`Describe a check: the total shows "$40.00", or note: …`,onChange:e=>a(e.target.value),className:`min-w-0 flex-[1_1_320px]`}),(0,Y.jsxs)(o,{type:`submit`,variant:`primary`,disabled:n||!r.trim()||e.state===`starting`,children:[(0,Y.jsx)(N,{size:16,"aria-hidden":`true`}),`Add`]})]})]}),(0,Y.jsxs)(`div`,{className:`flex flex-wrap items-start gap-x-6 gap-y-5`,children:[(0,Y.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,Y.jsxs)(`h2`,{id:`rec-steps`,className:`m-0 flex items-center gap-2 text-base font-semibold`,children:[(0,Y.jsx)(D,{size:18,"aria-hidden":`true`,className:`text-accent`}),`Steps`,(0,Y.jsx)(`span`,{className:`text-sm font-normal text-muted`,children:e.summary})]}),e.steps.length?(0,Y.jsx)(`ol`,{className:`m-0 flex list-none flex-col p-0`,"aria-live":`polite`,children:e.steps.map(e=>(0,Y.jsx)(pe,{s:e,live:!d,onDelete:()=>t({cmd:`delete`,n:e.n})},`${e.n}-${e.text}`))}):(0,Y.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,Y.jsxs)(`div`,{className:`flex min-w-0 flex-[1_1_300px] flex-col gap-5`,children:[e.case&&(0,Y.jsxs)(`section`,{"aria-labelledby":`rec-case`,className:`flex flex-col gap-2 rounded-[14px] border border-line-soft px-4 py-3`,children:[(0,Y.jsxs)(`h2`,{id:`rec-case`,className:`m-0 flex items-center gap-2 text-base font-semibold`,children:[(0,Y.jsx)(D,{size:18,"aria-hidden":`true`,className:`text-accent`}),e.case.id]}),(0,Y.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,Y.jsx)(`ol`,{className:`m-0 flex list-none flex-col p-0`,children:e.case.steps.map(r=>{let i=e.steps.some(e=>e.case_step===r.n);return(0,Y.jsx)(`li`,{className:`border-t border-line-soft first:border-t-0`,children:(0,Y.jsxs)(`button`,{type:`button`,disabled:n,"aria-current":r.n===g?`step`:void 0,onClick:()=>t({cmd:`case_step`,n:r.n}),className:f(`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`,r.n===g&&`bg-accent-soft`),children:[i?(0,Y.jsx)(l,{size:16,"aria-hidden":`true`,className:`mt-0.5 flex-none text-pass`}):(0,Y.jsx)(`span`,{className:`mt-0.5 w-4 flex-none text-center text-[12px] text-muted`,children:r.n}),(0,Y.jsxs)(`span`,{className:`flex min-w-0 flex-col`,children:[(0,Y.jsx)(`span`,{className:`[overflow-wrap:anywhere]`,children:r.action}),r.expected&&(0,Y.jsxs)(`span`,{className:`text-[12px] text-muted [overflow-wrap:anywhere]`,children:[`Expect: `,r.expected]})]})]})},r.n)})}),(0,Y.jsx)(o,{disabled:n,onClick:()=>t({cmd:`case_next`}),className:`self-start`,children:`Next case step`})]}),(0,Y.jsxs)(`section`,{"aria-labelledby":`rec-log`,className:`flex flex-col gap-2 rounded-[14px] border border-line-soft px-4 py-3`,children:[(0,Y.jsx)(`h2`,{id:`rec-log`,className:`m-0 text-base font-semibold`,children:`What the recorder says`}),(0,Y.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,Y.jsx)(`p`,{className:f(`m-0 [overflow-wrap:anywhere]`,e.level===`warn`?`text-warn`:e.level===`error`?`text-fail`:`text-ink-2`),children:e.text},t)):(0,Y.jsxs)(`p`,{className:`m-0 text-muted`,children:[`Waiting for `,d?`the app`:`the browser`,`…`]})})]})]})]})]})}function he({s:e}){let i=e.result,[a,s]=(0,J.useState)(``),[c,l]=(0,J.useState)(!1);if(!i)return null;let u=e.kind===`desktop`;return(0,Y.jsxs)(`section`,{"aria-labelledby":`rec-result`,className:`flex flex-col gap-3 rounded-[14px] border border-line-soft p-4`,children:[(0,Y.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,Y.jsx)(`h2`,{id:`rec-result`,className:`m-0 flex-1 text-base font-semibold [overflow-wrap:anywhere]`,children:e.name}),(0,Y.jsx)(k,{tone:i.ok?`pass`:`fail`,children:i.ok?`Recorded`:`Not recorded`})]}),(0,Y.jsxs)(`p`,{className:`m-0 text-sm text-ink-2`,children:[i.ok?i.summary||e.summary:i.error||`Nothing was recorded.`,` `,e.endedAt&&(0,Y.jsxs)(`span`,{className:`text-muted`,children:[`· `,X(e.startedAt,e.endedAt)]})]}),i.ok&&(0,Y.jsxs)(`div`,{className:`flex flex-wrap gap-2 text-sm`,children:[(0,Y.jsx)(k,{tone:`neutral`,icon:!1,children:h(e.steps.length,`step`)}),(0,Y.jsxs)(k,{tone:i.checks?`pass`:`warn`,children:[i.checks,` check`,i.checks===1?``:`s`]}),i.variables.length>0&&(0,Y.jsxs)(k,{tone:`neutral`,icon:!1,children:[`kept: `,i.variables.join(`, `)]}),i.testData.length>0&&(0,Y.jsxs)(k,{tone:`neutral`,icon:!1,children:[`test data: `,i.testData.join(`, `)]})]}),!u&&i.secrets.length>0&&(0,Y.jsxs)(`p`,{className:`m-0 text-sm text-ink-2`,children:[`The test reads`,` `,i.secrets.map((e,t)=>(0,Y.jsxs)(`span`,{children:[t?`, `:``,(0,Y.jsx)(`code`,{className:`font-mono text-[13px]`,children:e})]},e)),` `,`from the environment: what you typed there was not saved. Set `,i.secrets.length===1?`it`:`them`,` before the test runs.`]}),i.caseGaps.length>0&&(0,Y.jsx)(`ul`,{className:`m-0 flex flex-col gap-1 pl-5 text-sm text-warn`,children:i.caseGaps.map(e=>(0,Y.jsxs)(`li`,{children:[`No check for case step `,e.n,`: expects “`,e.expected,`”`]},e.n))}),(i.notes??[]).length>0&&(0,Y.jsx)(`ul`,{className:`m-0 flex flex-col gap-1 pl-5 text-sm text-ink-2`,children:i.notes.map((e,t)=>(0,Y.jsx)(`li`,{children:e},t))}),i.skipped.length>0&&(0,Y.jsx)(`ul`,{className:`m-0 flex flex-col gap-1 pl-5 text-sm text-muted`,children:i.skipped.map((e,t)=>(0,Y.jsxs)(`li`,{children:[`Not recorded: `,e.action,`: `,e.reason]},t))}),(0,Y.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[u&&i.workflow&&(0,Y.jsxs)(o,{variant:`primary`,disabled:c,onClick:async()=>{l(!0);try{await Q(i.workflow)}catch(e){s(e.message),l(!1)}},children:[(0,Y.jsx)(t,{size:16,"aria-hidden":`true`}),`Replay it`]}),u&&i.report&&(0,Y.jsxs)(o,{onClick:async()=>s(await C(i.report)),children:[(0,Y.jsx)(r,{size:16,"aria-hidden":`true`}),`Open the report`]}),!u&&i.ok&&(0,Y.jsxs)(o,{variant:`primary`,disabled:c,onClick:async()=>{l(!0);try{await Z(e.runDir)}catch(e){s(e.message),l(!1)}},children:[(0,Y.jsx)(N,{size:16,"aria-hidden":`true`}),`Write a test from it`]}),!u&&i.ok&&(0,Y.jsxs)(o,{onClick:async()=>s(await C(`${e.runDir}/trace.md`)),children:[(0,Y.jsx)(w,{size:16,"aria-hidden":`true`}),`Read the steps`]}),(0,Y.jsx)(`span`,{className:`text-sm`,children:(0,Y.jsx)(n,{path:u&&i.workflow?i.workflow:e.runDir})}),a&&(0,Y.jsx)(`span`,{className:`text-[13px] text-fail`,children:a})]})]})}function ge({view:e,busy:t,onStart:n}){let[r,i]=(0,J.useState)(e.session?.kind===`desktop`&&e.desktop.available?`desktop`:`web`),[a,s]=(0,J.useState)(e.desktop.apps[0]??``),[c,l]=(0,J.useState)(e.apps[0]?.name??``),[u,d]=(0,J.useState)(``),[p,h]=(0,J.useState)(``),[g,_]=(0,J.useState)(``),[v,b]=(0,J.useState)(e.environment??``),[x,S]=(0,J.useState)(!1),[C,w]=(0,J.useState)(!1),D=r===`desktop`?e.desktop.available&&!!a:c?!0:/^https?:\/\/\S+$/i.test(u.trim()),k=r===`web`||e.environments.length>1;return(0,Y.jsxs)(`form`,{"aria-labelledby":`rec-new`,className:`flex flex-col gap-3 rounded-[14px] border border-line-soft bg-bg p-4`,onSubmit:t=>{if(t.preventDefault(),D){if(r===`desktop`)return n({kind:`desktop`,app:a,...p.trim()?{name:p.trim()}:{},...v&&v!==e.environment?{env:v}:{}});n({...c?{app:c}:{url:u.trim()},...p.trim()?{name:p.trim()}:{},...g.trim()?{case:g.trim()}:{},...v&&v!==e.environment?{env:v}:{},signedIn:x})}},children:[(0,Y.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,Y.jsx)(`h2`,{id:`rec-new`,className:`m-0 flex-1 text-base font-semibold`,children:`New recording`}),(0,Y.jsx)(`div`,{role:`radiogroup`,"aria-label":`What to record`,className:`flex gap-1 rounded-full bg-surface-strong p-1`,children:[[`web`,`Web page`,O,!0],[`desktop`,`Desktop app`,T,e.desktop.available]].map(([t,n,a,o])=>(0,Y.jsxs)(`button`,{type:`button`,role:`radio`,"aria-checked":r===t,disabled:!o,title:o?void 0:e.desktop.reason||`Windows only.`,onClick:()=>i(t),className:f(`inline-flex min-h-8 cursor-pointer items-center gap-1.5 rounded-full border-0 px-3 text-sm font-medium disabled:cursor-not-allowed disabled:opacity-50`,r===t?`bg-bg text-ink shadow-sm`:`bg-transparent text-muted hover:text-ink`),children:[(0,Y.jsx)(a,{size:14,"aria-hidden":`true`}),n]},t))})]}),(0,Y.jsxs)(`div`,{className:`grid gap-2 @2xl:grid-cols-[minmax(0,1.2fr)_minmax(0,1fr)_auto]`,children:[r===`desktop`?e.desktop.apps.length?(0,Y.jsx)(A,{"aria-label":`App`,value:a,onChange:e=>s(e.target.value),children:e.desktop.apps.map(e=>(0,Y.jsx)(`option`,{value:e,children:e},e))}):(0,Y.jsxs)(`p`,{className:`m-0 self-center text-sm text-muted`,children:[`No desktop apps yet: add one in `,(0,Y.jsx)(`a`,{href:E(`settings`,`environments`),children:`Settings, Environments`}),`.`]}):(0,Y.jsxs)(`div`,{className:`flex min-w-0 gap-2`,children:[e.apps.length>0&&(0,Y.jsxs)(A,{"aria-label":`Start on`,value:c,onChange:e=>l(e.target.value),className:f(c?`min-w-0 flex-1`:`w-[150px] flex-none`),children:[e.apps.map(e=>(0,Y.jsxs)(`option`,{value:e.name,children:[e.name,` · `,e.url]},e.name)),(0,Y.jsx)(`option`,{value:``,children:`Another page…`})]}),!c&&(0,Y.jsx)(j,{"aria-label":`Page address`,type:`url`,value:u,spellCheck:!1,placeholder:`https://shop.example.com/`,onChange:e=>d(e.target.value),className:`min-w-0 flex-1`})]}),(0,Y.jsx)(j,{"aria-label":`What the test does`,value:p,maxLength:200,placeholder:`What the test does`,onChange:e=>h(e.target.value)}),(0,Y.jsxs)(o,{type:`submit`,variant:`primary`,disabled:t||!D,children:[(0,Y.jsx)(y,{size:16,"aria-hidden":`true`}),t?r===`desktop`?`Starting…`:`Opening…`:`Start recording`]})]}),k&&(0,Y.jsxs)(`button`,{type:`button`,"aria-expanded":C,onClick:()=>w(!C),className:`inline-flex cursor-pointer items-center gap-1 self-start border-0 bg-transparent p-0 text-[13px] text-muted hover:text-ink`,children:[(0,Y.jsx)(m,{size:14,"aria-hidden":`true`,className:f(`transition-transform`,C&&`rotate-180`)}),`More options`,!C&&(g||x||v&&v!==e.environment)?(0,Y.jsx)(`span`,{className:`text-accent`,children:`· set`}):null]}),C&&(0,Y.jsxs)(`div`,{className:`grid gap-3 rounded-[12px] bg-surface-muted p-3 @2xl:grid-cols-2`,children:[r===`web`&&(0,Y.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium`,children:[`Test case `,(0,Y.jsx)(`span`,{className:`-mt-1 text-[12.5px] font-normal text-muted`,children:`Its steps are listed while you record; each one you do is tied to it.`}),(0,Y.jsx)(j,{value:g,spellCheck:!1,placeholder:`SHOP-12, ado:1234 or a file`,onChange:e=>_(e.target.value),className:`bg-bg`})]}),e.environments.length>1&&(r===`desktop`||!!c)&&(0,Y.jsxs)(`label`,{className:`flex flex-col gap-1.5 text-sm font-medium`,children:[`Environment`,(0,Y.jsx)(A,{value:v,onChange:e=>b(e.target.value),children:e.environments.map(e=>(0,Y.jsx)(`option`,{value:e,children:e},e))})]}),r===`web`&&e.logins.length>0&&(0,Y.jsxs)(`div`,{className:`flex items-center gap-3 @2xl:col-span-2`,children:[(0,Y.jsx)(ne,{checked:x,onChange:S,label:`Start signed in`}),(0,Y.jsxs)(`span`,{className:`flex flex-col`,children:[(0,Y.jsx)(`span`,{className:`text-sm font-medium`,children:`Start signed in`}),(0,Y.jsxs)(`span`,{className:`text-[12.5px] text-muted`,children:[`With the saved sign-ins (`,e.logins.join(`, `),`). Off: the sign-in is recorded too, its password saved as a variable name, never as typed.`]})]})]})]}),(0,Y.jsx)(`span`,{className:`text-[12.5px] text-muted`,children:r===`desktop`?`The app starts on this computer; do the steps in it. It is saved as a workflow that replays with no model.`:`A browser opens on this computer with the Recorder panel in the page. A chat turns the recording into a test.`})]})}var $=20;function _e({rows:e}){let[n,i]=(0,J.useState)(``),[a,s]=(0,J.useState)(``),[u,d]=(0,J.useState)(`all`),[f,p]=(0,J.useState)(0);if((0,J.useEffect)(()=>p(0),[a,u]),!e.length)return null;let m=a.toLowerCase().split(/\s+/).filter(Boolean),g=e.filter(e=>m.every(t=>`${e.name} ${e.caseId||``} ${e.app||``} ${e.startUrl}`.toLowerCase().includes(t))),_=u===`all`?g:g.filter(e=>e.kind===u),v=_.slice(f*$,f*$+$),y=e.some(e=>e.kind===`desktop`)&&e.some(e=>e.kind===`web`);return(0,Y.jsxs)(`section`,{"aria-labelledby":`rec-list`,className:`flex min-w-0 flex-col gap-2`,children:[(0,Y.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,Y.jsxs)(`h2`,{id:`rec-list`,className:`m-0 mr-2 text-base font-semibold`,children:[`Recordings `,(0,Y.jsx)(`span`,{className:`font-normal text-muted tabular-nums`,children:e.length})]}),(0,Y.jsx)(re,{id:`rec-search`,label:`Find a recording`,value:a,onChange:s}),y&&(0,Y.jsx)(ie,{items:[{id:`all`,label:`All`,n:g.length},{id:`web`,label:`Web`,n:g.filter(e=>e.kind===`web`).length},{id:`desktop`,label:`Desktop`,n:g.filter(e=>e.kind===`desktop`).length}],value:u,onChange:d})]}),n&&(0,Y.jsx)(`p`,{className:`m-0 text-[13px] text-fail`,children:n}),(0,Y.jsxs)(`div`,{className:`rounded-[14px] border border-line-soft bg-bg`,children:[(0,Y.jsxs)(`ul`,{className:`m-0 list-none p-1.5`,children:[v.length===0&&(0,Y.jsx)(`li`,{className:`px-3 py-4 text-sm text-muted`,children:`Nothing matches.`}),v.map((e,n)=>{let a=F(e.startedAt);return(0,Y.jsxs)(`li`,{className:`contents`,children:[(n===0||F(v[n-1].startedAt)!==a)&&(0,Y.jsx)(`span`,{className:`block px-3 pt-3 pb-1 text-[12px] font-semibold text-muted`,children:a}),(0,Y.jsxs)(`div`,{className:`grid min-h-12 grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-x-3 rounded-[10px] px-3 py-1.5 hover:bg-surface-muted`,children:[e.ok?(0,Y.jsx)(l,{size:16,"aria-label":`Recorded`,className:`text-pass`}):(0,Y.jsx)(c,{size:16,"aria-label":`Not recorded`,className:`text-fail`}),(0,Y.jsxs)(`span`,{className:`flex min-w-0 flex-col`,children:[(0,Y.jsxs)(`span`,{className:`flex min-w-0 items-center gap-1.5`,children:[e.kind===`desktop`?(0,Y.jsx)(T,{size:13,"aria-label":`Desktop app`,className:`flex-none text-muted`}):(0,Y.jsx)(O,{size:13,"aria-label":`Web page`,className:`flex-none text-muted`}),(0,Y.jsx)(`span`,{className:`truncate text-[14px] font-medium`,title:e.name,children:e.name})]}),(0,Y.jsx)(`span`,{className:`truncate text-[12px] text-muted`,children:[h(e.steps,`step`),h(e.checks,`check`),e.caseId,e.app,ae(e.startedAt)].filter(Boolean).join(` · `)})]}),(0,Y.jsxs)(`span`,{className:`flex flex-none items-center gap-1`,children:[e.trace&&(0,Y.jsx)(o,{size:`icon`,variant:`icon`,"aria-label":`Read the steps of ${e.name}`,title:`Read the steps`,onClick:async()=>i(await C(e.trace)),children:(0,Y.jsx)(w,{size:15,"aria-hidden":`true`})}),e.report&&(0,Y.jsx)(o,{size:`icon`,variant:`icon`,"aria-label":`Open the report of ${e.name}`,title:`Open the report`,onClick:async()=>i(await C(e.report)),children:(0,Y.jsx)(r,{size:15,"aria-hidden":`true`})}),e.kind===`desktop`&&e.workflow&&(0,Y.jsxs)(o,{size:`sm`,onClick:()=>Q(e.workflow).catch(e=>i(e.message)),children:[(0,Y.jsx)(t,{size:14,"aria-hidden":`true`}),(0,Y.jsx)(`span`,{className:`hidden @md:inline`,children:`Replay`})]}),e.kind===`web`&&e.ok&&(0,Y.jsxs)(o,{size:`sm`,onClick:()=>Z(e.runDir).catch(e=>i(e.message)),children:[(0,Y.jsx)(N,{size:14,"aria-hidden":`true`}),(0,Y.jsx)(`span`,{className:`hidden @md:inline`,children:`Write a test`})]})]})]})]},e.runDir)})]}),(0,Y.jsx)(P,{page:f,size:$,total:_.length,onPage:p,label:`Pages of recordings`})]})]})}function ve(){let[e,t]=(0,J.useState)(null),[n,r]=(0,J.useState)(``),[i,a]=(0,J.useState)(!1),o=(0,J.useCallback)(()=>{x.get().then(e=>{t(e),r(``)},e=>r(e.message))},[]);(0,J.useEffect)(o,[o]);let s=!!e?.session&&fe.has(e.session.state);(0,J.useEffect)(()=>{if(!s)return;let e=setInterval(o,800);return()=>clearInterval(e)},[s,o]);let c=async e=>{a(!0),r(``);try{t(await e())}catch(e){r(e.message)}finally{a(!1)}},l=e?.session;return(0,Y.jsxs)(`div`,{className:`flex min-h-screen flex-col`,children:[(0,Y.jsx)(ee,{}),(0,Y.jsxs)(`main`,{className:`@container mx-auto box-border flex w-full max-w-[1100px] flex-auto flex-col gap-4 px-6 pt-8 pb-10`,children:[(0,Y.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,Y.jsx)(_,{icon:y,size:`lg`}),(0,Y.jsx)(`h1`,{className:`m-0 text-[26px] font-semibold tracking-[-0.01em]`,children:`Record`})]}),(0,Y.jsx)(`p`,{className:`m-0 max-w-[74ch] text-sm leading-relaxed text-muted`,children:`Do a test's steps yourself in a browser or a Windows app ForgeQE opens; each click, entry and check you add is recorded in order.`}),n&&(0,Y.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,Y.jsx)(`p`,{role:`status`,className:`m-0 text-sm text-muted`,children:`Loading…`}),e&&l&&s&&(0,Y.jsx)(me,{s:l,busy:i,send:e=>void c(()=>x.command(e))}),e&&!s&&(0,Y.jsxs)(Y.Fragment,{children:[l?.result&&(0,Y.jsx)(he,{s:l}),(0,Y.jsx)(ge,{view:e,busy:i,onStart:e=>void c(()=>x.start(e))}),(0,Y.jsx)(_e,{rows:e.recordings})]})]})]})}export{ve as Record};
@@ -0,0 +1,2 @@
1
+ import{t as e}from"./external-link-B9T-hxHX.js";import{t}from"./sticky-note-CFIxnO7K.js";import{A as n,H as r,Ht as i,M as a,N as o,O as s,P as c,Pt as l,S as u,U as d,Xt as f,b as p,c as m,ft as h,g,i as _,it as v,o as y,p as b,qt as x,rt as S,x as C}from"./index-KUNDUjo0.js";var w={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`}]]};w.node;var T=l(w),E={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`}]]};E.node;var D=l(E),O={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`]};O.node;var k=l(O),A={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`}]]};A.node;var j=l(A),M=f(x(),1),N=a(),P={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`}},F={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.`}},I={work_item:r,pr:h,commit:k,note:t},L=e=>new Date(e).toLocaleString([],{month:`short`,day:`numeric`,hour:`numeric`,minute:`2-digit`}),R=e=>e?e.status===`passed`?`pass`:e.status===`failed`?`fail`:`warn`:`neutral`,z=e=>e<60?`${Math.round(e)}s`:`${Math.floor(e/60)}m ${Math.round(e%60)}s`,B=e=>e&&/^https?:\/\//i.test(e)?e:void 0;function V({job:e,onStop:t}){let r=(0,M.useRef)(null),[,i]=(0,M.useState)(0);(0,M.useEffect)(()=>{if(!e.running)return;let t=setInterval(()=>i(e=>e+1),1e3);return()=>clearInterval(t)},[e.running]),(0,M.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,N.jsxs)(`section`,{"aria-label":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:a}),(0,N.jsx)(_,{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,N.jsx)(`span`,{className:`text-[13px] text-muted tabular-nums`,children:z(o)}),e.running&&(0,N.jsxs)(n,{onClick:t,className:`border-fail text-fail hover:bg-fail-soft`,children:[(0,N.jsx)(d,{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: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 H({title:e,aside:t,children:n}){return(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.jsx)(`h3`,{className:`m-0 flex-1 text-base font-semibold`,children:e}),t]}),n]})}function U(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 W({name:r,busy:a,act:s,version:c}){let[l,u]=(0,M.useState)(null),[d,f]=(0,M.useState)(``),[p,h]=(0,M.useState)(`all`),[g,v]=(0,M.useState)(null),[y,b]=(0,M.useState)(new Set);if((0,M.useEffect)(()=>{let e=!1;return i.detail(r).then(t=>!e&&(u(t),f(``)),t=>!e&&f(t.message)),()=>{e=!0}},[r,c]),(0,M.useEffect)(()=>{h(`all`),v(null),b(new Set)},[r]),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 x=l.runs.at(-1),{fresh:C,fixed:w}=U(l.runs),E=l.tests.filter(e=>p===`all`||e.reasons.some(e=>e.kind===p)),O=(l.triage?.items??[]).filter(e=>e.route===`heal`||e.route===`check`),k=Object.keys(F).filter(e=>l.triage?.items.some(t=>t.route===e)),A=Object.keys(P).filter(e=>l.tests.some(t=>t.reasons.some(t=>t.kind===e)));return(0,N.jsxs)(`section`,{"aria-labelledby":`pack-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:`pack-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`,children:[`Built `,L(l.createdAt)]}),l.scope.length>0&&(0,N.jsx)(`span`,{className:`flex flex-wrap gap-1.5 pt-1`,children:l.scope.map(e=>(0,N.jsx)(`span`,{className:`rounded-full bg-surface-strong px-2.5 py-0.5 text-[12px] text-ink-2`,children:e},e))})]}),g?(0,N.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2 rounded-[12px] bg-accent-soft px-3 py-2`,children:[(0,N.jsxs)(`span`,{className:`text-sm`,children:[`Run `,l.tests.length,` test`,l.tests.length===1?``:`s`,` `,g===`local`?`on this machine`:`on TestMu AI`,`?`]}),(0,N.jsx)(n,{variant:`primary`,disabled:a,onClick:()=>{v(null),s(()=>i.run(l.name,g))},children:`Run`}),(0,N.jsx)(n,{onClick:()=>v(null),children:`Don't run`})]}):(0,N.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,N.jsxs)(n,{variant:`primary`,disabled:a||!l.tests.length,onClick:()=>v(`local`),children:[(0,N.jsx)(S,{size:16,"aria-hidden":`true`}),`Run here`]}),(0,N.jsx)(n,{disabled:a||!l.tests.length,onClick:()=>v(`testmu`),children:`Run on TestMu AI`})]})]}),(0,N.jsxs)(`div`,{className:`flex flex-wrap gap-3`,children:[(0,N.jsx)(m,{label:`Tests`,value:String(l.tests.length),note:A.map(e=>`${l.tests.filter(t=>t.reasons.some(t=>t.kind===e)).length} ${P[e].label.toLowerCase()}`).join(` · `)}),(0,N.jsx)(m,{label:`Not covered`,value:String(l.uncovered.length),note:`changes no test is in for`}),(0,N.jsx)(m,{label:`Risk areas`,value:String(l.risk.length)}),(0,N.jsx)(m,{label:`Last run`,value:x?x.status===`passed`?`Passed`:`${x.counts.failed} failed`:`–`,note:x?`${L(x.at)} · ${x.on===`testmu`?`TestMu AI`:x.on===`pipeline`?`pipeline`:`this machine`}`:`Not run yet`})]}),l.triage&&(0,N.jsxs)(H,{title:`Triage of the last run`,aside:(0,N.jsxs)(`span`,{className:`text-[13px] text-muted`,children:[l.triage.items.length,` failed or flaky`]}),children:[l.triage.environmentDown&&(0,N.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,N.jsxs)(`div`,{className:`flex flex-col gap-1 rounded-[12px] border border-line-soft px-4 py-3`,children:[(0,N.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,N.jsx)(_,{tone:F[e].tone,children:F[e].label}),(0,N.jsx)(`span`,{className:`text-[13px] text-muted`,children:F[e].does})]}),(0,N.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:l.triage.items.filter(t=>t.route===e).map(t=>(0,N.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,N.jsx)(`input`,{type:`checkbox`,"aria-label":`Heal ${t.name}`,checked:y.has(t.name),onChange:e=>{let n=new Set(y);e.target.checked?n.add(t.name):n.delete(t.name),b(n)},className:`mt-1 size-5 accent-accent`}),(0,N.jsxs)(`span`,{className:`flex min-w-0 flex-col gap-0.5`,children:[(0,N.jsx)(`span`,{className:`[overflow-wrap:anywhere]`,children:t.name}),(0,N.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,N.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,N.jsxs)(n,{disabled:a||!y.size,onClick:()=>s(()=>i.heal(l.name,[...y])),children:[(0,N.jsx)(T,{size:16,"aria-hidden":`true`}),`Heal `,y.size||``,` selected`]}),(0,N.jsxs)(n,{variant:`ghost`,disabled:a,onClick:()=>b(new Set(O.map(e=>e.name))),children:[`Select all `,O.length]}),(0,N.jsx)(`span`,{className:`text-[13px] text-muted`,children:`The healing agent re-runs each flow in the browser before it changes anything.`})]})]}),l.runs.length>0&&(0,N.jsxs)(H,{title:`Runs`,aside:C.length||w.length?(0,N.jsxs)(`span`,{className:`text-[13px] text-muted`,children:[[C.length?`${C.length} newly failing`:``,w.length?`${w.length} fixed`:``].filter(Boolean).join(` · `),` since the run before`]}):void 0,children:[(0,N.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:[...l.runs].reverse().map((t,n)=>(0,N.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,N.jsx)(_,{tone:R(t),children:t.status===`passed`?`Passed`:t.status===`failed`?`Failed`:t.status}),(0,N.jsxs)(`span`,{className:`flex-1`,children:[L(t.at),` · `,t.on===`testmu`?`TestMu AI`:t.on===`pipeline`?`pipeline`:`this machine`]}),(0,N.jsxs)(`span`,{className:`text-muted tabular-nums`,children:[t.counts.passed,` passed · `,t.counts.failed,` failed`,t.counts.skipped?` · ${t.counts.skipped} skipped`:``,` · `,z(t.seconds)]}),B(t.url)&&(0,N.jsxs)(`a`,{href:t.url,target:`_blank`,rel:`noopener noreferrer`,className:`inline-flex items-center gap-1`,children:[`Pipeline `,(0,N.jsx)(e,{size:13,"aria-hidden":`true`})]})]},`${t.at}-${n}`))}),C.length>0&&(0,N.jsxs)(`p`,{className:`m-0 text-[13px] text-fail`,children:[`Newly failing: `,C.join(`, `)]}),w.length>0&&(0,N.jsxs)(`p`,{className:`m-0 text-[13px] text-pass`,children:[`Fixed: `,w.join(`, `)]})]}),(0,N.jsxs)(H,{title:`Tests (${l.tests.length})`,aside:(0,N.jsx)(`div`,{role:`group`,"aria-label":`Why in`,className:`flex flex-wrap gap-1`,children:[`all`,...A].map(e=>(0,N.jsx)(`button`,{type:`button`,"aria-pressed":p===e,title:P[e]?.help,onClick:()=>h(e),className:o(`min-h-8 cursor-pointer rounded-full border px-3 text-[13px]`,p===e?`border-ink bg-ink text-bg`:`border-line-soft bg-bg text-ink hover:bg-surface-muted`),children:e===`all`?`All`:P[e].label},e))}),children:[(0,N.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:E.slice(0,300).map(e=>(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 flex-wrap items-center gap-2`,children:[(0,N.jsx)(`span`,{className:`font-medium [overflow-wrap:anywhere]`,children:e.test}),e.flaky&&(0,N.jsx)(_,{tone:`warn`,children:`Flaky`})]}),(0,N.jsxs)(`span`,{className:`font-mono text-[12px] text-muted [overflow-wrap:anywhere]`,children:[e.file,e.line?`:${e.line}`:``]}),(0,N.jsx)(`span`,{className:`flex flex-wrap gap-1.5`,children:e.reasons.map((e,t)=>(0,N.jsxs)(`span`,{className:`inline-flex items-center gap-1.5 text-[13px] text-ink-2`,children:[(0,N.jsx)(_,{tone:P[e.kind]?.tone??`neutral`,icon:!1,children:P[e.kind]?.label??e.kind}),e.text]},t))})]},`${e.file}:${e.test}`))}),E.length>300&&(0,N.jsxs)(`p`,{className:`m-0 text-[13px] text-muted`,children:[`The first 300 of `,E.length,`.`]})]}),l.uncovered.length>0&&(0,N.jsx)(H,{title:`Not covered (${l.uncovered.length})`,aside:(0,N.jsx)(`span`,{className:`text-[13px] text-muted`,children:`Extend the closest test rather than write a new one`}),children:(0,N.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:l.uncovered.map(e=>(0,N.jsxs)(`li`,{className:`flex flex-col gap-0.5 border-t border-line-soft py-2 text-sm first:border-t-0`,children:[(0,N.jsxs)(`span`,{className:`[overflow-wrap:anywhere]`,children:[(0,N.jsx)(`span`,{className:`mr-1.5 font-mono text-[12px] text-muted`,children:e.item}),e.title]}),(0,N.jsx)(`span`,{className:`text-[13px] text-muted`,children:e.why}),e.nearest&&(0,N.jsxs)(`span`,{className:`text-[13px] text-ink-2 [overflow-wrap:anywhere]`,children:[`Closest: `,e.nearest.test,` `,(0,N.jsxs)(`span`,{className:`font-mono text-[12px] text-muted`,children:[`(`,e.nearest.file,`)`]}),e.nearest.lacks.length?`, lacks ${e.nearest.lacks.join(`, `)}`:``]})]},e.item))})}),l.risk.length>0&&(0,N.jsx)(H,{title:`Risk areas`,children:(0,N.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:l.risk.map(e=>(0,N.jsxs)(`li`,{className:`flex items-start gap-3 border-t border-line-soft py-2 text-sm first:border-t-0`,children:[(0,N.jsx)(D,{size:16,"aria-hidden":`true`,className:o(`mt-0.5 flex-none`,e.level===`high`?`text-fail`:`text-warn`)}),(0,N.jsxs)(`span`,{className:`flex flex-col`,children:[(0,N.jsxs)(`span`,{children:[e.area,` `,(0,N.jsxs)(`span`,{className:`text-muted`,children:[`· `,e.level]})]}),(0,N.jsx)(`span`,{className:`text-[13px] text-muted`,children:e.reasons.join(`; `)})]})]},e.area))})}),(0,N.jsx)(H,{title:`Changes (${l.items.length})`,children:(0,N.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:l.items.slice(0,200).map(n=>{let r=I[n.kind]||t;return(0,N.jsxs)(`li`,{className:`flex items-start gap-3 border-t border-line-soft py-2 text-sm first:border-t-0`,children:[(0,N.jsx)(r,{size:16,"aria-hidden":`true`,className:`mt-0.5 flex-none text-muted`}),(0,N.jsxs)(`span`,{className:`min-w-0 flex-1 [overflow-wrap:anywhere]`,children:[(0,N.jsxs)(`span`,{className:`mr-1.5 font-mono text-[12px] text-muted`,children:[n.type?`${n.type} `:``,n.id]}),n.title]}),B(n.url)&&(0,N.jsx)(`a`,{href:n.url,target:`_blank`,rel:`noopener noreferrer`,"aria-label":`Open ${n.id}`,className:`flex-none`,children:(0,N.jsx)(e,{size:14,"aria-hidden":`true`})})]},`${n.kind}-${n.id}`)})})}),l.notes.length>0&&(0,N.jsx)(`ul`,{className:`m-0 flex flex-col gap-1 pl-5 text-[13px] text-muted`,children:l.notes.map((e,t)=>(0,N.jsx)(`li`,{children:e},t))})]})}function G({open:e,onClose:t,view:i,onBuild:a,busy:s}){let[c,l]=(0,M.useState)({rangeRepo:`product`,prRepo:`product`,risk:!0,smoke:!0});(0,M.useEffect)(()=>{e&&l({rangeRepo:i.productRepo?`product`:`tests`,prRepo:i.productRepo?`product`:`tests`,risk:!0,smoke:!0})},[e,i.productRepo]);let u=(e,t)=>l({...c,[e]:t}),d=(e,t,n,r)=>(0,N.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,N.jsx)(`label`,{htmlFor:`pk-${e}`,className:`text-sm font-medium`,children:t}),(0,N.jsx)(b,{id:`pk-${e}`,value:String(c[e]??``),spellCheck:!1,maxLength:200,placeholder:n,onChange:t=>u(e,t.target.value)}),r&&(0,N.jsx)(`span`,{className:`text-[13px] text-muted`,children:r})]}),f=(e,t)=>(0,N.jsxs)(`fieldset`,{className:`m-0 flex flex-wrap items-center gap-x-4 gap-y-1 border-0 p-0`,children:[(0,N.jsx)(`legend`,{className:`mb-1 text-[13px] text-muted`,children:t}),(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:e,checked:c[e]===`product`,disabled:!i.productRepo,onChange:()=>u(e,`product`),className:`size-4 accent-accent`}),`The product's `,i.productRepo?(0,N.jsx)(`code`,{className:`font-mono text-[12px]`,children:i.productRepo}):(0,N.jsx)(`span`,{className:`text-muted`,children:`(set defects.product_repo)`})]}),(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:e,checked:c[e]===`tests`,onChange:()=>u(e,`tests`),className:`size-4 accent-accent`}),`The tests' `,i.repo===`.`?`(this project)`:(0,N.jsx)(`code`,{className:`font-mono text-[12px]`,children:i.repo})]})]}),m=`flex flex-col gap-3 rounded-[12px] border border-line-soft p-3`;return(0,N.jsx)(g,{open:e,onClose:t,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,N.jsxs)(N.Fragment,{children:[(0,N.jsx)(n,{onClick:t,children:`Cancel`}),(0,N.jsxs)(n,{variant:`primary`,disabled:s,onClick:()=>a(c),children:[(0,N.jsx)(j,{size:16,"aria-hidden":`true`}),`Build the pack`]})]}),children:(0,N.jsxs)(`div`,{className:`flex flex-col gap-4 pt-3`,children:[d(`name`,`Name`,`release-1.5`,`Lower-case letters, digits, dots and dashes. Empty: release-<date>.`),(0,N.jsxs)(`div`,{className:m,children:[(0,N.jsxs)(`h3`,{className:`m-0 flex items-center gap-2 text-sm font-semibold`,children:[(0,N.jsx)(k,{size:16,"aria-hidden":`true`,className:`text-accent`}),`Code changes`]}),(0,N.jsxs)(`div`,{className:`grid grid-cols-[repeat(auto-fit,minmax(200px,1fr))] gap-3`,children:[d(`from`,`Commits after`,`release/1.4, a tag or a commit`),d(`to`,`Up to`,`HEAD`)]}),f(`rangeRepo`,`In the repository`),(0,N.jsxs)(`div`,{className:`grid grid-cols-[repeat(auto-fit,minmax(200px,1fr))] gap-3`,children:[d(`prs`,`Pull requests`,`57, 58 or their links`),(0,N.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,N.jsx)(`label`,{htmlFor:`pk-days`,className:`text-sm font-medium`,children:`Or merged in the last`}),(0,N.jsxs)(`span`,{className:`flex items-center gap-2 text-sm`,children:[(0,N.jsx)(b,{id:`pk-days`,type:`number`,min:1,max:365,value:c.mergedDays??``,onChange:e=>u(`mergedDays`,e.target.value?Number(e.target.value):void 0),className:`w-24`}),` days`]})]})]}),(c.prs||c.mergedDays)&&f(`prRepo`,`Pull requests of`)]}),(0,N.jsxs)(`div`,{className:o(m,!i.azureDevops&&`opacity-70`),children:[(0,N.jsxs)(`h3`,{className:`m-0 flex items-center gap-2 text-sm font-semibold`,children:[(0,N.jsx)(r,{size:16,"aria-hidden":`true`,className:`text-accent`}),`Azure DevOps work items and their linked test cases`]}),!i.azureDevops&&(0,N.jsxs)(`p`,{className:`m-0 text-[13px] text-muted`,children:[`Not set up: `,(0,N.jsx)(`a`,{href:p(`settings`,`integrations`),children:`Settings, Integrations`}),`.`]}),(0,N.jsxs)(`div`,{className:`grid grid-cols-[repeat(auto-fit,minmax(200px,1fr))] gap-3`,children:[d(`sprint`,`Sprint`,`current, or Sprint 41`),d(`iteration`,`Iteration path`,`Shop\\Release 2026.09`),d(`area`,`Area path`,`Shop\\Checkout`),d(`parent`,`Epic or feature`,`1200`),d(`workItems`,`Work items`,`2211, 2212`)]}),(0,N.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,N.jsxs)(`div`,{className:`flex flex-wrap gap-x-6 gap-y-2`,children:[(0,N.jsxs)(`span`,{className:`flex items-center gap-2 text-sm`,children:[(0,N.jsx)(C,{checked:c.risk!==!1,onChange:e=>u(`risk`,e),label:`Add tests for risk areas`}),`Add tests for risk areas the release touches`]}),(0,N.jsxs)(`span`,{className:`flex items-center gap-2 text-sm`,children:[(0,N.jsx)(C,{checked:c.smoke!==!1,onChange:e=>u(`smoke`,e),label:`Add smoke tests`}),`Add smoke-tagged tests`]})]})]})})}function K({sub:e}){let[t,r]=(0,M.useState)(null),[a,l]=(0,M.useState)(``),[d,f]=(0,M.useState)(!1),[m,h]=(0,M.useState)(!1),g=(0,M.useCallback)(()=>{i.list().then(e=>{r(e),l(``)},e=>l(e.message))},[]);(0,M.useEffect)(g,[g]);let _=!!t?.job?.running;(0,M.useEffect)(()=>{if(!_)return;let e=setInterval(g,1500);return()=>clearInterval(e)},[_,g]);let b=async e=>{f(!0),l(``);try{await e(),g()}catch(e){l(e.message)}finally{f(!1)}},x=e?decodeURIComponent(e):``,S=t?.packs.find(e=>e.name===x)||t?.packs[0],C=(0,M.useMemo)(()=>`${t?.job?.endedAt??``}|${S?.runs??0}|${S?.createdAt??``}`,[t,S]);return(0,N.jsxs)(`div`,{className:`flex min-h-screen flex-col`,children:[(0,N.jsx)(y,{}),(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:v,size:`lg`}),(0,N.jsx)(`h1`,{className:`m-0 flex-1 text-[26px] font-semibold tracking-[-0.01em]`,children:`Regression`}),(0,N.jsxs)(n,{variant:`primary`,disabled:!t||_,onClick:()=>h(!0),children:[(0,N.jsx)(j,{size:16,"aria-hidden":`true`}),`Build a pack`]})]}),(0,N.jsxs)(`p`,{className:`m-0 max-w-[74ch] text-sm leading-relaxed text-muted`,children:[`A regression pack is the set of tests in `,t&&t.repo!==`.`?(0,N.jsx)(`code`,{className:`font-mono text-[13px]`,children:t.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.`]}),a&&(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:a}),!t&&!a&&(0,N.jsx)(`p`,{role:`status`,className:`m-0 text-sm text-muted`,children:`Loading…`}),t?.job&&(0,N.jsx)(V,{job:t.job,onStop:()=>b(()=>i.stop())}),t&&(t.packs.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":`Packs`,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:[`Packs (`,t.packs.length,`)`]}),(0,N.jsx)(`ul`,{className:`m-0 flex list-none flex-col p-0`,children:t.packs.map(e=>(0,N.jsx)(`li`,{children:(0,N.jsxs)(`a`,{href:p(`regression`,encodeURIComponent(e.name)),"aria-current":S?.name===e.name?`page`:void 0,className:o(`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:[t?.job?.running&&t.job.pack===e.name?(0,N.jsx)(s,{state:t.job.kind===`heal`?`retrying`:`working`,label:`Working on it`,px:16}):(0,N.jsx)(`span`,{className:o(`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,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:[c(e.tests,`test`),` · `,e.uncovered,` not covered · `,L(e.createdAt)]})]})]})},e.name))})]}),S&&(0,N.jsx)(W,{name:S.name,busy:d||_,act:b,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)(v,{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 regression packs yet. Build one from a sprint, the commits since the last release, or the pull requests merged this week.`}),(0,N.jsx)(n,{onClick:()=>h(!0),children:`Build a pack`})]}))]}),t&&(0,N.jsx)(G,{open:m,onClose:()=>h(!1),view:t,busy:d||_,onBuild:e=>b(async()=>{await i.build(e),h(!1)})})]})}export{K as Regression};