@botbuddy/react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # @botbuddy/react
2
+
3
+ Draggable, translucent test coordination widgets for React. Drop into any app for human-in-the-loop QA powered by BotBuddy.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @botbuddy/react
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```tsx
14
+ import { TestRunWidget } from "@botbuddy/react";
15
+
16
+ function App() {
17
+ return (
18
+ <>
19
+ {/* Your app */}
20
+ <TestRunWidget
21
+ config={{
22
+ serverUrl: "https://your-project.supabase.co/functions/v1/mcp-server",
23
+ anonKey: "your-supabase-anon-key",
24
+ }}
25
+ appSlug="my-app"
26
+ environment="staging"
27
+ targetUrl={window.location.origin}
28
+ onNavigate={(path) => (window.location.href = path)}
29
+ />
30
+ </>
31
+ );
32
+ }
33
+ ```
34
+
35
+ ## Widget Modes
36
+
37
+ | Mode | Shows |
38
+ |------|-------|
39
+ | **Mini** | Pill with pass/total count + clickable PR & ticket links |
40
+ | **Expanded** | Title, environment badge, stats, description, links |
41
+ | **Max** | Full test case list with verdict buttons + fail reason input |
42
+
43
+ Click the pill to cycle through modes. Drag to reposition anywhere on screen.
44
+
45
+ ## Props
46
+
47
+ | Prop | Type | Description |
48
+ |------|------|-------------|
49
+ | `config` | `BotBuddyConfig` | Server URL and anon key (required) |
50
+ | `appSlug` | `string` | App identifier (required) |
51
+ | `environment` | `string` | `local`, `preview`, `staging`, `production`, `custom` (required) |
52
+ | `testRunId` | `string` | Direct test run ID lookup |
53
+ | `targetUrl` | `string` | Match by deployed URL (e.g. `window.location.origin`) |
54
+ | `version` | `string` | Match by version |
55
+ | `commitSha` | `string` | Match by commit SHA |
56
+ | `branchName` | `string` | Match by branch name |
57
+ | `onNavigate` | `(path: string) => void` | Called when user clicks a test case URL |
58
+ | `displayName` | `string` | Attribution name for verdicts (default: `"human"`) |
59
+ | `defaultMode` | `WidgetMode` | Initial mode: `"mini"`, `"expanded"`, or `"max"` |
60
+ | `defaultPosition` | `{ x: number; y: number }` | Initial pixel position |
61
+
62
+ ## Resolution Priority
63
+
64
+ The widget finds the active test run by checking (in order):
65
+
66
+ 1. `testRunId` — exact match
67
+ 2. `targetUrl` — matches `target_url` on the run
68
+ 3. `appSlug` + `environment` + `version`
69
+ 4. `commitSha`
70
+ 5. `branchName`
71
+
72
+ ## Hooks
73
+
74
+ Use the hooks directly for custom UIs:
75
+
76
+ ```tsx
77
+ import { useTestRun, useDraggable } from "@botbuddy/react";
78
+
79
+ const { run, widgetState, setVerdict, markComplete } = useTestRun({
80
+ config: { serverUrl: "...", anonKey: "..." },
81
+ appSlug: "my-app",
82
+ environment: "staging",
83
+ });
84
+
85
+ const { pos, onPointerDown, onPointerMove, onPointerUp } = useDraggable(16, 600);
86
+ ```
87
+
88
+ ## Zero Dependencies
89
+
90
+ The widget uses inline styles and inline SVG icons — no Tailwind, no icon library, no CSS imports needed. Just React.
91
+
92
+ ## License
93
+
94
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ 'use strict';var react=require('react'),jsxRuntime=require('react/jsx-runtime');function U(e,p){let[f,_]=react.useState({x:e,y:p}),x=react.useRef(false),m=react.useRef({x:0,y:0}),h=react.useRef(false),w=react.useCallback(c=>{x.current=true,h.current=false,m.current={x:c.clientX-f.x,y:c.clientY-f.y},c.target.setPointerCapture(c.pointerId);},[f]),s=react.useCallback(c=>{x.current&&(h.current=true,_({x:c.clientX-m.current.x,y:c.clientY-m.current.y}));},[]),u=react.useCallback(()=>{x.current=false;},[]);return {pos:f,onPointerDown:w,onPointerMove:s,onPointerUp:u,didDrag:h}}function W(e){let[p,f]=react.useState(null),[_,x]=react.useState("loading"),m=react.useCallback(async()=>{try{let s=new URLSearchParams;e.testRunId&&s.set("test_run_id",e.testRunId),e.targetUrl&&s.set("target_url",e.targetUrl),s.set("app_slug",e.appSlug),s.set("environment",e.environment),e.version&&s.set("version",e.version),e.commitSha&&s.set("commit_sha",e.commitSha),e.branchName&&s.set("branch_name",e.branchName);let u=await fetch(`${e.config.serverUrl}/widget/test-run?${s.toString()}`,{headers:{Authorization:`Bearer ${e.config.anonKey}`,apikey:e.config.anonKey}});if(u.status===404){x("no-match"),f(null);return}if(!u.ok){x("error");return}let c=await u.json();f(c),x(c.status==="completed"?"completed":"ready");}catch{x("error");}},[e.config.serverUrl,e.config.anonKey,e.appSlug,e.environment,e.testRunId,e.targetUrl,e.version,e.commitSha,e.branchName]);return react.useEffect(()=>{m();},[m]),{run:p,widgetState:_,fetchRun:m,setVerdict:async(s,u,c,M="human")=>{let b=await fetch(`${e.config.serverUrl}/widget/test-cases/${s}/verdict`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e.config.anonKey}`,apikey:e.config.anonKey},body:JSON.stringify({verdict:u,fail_reason:c,display_name:M})});if(b.ok){let{summary:k}=await b.json();f(y=>y?{...y,summary:k,test_cases:y.test_cases.map(v=>v.id===s?{...v,verdict:u,fail_reason:u==="fail"&&c||null,verdict_by_display_name:M,verdict_at:new Date().toISOString()}:v)}:null);}return b.ok},markComplete:async()=>{if(!p)return false;let s=await fetch(`${e.config.serverUrl}/widget/test-runs/${p.id}/complete`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e.config.anonKey}`,apikey:e.config.anonKey}});return s.ok&&m(),s.ok},setRun:f}}var g=({d:e,className:p})=>jsxRuntime.jsx("svg",{className:p,width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:jsxRuntime.jsx("path",{d:e})}),a={clipboard:"M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2",check:"M22 11.08V12a10 10 0 1 1-5.93-9.14M22 4 12 14.01l-3-3",x:"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20M15 9l-6 6M9 9l6 6",circle:"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20",skip:"M5 4l10 8-10 8V4M19 5v14",grip:"M9 5h.01M15 5h.01M9 12h.01M15 12h.01M9 19h.01M15 19h.01",minus:"M5 12h14",chevronUp:"M18 15l-6-6-6 6",chevronDown:"M6 9l6 6 6-6",externalLink:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14L21 3",pr:"M6 3v18M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6M6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6M18 9a9 9 0 0 1-9 9",ticket:"M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z",loader:"M21 12a9 9 0 1 1-6.219-8.56",checkAll:"M18 6L7 17l-5-5M22 10l-7.5 7.5L13 16",alert:"M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0M12 9v4M12 17h.01"};var re={pending:a.circle,pass:a.check,fail:a.x,skip:a.skip},F={local:{bg:"rgba(59,130,246,0.15)",text:"#60a5fa",border:"rgba(59,130,246,0.3)"},preview:{bg:"rgba(168,85,247,0.15)",text:"#c084fc",border:"rgba(168,85,247,0.3)"},staging:{bg:"rgba(245,158,11,0.15)",text:"#fbbf24",border:"rgba(245,158,11,0.3)"},production:{bg:"rgba(239,68,68,0.15)",text:"#f87171",border:"rgba(239,68,68,0.3)"},custom:{bg:"rgba(128,128,128,0.15)",text:"#888",border:"rgba(128,128,128,0.3)"}},i={root:{position:"fixed",zIndex:9999,userSelect:"none",fontFamily:"ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace"},pill:{display:"inline-flex",alignItems:"center",gap:"6px",padding:"4px 8px",borderRadius:"9999px",background:"rgb(30,30,30)",border:"1px solid rgba(255,255,255,0.15)",boxShadow:"0 4px 12px rgba(0,0,0,0.3)",cursor:"pointer",fontSize:"9px",color:"#e5e5e5"},card:e=>({width:`${e}px`,borderRadius:"8px",background:"rgb(30,30,30)",border:"1px solid rgba(255,255,255,0.15)",boxShadow:"0 8px 24px rgba(0,0,0,0.4)",overflow:"hidden",display:"flex",flexDirection:"column"}),handle:{padding:"8px 12px",display:"flex",alignItems:"center",gap:"8px",cursor:"grab",borderBottom:"1px solid rgba(255,255,255,0.06)"},title:{fontSize:"10px",fontWeight:600,color:"#e5e5e5",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},sub:{fontSize:"9px",color:"rgba(255,255,255,0.4)",display:"flex",alignItems:"center",gap:"6px"},btn:{padding:"2px",borderRadius:"4px",background:"transparent",border:"none",cursor:"pointer",color:"#888",display:"flex",flexShrink:0},stats:{padding:"4px 12px",display:"flex",alignItems:"center",gap:"8px",fontSize:"9px",borderBottom:"1px solid rgba(255,255,255,0.06)"},badge:e=>{let p=F[e]||F.custom;return {fontSize:"8px",padding:"0 4px",borderRadius:"4px",background:p.bg,color:p.text,border:`1px solid ${p.border}`,lineHeight:"14px"}},progress:{height:"2px",background:"rgba(255,255,255,0.06)",flexShrink:0},progressBar:e=>({height:"100%",width:`${e}%`,background:"linear-gradient(to right, #34d399, #3b82f6)",transition:"width 0.5s"}),link:e=>({display:"inline-flex",alignItems:"center",gap:"4px",fontSize:"9px",padding:"2px 6px",borderRadius:"4px",background:`${e}15`,color:e,textDecoration:"none",border:"none",cursor:"pointer"}),caseRow:{padding:"6px 12px",borderBottom:"1px solid rgba(255,255,255,0.06)",transition:"background 0.15s"},verdictBtn:(e,p)=>({fontSize:"8px",padding:"2px 6px",borderRadius:"4px",border:"none",cursor:"pointer",background:e?`${p}33`:"transparent",color:e?p:"#888",transition:"all 0.15s"}),input:{flex:1,fontSize:"9px",background:"rgba(0,0,0,0.3)",border:"1px solid rgba(255,255,255,0.1)",borderRadius:"4px",padding:"2px 6px",color:"#e5e5e5",outline:"none"}};function oe({config:e,appSlug:p,environment:f,testRunId:_,targetUrl:x,version:m,commitSha:h,branchName:w,onNavigate:s,displayName:u="human",defaultMode:c="mini",defaultPosition:M}){let[b,k]=react.useState(c),[y,v]=react.useState(""),[Y,P]=react.useState(null),[L,V]=react.useState(null),j=M?.x??16,X=M?.y??(typeof window<"u"?window.innerHeight-56:600),{pos:$,onPointerDown:z,onPointerMove:B,onPointerUp:I,didDrag:H}=U(j,X),{run:o,widgetState:S,fetchRun:J,setVerdict:A,markComplete:Z}=W({config:e,appSlug:p,environment:f,testRunId:_,targetUrl:x,version:m,commitSha:h,branchName:w}),R=react.useCallback(async(r,d,G)=>{V(r);try{await A(r,d,G,u);}finally{V(null),P(null),v("");}},[A,u]),l=o?.summary||{total:0,pending:0,pass:0,fail:0},q=l.total>0&&l.pending===0,K=l.total>0?(l.pass+l.fail)/l.total*100:0;if(S==="no-match"||S==="loading")return null;let E=()=>{H.current||k(r=>r==="mini"?"expanded":r==="expanded"?"max":"mini");},C=({icon:r,onClick:d})=>jsxRuntime.jsx("button",{style:i.btn,onClick:d,children:jsxRuntime.jsx(g,{d:r,className:""})});return jsxRuntime.jsxs("div",{style:{...i.root,left:$.x,top:$.y},children:[b==="mini"&&jsxRuntime.jsxs("div",{style:i.pill,onPointerDown:z,onPointerMove:B,onPointerUp:()=>{I(),H.current||E();},children:[jsxRuntime.jsx(g,{d:a.clipboard,className:""}),jsxRuntime.jsxs("span",{children:[l.pass,"/",l.total]}),l.fail>0&&jsxRuntime.jsxs("span",{style:{color:"#f87171"},children:[l.fail,"\u2717"]}),o?.pr_url&&jsxRuntime.jsxs("a",{href:o.pr_url,target:"_blank",rel:"noopener noreferrer",onClick:r=>r.stopPropagation(),style:{...i.link("#60a5fa"),textDecoration:"none"},children:[jsxRuntime.jsx(g,{d:a.pr,className:""}),o.pr_id||"PR"]}),o?.ticket_url&&jsxRuntime.jsxs("a",{href:o.ticket_url,target:"_blank",rel:"noopener noreferrer",onClick:r=>r.stopPropagation(),style:{...i.link("#fbbf24"),textDecoration:"none"},children:[jsxRuntime.jsx(g,{d:a.ticket,className:""}),o.ticket_id||"TKT"]})]}),b==="expanded"&&jsxRuntime.jsxs("div",{style:i.card(300),children:[jsxRuntime.jsxs("div",{style:i.handle,onPointerDown:z,onPointerMove:B,onPointerUp:I,children:[jsxRuntime.jsx(g,{d:a.grip,className:""}),jsxRuntime.jsxs("div",{style:{flex:1,minWidth:0},children:[jsxRuntime.jsx("div",{style:i.title,children:o?.title}),jsxRuntime.jsxs("div",{style:i.sub,children:[jsxRuntime.jsx("span",{style:i.badge(o?.environment||"custom"),children:o?.environment}),o?.branch_name&&jsxRuntime.jsxs("span",{children:["\u2387 ",o.branch_name]})]})]}),jsxRuntime.jsx(C,{icon:a.chevronUp,onClick:E}),jsxRuntime.jsx(C,{icon:a.minus,onClick:()=>k("mini")})]}),jsxRuntime.jsxs("div",{style:i.stats,children:[jsxRuntime.jsxs("span",{style:{color:"#34d399"},children:[l.pass,"\u2713"]}),jsxRuntime.jsxs("span",{style:{color:"#f87171"},children:[l.fail,"\u2717"]}),jsxRuntime.jsxs("span",{style:{color:"#888"},children:[l.pending,"\u25CC"]}),jsxRuntime.jsx("span",{style:{color:"#555",marginLeft:"auto"},children:l.total})]}),o?.description&&jsxRuntime.jsx("div",{style:{padding:"6px 12px",fontSize:"10px",color:"#888",borderBottom:"1px solid rgba(255,255,255,0.06)",maxHeight:"64px",overflowY:"auto"},children:o.description}),(o?.ticket_url||o?.pr_url)&&jsxRuntime.jsxs("div",{style:{padding:"6px 12px",display:"flex",gap:"8px",flexWrap:"wrap",borderBottom:"1px solid rgba(255,255,255,0.06)"},children:[o?.pr_url&&jsxRuntime.jsxs("a",{href:o.pr_url,target:"_blank",rel:"noopener noreferrer",style:{...i.link("#60a5fa"),textDecoration:"none"},children:[jsxRuntime.jsx(g,{d:a.pr,className:""}),o.pr_id||"PR"]}),o?.ticket_url&&jsxRuntime.jsxs("a",{href:o.ticket_url,target:"_blank",rel:"noopener noreferrer",style:{...i.link("#fbbf24"),textDecoration:"none"},children:[jsxRuntime.jsx(g,{d:a.ticket,className:""}),o.ticket_id||"Ticket"]})]}),jsxRuntime.jsx("div",{style:i.progress,children:jsxRuntime.jsx("div",{style:i.progressBar(K)})})]}),b==="max"&&jsxRuntime.jsxs("div",{style:{...i.card(340),maxHeight:"70vh"},children:[jsxRuntime.jsxs("div",{style:i.handle,onPointerDown:z,onPointerMove:B,onPointerUp:I,children:[jsxRuntime.jsx(g,{d:a.grip,className:""}),jsxRuntime.jsxs("div",{style:{flex:1,minWidth:0},children:[jsxRuntime.jsx("div",{style:i.title,children:o?.title}),jsxRuntime.jsxs("div",{style:i.sub,children:[jsxRuntime.jsx("span",{style:i.badge(o?.environment||"custom"),children:o?.environment}),o?.created_by_agent_name&&jsxRuntime.jsxs("span",{children:["by ",o.created_by_agent_name]})]})]}),jsxRuntime.jsx(C,{icon:a.chevronDown,onClick:()=>k("expanded")}),jsxRuntime.jsx(C,{icon:a.minus,onClick:()=>k("mini")})]}),jsxRuntime.jsxs("div",{style:i.stats,children:[jsxRuntime.jsxs("span",{style:{color:"#34d399"},children:[l.pass,"\u2713"]}),jsxRuntime.jsxs("span",{style:{color:"#f87171"},children:[l.fail,"\u2717"]}),jsxRuntime.jsxs("span",{style:{color:"#888"},children:[l.pending,"\u25CC"]}),jsxRuntime.jsx("span",{style:{color:"#555",marginLeft:"auto"},children:l.total})]}),S==="error"&&jsxRuntime.jsxs("div",{style:{padding:"12px",textAlign:"center"},children:[jsxRuntime.jsx(g,{d:a.alert,className:""}),jsxRuntime.jsx("div",{style:{fontSize:"10px",color:"#f87171"},children:"Failed to load"}),jsxRuntime.jsx("button",{onClick:J,style:{fontSize:"9px",color:"#3b82f6",background:"none",border:"none",cursor:"pointer",marginTop:"4px"},children:"Retry"})]}),S==="completed"&&jsxRuntime.jsxs("div",{style:{padding:"12px",textAlign:"center",borderBottom:"1px solid rgba(255,255,255,0.06)"},children:[jsxRuntime.jsx(g,{d:a.checkAll,className:""}),jsxRuntime.jsx("div",{style:{fontSize:"10px",color:"#34d399"},children:"Run completed"})]}),o?.intro_notes&&jsxRuntime.jsx("div",{style:{padding:"6px 12px",borderBottom:"1px solid rgba(255,255,255,0.06)",fontSize:"10px",color:"rgba(255,255,255,0.4)",fontStyle:"italic",maxHeight:"48px",overflowY:"auto"},children:o.intro_notes}),S!=="error"&&jsxRuntime.jsx("div",{style:{flex:1,overflowY:"auto",minHeight:0},children:(o?.test_cases||[]).length===0?jsxRuntime.jsx("div",{style:{padding:"16px 12px",textAlign:"center",fontSize:"10px",color:"#888"},children:"No test cases"}):(o?.test_cases||[]).map(r=>jsxRuntime.jsxs("div",{style:i.caseRow,children:[jsxRuntime.jsxs("div",{style:{display:"flex",alignItems:"flex-start",gap:"8px"},children:[jsxRuntime.jsx("div",{style:{marginTop:"2px",flexShrink:0},children:L===r.id?jsxRuntime.jsx(g,{d:a.loader,className:""}):jsxRuntime.jsx(g,{d:re[r.verdict],className:""})}),jsxRuntime.jsxs("div",{style:{minWidth:0,flex:1},children:[jsxRuntime.jsx("div",{style:{fontSize:"10px",color:"#e5e5e5",lineHeight:1.3},children:r.title}),r.description&&jsxRuntime.jsx("div",{style:{fontSize:"9px",color:"#888",marginTop:"2px"},children:r.description}),r.url_path&&jsxRuntime.jsxs("button",{onClick:()=>s?.(r.url_path),style:{fontSize:"9px",color:"#3b82f6",background:"none",border:"none",cursor:"pointer",padding:0,marginTop:"2px",display:"flex",alignItems:"center",gap:"2px"},children:[jsxRuntime.jsx(g,{d:a.externalLink,className:""}),r.url_path]}),r.verdict==="fail"&&r.fail_reason&&jsxRuntime.jsxs("div",{style:{fontSize:"9px",color:"#f87171",marginTop:"2px"},children:["\u2717 ",r.fail_reason]})]})]}),jsxRuntime.jsxs("div",{style:{display:"flex",alignItems:"center",gap:"4px",marginTop:"4px",marginLeft:"22px"},children:[["pass","fail","skip"].map(d=>jsxRuntime.jsx("button",{onClick:()=>{if(d==="fail"&&r.verdict!=="fail"){P(r.id);return}if(r.verdict===d){R(r.id,"pending");return}R(r.id,d);},disabled:L!==null,style:i.verdictBtn(r.verdict===d,d==="pass"?"#34d399":d==="fail"?"#f87171":"#888"),children:d.toUpperCase()},d)),r.verdict_by_display_name&&jsxRuntime.jsx("span",{style:{fontSize:"8px",color:"#555",marginLeft:"auto"},children:r.verdict_by_display_name})]}),Y===r.id&&jsxRuntime.jsxs("div",{style:{marginLeft:"22px",marginTop:"4px",display:"flex",gap:"4px"},children:[jsxRuntime.jsx("input",{autoFocus:true,value:y,onChange:d=>v(d.target.value),onKeyDown:d=>{d.key==="Enter"&&R(r.id,"fail",y),d.key==="Escape"&&(P(null),v(""));},placeholder:"Reason...",style:i.input}),jsxRuntime.jsx("button",{onClick:()=>R(r.id,"fail",y),style:i.verdictBtn(true,"#f87171"),children:"\u2713"})]})]},r.id))}),q&&S!=="completed"&&jsxRuntime.jsxs("div",{style:{padding:"6px 12px",borderTop:"1px solid rgba(255,255,255,0.06)",display:"flex",alignItems:"center",justifyContent:"space-between",flexShrink:0},children:[jsxRuntime.jsx("span",{style:{fontSize:"9px",color:"#34d399"},children:"All resolved"}),jsxRuntime.jsx("button",{onClick:Z,style:{fontSize:"8px",padding:"2px 8px",borderRadius:"4px",background:"rgba(59,130,246,0.15)",color:"#60a5fa",border:"none",cursor:"pointer"},children:"COMPLETE"})]}),jsxRuntime.jsx("div",{style:i.progress,children:jsxRuntime.jsx("div",{style:i.progressBar(K)})})]})]})}exports.TestRunWidget=oe;exports.useDraggable=U;exports.useTestRun=W;
@@ -0,0 +1,119 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
3
+
4
+ interface TestRun {
5
+ id: string;
6
+ app_slug: string;
7
+ environment: string;
8
+ title: string;
9
+ status: string;
10
+ description: string | null;
11
+ version: string | null;
12
+ commit_sha: string | null;
13
+ branch_name: string | null;
14
+ target_url: string | null;
15
+ intro_notes: string | null;
16
+ created_by_agent_name: string | null;
17
+ ticket_id: string | null;
18
+ ticket_url: string | null;
19
+ pr_id: string | null;
20
+ pr_url: string | null;
21
+ pr_number: number | null;
22
+ summary: TestRunSummary;
23
+ test_cases: TestCase[];
24
+ }
25
+ interface TestRunSummary {
26
+ total: number;
27
+ pending: number;
28
+ pass: number;
29
+ fail: number;
30
+ skip: number;
31
+ }
32
+ interface TestCase {
33
+ id: string;
34
+ test_run_id: string;
35
+ title: string;
36
+ description: string | null;
37
+ url_path: string | null;
38
+ expected_result: string | null;
39
+ severity: string | null;
40
+ sort_order: number;
41
+ verdict: Verdict;
42
+ verdict_by_display_name: string | null;
43
+ verdict_at: string | null;
44
+ fail_reason: string | null;
45
+ }
46
+ type Verdict = "pending" | "pass" | "fail" | "skip";
47
+ type WidgetMode = "mini" | "expanded" | "max";
48
+ type WidgetState = "loading" | "ready" | "no-match" | "error" | "completed";
49
+ interface BotBuddyConfig {
50
+ /** BotBuddy MCP server base URL */
51
+ serverUrl: string;
52
+ /** Supabase anon key for auth */
53
+ anonKey: string;
54
+ /** Supabase project URL for realtime subscriptions (optional) */
55
+ supabaseUrl?: string;
56
+ }
57
+ interface TestRunWidgetProps {
58
+ /** BotBuddy configuration */
59
+ config: BotBuddyConfig;
60
+ /** App identifier */
61
+ appSlug: string;
62
+ /** Deployment environment */
63
+ environment: string;
64
+ /** Direct test run ID lookup */
65
+ testRunId?: string;
66
+ /** Match by target URL (e.g. window.location.origin) */
67
+ targetUrl?: string;
68
+ /** Match by version */
69
+ version?: string;
70
+ /** Match by commit SHA */
71
+ commitSha?: string;
72
+ /** Match by branch name */
73
+ branchName?: string;
74
+ /** Callback when user clicks a test case URL */
75
+ onNavigate?: (path: string) => void;
76
+ /** Display name for verdict attribution */
77
+ displayName?: string;
78
+ /** Initial widget mode */
79
+ defaultMode?: WidgetMode;
80
+ /** Initial position */
81
+ defaultPosition?: {
82
+ x: number;
83
+ y: number;
84
+ };
85
+ }
86
+
87
+ declare function TestRunWidget({ config, appSlug, environment, testRunId, targetUrl, version, commitSha, branchName, onNavigate, displayName, defaultMode, defaultPosition, }: TestRunWidgetProps): react_jsx_runtime.JSX.Element | null;
88
+
89
+ declare function useDraggable(initialX: number, initialY: number): {
90
+ pos: {
91
+ x: number;
92
+ y: number;
93
+ };
94
+ onPointerDown: (e: React.PointerEvent) => void;
95
+ onPointerMove: (e: React.PointerEvent) => void;
96
+ onPointerUp: () => void;
97
+ didDrag: react.MutableRefObject<boolean>;
98
+ };
99
+
100
+ interface UseTestRunOptions {
101
+ config: BotBuddyConfig;
102
+ appSlug: string;
103
+ environment: string;
104
+ testRunId?: string;
105
+ targetUrl?: string;
106
+ version?: string;
107
+ commitSha?: string;
108
+ branchName?: string;
109
+ }
110
+ declare function useTestRun(opts: UseTestRunOptions): {
111
+ run: TestRun | null;
112
+ widgetState: WidgetState;
113
+ fetchRun: () => Promise<void>;
114
+ setVerdict: (caseId: string, verdict: string, failReason?: string, displayName?: string) => Promise<boolean>;
115
+ markComplete: () => Promise<boolean>;
116
+ setRun: react.Dispatch<react.SetStateAction<TestRun | null>>;
117
+ };
118
+
119
+ export { type BotBuddyConfig, type TestCase, type TestRun, type TestRunSummary, TestRunWidget, type TestRunWidgetProps, type Verdict, type WidgetMode, type WidgetState, useDraggable, useTestRun };
@@ -0,0 +1,119 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
3
+
4
+ interface TestRun {
5
+ id: string;
6
+ app_slug: string;
7
+ environment: string;
8
+ title: string;
9
+ status: string;
10
+ description: string | null;
11
+ version: string | null;
12
+ commit_sha: string | null;
13
+ branch_name: string | null;
14
+ target_url: string | null;
15
+ intro_notes: string | null;
16
+ created_by_agent_name: string | null;
17
+ ticket_id: string | null;
18
+ ticket_url: string | null;
19
+ pr_id: string | null;
20
+ pr_url: string | null;
21
+ pr_number: number | null;
22
+ summary: TestRunSummary;
23
+ test_cases: TestCase[];
24
+ }
25
+ interface TestRunSummary {
26
+ total: number;
27
+ pending: number;
28
+ pass: number;
29
+ fail: number;
30
+ skip: number;
31
+ }
32
+ interface TestCase {
33
+ id: string;
34
+ test_run_id: string;
35
+ title: string;
36
+ description: string | null;
37
+ url_path: string | null;
38
+ expected_result: string | null;
39
+ severity: string | null;
40
+ sort_order: number;
41
+ verdict: Verdict;
42
+ verdict_by_display_name: string | null;
43
+ verdict_at: string | null;
44
+ fail_reason: string | null;
45
+ }
46
+ type Verdict = "pending" | "pass" | "fail" | "skip";
47
+ type WidgetMode = "mini" | "expanded" | "max";
48
+ type WidgetState = "loading" | "ready" | "no-match" | "error" | "completed";
49
+ interface BotBuddyConfig {
50
+ /** BotBuddy MCP server base URL */
51
+ serverUrl: string;
52
+ /** Supabase anon key for auth */
53
+ anonKey: string;
54
+ /** Supabase project URL for realtime subscriptions (optional) */
55
+ supabaseUrl?: string;
56
+ }
57
+ interface TestRunWidgetProps {
58
+ /** BotBuddy configuration */
59
+ config: BotBuddyConfig;
60
+ /** App identifier */
61
+ appSlug: string;
62
+ /** Deployment environment */
63
+ environment: string;
64
+ /** Direct test run ID lookup */
65
+ testRunId?: string;
66
+ /** Match by target URL (e.g. window.location.origin) */
67
+ targetUrl?: string;
68
+ /** Match by version */
69
+ version?: string;
70
+ /** Match by commit SHA */
71
+ commitSha?: string;
72
+ /** Match by branch name */
73
+ branchName?: string;
74
+ /** Callback when user clicks a test case URL */
75
+ onNavigate?: (path: string) => void;
76
+ /** Display name for verdict attribution */
77
+ displayName?: string;
78
+ /** Initial widget mode */
79
+ defaultMode?: WidgetMode;
80
+ /** Initial position */
81
+ defaultPosition?: {
82
+ x: number;
83
+ y: number;
84
+ };
85
+ }
86
+
87
+ declare function TestRunWidget({ config, appSlug, environment, testRunId, targetUrl, version, commitSha, branchName, onNavigate, displayName, defaultMode, defaultPosition, }: TestRunWidgetProps): react_jsx_runtime.JSX.Element | null;
88
+
89
+ declare function useDraggable(initialX: number, initialY: number): {
90
+ pos: {
91
+ x: number;
92
+ y: number;
93
+ };
94
+ onPointerDown: (e: React.PointerEvent) => void;
95
+ onPointerMove: (e: React.PointerEvent) => void;
96
+ onPointerUp: () => void;
97
+ didDrag: react.MutableRefObject<boolean>;
98
+ };
99
+
100
+ interface UseTestRunOptions {
101
+ config: BotBuddyConfig;
102
+ appSlug: string;
103
+ environment: string;
104
+ testRunId?: string;
105
+ targetUrl?: string;
106
+ version?: string;
107
+ commitSha?: string;
108
+ branchName?: string;
109
+ }
110
+ declare function useTestRun(opts: UseTestRunOptions): {
111
+ run: TestRun | null;
112
+ widgetState: WidgetState;
113
+ fetchRun: () => Promise<void>;
114
+ setVerdict: (caseId: string, verdict: string, failReason?: string, displayName?: string) => Promise<boolean>;
115
+ markComplete: () => Promise<boolean>;
116
+ setRun: react.Dispatch<react.SetStateAction<TestRun | null>>;
117
+ };
118
+
119
+ export { type BotBuddyConfig, type TestCase, type TestRun, type TestRunSummary, TestRunWidget, type TestRunWidgetProps, type Verdict, type WidgetMode, type WidgetState, useDraggable, useTestRun };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ import {useState,useRef,useCallback,useEffect}from'react';import {jsxs,jsx}from'react/jsx-runtime';function U(e,p){let[f,_]=useState({x:e,y:p}),x=useRef(false),m=useRef({x:0,y:0}),h=useRef(false),w=useCallback(c=>{x.current=true,h.current=false,m.current={x:c.clientX-f.x,y:c.clientY-f.y},c.target.setPointerCapture(c.pointerId);},[f]),s=useCallback(c=>{x.current&&(h.current=true,_({x:c.clientX-m.current.x,y:c.clientY-m.current.y}));},[]),u=useCallback(()=>{x.current=false;},[]);return {pos:f,onPointerDown:w,onPointerMove:s,onPointerUp:u,didDrag:h}}function W(e){let[p,f]=useState(null),[_,x]=useState("loading"),m=useCallback(async()=>{try{let s=new URLSearchParams;e.testRunId&&s.set("test_run_id",e.testRunId),e.targetUrl&&s.set("target_url",e.targetUrl),s.set("app_slug",e.appSlug),s.set("environment",e.environment),e.version&&s.set("version",e.version),e.commitSha&&s.set("commit_sha",e.commitSha),e.branchName&&s.set("branch_name",e.branchName);let u=await fetch(`${e.config.serverUrl}/widget/test-run?${s.toString()}`,{headers:{Authorization:`Bearer ${e.config.anonKey}`,apikey:e.config.anonKey}});if(u.status===404){x("no-match"),f(null);return}if(!u.ok){x("error");return}let c=await u.json();f(c),x(c.status==="completed"?"completed":"ready");}catch{x("error");}},[e.config.serverUrl,e.config.anonKey,e.appSlug,e.environment,e.testRunId,e.targetUrl,e.version,e.commitSha,e.branchName]);return useEffect(()=>{m();},[m]),{run:p,widgetState:_,fetchRun:m,setVerdict:async(s,u,c,M="human")=>{let b=await fetch(`${e.config.serverUrl}/widget/test-cases/${s}/verdict`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e.config.anonKey}`,apikey:e.config.anonKey},body:JSON.stringify({verdict:u,fail_reason:c,display_name:M})});if(b.ok){let{summary:k}=await b.json();f(y=>y?{...y,summary:k,test_cases:y.test_cases.map(v=>v.id===s?{...v,verdict:u,fail_reason:u==="fail"&&c||null,verdict_by_display_name:M,verdict_at:new Date().toISOString()}:v)}:null);}return b.ok},markComplete:async()=>{if(!p)return false;let s=await fetch(`${e.config.serverUrl}/widget/test-runs/${p.id}/complete`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e.config.anonKey}`,apikey:e.config.anonKey}});return s.ok&&m(),s.ok},setRun:f}}var g=({d:e,className:p})=>jsx("svg",{className:p,width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:jsx("path",{d:e})}),a={clipboard:"M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2",check:"M22 11.08V12a10 10 0 1 1-5.93-9.14M22 4 12 14.01l-3-3",x:"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20M15 9l-6 6M9 9l6 6",circle:"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20",skip:"M5 4l10 8-10 8V4M19 5v14",grip:"M9 5h.01M15 5h.01M9 12h.01M15 12h.01M9 19h.01M15 19h.01",minus:"M5 12h14",chevronUp:"M18 15l-6-6-6 6",chevronDown:"M6 9l6 6 6-6",externalLink:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14L21 3",pr:"M6 3v18M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6M6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6M18 9a9 9 0 0 1-9 9",ticket:"M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z",loader:"M21 12a9 9 0 1 1-6.219-8.56",checkAll:"M18 6L7 17l-5-5M22 10l-7.5 7.5L13 16",alert:"M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0M12 9v4M12 17h.01"};var re={pending:a.circle,pass:a.check,fail:a.x,skip:a.skip},F={local:{bg:"rgba(59,130,246,0.15)",text:"#60a5fa",border:"rgba(59,130,246,0.3)"},preview:{bg:"rgba(168,85,247,0.15)",text:"#c084fc",border:"rgba(168,85,247,0.3)"},staging:{bg:"rgba(245,158,11,0.15)",text:"#fbbf24",border:"rgba(245,158,11,0.3)"},production:{bg:"rgba(239,68,68,0.15)",text:"#f87171",border:"rgba(239,68,68,0.3)"},custom:{bg:"rgba(128,128,128,0.15)",text:"#888",border:"rgba(128,128,128,0.3)"}},i={root:{position:"fixed",zIndex:9999,userSelect:"none",fontFamily:"ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace"},pill:{display:"inline-flex",alignItems:"center",gap:"6px",padding:"4px 8px",borderRadius:"9999px",background:"rgb(30,30,30)",border:"1px solid rgba(255,255,255,0.15)",boxShadow:"0 4px 12px rgba(0,0,0,0.3)",cursor:"pointer",fontSize:"9px",color:"#e5e5e5"},card:e=>({width:`${e}px`,borderRadius:"8px",background:"rgb(30,30,30)",border:"1px solid rgba(255,255,255,0.15)",boxShadow:"0 8px 24px rgba(0,0,0,0.4)",overflow:"hidden",display:"flex",flexDirection:"column"}),handle:{padding:"8px 12px",display:"flex",alignItems:"center",gap:"8px",cursor:"grab",borderBottom:"1px solid rgba(255,255,255,0.06)"},title:{fontSize:"10px",fontWeight:600,color:"#e5e5e5",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},sub:{fontSize:"9px",color:"rgba(255,255,255,0.4)",display:"flex",alignItems:"center",gap:"6px"},btn:{padding:"2px",borderRadius:"4px",background:"transparent",border:"none",cursor:"pointer",color:"#888",display:"flex",flexShrink:0},stats:{padding:"4px 12px",display:"flex",alignItems:"center",gap:"8px",fontSize:"9px",borderBottom:"1px solid rgba(255,255,255,0.06)"},badge:e=>{let p=F[e]||F.custom;return {fontSize:"8px",padding:"0 4px",borderRadius:"4px",background:p.bg,color:p.text,border:`1px solid ${p.border}`,lineHeight:"14px"}},progress:{height:"2px",background:"rgba(255,255,255,0.06)",flexShrink:0},progressBar:e=>({height:"100%",width:`${e}%`,background:"linear-gradient(to right, #34d399, #3b82f6)",transition:"width 0.5s"}),link:e=>({display:"inline-flex",alignItems:"center",gap:"4px",fontSize:"9px",padding:"2px 6px",borderRadius:"4px",background:`${e}15`,color:e,textDecoration:"none",border:"none",cursor:"pointer"}),caseRow:{padding:"6px 12px",borderBottom:"1px solid rgba(255,255,255,0.06)",transition:"background 0.15s"},verdictBtn:(e,p)=>({fontSize:"8px",padding:"2px 6px",borderRadius:"4px",border:"none",cursor:"pointer",background:e?`${p}33`:"transparent",color:e?p:"#888",transition:"all 0.15s"}),input:{flex:1,fontSize:"9px",background:"rgba(0,0,0,0.3)",border:"1px solid rgba(255,255,255,0.1)",borderRadius:"4px",padding:"2px 6px",color:"#e5e5e5",outline:"none"}};function oe({config:e,appSlug:p,environment:f,testRunId:_,targetUrl:x,version:m,commitSha:h,branchName:w,onNavigate:s,displayName:u="human",defaultMode:c="mini",defaultPosition:M}){let[b,k]=useState(c),[y,v]=useState(""),[Y,P]=useState(null),[L,V]=useState(null),j=M?.x??16,X=M?.y??(typeof window<"u"?window.innerHeight-56:600),{pos:$,onPointerDown:z,onPointerMove:B,onPointerUp:I,didDrag:H}=U(j,X),{run:o,widgetState:S,fetchRun:J,setVerdict:A,markComplete:Z}=W({config:e,appSlug:p,environment:f,testRunId:_,targetUrl:x,version:m,commitSha:h,branchName:w}),R=useCallback(async(r,d,G)=>{V(r);try{await A(r,d,G,u);}finally{V(null),P(null),v("");}},[A,u]),l=o?.summary||{total:0,pending:0,pass:0,fail:0},q=l.total>0&&l.pending===0,K=l.total>0?(l.pass+l.fail)/l.total*100:0;if(S==="no-match"||S==="loading")return null;let E=()=>{H.current||k(r=>r==="mini"?"expanded":r==="expanded"?"max":"mini");},C=({icon:r,onClick:d})=>jsx("button",{style:i.btn,onClick:d,children:jsx(g,{d:r,className:""})});return jsxs("div",{style:{...i.root,left:$.x,top:$.y},children:[b==="mini"&&jsxs("div",{style:i.pill,onPointerDown:z,onPointerMove:B,onPointerUp:()=>{I(),H.current||E();},children:[jsx(g,{d:a.clipboard,className:""}),jsxs("span",{children:[l.pass,"/",l.total]}),l.fail>0&&jsxs("span",{style:{color:"#f87171"},children:[l.fail,"\u2717"]}),o?.pr_url&&jsxs("a",{href:o.pr_url,target:"_blank",rel:"noopener noreferrer",onClick:r=>r.stopPropagation(),style:{...i.link("#60a5fa"),textDecoration:"none"},children:[jsx(g,{d:a.pr,className:""}),o.pr_id||"PR"]}),o?.ticket_url&&jsxs("a",{href:o.ticket_url,target:"_blank",rel:"noopener noreferrer",onClick:r=>r.stopPropagation(),style:{...i.link("#fbbf24"),textDecoration:"none"},children:[jsx(g,{d:a.ticket,className:""}),o.ticket_id||"TKT"]})]}),b==="expanded"&&jsxs("div",{style:i.card(300),children:[jsxs("div",{style:i.handle,onPointerDown:z,onPointerMove:B,onPointerUp:I,children:[jsx(g,{d:a.grip,className:""}),jsxs("div",{style:{flex:1,minWidth:0},children:[jsx("div",{style:i.title,children:o?.title}),jsxs("div",{style:i.sub,children:[jsx("span",{style:i.badge(o?.environment||"custom"),children:o?.environment}),o?.branch_name&&jsxs("span",{children:["\u2387 ",o.branch_name]})]})]}),jsx(C,{icon:a.chevronUp,onClick:E}),jsx(C,{icon:a.minus,onClick:()=>k("mini")})]}),jsxs("div",{style:i.stats,children:[jsxs("span",{style:{color:"#34d399"},children:[l.pass,"\u2713"]}),jsxs("span",{style:{color:"#f87171"},children:[l.fail,"\u2717"]}),jsxs("span",{style:{color:"#888"},children:[l.pending,"\u25CC"]}),jsx("span",{style:{color:"#555",marginLeft:"auto"},children:l.total})]}),o?.description&&jsx("div",{style:{padding:"6px 12px",fontSize:"10px",color:"#888",borderBottom:"1px solid rgba(255,255,255,0.06)",maxHeight:"64px",overflowY:"auto"},children:o.description}),(o?.ticket_url||o?.pr_url)&&jsxs("div",{style:{padding:"6px 12px",display:"flex",gap:"8px",flexWrap:"wrap",borderBottom:"1px solid rgba(255,255,255,0.06)"},children:[o?.pr_url&&jsxs("a",{href:o.pr_url,target:"_blank",rel:"noopener noreferrer",style:{...i.link("#60a5fa"),textDecoration:"none"},children:[jsx(g,{d:a.pr,className:""}),o.pr_id||"PR"]}),o?.ticket_url&&jsxs("a",{href:o.ticket_url,target:"_blank",rel:"noopener noreferrer",style:{...i.link("#fbbf24"),textDecoration:"none"},children:[jsx(g,{d:a.ticket,className:""}),o.ticket_id||"Ticket"]})]}),jsx("div",{style:i.progress,children:jsx("div",{style:i.progressBar(K)})})]}),b==="max"&&jsxs("div",{style:{...i.card(340),maxHeight:"70vh"},children:[jsxs("div",{style:i.handle,onPointerDown:z,onPointerMove:B,onPointerUp:I,children:[jsx(g,{d:a.grip,className:""}),jsxs("div",{style:{flex:1,minWidth:0},children:[jsx("div",{style:i.title,children:o?.title}),jsxs("div",{style:i.sub,children:[jsx("span",{style:i.badge(o?.environment||"custom"),children:o?.environment}),o?.created_by_agent_name&&jsxs("span",{children:["by ",o.created_by_agent_name]})]})]}),jsx(C,{icon:a.chevronDown,onClick:()=>k("expanded")}),jsx(C,{icon:a.minus,onClick:()=>k("mini")})]}),jsxs("div",{style:i.stats,children:[jsxs("span",{style:{color:"#34d399"},children:[l.pass,"\u2713"]}),jsxs("span",{style:{color:"#f87171"},children:[l.fail,"\u2717"]}),jsxs("span",{style:{color:"#888"},children:[l.pending,"\u25CC"]}),jsx("span",{style:{color:"#555",marginLeft:"auto"},children:l.total})]}),S==="error"&&jsxs("div",{style:{padding:"12px",textAlign:"center"},children:[jsx(g,{d:a.alert,className:""}),jsx("div",{style:{fontSize:"10px",color:"#f87171"},children:"Failed to load"}),jsx("button",{onClick:J,style:{fontSize:"9px",color:"#3b82f6",background:"none",border:"none",cursor:"pointer",marginTop:"4px"},children:"Retry"})]}),S==="completed"&&jsxs("div",{style:{padding:"12px",textAlign:"center",borderBottom:"1px solid rgba(255,255,255,0.06)"},children:[jsx(g,{d:a.checkAll,className:""}),jsx("div",{style:{fontSize:"10px",color:"#34d399"},children:"Run completed"})]}),o?.intro_notes&&jsx("div",{style:{padding:"6px 12px",borderBottom:"1px solid rgba(255,255,255,0.06)",fontSize:"10px",color:"rgba(255,255,255,0.4)",fontStyle:"italic",maxHeight:"48px",overflowY:"auto"},children:o.intro_notes}),S!=="error"&&jsx("div",{style:{flex:1,overflowY:"auto",minHeight:0},children:(o?.test_cases||[]).length===0?jsx("div",{style:{padding:"16px 12px",textAlign:"center",fontSize:"10px",color:"#888"},children:"No test cases"}):(o?.test_cases||[]).map(r=>jsxs("div",{style:i.caseRow,children:[jsxs("div",{style:{display:"flex",alignItems:"flex-start",gap:"8px"},children:[jsx("div",{style:{marginTop:"2px",flexShrink:0},children:L===r.id?jsx(g,{d:a.loader,className:""}):jsx(g,{d:re[r.verdict],className:""})}),jsxs("div",{style:{minWidth:0,flex:1},children:[jsx("div",{style:{fontSize:"10px",color:"#e5e5e5",lineHeight:1.3},children:r.title}),r.description&&jsx("div",{style:{fontSize:"9px",color:"#888",marginTop:"2px"},children:r.description}),r.url_path&&jsxs("button",{onClick:()=>s?.(r.url_path),style:{fontSize:"9px",color:"#3b82f6",background:"none",border:"none",cursor:"pointer",padding:0,marginTop:"2px",display:"flex",alignItems:"center",gap:"2px"},children:[jsx(g,{d:a.externalLink,className:""}),r.url_path]}),r.verdict==="fail"&&r.fail_reason&&jsxs("div",{style:{fontSize:"9px",color:"#f87171",marginTop:"2px"},children:["\u2717 ",r.fail_reason]})]})]}),jsxs("div",{style:{display:"flex",alignItems:"center",gap:"4px",marginTop:"4px",marginLeft:"22px"},children:[["pass","fail","skip"].map(d=>jsx("button",{onClick:()=>{if(d==="fail"&&r.verdict!=="fail"){P(r.id);return}if(r.verdict===d){R(r.id,"pending");return}R(r.id,d);},disabled:L!==null,style:i.verdictBtn(r.verdict===d,d==="pass"?"#34d399":d==="fail"?"#f87171":"#888"),children:d.toUpperCase()},d)),r.verdict_by_display_name&&jsx("span",{style:{fontSize:"8px",color:"#555",marginLeft:"auto"},children:r.verdict_by_display_name})]}),Y===r.id&&jsxs("div",{style:{marginLeft:"22px",marginTop:"4px",display:"flex",gap:"4px"},children:[jsx("input",{autoFocus:true,value:y,onChange:d=>v(d.target.value),onKeyDown:d=>{d.key==="Enter"&&R(r.id,"fail",y),d.key==="Escape"&&(P(null),v(""));},placeholder:"Reason...",style:i.input}),jsx("button",{onClick:()=>R(r.id,"fail",y),style:i.verdictBtn(true,"#f87171"),children:"\u2713"})]})]},r.id))}),q&&S!=="completed"&&jsxs("div",{style:{padding:"6px 12px",borderTop:"1px solid rgba(255,255,255,0.06)",display:"flex",alignItems:"center",justifyContent:"space-between",flexShrink:0},children:[jsx("span",{style:{fontSize:"9px",color:"#34d399"},children:"All resolved"}),jsx("button",{onClick:Z,style:{fontSize:"8px",padding:"2px 8px",borderRadius:"4px",background:"rgba(59,130,246,0.15)",color:"#60a5fa",border:"none",cursor:"pointer"},children:"COMPLETE"})]}),jsx("div",{style:i.progress,children:jsx("div",{style:i.progressBar(K)})})]})]})}export{oe as TestRunWidget,U as useDraggable,W as useTestRun};
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@botbuddy/react",
3
+ "version": "0.1.0",
4
+ "description": "BotBuddy test coordination widgets for React — draggable, translucent overlays for human-in-the-loop QA",
5
+ "type": "module",
6
+ "main": "dist/index.cjs",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": ["dist", "README.md"],
17
+ "sideEffects": false,
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "dev": "tsup --watch",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "peerDependencies": {
24
+ "react": ">=18",
25
+ "react-dom": ">=18"
26
+ },
27
+ "devDependencies": {
28
+ "react": "^18.3.1",
29
+ "react-dom": "^18.3.1",
30
+ "tsup": "^8.4.0",
31
+ "typescript": "^5.8.3"
32
+ },
33
+ "keywords": ["botbuddy", "testing", "qa", "widget", "react", "overlay"],
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/botbuddy/react"
38
+ }
39
+ }