diff --git a/apps/fabro-web/app/routes/run-files.test.ts b/apps/fabro-web/app/routes/run-files.test.ts index 3f2c314f6..649258893 100644 --- a/apps/fabro-web/app/routes/run-files.test.ts +++ b/apps/fabro-web/app/routes/run-files.test.ts @@ -1,6 +1,30 @@ -import { describe, expect, test } from "bun:test"; +import { afterEach, describe, expect, test } from "bun:test"; -import { extractRequestId } from "./run-files"; +import { extractRequestId, loader } from "./run-files"; + +type StubResponseInit = { + status: number; + body?: string; + headers?: Record; +}; + +function stubFetchOnce(init: StubResponseInit) { + const original = globalThis.fetch; + globalThis.fetch = (() => { + const response = new Response(init.body ?? "", { + status: init.status, + headers: init.headers, + // `Response` constructor derives statusText from status for known + // codes; providing it explicitly keeps tests deterministic across + // engines that disagree on the default message. + statusText: init.status === 500 ? "Internal Server Error" : "", + }); + return Promise.resolve(response); + }) as typeof fetch; + return () => { + globalThis.fetch = original; + }; +} describe("extractRequestId", () => { test("reads `request_id` from the top level of the error body", () => { @@ -51,3 +75,104 @@ describe("extractRequestId", () => { ).toBe("RX-1A_2B-3C4D"); }); }); + +describe("loader", () => { + let restoreFetch: (() => void) | undefined; + + afterEach(() => { + restoreFetch?.(); + restoreFetch = undefined; + }); + + // Bun's Request constructor needs a URL; tests use a relative path + // because the loader only reads `request?.signal`. + const dummyRequest = { signal: undefined } as any; + const dummyParams = { id: "01ARZ3NDEKTSV4RRFFQ69G5FAV" }; + + test("200 OK returns { data, error: null } with parsed envelope", async () => { + const envelope = { + data: [], + meta: { truncated: false, total_changed: 0 }, + }; + restoreFetch = stubFetchOnce({ + status: 200, + body: JSON.stringify(envelope), + }); + const result = await loader({ request: dummyRequest, params: dummyParams }); + expect(result.error).toBeNull(); + expect(result.data).toEqual(envelope); + }); + + test("404 returns empty-envelope signal { data: null, error: null }", async () => { + restoreFetch = stubFetchOnce({ status: 404 }); + const result = await loader({ request: dummyRequest, params: dummyParams }); + expect(result.data).toBeNull(); + expect(result.error).toBeNull(); + }); + + test("501 returns empty-envelope signal { data: null, error: null }", async () => { + restoreFetch = stubFetchOnce({ status: 501 }); + const result = await loader({ request: dummyRequest, params: dummyParams }); + expect(result.data).toBeNull(); + expect(result.error).toBeNull(); + }); + + test("500 populates error.requestId from the uniform error envelope", async () => { + restoreFetch = stubFetchOnce({ + status: 500, + body: JSON.stringify({ + errors: [ + { + status: "500", + title: "Internal Server Error", + detail: "Run files materialization panicked.", + request_id: "req_deadbeef", + }, + ], + }), + }); + const result = await loader({ request: dummyRequest, params: dummyParams }); + expect(result.data).toBeNull(); + expect(result.error).not.toBeNull(); + expect(result.error!.status).toBe(500); + expect(result.error!.requestId).toBe("req_deadbeef"); + }); + + test("500 with no request_id leaves error.requestId as null", async () => { + restoreFetch = stubFetchOnce({ + status: 500, + body: JSON.stringify({ + errors: [{ status: "500", title: "Internal", detail: "whoops" }], + }), + }); + const result = await loader({ request: dummyRequest, params: dummyParams }); + expect(result.error).not.toBeNull(); + expect(result.error!.status).toBe(500); + expect(result.error!.requestId).toBeNull(); + }); + + test("500 with non-JSON body still surfaces the status", async () => { + restoreFetch = stubFetchOnce({ status: 500, body: "oops" }); + const result = await loader({ request: dummyRequest, params: dummyParams }); + expect(result.error).not.toBeNull(); + expect(result.error!.status).toBe(500); + expect(result.error!.requestId).toBeNull(); + }); + + test("503 populates error without requestId", async () => { + restoreFetch = stubFetchOnce({ + status: 503, + body: JSON.stringify({ errors: [{ detail: "rate limited" }] }), + }); + const result = await loader({ request: dummyRequest, params: dummyParams }); + expect(result.error).not.toBeNull(); + expect(result.error!.status).toBe(503); + }); + + test("401 still surfaces as an error (no in-loader redirect)", async () => { + restoreFetch = stubFetchOnce({ status: 401 }); + const result = await loader({ request: dummyRequest, params: dummyParams }); + expect(result.error).not.toBeNull(); + expect(result.error!.status).toBe(401); + }); +}); diff --git a/apps/fabro-web/app/routes/run-files/states.test.tsx b/apps/fabro-web/app/routes/run-files/states.test.tsx index cfaeb71e3..dd4c0a6d5 100644 --- a/apps/fabro-web/app/routes/run-files/states.test.tsx +++ b/apps/fabro-web/app/routes/run-files/states.test.tsx @@ -15,8 +15,10 @@ function renderToJson(element: React.ReactElement): any { } describe("deriveEmptyKind", () => { - test("submitted / starting / queued map to R4(a) 'starting'", () => { - for (const status of ["submitted", "Submitted", "starting", "queued"]) { + // Pre-work states → R4(a) "starting" + test.each(["submitted", "Submitted", "starting", "queued"])( + "%s maps to R4(a) 'starting'", + (status) => { expect( deriveEmptyKind({ runStatus: status, @@ -24,48 +26,64 @@ describe("deriveEmptyKind", () => { degraded: false, }), ).toBe("starting"); - } - }); + }, + ); - test("failed run without degraded fallback is R4(c1)", () => { - expect( - deriveEmptyKind({ - runStatus: "failed", - totalChanged: 0, - degraded: false, - }), - ).toBe("failed_before_checkpoint"); - }); + // Actively-in-progress states → R4(b) "no_changes yet" + test.each(["running", "blocked", "paused"])( + "%s with no files yet is R4(b) 'no_changes'", + (status) => { + expect( + deriveEmptyKind({ + runStatus: status, + totalChanged: 0, + degraded: false, + }), + ).toBe("no_changes"); + }, + ); - test("succeeded run with changes but no data is R4(c2) 'diff_lost'", () => { - expect( - deriveEmptyKind({ - runStatus: "succeeded", - totalChanged: 3, - degraded: false, - }), - ).toBe("diff_lost"); - }); + // Terminal-failure states → R4(c1) when no degraded patch available + test.each(["failed", "dead"])( + "%s without degraded fallback is R4(c1) 'failed_before_checkpoint'", + (status) => { + expect( + deriveEmptyKind({ + runStatus: status, + totalChanged: 0, + degraded: false, + }), + ).toBe("failed_before_checkpoint"); + }, + ); - test("succeeded run with no changes is R4(b)", () => { - expect( - deriveEmptyKind({ - runStatus: "succeeded", - totalChanged: 0, - degraded: false, - }), - ).toBe("no_changes"); - }); + // Terminal-success + teardown states → R4(b) or R4(c2) depending on + // whether files were ever changed + test.each(["succeeded", "removing"])( + "%s with changes but no data is R4(c2) 'diff_lost'", + (status) => { + expect( + deriveEmptyKind({ + runStatus: status, + totalChanged: 3, + degraded: false, + }), + ).toBe("diff_lost"); + }, + ); - test("running run with no changes is R4(b)", () => { - expect( - deriveEmptyKind({ - runStatus: "running", - totalChanged: 0, - degraded: false, - }), - ).toBe("no_changes"); - }); + test.each(["succeeded", "removing"])( + "%s with no changes is R4(b)", + (status) => { + expect( + deriveEmptyKind({ + runStatus: status, + totalChanged: 0, + degraded: false, + }), + ).toBe("no_changes"); + }, + ); test("missing runStatus collapses to 'unknown'", () => { expect( @@ -76,6 +94,43 @@ describe("deriveEmptyKind", () => { }), ).toBe("unknown"); }); + + test("unknown future status collapses to 'unknown'", () => { + expect( + deriveEmptyKind({ + runStatus: "some_future_state", + totalChanged: 0, + degraded: false, + }), + ).toBe("unknown"); + }); + + test("every documented RunStatus gets a non-unknown empty kind when applicable", () => { + // Regression guard: if a new RunStatus appears in + // `apps/fabro-web/app/data/runs.ts` without a matching branch here, + // the decision table silently returns "unknown" ("not available right + // now") — misleading copy for a cancelled or paused run. + const knownRunStatuses = [ + "submitted", + "queued", + "starting", + "running", + "blocked", + "paused", + "removing", + "succeeded", + "failed", + "dead", + ]; + for (const status of knownRunStatuses) { + const result = deriveEmptyKind({ + runStatus: status, + totalChanged: 0, + degraded: false, + }); + expect(result).not.toBe("unknown"); + } + }); }); describe("emptyStateCopy", () => { diff --git a/apps/fabro-web/app/routes/run-files/states.tsx b/apps/fabro-web/app/routes/run-files/states.tsx index 7b537fd9d..f1da48aba 100644 --- a/apps/fabro-web/app/routes/run-files/states.tsx +++ b/apps/fabro-web/app/routes/run-files/states.tsx @@ -43,8 +43,14 @@ export function emptyStateCopy(kind: EmptyKind): string { } /// Derive the empty-state variant from the full loader context. `runStatus` -/// comes from the parent run loader; its absence collapses to the "unknown" -/// catchall so the empty state never displays misleading copy. +/// comes from the parent run loader (`run.lifecycleStatus`); its absence +/// collapses to the "unknown" catchall so the empty state never displays +/// misleading copy. +/// +/// The full RunStatus enum (per fabro-types/src/status.rs) is: +/// submitted, queued, starting, running, blocked, paused, removing, +/// succeeded, failed, dead +/// partial_success is a stage status, not a run status. export function deriveEmptyKind(args: { runStatus: string | undefined; totalChanged: number; @@ -54,32 +60,41 @@ export function deriveEmptyKind(args: { if (!runStatus) { return "unknown"; } - const normalized = runStatus.toLowerCase(); - if ( - normalized === "submitted" || - normalized === "starting" || - normalized === "queued" - ) { + const s = runStatus.toLowerCase(); + + // Pre-work states: run has no base_sha / hasn't started producing a diff. + if (s === "submitted" || s === "queued" || s === "starting") { return "starting"; } - if (normalized === "failed" && !degraded) { - return "failed_before_checkpoint"; + + // Actively-in-progress states: the run is running but just hasn't + // changed any files yet. Avoid alarmist "diff lost" copy here — the + // user may refresh and see files appear. + if (s === "running" || s === "blocked" || s === "paused") { + return "no_changes"; } - if ( - (normalized === "succeeded" || normalized === "partialsuccess") && - !degraded - ) { - // If the run ran successfully and we still have no diff data, the - // projection's final_patch was never captured (or was lost). R4(c2). - if (totalChanged > 0) { - return "diff_lost"; + + // Terminal-failure states: Failed and Dead both mean the run stopped + // without a clean conclusion. If the degraded-fallback branch also + // couldn't surface a patch, we never captured a diff at all. + if (s === "failed" || s === "dead") { + return degraded ? "unknown" : "failed_before_checkpoint"; + } + + // Terminal-success + teardown states: the run ran to completion or is + // shutting down. Distinguish "succeeded with no changes" (R4b) from + // "succeeded but diff lost" (R4c2) via total_changed. + if (s === "succeeded" || s === "removing") { + if (degraded) { + // We have a patch; the component renders PatchDiff instead of an + // empty state. Shouldn't reach here in practice. + return "unknown"; } - return "no_changes"; + return totalChanged > 0 ? "diff_lost" : "no_changes"; } - if (totalChanged === 0) { - return "no_changes"; - } - return "diff_lost"; + + // Unknown future status — fail conservative. + return "unknown"; } export function LoadingSkeleton() { diff --git a/lib/crates/fabro-spa/assets/assets/entry-qzbwmrbr.js b/lib/crates/fabro-spa/assets/assets/entry-8hk343fy.js similarity index 98% rename from lib/crates/fabro-spa/assets/assets/entry-qzbwmrbr.js rename to lib/crates/fabro-spa/assets/assets/entry-8hk343fy.js index fc51c2345..3daeea9ba 100644 --- a/lib/crates/fabro-spa/assets/assets/entry-qzbwmrbr.js +++ b/lib/crates/fabro-spa/assets/assets/entry-8hk343fy.js @@ -2364,7 +2364,7 @@ Please report this to https://github.com/markedjs/marked.`,Z){let q="

An error review -> apply [label="Accept"] review -> propose [label="Revise", style=dashed] apply -> exit -}`}function l30(Z){let Y=Z.querySelector(".graph > title");if(!Y)return;let J=Y.nextElementSibling;while(J&&J.tagName==="text"){let q=J.nextElementSibling;J.remove(),J=q}Y.remove()}var YW=[25,50,75,100,150,200],r30=2;function Ym({loaderData:Z}){let{id:Y}=z7(),{stages:J,graphSvg:q}=Z,z=N7.useRef(null),B=N7.useRef(null),W=N7.useRef(null),[U,G]=N7.useState(null),[w,H]=N7.useState(r30),[O,A]=N7.useState("LR"),[_,P]=N7.useState({x:0,y:0}),L=N7.useRef(null),R=YW[w],{theme:T}=Z6(),C=Pq(T);N7.useEffect(()=>{let E=!1;async function g(){try{let s;if(q){let x=await fetch(`/api/v1/runs/${Y}/graph?direction=${O}`,{credentials:"include"});if(E)return;if(!x.ok){G("Failed to load graph");return}let l=await x.text(),n=new DOMParser().parseFromString(l,"image/svg+xml").documentElement;if(!(n instanceof SVGSVGElement)){G("Invalid SVG from server");return}s=n}else{let{instance:x}=await import("./chunk-a51kkeyv.js"),l=await x();if(E)return;s=l.renderSVGElement(d30(O,C))}if(l30(s),W.current=s,B.current)B.current.replaceChildren(s)}catch(s){G(s instanceof Error?s.message:"Failed to render diagram")}}return P({x:0,y:0}),g(),()=>{E=!0}},[O,C,q,Y]);let v=N7.useCallback((E)=>{if(E.target.closest("button"))return;E.currentTarget.setPointerCapture(E.pointerId),L.current={startX:E.clientX,startY:E.clientY,startPanX:_.x,startPanY:_.y}},[_]),b=N7.useCallback((E)=>{let g=L.current;if(!g)return;P({x:g.startPanX+E.clientX-g.startX,y:g.startPanY+E.clientY-g.startY})},[]),S=N7.useCallback(()=>{L.current=null},[]),j=N7.useCallback(()=>{let E=W.current,g=z.current;if(!E||!g)return;let s=E.viewBox.baseVal.width||E.getBoundingClientRect().width,x=E.viewBox.baseVal.height||E.getBoundingClientRect().height,l=48,X0=g.clientWidth-l,K0=g.clientHeight-l,n=Math.min(X0/s,K0/x)*100,p=0;for(let J0=YW.length-1;J0>=0;J0--)if(YW[J0]<=n){p=J0;break}H(p),P({x:0,y:0})},[]);if(U)return h5.jsxDEV("p",{className:"text-sm text-coral",children:U},void 0,!1,void 0,this);return h5.jsxDEV("div",{className:"flex gap-6",children:[h5.jsxDEV(TZ,{stages:J,runId:Y,activeLink:"graph"},void 0,!1,void 0,this),h5.jsxDEV("div",{className:"min-w-0 flex-1",children:h5.jsxDEV("div",{className:"graph-svg relative rounded-md border border-line bg-panel-alt/40",children:[h5.jsxDEV("div",{className:"absolute right-3 top-3 z-10 flex items-center gap-2",children:[h5.jsxDEV("div",{className:"flex items-center gap-0.5 rounded-md border border-line bg-panel/90 p-0.5",children:[h5.jsxDEV("button",{type:"button",title:"Left to right",onClick:()=>A("LR"),className:`flex size-7 items-center justify-center rounded transition-colors ${O==="LR"?"bg-overlay-strong text-fg-3":"text-fg-muted hover:bg-overlay hover:text-fg-3"}`,children:h5.jsxDEV(pY,{className:"size-3.5"},void 0,!1,void 0,this)},void 0,!1,void 0,this),h5.jsxDEV("button",{type:"button",title:"Top to bottom",onClick:()=>A("TB"),className:`flex size-7 items-center justify-center rounded transition-colors ${O==="TB"?"bg-overlay-strong text-fg-3":"text-fg-muted hover:bg-overlay hover:text-fg-3"}`,children:h5.jsxDEV(mY,{className:"size-3.5"},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),h5.jsxDEV("div",{className:"flex items-center rounded-md border border-line bg-panel/90 p-0.5",children:h5.jsxDEV("button",{type:"button",title:"Fit to window",onClick:j,className:"flex size-7 items-center justify-center rounded text-fg-muted transition-colors hover:bg-overlay hover:text-fg-3",children:h5.jsxDEV("svg",{viewBox:"0 0 14 14",fill:"none",stroke:"currentColor",className:"size-3.5","aria-hidden":"true",children:h5.jsxDEV("rect",{x:"1",y:"1",width:"12",height:"12",rx:"1.5",strokeWidth:"1.5",strokeDasharray:"3 2"},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),h5.jsxDEV("div",{className:"flex items-center gap-0.5 rounded-md border border-line bg-panel/90 p-0.5",children:[h5.jsxDEV("button",{type:"button",title:"Zoom out",onClick:()=>H((E)=>Math.max(0,E-1)),disabled:w===0,className:"flex size-7 items-center justify-center rounded text-fg-muted transition-colors hover:bg-overlay hover:text-fg-3 disabled:opacity-30 disabled:hover:bg-transparent disabled:hover:text-fg-muted",children:h5.jsxDEV(cY,{className:"size-4"},void 0,!1,void 0,this)},void 0,!1,void 0,this),h5.jsxDEV("button",{type:"button",title:"Zoom in",onClick:()=>H((E)=>Math.min(YW.length-1,E+1)),disabled:w===YW.length-1,className:"flex size-7 items-center justify-center rounded text-fg-muted transition-colors hover:bg-overlay hover:text-fg-3 disabled:opacity-30 disabled:hover:bg-transparent disabled:hover:text-fg-muted",children:h5.jsxDEV(f8,{className:"size-4"},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this),h5.jsxDEV("div",{ref:z,className:"overflow-hidden p-6",style:{cursor:L.current?"grabbing":"grab"},onPointerDown:v,onPointerMove:b,onPointerUp:S,onPointerCancel:S,children:h5.jsxDEV("div",{ref:B,className:"flex items-center justify-center",style:{transform:`translate(${_.x}px, ${_.y}px) scale(${R/100})`,transformOrigin:"center center"},children:h5.jsxDEV("p",{className:"text-sm text-fg-muted",children:"Loading diagram..."},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var xL={};o5(xL,{loader:()=>Q80,handle:()=>Y80,extractRequestId:()=>Gm,default:()=>wm,ErrorBoundary:()=>q80});var R5=u(Q0(),1);var x2=u(f0(),1);function a30({name:Z}){return x2.jsxDEV("div",{className:"flex items-center justify-between rounded-md border border-line bg-panel/60 px-4 py-3 text-sm text-fg-muted",children:[x2.jsxDEV("span",{className:"font-mono text-fg-2",children:Z},void 0,!1,void 0,this),x2.jsxDEV("span",{className:"rounded bg-rose-950/40 px-2 py-0.5 text-xs text-rose-200",children:"sensitive — contents omitted"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function s30({name:Z}){return x2.jsxDEV("div",{className:"flex items-center justify-between rounded-md border border-line bg-panel/60 px-4 py-3 text-sm text-fg-muted",children:[x2.jsxDEV("span",{className:"font-mono text-fg-2",children:Z},void 0,!1,void 0,this),x2.jsxDEV("span",{className:"rounded bg-panel-alt/80 px-2 py-0.5 text-xs text-fg-3",children:"binary — not shown inline"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function o30({name:Z,reason:Y}){return x2.jsxDEV("div",{className:"flex items-center justify-between rounded-md border border-line bg-panel/60 px-4 py-3 text-sm text-fg-muted",children:[x2.jsxDEV("span",{className:"font-mono text-fg-2",children:Z},void 0,!1,void 0,this),x2.jsxDEV("span",{className:"rounded bg-panel-alt/80 px-2 py-0.5 text-xs text-fg-3",children:Y==="budget_exhausted"?"omitted — too many files changed":"too large to render inline"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function Qm({name:Z,kind:Y}){return x2.jsxDEV("div",{className:"flex items-center justify-between rounded-md border border-line bg-panel/60 px-4 py-3 text-sm text-fg-muted",children:[x2.jsxDEV("span",{className:"font-mono text-fg-2",children:Z},void 0,!1,void 0,this),x2.jsxDEV("span",{className:"rounded bg-panel-alt/80 px-2 py-0.5 text-xs text-fg-3",children:Y},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function Jm(Z){let Y=Z.new_file.name||Z.old_file.name;if(Z.sensitive)return x2.jsxDEV(a30,{name:Y},void 0,!1,void 0,this);if(Z.binary)return x2.jsxDEV(s30,{name:Y},void 0,!1,void 0,this);if(Z.change_kind==="symlink")return x2.jsxDEV(Qm,{name:Y,kind:"symlink"},void 0,!1,void 0,this);if(Z.change_kind==="submodule")return x2.jsxDEV(Qm,{name:Y,kind:"submodule"},void 0,!1,void 0,this);if(Z.truncated)return x2.jsxDEV(o30,{name:Y,reason:Z.truncation_reason},void 0,!1,void 0,this);return null}function Xm({reason:Z}){return x2.jsxDEV("div",{className:"rounded-md border border-amber-500/30 bg-amber-950/20 px-4 py-3 text-sm text-amber-100",children:i30(Z)},void 0,!1,void 0,this)}function i30(Z){switch(Z){case"sandbox_gone":return"Showing final patch only. This run's sandbox has been cleaned up, so individual file contents are no longer available.";case"provider_unsupported":return"Live diff isn't supported for this sandbox provider. Showing the patch captured at the last checkpoint.";case"sandbox_unreachable":default:return"Couldn't reach this run's sandbox. Showing the patch captured at the last checkpoint — refresh to try again."}}var i7=u(f0(),1);function IL({kind:Z}){return i7.jsxDEV("div",{role:"status",className:"rounded-md border border-dashed border-line bg-panel/40 px-6 py-10 text-center text-sm text-fg-muted",children:t30(Z)},void 0,!1,void 0,this)}function t30(Z){switch(Z){case"starting":return"Run is still starting. Files will appear once it begins.";case"no_changes":return"This run didn't change any files.";case"failed_before_checkpoint":return"This run failed before capturing any changes.";case"diff_lost":return"The diff for this run is no longer available. If you expect files here, please report it.";case"unknown":default:return"The diff for this run is not available right now."}}function jL(Z){let{runStatus:Y,totalChanged:J,degraded:q}=Z;if(!Y)return"unknown";let z=Y.toLowerCase();if(z==="submitted"||z==="starting"||z==="queued")return"starting";if(z==="failed"&&!q)return"failed_before_checkpoint";if((z==="succeeded"||z==="partialsuccess")&&!q){if(J>0)return"diff_lost";return"no_changes"}if(J===0)return"no_changes";return"diff_lost"}function qm(){return i7.jsxDEV("div",{className:"flex flex-col gap-3","aria-label":"Loading files",children:[i7.jsxDEV("div",{className:"h-8 rounded-md bg-panel/60 motion-safe:animate-pulse"},void 0,!1,void 0,this),i7.jsxDEV("div",{className:"h-32 rounded-md bg-panel/60 motion-safe:animate-pulse"},void 0,!1,void 0,this),i7.jsxDEV("div",{className:"h-32 rounded-md bg-panel/60 motion-safe:animate-pulse"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function Dq({message:Z,onRetry:Y}){return i7.jsxDEV("div",{className:"flex items-center justify-between gap-3 rounded-md border border-rose-500/30 bg-rose-950/20 px-4 py-3 text-sm text-rose-100",children:[i7.jsxDEV("span",{children:Z},void 0,!1,void 0,this),i7.jsxDEV("button",{type:"button",onClick:Y,className:"min-h-[32px] rounded-md border border-rose-500/40 bg-rose-950/40 px-3 py-1 text-xs font-medium text-rose-50 transition-colors hover:bg-rose-950/60",children:"Retry"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function FQ({children:Z}){return i7.jsxDEV("div",{role:"status","aria-live":"polite",className:"pointer-events-none fixed bottom-6 right-6 z-50 rounded-md border border-line bg-panel/95 px-3 py-2 text-xs text-fg-2 shadow-lg",children:Z},void 0,!1,void 0,this)}function zm(){let Z=bU();if(F3(Z)){if(Z.status===401||Z.status===403)return i7.jsxDEV("div",{className:"rounded-md border border-dashed border-line bg-panel/40 px-6 py-10 text-center text-sm text-fg-muted",children:"You don't have access to this run's files."},void 0,!1,void 0,this);if(Z.status===503||Z.status===429)return i7.jsxDEV(Dq,{message:"The diff service is temporarily unavailable.",onRetry:()=>window.location.reload()},void 0,!1,void 0,this);if(Z.status===500){let Y=n30(Z.data);return i7.jsxDEV("div",{className:"rounded-md border border-dashed border-line bg-panel/40 px-6 py-10 text-center text-sm text-fg-muted",children:["Something went wrong.",Y?` Request ID: ${Y}.`:null," Please contact support if this persists."]},void 0,!0,void 0,this)}return i7.jsxDEV("div",{className:"rounded-md border border-dashed border-line bg-panel/40 px-6 py-10 text-center text-sm text-fg-muted",children:["Something went wrong (",Z.status,")."]},void 0,!0,void 0,this)}return i7.jsxDEV("div",{className:"rounded-md border border-dashed border-line bg-panel/40 px-6 py-10 text-center text-sm text-fg-muted",children:"Something went wrong loading this run's files."},void 0,!1,void 0,this)}function n30(Z){if(!Z||typeof Z!=="object")return null;let Y=Z;if(typeof Y.request_id==="string")return Y.request_id;let J=Y.errors;if(Array.isArray(J)&&J.length>0){let q=J[0];if(q&&typeof q==="object"){let z=q.detail;if(typeof z==="string"){let W=z.match(/request[_ ]id[=:]?\s*([a-zA-Z0-9-_]+)/i);if(W)return W[1]}let B=q.request_id;if(typeof B==="string")return B}}return null}var Km=u(Q0(),1);function e30(Z){if(!Z)return!1;let Y=Z.tagName.toLowerCase();if(Y==="input"||Y==="textarea"||Y==="select")return!0;return Z.isContentEditable===!0}function Bm(Z,Y){Km.useEffect(()=>{if(!Z.current)return;let J=(q)=>{if(q.key!=="j"&&q.key!=="k")return;if(q.metaKey||q.ctrlKey||q.altKey)return;if(e30(document.activeElement))return;let z=Z.current;if(!z)return;let B=Array.from(z.querySelectorAll('[data-run-file-row="true"]'));if(B.length===0)return;let W=document.activeElement,U=B.findIndex((H)=>H.contains(W)),G;if(U<0)G=0;else G=q.key==="j"?U+1:U-1;if(G<0||G>=B.length)return;q.preventDefault();let w=B[G];w.focus({preventScroll:!1}),w.scrollIntoView({block:"nearest",behavior:"smooth"})};return document.addEventListener("keydown",J),()=>document.removeEventListener("keydown",J)},[Z,Y])}var r3=u(f0(),1);function Wm({onRefresh:Z,refreshing:Y,refreshDisabled:J,freshness:q,refreshButtonRef:z,diffStyle:B,onDiffStyleChange:W,diffStyleForced:U}){return r3.jsxDEV("div",{className:"flex items-center justify-between gap-3 rounded-md border border-line bg-panel/40 px-3 py-2 text-xs text-fg-muted",children:[r3.jsxDEV("div",{className:"flex items-center gap-3",children:r3.jsxDEV("span",{"aria-live":"polite",className:"min-w-0 truncate",children:q??" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),r3.jsxDEV("div",{className:"flex items-center gap-2",children:[r3.jsxDEV(Z80,{value:B,onChange:W,forced:U},void 0,!1,void 0,this),r3.jsxDEV("button",{ref:z,type:"button",onClick:Z,disabled:Y||J,"aria-label":Y?"Refreshing files":"Refresh files",className:"min-h-[44px] min-w-[44px] rounded-md border border-line bg-panel px-3 py-1 text-xs font-medium text-fg-2 transition-colors hover:bg-overlay disabled:opacity-60",children:Y?"Refreshing…":"Refresh"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}function Z80({value:Z,onChange:Y,forced:J}){return r3.jsxDEV("div",{className:"flex items-center gap-1",role:"group","aria-label":"Diff layout",children:[r3.jsxDEV("button",{type:"button",onClick:()=>Y("split"),disabled:J,"aria-pressed":Z==="split",className:`min-h-[44px] rounded-md border border-line px-3 py-1 text-xs font-medium transition-colors disabled:opacity-60 ${Z==="split"?"bg-overlay text-fg-1":"bg-panel text-fg-2 hover:bg-overlay"}`,children:"Split"},void 0,!1,void 0,this),r3.jsxDEV("button",{type:"button",onClick:()=>Y("unified"),disabled:J,"aria-pressed":Z==="unified",className:`min-h-[44px] rounded-md border border-line px-3 py-1 text-xs font-medium transition-colors disabled:opacity-60 ${Z==="unified"?"bg-overlay text-fg-1":"bg-panel text-fg-2 hover:bg-overlay"}`,children:"Unified"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var W5=u(f0(),1),Y80={wide:!0};async function Q80({request:Z,params:Y}){let J=await fetch(`/api/v1/runs/${Y.id}/files`,{credentials:"include",...Z?.signal?{signal:Z.signal}:{}});if(J.status===404||J.status===501)return{data:null,error:null};if(J.ok)return{data:await J.json(),error:null};let q="";try{q=await J.text()}catch{}let z=null;if(q)try{z=JSON.parse(q)}catch{}return{data:null,error:{status:J.status,message:J.statusText||`HTTP ${J.status}`,requestId:Gm(z)}}}function Gm(Z){if(!Z||typeof Z!=="object")return null;let Y=Z;if(typeof Y.request_id==="string")return Y.request_id;let J=Y.errors;if(Array.isArray(J)&&J.length>0){let q=J[0];if(q&&typeof q==="object"){let z=q;if(typeof z.request_id==="string")return z.request_id;if(typeof z.detail==="string"){let B=z.detail.match(/request[_ ]id[=:]?\s*([a-zA-Z0-9-_]+)/i);if(B)return B[1]}}}return null}var J80=new Set(["checkpoint.completed","run.completed","run.failed"]),X80=768,Mm="fabro.run-files.diff-style",q80=zm;function z80(){let[Z,Y]=R5.useState(!1);return R5.useEffect(()=>{if(typeof window>"u")return;let J=window.matchMedia(`(max-width: ${X80-1}px)`),q=()=>Y(J.matches);return q(),J.addEventListener("change",q),()=>J.removeEventListener("change",q)},[]),Z}function K80(Z){let Y=V3();R5.useEffect(()=>{if(!Z)return;let J=new EventSource(`/api/v1/runs/${Z}/attach?since_seq=1`),q;return J.onmessage=(z)=>{try{let B=JSON.parse(z.data);if(J80.has(B.event))clearTimeout(q),q=setTimeout(()=>Y.revalidate(),500)}catch{}},()=>{clearTimeout(q),J.close()}},[Z])}function B80(Z,Y){let[,J]=R5.useState(0);if(R5.useEffect(()=>{let W=setInterval(()=>J((U)=>U+1),1e4);return()=>clearInterval(W)},[]),!Z)return null;let q=Date.now(),z=Z.to_sha_committed_at?`Captured ${$m(Z.to_sha_committed_at,q)}`:null,B=Y?`Fetched ${$m(new Date(Y).toISOString(),q)}`:null;if(Z.degraded&&z&&B)return`${z} · ${B}`;return z??B??null}function $m(Z,Y){if(!Z)return"";let J=Date.parse(Z);if(Number.isNaN(J))return"";let q=Math.max(0,Math.floor((Y-J)/1000));if(q<5)return"just now";if(q<60)return`${q}s ago`;let z=Math.floor(q/60);if(z<60)return`${z}m ago`;let B=Math.floor(z/60);if(B<24)return`${B}h ago`;return`${Math.floor(B/24)}d ago`}function W80(){if(typeof window>"u")return"split";try{let Z=window.localStorage.getItem(Mm);if(Z==="split"||Z==="unified")return Z}catch{}return"split"}function $80(Z){if(typeof window>"u")return;try{window.localStorage.setItem(Mm,Z)}catch{}}function SL(Z){return`run-file:${Z}`}function Um(Z){if(!Z)return null;let Y=Z.startsWith("#")?Z.slice(1):Z,J="file=";if(!Y.startsWith(J))return null;try{return decodeURIComponent(Y.slice(J.length))}catch{return null}}function U80(Z){for(let Y of Z){let J=Y.data;if(!J)continue;if(typeof J?.run?.lifecycleStatus==="string")return J.run.lifecycleStatus;if(typeof J?.lifecycleStatus==="string")return J.lifecycleStatus}return}function wm({loaderData:Z}){let Y=Z6(),J=z7(),q=DU(),z=V3(),B=HY(),W=Z,U=z80(),G=U80(B),w=R5.useRef(null);if(W?.data)w.current=W.data;let H=W?.data??w.current,O=R5.useRef(null),A=R5.useRef(null),_=R5.useRef(null),[P,L]=R5.useState(null),[R,T]=R5.useState(null);R5.useEffect(()=>{if(!W?.data)return;O.current=Date.now();let u0=W.data.meta?.to_sha??null,W0=_.current;if(W0!==null&&W0>0&&W.data.data.length===0){L("No changes in this run.");let x0=setTimeout(()=>L(null),3500);return()=>clearTimeout(x0)}_.current=W.data.data.length,A.current=u0;return},[W?.data]),K80(J.id);let C=q.state==="loading"&&!Z,v=z.state==="loading",b=W?.error&&w.current?`Couldn't refresh (${W.error.status}).`:null,S=W?.error&&!w.current?W.error:null,j=B80(H?.meta??null,O.current),[E,g]=R5.useState(W80),s=U?"unified":E,x=U,l=R5.useCallback((u0)=>{if(x)return;g(u0),$80(u0)},[x]),X0=Y.theme==="dark"?"pierre-dark":"pierre-light",K0=R5.useRef(null),n=R5.useRef(null),p=R5.useRef(!1);R5.useEffect(()=>{if(p.current&&!v)K0.current?.focus({preventScroll:!0});p.current=v},[v]);let J0=H?.data.length??0;Bm(n,J0);let[e,i]=R5.useState(()=>{if(typeof window>"u")return null;return Um(window.location.hash)});R5.useEffect(()=>{if(typeof window>"u")return;let u0=()=>i(Um(window.location.hash));return window.addEventListener("hashchange",u0),()=>window.removeEventListener("hashchange",u0)},[]),R5.useEffect(()=>{if(!e||!H)return;if(H.meta.degraded&&H.meta.patch){T("File-level navigation isn't available in the patch-only view.");let x0=setTimeout(()=>T(null),5000);return()=>clearTimeout(x0)}if(!H.data.some((x0)=>x0.new_file.name===e||x0.old_file.name===e)){T(`File ${e} is not in this run.`);let x0=setTimeout(()=>T(null),5000);return()=>clearTimeout(x0)}let W0=document.getElementById(SL(e));if(W0)W0.scrollIntoView({block:"start",behavior:"smooth"}),W0.focus({preventScroll:!0})},[e,H]);let G0=R5.useCallback((u0)=>u0.map((W0,x0)=>{let d0=W0.new_file.name||W0.old_file.name,w1=Jm(W0);if(w1)return W5.jsxDEV("div",{id:SL(d0),tabIndex:-1,"data-run-file-row":"true",role:"region","aria-label":`${W0.change_kind??"changed"}: ${d0}`,className:"focus:outline-2 focus:outline-focus focus:outline-offset-2 rounded-md",children:w1},`${d0}-${x0}`,!1,void 0,this);let X1=!!e&&(W0.new_file.name===e||W0.old_file.name===e);return W5.jsxDEV("div",{id:SL(d0),tabIndex:-1,"data-run-file-row":"true",role:"region","aria-label":`${W0.change_kind??"modified"}: ${W0.new_file.name}`,className:"focus:outline-2 focus:outline-focus focus:outline-offset-2 rounded-md",children:W5.jsxDEV(iV,{oldFile:W0.old_file,newFile:W0.new_file,options:{diffStyle:s,theme:X0,expandUnchanged:X1?!0:void 0}},void 0,!1,void 0,this)},`${W0.new_file.name}-${x0}`,!1,void 0,this)}),[s,X0,e]);if(C)return W5.jsxDEV(qm,{},void 0,!1,void 0,this);if(S){if(S.status===401||S.status===403)return W5.jsxDEV("div",{role:"status",className:"rounded-md border border-dashed border-line bg-panel/40 px-6 py-10 text-center text-sm text-fg-muted",children:"You don't have access to this run's files."},void 0,!1,void 0,this);if(S.status===429||S.status===503)return W5.jsxDEV(Dq,{message:"The diff service is temporarily unavailable.",onRetry:()=>z.revalidate()},void 0,!1,void 0,this);if(S.status>=500){let u0=S.requestId?` Request ID: ${S.requestId}.`:"";return W5.jsxDEV("div",{role:"status",className:"rounded-md border border-dashed border-line bg-panel/40 px-6 py-10 text-center text-sm text-fg-muted",children:["Something went wrong.",u0," Please contact support if this persists."]},void 0,!0,void 0,this)}return W5.jsxDEV(Dq,{message:`Couldn't load files (${S.status}).`,onRetry:()=>z.revalidate()},void 0,!1,void 0,this)}if(!H)return W5.jsxDEV(IL,{kind:jL({runStatus:G,totalChanged:0,degraded:!1})},void 0,!1,void 0,this);let{data:k,meta:B0}=H,H0=!!B0.to_sha&&A.current!==null&&A.current===B0.to_sha,w0=W5.jsxDEV(Wm,{onRefresh:()=>z.revalidate(),refreshing:v,refreshDisabled:H0,freshness:j,refreshButtonRef:K0,diffStyle:s,onDiffStyleChange:l,diffStyleForced:x},void 0,!1,void 0,this);if(B0.degraded&&B0.patch)return W5.jsxDEV("div",{ref:n,className:"flex flex-col gap-4",children:[w0,b?W5.jsxDEV(Dq,{message:b,onRetry:()=>z.revalidate()},void 0,!1,void 0,this):null,W5.jsxDEV(Xm,{reason:B0.degraded_reason},void 0,!1,void 0,this),W5.jsxDEV(tV,{patch:B0.patch,options:{diffStyle:s,theme:X0}},void 0,!1,void 0,this),P&&W5.jsxDEV(FQ,{children:P},void 0,!1,void 0,this),R&&W5.jsxDEV(FQ,{children:R},void 0,!1,void 0,this)]},void 0,!0,void 0,this);if(k.length===0)return W5.jsxDEV("div",{ref:n,className:"flex flex-col gap-4",children:[w0,W5.jsxDEV(IL,{kind:jL({runStatus:G,totalChanged:B0.total_changed,degraded:B0.degraded??!1})},void 0,!1,void 0,this),P&&W5.jsxDEV(FQ,{children:P},void 0,!1,void 0,this),R&&W5.jsxDEV(FQ,{children:R},void 0,!1,void 0,this)]},void 0,!0,void 0,this);let b0=k.length>20?W5.jsxDEV(vV,{children:G0(k)},void 0,!1,void 0,this):W5.jsxDEV(W5.Fragment,{children:G0(k)},void 0,!1,void 0,this);return W5.jsxDEV("div",{ref:n,className:"flex flex-col gap-4",children:[w0,b?W5.jsxDEV(Dq,{message:b,onRetry:()=>z.revalidate()},void 0,!1,void 0,this):null,b0,P&&W5.jsxDEV(FQ,{children:P},void 0,!1,void 0,this),R&&W5.jsxDEV(FQ,{children:R},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var kL={};o5(kL,{loader:()=>G80,default:()=>Nm});var Y1=u(f0(),1);function DZ(Z){return`${(Z/1000).toFixed(1)}k`}function BN(Z){return Z==null?"-":`$${(Z/1e6).toFixed(2)}`}async function G80({request:Z,params:Y}){let J=await y4(`/runs/${Y.id}/billing`,{request:Z}),q=J.stages.map((w)=>({stage:w.stage.name,model:w.model.id,inputTokens:w.billing.input_tokens,outputTokens:w.billing.output_tokens+(w.billing.reasoning_tokens??0),runtime:v7(w.runtime_secs),totalUsdMicros:w.billing.total_usd_micros})),z=v7(J.totals.runtime_secs),B=J.totals.input_tokens,W=J.totals.output_tokens+(J.totals.reasoning_tokens??0),U=J.totals.total_usd_micros,G=J.by_model.map((w)=>({model:w.model.id,stages:w.stages,inputTokens:w.billing.input_tokens,outputTokens:w.billing.output_tokens+(w.billing.reasoning_tokens??0),totalUsdMicros:w.billing.total_usd_micros})).sort((w,H)=>(H.totalUsdMicros??-1)-(w.totalUsdMicros??-1));return{stages:q,totalRuntime:z,totalUsdMicros:U,totalInput:B,totalOutput:W,modelBreakdown:G}}function Nm({loaderData:Z}){let{stages:Y,totalRuntime:J,totalUsdMicros:q,totalInput:z,totalOutput:B,modelBreakdown:W}=Z;return Y1.jsxDEV("div",{className:"space-y-6",children:[Y1.jsxDEV("div",{className:"overflow-hidden rounded-md border border-line",children:Y1.jsxDEV("table",{className:"w-full text-sm",children:[Y1.jsxDEV("thead",{children:Y1.jsxDEV("tr",{className:"border-b border-line bg-panel/60 text-left text-xs text-fg-muted",children:[Y1.jsxDEV("th",{className:"px-4 py-2.5 font-medium",children:"Stage"},void 0,!1,void 0,this),Y1.jsxDEV("th",{className:"px-4 py-2.5 font-medium",children:"Model"},void 0,!1,void 0,this),Y1.jsxDEV("th",{className:"px-4 py-2.5 font-medium text-right",children:"Tokens"},void 0,!1,void 0,this),Y1.jsxDEV("th",{className:"px-4 py-2.5 font-medium text-right",children:"Run time"},void 0,!1,void 0,this),Y1.jsxDEV("th",{className:"px-4 py-2.5 font-medium text-right",children:"Billing"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Y1.jsxDEV("tbody",{children:Y.map((U)=>Y1.jsxDEV("tr",{className:"border-b border-line last:border-b-0",children:[Y1.jsxDEV("td",{className:"px-4 py-3 text-fg-2",children:U.stage},void 0,!1,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 font-mono text-xs text-fg-3",children:U.model},void 0,!1,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs tabular-nums text-fg-3",children:[DZ(U.inputTokens)," ",Y1.jsxDEV("span",{className:"text-fg-muted",children:"/"},void 0,!1,void 0,this)," ",DZ(U.outputTokens)]},void 0,!0,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs text-fg-3",children:U.runtime},void 0,!1,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs text-fg-3",children:BN(U.totalUsdMicros)},void 0,!1,void 0,this)]},U.stage,!0,void 0,this))},void 0,!1,void 0,this),Y1.jsxDEV("tfoot",{children:Y1.jsxDEV("tr",{className:"border-t border-line-strong bg-panel/40",children:[Y1.jsxDEV("td",{className:"px-4 py-3 font-medium text-fg",children:"Total"},void 0,!1,void 0,this),Y1.jsxDEV("td",{},void 0,!1,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs tabular-nums font-medium text-fg",children:[DZ(z)," ",Y1.jsxDEV("span",{className:"text-fg-muted",children:"/"},void 0,!1,void 0,this)," ",DZ(B)]},void 0,!0,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs font-medium text-fg",children:J},void 0,!1,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs font-medium text-fg",children:BN(q)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Y1.jsxDEV("div",{children:[Y1.jsxDEV("h3",{className:"mb-3 text-xs font-medium uppercase tracking-wider text-fg-muted",children:"By Model"},void 0,!1,void 0,this),Y1.jsxDEV("div",{className:"overflow-hidden rounded-md border border-line",children:Y1.jsxDEV("table",{className:"w-full text-sm",children:[Y1.jsxDEV("thead",{children:Y1.jsxDEV("tr",{className:"border-b border-line bg-panel/60 text-left text-xs text-fg-muted",children:[Y1.jsxDEV("th",{className:"px-4 py-2.5 font-medium",children:"Model"},void 0,!1,void 0,this),Y1.jsxDEV("th",{className:"px-4 py-2.5 font-medium text-right",children:"Stages"},void 0,!1,void 0,this),Y1.jsxDEV("th",{className:"px-4 py-2.5 font-medium text-right",children:"Tokens"},void 0,!1,void 0,this),Y1.jsxDEV("th",{className:"px-4 py-2.5 font-medium text-right",children:"Billing"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Y1.jsxDEV("tbody",{children:W.map((U)=>Y1.jsxDEV("tr",{className:"border-b border-line last:border-b-0",children:[Y1.jsxDEV("td",{className:"px-4 py-3 font-mono text-xs text-fg-2",children:U.model},void 0,!1,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs tabular-nums text-fg-3",children:U.stages},void 0,!1,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs tabular-nums text-fg-3",children:[DZ(U.inputTokens)," ",Y1.jsxDEV("span",{className:"text-fg-muted",children:"/"},void 0,!1,void 0,this)," ",DZ(U.outputTokens)]},void 0,!0,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs text-fg-3",children:BN(U.totalUsdMicros)},void 0,!1,void 0,this)]},U.model,!0,void 0,this))},void 0,!1,void 0,this),Y1.jsxDEV("tfoot",{children:Y1.jsxDEV("tr",{className:"border-t border-line-strong bg-panel/40",children:[Y1.jsxDEV("td",{className:"px-4 py-3 font-medium text-fg",children:"Total"},void 0,!1,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs tabular-nums font-medium text-fg",children:Y.length},void 0,!1,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs tabular-nums font-medium text-fg",children:[DZ(z)," ",Y1.jsxDEV("span",{className:"text-fg-muted",children:"/"},void 0,!1,void 0,this)," ",DZ(B)]},void 0,!0,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs font-medium text-fg",children:BN(q)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}var fL={};o5(fL,{meta:()=>M80,loader:()=>N80,handle:()=>w80,default:()=>Hm});var L2=u(f0(),1);function M80({}){return[{title:"Insights — Fabro"}]}var w80={wide:!0};async function N80({request:Z}){let[{data:Y},{data:J}]=await Promise.all([y4("/insights/queries",{request:Z}),y4("/insights/history",{request:Z})]),q=Y.map((B)=>({id:B.id,name:B.name,sql:B.sql})),z=J.map((B)=>({id:B.id,sql:B.sql,timestamp:B.timestamp,elapsed:B.elapsed,rowsReturned:B.row_count}));return{savedQueries:q,historyEntries:z}}function Hm({loaderData:Z}){let{savedQueries:Y,historyEntries:J}=Z,q=E4();return L2.jsxDEV("div",{className:"flex gap-6",children:[L2.jsxDEV("div",{className:"w-56 shrink-0",children:L2.jsxDEV("div",{className:"sticky top-6 space-y-4",children:[L2.jsxDEV(s1,{to:"/insights/new",className:"inline-flex w-full items-center justify-center gap-1.5 rounded-md border border-line bg-panel/80 px-3 py-2 text-sm font-medium text-fg-3 transition-colors hover:border-line-strong hover:bg-panel hover:text-fg",children:[L2.jsxDEV(WP,{className:"size-3.5"},void 0,!1,void 0,this),"New Query"]},void 0,!0,void 0,this),L2.jsxDEV("div",{children:[L2.jsxDEV("h3",{className:"mb-2 text-[11px] font-semibold uppercase tracking-wider text-fg-muted",children:"Saved Queries"},void 0,!1,void 0,this),L2.jsxDEV("div",{className:"space-y-0.5",children:Y.map((z)=>L2.jsxDEV("button",{type:"button",onClick:()=>{q("/insights",{state:{sql:z.sql,name:z.name}})},className:"flex w-full flex-col gap-0.5 rounded-md px-2.5 py-2 text-left transition-colors hover:bg-overlay",children:[L2.jsxDEV("span",{className:"text-sm font-medium text-fg-2",children:z.name},void 0,!1,void 0,this),L2.jsxDEV("span",{className:"truncate font-mono text-[10px] text-fg-muted",children:z.sql.split(` +}`}function l30(Z){let Y=Z.querySelector(".graph > title");if(!Y)return;let J=Y.nextElementSibling;while(J&&J.tagName==="text"){let q=J.nextElementSibling;J.remove(),J=q}Y.remove()}var YW=[25,50,75,100,150,200],r30=2;function Ym({loaderData:Z}){let{id:Y}=z7(),{stages:J,graphSvg:q}=Z,z=N7.useRef(null),B=N7.useRef(null),W=N7.useRef(null),[U,G]=N7.useState(null),[w,H]=N7.useState(r30),[O,A]=N7.useState("LR"),[_,P]=N7.useState({x:0,y:0}),L=N7.useRef(null),R=YW[w],{theme:T}=Z6(),C=Pq(T);N7.useEffect(()=>{let E=!1;async function g(){try{let s;if(q){let x=await fetch(`/api/v1/runs/${Y}/graph?direction=${O}`,{credentials:"include"});if(E)return;if(!x.ok){G("Failed to load graph");return}let l=await x.text(),n=new DOMParser().parseFromString(l,"image/svg+xml").documentElement;if(!(n instanceof SVGSVGElement)){G("Invalid SVG from server");return}s=n}else{let{instance:x}=await import("./chunk-a51kkeyv.js"),l=await x();if(E)return;s=l.renderSVGElement(d30(O,C))}if(l30(s),W.current=s,B.current)B.current.replaceChildren(s)}catch(s){G(s instanceof Error?s.message:"Failed to render diagram")}}return P({x:0,y:0}),g(),()=>{E=!0}},[O,C,q,Y]);let v=N7.useCallback((E)=>{if(E.target.closest("button"))return;E.currentTarget.setPointerCapture(E.pointerId),L.current={startX:E.clientX,startY:E.clientY,startPanX:_.x,startPanY:_.y}},[_]),b=N7.useCallback((E)=>{let g=L.current;if(!g)return;P({x:g.startPanX+E.clientX-g.startX,y:g.startPanY+E.clientY-g.startY})},[]),S=N7.useCallback(()=>{L.current=null},[]),j=N7.useCallback(()=>{let E=W.current,g=z.current;if(!E||!g)return;let s=E.viewBox.baseVal.width||E.getBoundingClientRect().width,x=E.viewBox.baseVal.height||E.getBoundingClientRect().height,l=48,X0=g.clientWidth-l,K0=g.clientHeight-l,n=Math.min(X0/s,K0/x)*100,p=0;for(let J0=YW.length-1;J0>=0;J0--)if(YW[J0]<=n){p=J0;break}H(p),P({x:0,y:0})},[]);if(U)return h5.jsxDEV("p",{className:"text-sm text-coral",children:U},void 0,!1,void 0,this);return h5.jsxDEV("div",{className:"flex gap-6",children:[h5.jsxDEV(TZ,{stages:J,runId:Y,activeLink:"graph"},void 0,!1,void 0,this),h5.jsxDEV("div",{className:"min-w-0 flex-1",children:h5.jsxDEV("div",{className:"graph-svg relative rounded-md border border-line bg-panel-alt/40",children:[h5.jsxDEV("div",{className:"absolute right-3 top-3 z-10 flex items-center gap-2",children:[h5.jsxDEV("div",{className:"flex items-center gap-0.5 rounded-md border border-line bg-panel/90 p-0.5",children:[h5.jsxDEV("button",{type:"button",title:"Left to right",onClick:()=>A("LR"),className:`flex size-7 items-center justify-center rounded transition-colors ${O==="LR"?"bg-overlay-strong text-fg-3":"text-fg-muted hover:bg-overlay hover:text-fg-3"}`,children:h5.jsxDEV(pY,{className:"size-3.5"},void 0,!1,void 0,this)},void 0,!1,void 0,this),h5.jsxDEV("button",{type:"button",title:"Top to bottom",onClick:()=>A("TB"),className:`flex size-7 items-center justify-center rounded transition-colors ${O==="TB"?"bg-overlay-strong text-fg-3":"text-fg-muted hover:bg-overlay hover:text-fg-3"}`,children:h5.jsxDEV(mY,{className:"size-3.5"},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),h5.jsxDEV("div",{className:"flex items-center rounded-md border border-line bg-panel/90 p-0.5",children:h5.jsxDEV("button",{type:"button",title:"Fit to window",onClick:j,className:"flex size-7 items-center justify-center rounded text-fg-muted transition-colors hover:bg-overlay hover:text-fg-3",children:h5.jsxDEV("svg",{viewBox:"0 0 14 14",fill:"none",stroke:"currentColor",className:"size-3.5","aria-hidden":"true",children:h5.jsxDEV("rect",{x:"1",y:"1",width:"12",height:"12",rx:"1.5",strokeWidth:"1.5",strokeDasharray:"3 2"},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),h5.jsxDEV("div",{className:"flex items-center gap-0.5 rounded-md border border-line bg-panel/90 p-0.5",children:[h5.jsxDEV("button",{type:"button",title:"Zoom out",onClick:()=>H((E)=>Math.max(0,E-1)),disabled:w===0,className:"flex size-7 items-center justify-center rounded text-fg-muted transition-colors hover:bg-overlay hover:text-fg-3 disabled:opacity-30 disabled:hover:bg-transparent disabled:hover:text-fg-muted",children:h5.jsxDEV(cY,{className:"size-4"},void 0,!1,void 0,this)},void 0,!1,void 0,this),h5.jsxDEV("button",{type:"button",title:"Zoom in",onClick:()=>H((E)=>Math.min(YW.length-1,E+1)),disabled:w===YW.length-1,className:"flex size-7 items-center justify-center rounded text-fg-muted transition-colors hover:bg-overlay hover:text-fg-3 disabled:opacity-30 disabled:hover:bg-transparent disabled:hover:text-fg-muted",children:h5.jsxDEV(f8,{className:"size-4"},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this),h5.jsxDEV("div",{ref:z,className:"overflow-hidden p-6",style:{cursor:L.current?"grabbing":"grab"},onPointerDown:v,onPointerMove:b,onPointerUp:S,onPointerCancel:S,children:h5.jsxDEV("div",{ref:B,className:"flex items-center justify-center",style:{transform:`translate(${_.x}px, ${_.y}px) scale(${R/100})`,transformOrigin:"center center"},children:h5.jsxDEV("p",{className:"text-sm text-fg-muted",children:"Loading diagram..."},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var xL={};o5(xL,{loader:()=>Q80,handle:()=>Y80,extractRequestId:()=>Gm,default:()=>wm,ErrorBoundary:()=>q80});var R5=u(Q0(),1);var x2=u(f0(),1);function a30({name:Z}){return x2.jsxDEV("div",{className:"flex items-center justify-between rounded-md border border-line bg-panel/60 px-4 py-3 text-sm text-fg-muted",children:[x2.jsxDEV("span",{className:"font-mono text-fg-2",children:Z},void 0,!1,void 0,this),x2.jsxDEV("span",{className:"rounded bg-rose-950/40 px-2 py-0.5 text-xs text-rose-200",children:"sensitive — contents omitted"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function s30({name:Z}){return x2.jsxDEV("div",{className:"flex items-center justify-between rounded-md border border-line bg-panel/60 px-4 py-3 text-sm text-fg-muted",children:[x2.jsxDEV("span",{className:"font-mono text-fg-2",children:Z},void 0,!1,void 0,this),x2.jsxDEV("span",{className:"rounded bg-panel-alt/80 px-2 py-0.5 text-xs text-fg-3",children:"binary — not shown inline"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function o30({name:Z,reason:Y}){return x2.jsxDEV("div",{className:"flex items-center justify-between rounded-md border border-line bg-panel/60 px-4 py-3 text-sm text-fg-muted",children:[x2.jsxDEV("span",{className:"font-mono text-fg-2",children:Z},void 0,!1,void 0,this),x2.jsxDEV("span",{className:"rounded bg-panel-alt/80 px-2 py-0.5 text-xs text-fg-3",children:Y==="budget_exhausted"?"omitted — too many files changed":"too large to render inline"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function Qm({name:Z,kind:Y}){return x2.jsxDEV("div",{className:"flex items-center justify-between rounded-md border border-line bg-panel/60 px-4 py-3 text-sm text-fg-muted",children:[x2.jsxDEV("span",{className:"font-mono text-fg-2",children:Z},void 0,!1,void 0,this),x2.jsxDEV("span",{className:"rounded bg-panel-alt/80 px-2 py-0.5 text-xs text-fg-3",children:Y},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function Jm(Z){let Y=Z.new_file.name||Z.old_file.name;if(Z.sensitive)return x2.jsxDEV(a30,{name:Y},void 0,!1,void 0,this);if(Z.binary)return x2.jsxDEV(s30,{name:Y},void 0,!1,void 0,this);if(Z.change_kind==="symlink")return x2.jsxDEV(Qm,{name:Y,kind:"symlink"},void 0,!1,void 0,this);if(Z.change_kind==="submodule")return x2.jsxDEV(Qm,{name:Y,kind:"submodule"},void 0,!1,void 0,this);if(Z.truncated)return x2.jsxDEV(o30,{name:Y,reason:Z.truncation_reason},void 0,!1,void 0,this);return null}function Xm({reason:Z}){return x2.jsxDEV("div",{className:"rounded-md border border-amber-500/30 bg-amber-950/20 px-4 py-3 text-sm text-amber-100",children:i30(Z)},void 0,!1,void 0,this)}function i30(Z){switch(Z){case"sandbox_gone":return"Showing final patch only. This run's sandbox has been cleaned up, so individual file contents are no longer available.";case"provider_unsupported":return"Live diff isn't supported for this sandbox provider. Showing the patch captured at the last checkpoint.";case"sandbox_unreachable":default:return"Couldn't reach this run's sandbox. Showing the patch captured at the last checkpoint — refresh to try again."}}var i7=u(f0(),1);function IL({kind:Z}){return i7.jsxDEV("div",{role:"status",className:"rounded-md border border-dashed border-line bg-panel/40 px-6 py-10 text-center text-sm text-fg-muted",children:t30(Z)},void 0,!1,void 0,this)}function t30(Z){switch(Z){case"starting":return"Run is still starting. Files will appear once it begins.";case"no_changes":return"This run didn't change any files.";case"failed_before_checkpoint":return"This run failed before capturing any changes.";case"diff_lost":return"The diff for this run is no longer available. If you expect files here, please report it.";case"unknown":default:return"The diff for this run is not available right now."}}function jL(Z){let{runStatus:Y,totalChanged:J,degraded:q}=Z;if(!Y)return"unknown";let z=Y.toLowerCase();if(z==="submitted"||z==="queued"||z==="starting")return"starting";if(z==="running"||z==="blocked"||z==="paused")return"no_changes";if(z==="failed"||z==="dead")return q?"unknown":"failed_before_checkpoint";if(z==="succeeded"||z==="removing"){if(q)return"unknown";return J>0?"diff_lost":"no_changes"}return"unknown"}function qm(){return i7.jsxDEV("div",{className:"flex flex-col gap-3","aria-label":"Loading files",children:[i7.jsxDEV("div",{className:"h-8 rounded-md bg-panel/60 motion-safe:animate-pulse"},void 0,!1,void 0,this),i7.jsxDEV("div",{className:"h-32 rounded-md bg-panel/60 motion-safe:animate-pulse"},void 0,!1,void 0,this),i7.jsxDEV("div",{className:"h-32 rounded-md bg-panel/60 motion-safe:animate-pulse"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function Dq({message:Z,onRetry:Y}){return i7.jsxDEV("div",{className:"flex items-center justify-between gap-3 rounded-md border border-rose-500/30 bg-rose-950/20 px-4 py-3 text-sm text-rose-100",children:[i7.jsxDEV("span",{children:Z},void 0,!1,void 0,this),i7.jsxDEV("button",{type:"button",onClick:Y,className:"min-h-[32px] rounded-md border border-rose-500/40 bg-rose-950/40 px-3 py-1 text-xs font-medium text-rose-50 transition-colors hover:bg-rose-950/60",children:"Retry"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function FQ({children:Z}){return i7.jsxDEV("div",{role:"status","aria-live":"polite",className:"pointer-events-none fixed bottom-6 right-6 z-50 rounded-md border border-line bg-panel/95 px-3 py-2 text-xs text-fg-2 shadow-lg",children:Z},void 0,!1,void 0,this)}function zm(){let Z=bU();if(F3(Z)){if(Z.status===401||Z.status===403)return i7.jsxDEV("div",{className:"rounded-md border border-dashed border-line bg-panel/40 px-6 py-10 text-center text-sm text-fg-muted",children:"You don't have access to this run's files."},void 0,!1,void 0,this);if(Z.status===503||Z.status===429)return i7.jsxDEV(Dq,{message:"The diff service is temporarily unavailable.",onRetry:()=>window.location.reload()},void 0,!1,void 0,this);if(Z.status===500){let Y=n30(Z.data);return i7.jsxDEV("div",{className:"rounded-md border border-dashed border-line bg-panel/40 px-6 py-10 text-center text-sm text-fg-muted",children:["Something went wrong.",Y?` Request ID: ${Y}.`:null," Please contact support if this persists."]},void 0,!0,void 0,this)}return i7.jsxDEV("div",{className:"rounded-md border border-dashed border-line bg-panel/40 px-6 py-10 text-center text-sm text-fg-muted",children:["Something went wrong (",Z.status,")."]},void 0,!0,void 0,this)}return i7.jsxDEV("div",{className:"rounded-md border border-dashed border-line bg-panel/40 px-6 py-10 text-center text-sm text-fg-muted",children:"Something went wrong loading this run's files."},void 0,!1,void 0,this)}function n30(Z){if(!Z||typeof Z!=="object")return null;let Y=Z;if(typeof Y.request_id==="string")return Y.request_id;let J=Y.errors;if(Array.isArray(J)&&J.length>0){let q=J[0];if(q&&typeof q==="object"){let z=q.detail;if(typeof z==="string"){let W=z.match(/request[_ ]id[=:]?\s*([a-zA-Z0-9-_]+)/i);if(W)return W[1]}let B=q.request_id;if(typeof B==="string")return B}}return null}var Km=u(Q0(),1);function e30(Z){if(!Z)return!1;let Y=Z.tagName.toLowerCase();if(Y==="input"||Y==="textarea"||Y==="select")return!0;return Z.isContentEditable===!0}function Bm(Z,Y){Km.useEffect(()=>{if(!Z.current)return;let J=(q)=>{if(q.key!=="j"&&q.key!=="k")return;if(q.metaKey||q.ctrlKey||q.altKey)return;if(e30(document.activeElement))return;let z=Z.current;if(!z)return;let B=Array.from(z.querySelectorAll('[data-run-file-row="true"]'));if(B.length===0)return;let W=document.activeElement,U=B.findIndex((H)=>H.contains(W)),G;if(U<0)G=0;else G=q.key==="j"?U+1:U-1;if(G<0||G>=B.length)return;q.preventDefault();let w=B[G];w.focus({preventScroll:!1}),w.scrollIntoView({block:"nearest",behavior:"smooth"})};return document.addEventListener("keydown",J),()=>document.removeEventListener("keydown",J)},[Z,Y])}var r3=u(f0(),1);function Wm({onRefresh:Z,refreshing:Y,refreshDisabled:J,freshness:q,refreshButtonRef:z,diffStyle:B,onDiffStyleChange:W,diffStyleForced:U}){return r3.jsxDEV("div",{className:"flex items-center justify-between gap-3 rounded-md border border-line bg-panel/40 px-3 py-2 text-xs text-fg-muted",children:[r3.jsxDEV("div",{className:"flex items-center gap-3",children:r3.jsxDEV("span",{"aria-live":"polite",className:"min-w-0 truncate",children:q??" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),r3.jsxDEV("div",{className:"flex items-center gap-2",children:[r3.jsxDEV(Z80,{value:B,onChange:W,forced:U},void 0,!1,void 0,this),r3.jsxDEV("button",{ref:z,type:"button",onClick:Z,disabled:Y||J,"aria-label":Y?"Refreshing files":"Refresh files",className:"min-h-[44px] min-w-[44px] rounded-md border border-line bg-panel px-3 py-1 text-xs font-medium text-fg-2 transition-colors hover:bg-overlay disabled:opacity-60",children:Y?"Refreshing…":"Refresh"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}function Z80({value:Z,onChange:Y,forced:J}){return r3.jsxDEV("div",{className:"flex items-center gap-1",role:"group","aria-label":"Diff layout",children:[r3.jsxDEV("button",{type:"button",onClick:()=>Y("split"),disabled:J,"aria-pressed":Z==="split",className:`min-h-[44px] rounded-md border border-line px-3 py-1 text-xs font-medium transition-colors disabled:opacity-60 ${Z==="split"?"bg-overlay text-fg-1":"bg-panel text-fg-2 hover:bg-overlay"}`,children:"Split"},void 0,!1,void 0,this),r3.jsxDEV("button",{type:"button",onClick:()=>Y("unified"),disabled:J,"aria-pressed":Z==="unified",className:`min-h-[44px] rounded-md border border-line px-3 py-1 text-xs font-medium transition-colors disabled:opacity-60 ${Z==="unified"?"bg-overlay text-fg-1":"bg-panel text-fg-2 hover:bg-overlay"}`,children:"Unified"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var W5=u(f0(),1),Y80={wide:!0};async function Q80({request:Z,params:Y}){let J=await fetch(`/api/v1/runs/${Y.id}/files`,{credentials:"include",...Z?.signal?{signal:Z.signal}:{}});if(J.status===404||J.status===501)return{data:null,error:null};if(J.ok)return{data:await J.json(),error:null};let q="";try{q=await J.text()}catch{}let z=null;if(q)try{z=JSON.parse(q)}catch{}return{data:null,error:{status:J.status,message:J.statusText||`HTTP ${J.status}`,requestId:Gm(z)}}}function Gm(Z){if(!Z||typeof Z!=="object")return null;let Y=Z;if(typeof Y.request_id==="string")return Y.request_id;let J=Y.errors;if(Array.isArray(J)&&J.length>0){let q=J[0];if(q&&typeof q==="object"){let z=q;if(typeof z.request_id==="string")return z.request_id;if(typeof z.detail==="string"){let B=z.detail.match(/request[_ ]id[=:]?\s*([a-zA-Z0-9-_]+)/i);if(B)return B[1]}}}return null}var J80=new Set(["checkpoint.completed","run.completed","run.failed"]),X80=768,Mm="fabro.run-files.diff-style",q80=zm;function z80(){let[Z,Y]=R5.useState(!1);return R5.useEffect(()=>{if(typeof window>"u")return;let J=window.matchMedia(`(max-width: ${X80-1}px)`),q=()=>Y(J.matches);return q(),J.addEventListener("change",q),()=>J.removeEventListener("change",q)},[]),Z}function K80(Z){let Y=V3();R5.useEffect(()=>{if(!Z)return;let J=new EventSource(`/api/v1/runs/${Z}/attach?since_seq=1`),q;return J.onmessage=(z)=>{try{let B=JSON.parse(z.data);if(J80.has(B.event))clearTimeout(q),q=setTimeout(()=>Y.revalidate(),500)}catch{}},()=>{clearTimeout(q),J.close()}},[Z])}function B80(Z,Y){let[,J]=R5.useState(0);if(R5.useEffect(()=>{let W=setInterval(()=>J((U)=>U+1),1e4);return()=>clearInterval(W)},[]),!Z)return null;let q=Date.now(),z=Z.to_sha_committed_at?`Captured ${$m(Z.to_sha_committed_at,q)}`:null,B=Y?`Fetched ${$m(new Date(Y).toISOString(),q)}`:null;if(Z.degraded&&z&&B)return`${z} · ${B}`;return z??B??null}function $m(Z,Y){if(!Z)return"";let J=Date.parse(Z);if(Number.isNaN(J))return"";let q=Math.max(0,Math.floor((Y-J)/1000));if(q<5)return"just now";if(q<60)return`${q}s ago`;let z=Math.floor(q/60);if(z<60)return`${z}m ago`;let B=Math.floor(z/60);if(B<24)return`${B}h ago`;return`${Math.floor(B/24)}d ago`}function W80(){if(typeof window>"u")return"split";try{let Z=window.localStorage.getItem(Mm);if(Z==="split"||Z==="unified")return Z}catch{}return"split"}function $80(Z){if(typeof window>"u")return;try{window.localStorage.setItem(Mm,Z)}catch{}}function SL(Z){return`run-file:${Z}`}function Um(Z){if(!Z)return null;let Y=Z.startsWith("#")?Z.slice(1):Z,J="file=";if(!Y.startsWith(J))return null;try{return decodeURIComponent(Y.slice(J.length))}catch{return null}}function U80(Z){for(let Y of Z){let J=Y.data;if(!J)continue;if(typeof J?.run?.lifecycleStatus==="string")return J.run.lifecycleStatus;if(typeof J?.lifecycleStatus==="string")return J.lifecycleStatus}return}function wm({loaderData:Z}){let Y=Z6(),J=z7(),q=DU(),z=V3(),B=HY(),W=Z,U=z80(),G=U80(B),w=R5.useRef(null);if(W?.data)w.current=W.data;let H=W?.data??w.current,O=R5.useRef(null),A=R5.useRef(null),_=R5.useRef(null),[P,L]=R5.useState(null),[R,T]=R5.useState(null);R5.useEffect(()=>{if(!W?.data)return;O.current=Date.now();let u0=W.data.meta?.to_sha??null,W0=_.current;if(W0!==null&&W0>0&&W.data.data.length===0){L("No changes in this run.");let x0=setTimeout(()=>L(null),3500);return()=>clearTimeout(x0)}_.current=W.data.data.length,A.current=u0;return},[W?.data]),K80(J.id);let C=q.state==="loading"&&!Z,v=z.state==="loading",b=W?.error&&w.current?`Couldn't refresh (${W.error.status}).`:null,S=W?.error&&!w.current?W.error:null,j=B80(H?.meta??null,O.current),[E,g]=R5.useState(W80),s=U?"unified":E,x=U,l=R5.useCallback((u0)=>{if(x)return;g(u0),$80(u0)},[x]),X0=Y.theme==="dark"?"pierre-dark":"pierre-light",K0=R5.useRef(null),n=R5.useRef(null),p=R5.useRef(!1);R5.useEffect(()=>{if(p.current&&!v)K0.current?.focus({preventScroll:!0});p.current=v},[v]);let J0=H?.data.length??0;Bm(n,J0);let[e,i]=R5.useState(()=>{if(typeof window>"u")return null;return Um(window.location.hash)});R5.useEffect(()=>{if(typeof window>"u")return;let u0=()=>i(Um(window.location.hash));return window.addEventListener("hashchange",u0),()=>window.removeEventListener("hashchange",u0)},[]),R5.useEffect(()=>{if(!e||!H)return;if(H.meta.degraded&&H.meta.patch){T("File-level navigation isn't available in the patch-only view.");let x0=setTimeout(()=>T(null),5000);return()=>clearTimeout(x0)}if(!H.data.some((x0)=>x0.new_file.name===e||x0.old_file.name===e)){T(`File ${e} is not in this run.`);let x0=setTimeout(()=>T(null),5000);return()=>clearTimeout(x0)}let W0=document.getElementById(SL(e));if(W0)W0.scrollIntoView({block:"start",behavior:"smooth"}),W0.focus({preventScroll:!0})},[e,H]);let G0=R5.useCallback((u0)=>u0.map((W0,x0)=>{let d0=W0.new_file.name||W0.old_file.name,w1=Jm(W0);if(w1)return W5.jsxDEV("div",{id:SL(d0),tabIndex:-1,"data-run-file-row":"true",role:"region","aria-label":`${W0.change_kind??"changed"}: ${d0}`,className:"focus:outline-2 focus:outline-focus focus:outline-offset-2 rounded-md",children:w1},`${d0}-${x0}`,!1,void 0,this);let X1=!!e&&(W0.new_file.name===e||W0.old_file.name===e);return W5.jsxDEV("div",{id:SL(d0),tabIndex:-1,"data-run-file-row":"true",role:"region","aria-label":`${W0.change_kind??"modified"}: ${W0.new_file.name}`,className:"focus:outline-2 focus:outline-focus focus:outline-offset-2 rounded-md",children:W5.jsxDEV(iV,{oldFile:W0.old_file,newFile:W0.new_file,options:{diffStyle:s,theme:X0,expandUnchanged:X1?!0:void 0}},void 0,!1,void 0,this)},`${W0.new_file.name}-${x0}`,!1,void 0,this)}),[s,X0,e]);if(C)return W5.jsxDEV(qm,{},void 0,!1,void 0,this);if(S){if(S.status===401||S.status===403)return W5.jsxDEV("div",{role:"status",className:"rounded-md border border-dashed border-line bg-panel/40 px-6 py-10 text-center text-sm text-fg-muted",children:"You don't have access to this run's files."},void 0,!1,void 0,this);if(S.status===429||S.status===503)return W5.jsxDEV(Dq,{message:"The diff service is temporarily unavailable.",onRetry:()=>z.revalidate()},void 0,!1,void 0,this);if(S.status>=500){let u0=S.requestId?` Request ID: ${S.requestId}.`:"";return W5.jsxDEV("div",{role:"status",className:"rounded-md border border-dashed border-line bg-panel/40 px-6 py-10 text-center text-sm text-fg-muted",children:["Something went wrong.",u0," Please contact support if this persists."]},void 0,!0,void 0,this)}return W5.jsxDEV(Dq,{message:`Couldn't load files (${S.status}).`,onRetry:()=>z.revalidate()},void 0,!1,void 0,this)}if(!H)return W5.jsxDEV(IL,{kind:jL({runStatus:G,totalChanged:0,degraded:!1})},void 0,!1,void 0,this);let{data:k,meta:B0}=H,H0=!!B0.to_sha&&A.current!==null&&A.current===B0.to_sha,w0=W5.jsxDEV(Wm,{onRefresh:()=>z.revalidate(),refreshing:v,refreshDisabled:H0,freshness:j,refreshButtonRef:K0,diffStyle:s,onDiffStyleChange:l,diffStyleForced:x},void 0,!1,void 0,this);if(B0.degraded&&B0.patch)return W5.jsxDEV("div",{ref:n,className:"flex flex-col gap-4",children:[w0,b?W5.jsxDEV(Dq,{message:b,onRetry:()=>z.revalidate()},void 0,!1,void 0,this):null,W5.jsxDEV(Xm,{reason:B0.degraded_reason},void 0,!1,void 0,this),W5.jsxDEV(tV,{patch:B0.patch,options:{diffStyle:s,theme:X0}},void 0,!1,void 0,this),P&&W5.jsxDEV(FQ,{children:P},void 0,!1,void 0,this),R&&W5.jsxDEV(FQ,{children:R},void 0,!1,void 0,this)]},void 0,!0,void 0,this);if(k.length===0)return W5.jsxDEV("div",{ref:n,className:"flex flex-col gap-4",children:[w0,W5.jsxDEV(IL,{kind:jL({runStatus:G,totalChanged:B0.total_changed,degraded:B0.degraded??!1})},void 0,!1,void 0,this),P&&W5.jsxDEV(FQ,{children:P},void 0,!1,void 0,this),R&&W5.jsxDEV(FQ,{children:R},void 0,!1,void 0,this)]},void 0,!0,void 0,this);let b0=k.length>20?W5.jsxDEV(vV,{children:G0(k)},void 0,!1,void 0,this):W5.jsxDEV(W5.Fragment,{children:G0(k)},void 0,!1,void 0,this);return W5.jsxDEV("div",{ref:n,className:"flex flex-col gap-4",children:[w0,b?W5.jsxDEV(Dq,{message:b,onRetry:()=>z.revalidate()},void 0,!1,void 0,this):null,b0,P&&W5.jsxDEV(FQ,{children:P},void 0,!1,void 0,this),R&&W5.jsxDEV(FQ,{children:R},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var kL={};o5(kL,{loader:()=>G80,default:()=>Nm});var Y1=u(f0(),1);function DZ(Z){return`${(Z/1000).toFixed(1)}k`}function BN(Z){return Z==null?"-":`$${(Z/1e6).toFixed(2)}`}async function G80({request:Z,params:Y}){let J=await y4(`/runs/${Y.id}/billing`,{request:Z}),q=J.stages.map((w)=>({stage:w.stage.name,model:w.model.id,inputTokens:w.billing.input_tokens,outputTokens:w.billing.output_tokens+(w.billing.reasoning_tokens??0),runtime:v7(w.runtime_secs),totalUsdMicros:w.billing.total_usd_micros})),z=v7(J.totals.runtime_secs),B=J.totals.input_tokens,W=J.totals.output_tokens+(J.totals.reasoning_tokens??0),U=J.totals.total_usd_micros,G=J.by_model.map((w)=>({model:w.model.id,stages:w.stages,inputTokens:w.billing.input_tokens,outputTokens:w.billing.output_tokens+(w.billing.reasoning_tokens??0),totalUsdMicros:w.billing.total_usd_micros})).sort((w,H)=>(H.totalUsdMicros??-1)-(w.totalUsdMicros??-1));return{stages:q,totalRuntime:z,totalUsdMicros:U,totalInput:B,totalOutput:W,modelBreakdown:G}}function Nm({loaderData:Z}){let{stages:Y,totalRuntime:J,totalUsdMicros:q,totalInput:z,totalOutput:B,modelBreakdown:W}=Z;return Y1.jsxDEV("div",{className:"space-y-6",children:[Y1.jsxDEV("div",{className:"overflow-hidden rounded-md border border-line",children:Y1.jsxDEV("table",{className:"w-full text-sm",children:[Y1.jsxDEV("thead",{children:Y1.jsxDEV("tr",{className:"border-b border-line bg-panel/60 text-left text-xs text-fg-muted",children:[Y1.jsxDEV("th",{className:"px-4 py-2.5 font-medium",children:"Stage"},void 0,!1,void 0,this),Y1.jsxDEV("th",{className:"px-4 py-2.5 font-medium",children:"Model"},void 0,!1,void 0,this),Y1.jsxDEV("th",{className:"px-4 py-2.5 font-medium text-right",children:"Tokens"},void 0,!1,void 0,this),Y1.jsxDEV("th",{className:"px-4 py-2.5 font-medium text-right",children:"Run time"},void 0,!1,void 0,this),Y1.jsxDEV("th",{className:"px-4 py-2.5 font-medium text-right",children:"Billing"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Y1.jsxDEV("tbody",{children:Y.map((U)=>Y1.jsxDEV("tr",{className:"border-b border-line last:border-b-0",children:[Y1.jsxDEV("td",{className:"px-4 py-3 text-fg-2",children:U.stage},void 0,!1,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 font-mono text-xs text-fg-3",children:U.model},void 0,!1,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs tabular-nums text-fg-3",children:[DZ(U.inputTokens)," ",Y1.jsxDEV("span",{className:"text-fg-muted",children:"/"},void 0,!1,void 0,this)," ",DZ(U.outputTokens)]},void 0,!0,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs text-fg-3",children:U.runtime},void 0,!1,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs text-fg-3",children:BN(U.totalUsdMicros)},void 0,!1,void 0,this)]},U.stage,!0,void 0,this))},void 0,!1,void 0,this),Y1.jsxDEV("tfoot",{children:Y1.jsxDEV("tr",{className:"border-t border-line-strong bg-panel/40",children:[Y1.jsxDEV("td",{className:"px-4 py-3 font-medium text-fg",children:"Total"},void 0,!1,void 0,this),Y1.jsxDEV("td",{},void 0,!1,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs tabular-nums font-medium text-fg",children:[DZ(z)," ",Y1.jsxDEV("span",{className:"text-fg-muted",children:"/"},void 0,!1,void 0,this)," ",DZ(B)]},void 0,!0,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs font-medium text-fg",children:J},void 0,!1,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs font-medium text-fg",children:BN(q)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Y1.jsxDEV("div",{children:[Y1.jsxDEV("h3",{className:"mb-3 text-xs font-medium uppercase tracking-wider text-fg-muted",children:"By Model"},void 0,!1,void 0,this),Y1.jsxDEV("div",{className:"overflow-hidden rounded-md border border-line",children:Y1.jsxDEV("table",{className:"w-full text-sm",children:[Y1.jsxDEV("thead",{children:Y1.jsxDEV("tr",{className:"border-b border-line bg-panel/60 text-left text-xs text-fg-muted",children:[Y1.jsxDEV("th",{className:"px-4 py-2.5 font-medium",children:"Model"},void 0,!1,void 0,this),Y1.jsxDEV("th",{className:"px-4 py-2.5 font-medium text-right",children:"Stages"},void 0,!1,void 0,this),Y1.jsxDEV("th",{className:"px-4 py-2.5 font-medium text-right",children:"Tokens"},void 0,!1,void 0,this),Y1.jsxDEV("th",{className:"px-4 py-2.5 font-medium text-right",children:"Billing"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Y1.jsxDEV("tbody",{children:W.map((U)=>Y1.jsxDEV("tr",{className:"border-b border-line last:border-b-0",children:[Y1.jsxDEV("td",{className:"px-4 py-3 font-mono text-xs text-fg-2",children:U.model},void 0,!1,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs tabular-nums text-fg-3",children:U.stages},void 0,!1,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs tabular-nums text-fg-3",children:[DZ(U.inputTokens)," ",Y1.jsxDEV("span",{className:"text-fg-muted",children:"/"},void 0,!1,void 0,this)," ",DZ(U.outputTokens)]},void 0,!0,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs text-fg-3",children:BN(U.totalUsdMicros)},void 0,!1,void 0,this)]},U.model,!0,void 0,this))},void 0,!1,void 0,this),Y1.jsxDEV("tfoot",{children:Y1.jsxDEV("tr",{className:"border-t border-line-strong bg-panel/40",children:[Y1.jsxDEV("td",{className:"px-4 py-3 font-medium text-fg",children:"Total"},void 0,!1,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs tabular-nums font-medium text-fg",children:Y.length},void 0,!1,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs tabular-nums font-medium text-fg",children:[DZ(z)," ",Y1.jsxDEV("span",{className:"text-fg-muted",children:"/"},void 0,!1,void 0,this)," ",DZ(B)]},void 0,!0,void 0,this),Y1.jsxDEV("td",{className:"px-4 py-3 text-right font-mono text-xs font-medium text-fg",children:BN(q)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}var fL={};o5(fL,{meta:()=>M80,loader:()=>N80,handle:()=>w80,default:()=>Hm});var L2=u(f0(),1);function M80({}){return[{title:"Insights — Fabro"}]}var w80={wide:!0};async function N80({request:Z}){let[{data:Y},{data:J}]=await Promise.all([y4("/insights/queries",{request:Z}),y4("/insights/history",{request:Z})]),q=Y.map((B)=>({id:B.id,name:B.name,sql:B.sql})),z=J.map((B)=>({id:B.id,sql:B.sql,timestamp:B.timestamp,elapsed:B.elapsed,rowsReturned:B.row_count}));return{savedQueries:q,historyEntries:z}}function Hm({loaderData:Z}){let{savedQueries:Y,historyEntries:J}=Z,q=E4();return L2.jsxDEV("div",{className:"flex gap-6",children:[L2.jsxDEV("div",{className:"w-56 shrink-0",children:L2.jsxDEV("div",{className:"sticky top-6 space-y-4",children:[L2.jsxDEV(s1,{to:"/insights/new",className:"inline-flex w-full items-center justify-center gap-1.5 rounded-md border border-line bg-panel/80 px-3 py-2 text-sm font-medium text-fg-3 transition-colors hover:border-line-strong hover:bg-panel hover:text-fg",children:[L2.jsxDEV(WP,{className:"size-3.5"},void 0,!1,void 0,this),"New Query"]},void 0,!0,void 0,this),L2.jsxDEV("div",{children:[L2.jsxDEV("h3",{className:"mb-2 text-[11px] font-semibold uppercase tracking-wider text-fg-muted",children:"Saved Queries"},void 0,!1,void 0,this),L2.jsxDEV("div",{className:"space-y-0.5",children:Y.map((z)=>L2.jsxDEV("button",{type:"button",onClick:()=>{q("/insights",{state:{sql:z.sql,name:z.name}})},className:"flex w-full flex-col gap-0.5 rounded-md px-2.5 py-2 text-left transition-colors hover:bg-overlay",children:[L2.jsxDEV("span",{className:"text-sm font-medium text-fg-2",children:z.name},void 0,!1,void 0,this),L2.jsxDEV("span",{className:"truncate font-mono text-[10px] text-fg-muted",children:z.sql.split(` `)[0]},void 0,!1,void 0,this)]},z.id,!0,void 0,this))},void 0,!1,void 0,this)]},void 0,!0,void 0,this),L2.jsxDEV("div",{children:[L2.jsxDEV("h3",{className:"mb-2 text-[11px] font-semibold uppercase tracking-wider text-fg-muted",children:"History"},void 0,!1,void 0,this),L2.jsxDEV("div",{className:"space-y-0.5",children:J.map((z)=>L2.jsxDEV("button",{type:"button",onClick:()=>{q("/insights",{state:{sql:z.sql}})},className:"flex w-full flex-col gap-0.5 rounded-md px-2.5 py-2 text-left transition-colors hover:bg-overlay",children:[L2.jsxDEV("span",{className:"truncate font-mono text-[10px] text-fg-3",children:z.sql},void 0,!1,void 0,this),L2.jsxDEV("span",{className:"font-mono text-[10px] text-fg-muted",children:[YM(z.timestamp)," · ",z.rowsReturned," rows"]},void 0,!0,void 0,this)]},z.id,!0,void 0,this))},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),L2.jsxDEV("div",{className:"min-w-0 flex-1",children:L2.jsxDEV(I6,{},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var gL={};o5(gL,{default:()=>Am});var r5=u(Q0(),1);var C0=u(f0(),1);function H80(Z){let Y=Z.toLowerCase();if(Y.includes("workflow_name")&&Y.includes("avg"))return{columns:["workflow_name","avg_duration","run_count"],rows:[{workflow_name:"Expand Product",avg_duration:342.5,run_count:48},{workflow_name:"Implement Feature",avg_duration:287.3,run_count:156},{workflow_name:"Security Scan",avg_duration:198.1,run_count:312},{workflow_name:"Fix Build",avg_duration:145.7,run_count:482},{workflow_name:"Sync Drift",avg_duration:89.2,run_count:94},{workflow_name:"Dependency Audit",avg_duration:67.4,run_count:201}],elapsed:0.531,rowsRead:5182366,bytesRead:357780000,rowsReturned:6};if(Y.includes("failure_rate")||Y.includes("failed"))return{columns:["day","failures","total","failure_rate"],rows:Array.from({length:14},(J,q)=>{let z=new Date;z.setDate(z.getDate()-q);let B=80+Math.floor(Math.random()*60),W=Math.floor(Math.random()*15);return{day:z.toISOString().slice(0,10),failures:W,total:B,failure_rate:Math.round(1000*W/B)/10}}),elapsed:0.287,rowsRead:2841092,bytesRead:198400000,rowsReturned:14};return{columns:["repo","runs","total_additions","total_deletions"],rows:[{repo:"fabro-engine",runs:482,total_additions:28450,total_deletions:12300},{repo:"fabro-web",runs:356,total_additions:19200,total_deletions:8900},{repo:"fabro-cli",runs:198,total_additions:8700,total_deletions:4200},{repo:"fabro-docs",runs:145,total_additions:12100,total_deletions:3400},{repo:"fabro-sdk",runs:89,total_additions:5600,total_deletions:2100},{repo:"fabro-infra",runs:67,total_additions:3200,total_deletions:1800},{repo:"fabro-actions",runs:42,total_additions:2100,total_deletions:980},{repo:"fabro-proto",runs:28,total_additions:1400,total_deletions:650}],elapsed:0.148,rowsRead:1204588,bytesRead:89200000,rowsReturned:8}}function O80(Z){if(Z>=1e9)return`${(Z/1e9).toFixed(2)} GB`;if(Z>=1e6)return`${(Z/1e6).toFixed(2)} MB`;if(Z>=1000)return`${(Z/1000).toFixed(2)} KB`;return`${Z} B`}function WN(Z){return Z.toLocaleString()}var Om=["rgba(90, 200, 168, 0.85)","rgba(103, 178, 215, 0.85)","rgba(181, 221, 239, 0.65)","rgba(240, 164, 91, 0.75)"];function A80({result:Z}){let Y=r5.useRef(null),[J,q]=r5.useState(0);r5.useEffect(()=>{let C=Y.current;if(!C)return;let v=new ResizeObserver((b)=>{let S=b[0];if(S)q(S.contentRect.width)});return v.observe(C),()=>v.disconnect()},[]);let z=Z.columns[0],B=Z.columns.slice(1).filter((C)=>{return typeof Z.rows[0]?.[C]==="number"});if(B.length===0||Z.rows.length===0)return C0.jsxDEV("div",{className:"flex h-64 items-center justify-center text-sm text-fg-muted",children:"No numeric columns to chart"},void 0,!1,void 0,this);let W=B[0],U=Math.max(...Z.rows.map((C)=>{let v=C[W];return typeof v==="number"?v:0})),G=260,w=52,H={top:12,bottom:48,right:16},O=G-H.top-H.bottom,A=J-w-H.right,_=Z.rows.length,P=Math.max(8,Math.min(16,A/_*0.3)),L=Math.max(12,(A-P*(_+1))/_),R=5,T=Array.from({length:R},(C,v)=>Math.round(U*(R-1-v)/(R-1)));return C0.jsxDEV("div",{ref:Y,children:[J>0&&C0.jsxDEV("svg",{width:J,height:G,className:"select-none",children:[T.map((C,v)=>{let b=H.top+O*v/(R-1);return C0.jsxDEV("g",{children:[C0.jsxDEV("line",{x1:w,y1:b,x2:J-H.right,y2:b,stroke:"var(--chart-gridline)",strokeDasharray:"4,4"},void 0,!1,void 0,this),C0.jsxDEV("text",{x:w-8,y:b+4,textAnchor:"end",fill:"var(--chart-axis-label)",fontSize:"11",fontFamily:"JetBrains Mono, monospace",children:C>=1000?`${(C/1000).toFixed(C>=1e4?0:1)}k`:C},void 0,!1,void 0,this)]},C,!0,void 0,this)}),Z.rows.map((C,v)=>{let b=C[W],S=typeof b==="number"?b:0,j=U>0?S/U*O:0,E=w+P+v*(L+P),g=H.top+O-j,s=String(C[z]),x=v%Om.length,l=Math.floor(L/6);return C0.jsxDEV("g",{children:[C0.jsxDEV("rect",{x:E,y:g,width:L,height:j,rx:3,fill:Om[x],className:"transition-opacity hover:opacity-100",opacity:0.85},void 0,!1,void 0,this),C0.jsxDEV("title",{children:[s,": ",WN(S)]},void 0,!0,void 0,this),C0.jsxDEV("text",{x:E+L/2,y:G-H.bottom+16,textAnchor:"middle",fill:"var(--chart-axis-label)",fontSize:"10",fontFamily:"JetBrains Mono, monospace",children:s.length>l?s.slice(0,Math.max(3,l-1))+"…":s},void 0,!1,void 0,this)]},s+v,!0,void 0,this)})]},void 0,!0,void 0,this),C0.jsxDEV("div",{className:"mt-1 flex items-center gap-4 px-2",children:C0.jsxDEV("span",{className:"font-mono text-[10px] uppercase tracking-wider text-fg-muted",children:W.replace(/_/g," ")},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function _80({result:Z}){return C0.jsxDEV("div",{className:"overflow-x-auto",children:C0.jsxDEV("table",{className:"w-full text-left",children:[C0.jsxDEV("thead",{children:C0.jsxDEV("tr",{className:"border-b border-line",children:Z.columns.map((Y)=>C0.jsxDEV("th",{className:"whitespace-nowrap px-3 py-2.5 font-mono text-[11px] font-semibold uppercase tracking-wider text-fg-muted",children:Y},Y,!1,void 0,this))},void 0,!1,void 0,this)},void 0,!1,void 0,this),C0.jsxDEV("tbody",{children:Z.rows.map((Y)=>C0.jsxDEV("tr",{className:"border-b border-line transition-colors hover:bg-overlay",children:Z.columns.map((J)=>{let q=Y[J],z=typeof q==="number";return C0.jsxDEV("td",{className:`whitespace-nowrap px-3 py-2 font-mono text-xs ${z?"tabular-nums text-fg-2":"text-fg-3"}`,children:z?WN(q):String(q)},J,!1,void 0,this)})},String(Y[Z.columns[0]]),!1,void 0,this))},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function F80({value:Z,onChange:Y,onRun:J}){let q=r5.useRef(null),z=r5.useRef(null),B=Z.split(` `).length,W=r5.useCallback(()=>{if(q.current&&z.current)z.current.scrollTop=q.current.scrollTop},[]),U=(G)=>{if(G.key==="Enter"&&(G.metaKey||G.ctrlKey)){G.preventDefault(),J();return}if(G.key==="Tab"){G.preventDefault();let w=G.currentTarget,H=w.selectionStart,O=w.selectionEnd,A=Z.slice(0,H)+" "+Z.slice(O);Y(A),requestAnimationFrame(()=>{w.selectionStart=H+2,w.selectionEnd=H+2})}};return C0.jsxDEV("div",{className:"relative flex overflow-hidden rounded-md border border-line bg-page/80 font-mono text-sm",children:[C0.jsxDEV("div",{ref:z,className:"pointer-events-none flex shrink-0 flex-col overflow-hidden border-r border-line bg-page/60 px-3 py-3 text-right leading-[1.625rem] text-fg-muted select-none","aria-hidden":"true",children:Array.from({length:B},(G,w)=>C0.jsxDEV("span",{className:"text-[11px]",children:w+1},w+1,!1,void 0,this))},void 0,!1,void 0,this),C0.jsxDEV("textarea",{ref:q,value:Z,onChange:(G)=>Y(G.target.value),onScroll:W,onKeyDown:U,spellCheck:!1,autoCapitalize:"off",autoCorrect:"off",className:"min-h-[7lh] w-full resize-y bg-transparent px-4 py-3 leading-[1.625rem] text-fg-2 placeholder-fg-muted outline-none",placeholder:"SELECT * FROM runs LIMIT 10"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var P80=`SELECT workflow_name, AVG(duration_seconds) as avg_duration, COUNT(*) as run_count diff --git a/lib/crates/fabro-spa/assets/index.html b/lib/crates/fabro-spa/assets/index.html index 9b8a8bae7..3f19caff8 100644 --- a/lib/crates/fabro-spa/assets/index.html +++ b/lib/crates/fabro-spa/assets/index.html @@ -61,7 +61,7 @@ - +