@venturewild/workspace 0.1.13 → 0.1.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@venturewild/workspace",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "Claude Code Web — Replit/Lovable-style chat-first browser UI that wraps the AI agent already installed on your machine.",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -807,29 +807,24 @@ export async function createServer(overrides = {}) {
807
807
  const forbidden = require(c, 'share');
808
808
  if (forbidden) return forbidden;
809
809
  const body = await c.req.json().catch(() => ({}));
810
- const code = String(body.code ?? '').trim();
811
- // The owner types the code shown on the new device; resolve it to the one
812
- // pending request (confused-deputy defense possession of the code). A code
813
- // that matches nothing pending mints nothing.
814
- const pending = pairing.findPendingByCode(code);
815
- if (!pending) {
816
- return c.json({ error: 'no_match' }, 404);
810
+ const requestId = typeof body.requestId === 'string' ? body.requestId : '';
811
+ // One tap: the owner approves the specific pending request they identified
812
+ // (by its code + label) on the new device. No token is minted unless that
813
+ // request is genuinely pending.
814
+ const pending = pairing.get(requestId);
815
+ if (!pending || pending.status !== 'pending') {
816
+ return c.json({ error: 'not_pending' }, 404);
817
817
  }
818
818
  const minted = await mintDeviceToken({
819
819
  secret: config.shareSecret,
820
820
  workspaceId: config.workspaceId,
821
821
  });
822
- // Re-validate inside approve (status + code) to close the await-window race.
823
- if (!pairing.approve(pending.requestId, code, minted)) {
822
+ // Re-validate inside approve to close the await-window race.
823
+ if (!pairing.approve(requestId, minted)) {
824
824
  return c.json({ error: 'not_pending' }, 409);
825
825
  }
826
826
  tokenRegistry.add({ ...minted, kind: 'device', label: pending.label, createdAt: Date.now() });
827
- activityBus.publish({
828
- type: 'pair-approved',
829
- requestId: pending.requestId,
830
- sub: minted.sub,
831
- label: pending.label,
832
- });
827
+ activityBus.publish({ type: 'pair-approved', requestId, sub: minted.sub, label: pending.label });
833
828
  auditAction(c, 'pair-approve', `label="${pending.label}" grantSub=${minted.sub}`);
834
829
  return c.json({ ok: true, label: pending.label });
835
830
  });
@@ -79,17 +79,6 @@ export class PairingStore {
79
79
  return { requestId, code: rec.code, expiresAt: rec.expiresAt };
80
80
  }
81
81
 
82
- // Resolve a code (typed by the owner off the new device's screen) to its
83
- // pending request. The code is the possession factor — it is never exposed in
84
- // listPending, only shown on the requesting device.
85
- findPendingByCode(code) {
86
- this.prune();
87
- const want = String(code);
88
- for (const r of this.requests.values()) {
89
- if (r.status === 'pending' && r.code === want) return r;
90
- }
91
- return null;
92
- }
93
82
 
94
83
  get(requestId) {
95
84
  this.prune();
@@ -100,20 +89,22 @@ export class PairingStore {
100
89
  // so the owner can visually match it against the new device's screen).
101
90
  listPending() {
102
91
  this.prune();
103
- // NB: the code is deliberately NOT included the owner proves possession by
104
- // typing the code off the new device; exposing it here would weaken that.
92
+ // Includes the code so the owner can match it against the new device's
93
+ // screen and approve the right one if more than one is pending. Approval
94
+ // itself is by requestId (one tap); the code is the human "is this mine?"
95
+ // confirmation, not a typed secret.
105
96
  return [...this.requests.values()]
106
97
  .filter((r) => r.status === 'pending')
107
- .map((r) => ({ requestId: r.requestId, label: r.label, createdAt: r.createdAt }));
98
+ .map((r) => ({ requestId: r.requestId, code: r.code, label: r.label, createdAt: r.createdAt }));
108
99
  }
109
100
 
110
- // Approve only if the owner echoed the matching code. Returns true on
111
- // success. `tokenInfo` = { token, sub, exp } from mintDeviceToken.
112
- approve(requestId, code, tokenInfo) {
101
+ // Approve a specific pending request (the one the owner tapped, having matched
102
+ // its code against the new device). Returns true on success. `tokenInfo` =
103
+ // { token, sub, exp } from mintDeviceToken.
104
+ approve(requestId, tokenInfo) {
113
105
  this.prune();
114
106
  const r = this.requests.get(requestId);
115
107
  if (!r || r.status !== 'pending') return false;
116
- if (String(code) !== r.code) return false;
117
108
  r.status = 'approved';
118
109
  r.token = tokenInfo.token;
119
110
  r.sub = tokenInfo.sub;
@@ -86,4 +86,4 @@ prismjs/prism.js:
86
86
  `));const n=[],r=e.split(/(\n|\r\n)/);r[r.length-1]||r.pop();for(let i=0;i<r.length;i++){const l=r[i];i%2&&!t.newlineIsToken?n[n.length-1]+=l:n.push(l)}return n}function lE(e,t){const n=rE(e??"",t??""),r=[];for(const i of n){const l=i.added?"add":i.removed?"del":"ctx",o=i.value.split(`
87
87
  `);o.length>1&&o[o.length-1]===""&&o.pop();for(const a of o)r.push({type:l,text:a})}return r}const oE={add:"+",del:"−",ctx:" "};function vl({oldText:e,newText:t,maxRows:n=80}){const r=lE(e,t),i=r.reduce((a,s)=>a+(s.type==="add"?1:0),0),l=r.reduce((a,s)=>a+(s.type==="del"?1:0),0),o=r.slice(0,n);return d.jsxs("div",{className:"diff-view","data-testid":"diff-view",children:[d.jsxs("div",{className:"diff-stat",children:[d.jsxs("span",{className:"diff-stat-add",children:["+",i]}),d.jsxs("span",{className:"diff-stat-del",children:["−",l]})]}),d.jsx("pre",{className:"diff-body",children:o.map((a,s)=>d.jsxs("div",{className:`diff-line ${a.type}`,children:[d.jsx("span",{className:"diff-gutter",children:oE[a.type]}),d.jsx("span",{className:"diff-text",children:a.text===""?" ":a.text})]},s))}),r.length>n&&d.jsxs("div",{className:"diff-more",children:["+",r.length-n," more lines"]})]})}function Wf(e){if(!e)return"";const t=String(e).split(/[\\/]+/).filter(Boolean);return t.length>2?"…/"+t.slice(-2).join("/"):t.join("/")}function aE(e){if(!e||typeof e!="object")return"";for(const t of Object.values(e))if(typeof t=="string"&&t)return t;return""}const sE={Read:{icon:"📄",verb:"Read"},Edit:{icon:"✏️",verb:"Edit"},MultiEdit:{icon:"✏️",verb:"Edit"},Write:{icon:"📝",verb:"Write"},NotebookEdit:{icon:"📓",verb:"Edit notebook"},Bash:{icon:"▶",verb:"Run"},PowerShell:{icon:"▶",verb:"Run"},Glob:{icon:"🔍",verb:"Find"},Grep:{icon:"🔍",verb:"Search"},Task:{icon:"🤖",verb:"Agent"},WebFetch:{icon:"🌐",verb:"Fetch"},WebSearch:{icon:"🌐",verb:"Web search"},Skill:{icon:"✨",verb:"Skill"},TodoWrite:{icon:"☑",verb:"Plan"}};function uE(e,t){if(!t)return"";switch(e){case"Read":case"Edit":case"MultiEdit":case"Write":return Wf(t.file_path);case"NotebookEdit":return Wf(t.notebook_path);case"Bash":case"PowerShell":return t.command||"";case"Glob":case"Grep":return t.pattern||"";case"Task":return t.description||t.subagent_type||"";case"WebFetch":return t.url||"";case"WebSearch":return t.query||"";case"Skill":return t.command||t.name||"";default:return aE(t)}}function cE({name:e,input:t}){return(t==null?void 0:t._raw)!=null?d.jsx("pre",{className:"tool-raw",children:String(t._raw)}):e==="Edit"?d.jsx(vl,{oldText:t.old_string||"",newText:t.new_string||""}):e==="MultiEdit"&&Array.isArray(t.edits)?d.jsx(d.Fragment,{children:t.edits.map((n,r)=>d.jsx(vl,{oldText:n.old_string||"",newText:n.new_string||""},r))}):e==="Write"?d.jsx(vl,{oldText:"",newText:t.content||""}):(e==="Bash"||e==="PowerShell")&&t.description?d.jsx("div",{className:"tool-desc",children:t.description}):null}function fE({content:e,isError:t}){if(e==null)return null;const n=String(e);if(!n.trim()&&!t)return null;const i=n.length>4e3?n.slice(0,4e3)+`
88
88
  … (truncated)`:n,l=n.split(`
89
- `).find(o=>o.trim())||(t?"error":"done");return d.jsxs("details",{className:`tool-result ${t?"err":""}`,open:t,children:[d.jsxs("summary",{children:[t?"✗ ":"",l.slice(0,140)]}),d.jsx("pre",{children:i})]})}function dE({block:e}){const{name:t,input:n={},result:r,done:i,isError:l}=e,o=sE[t]||{icon:"⚙",verb:t},a=uE(t,n),s=i?l?"error":"done":"running";return d.jsxs("div",{className:`tool-card ${s}`,"data-testid":"tool-card","data-tool":t,children:[d.jsxs("div",{className:"tool-head",children:[d.jsx("span",{className:"tool-icon",children:o.icon}),d.jsx("span",{className:"tool-verb",children:o.verb}),a&&d.jsx("code",{className:"tool-target",children:a}),t==="Edit"&&n.replace_all&&d.jsx("span",{className:"tool-tag",children:"all"}),d.jsx("span",{className:"tool-status","data-status":s,children:s==="running"?d.jsx("span",{className:"tool-spinner","aria-label":"running"}):s==="error"?"✗":"✓"})]}),d.jsx(cE,{name:t,input:n}),d.jsx(fE,{content:r,isError:l})]})}const il="wild-workspace.chat.v2";function pE(e,t){var i;const n=e.blocks?e.blocks.slice():[],r=n[n.length-1];switch(t.type){case"text":return r&&r.type==="text"?n[n.length-1]={...r,text:r.text+t.text}:n.push({type:"text",text:t.text}),{...e,blocks:n};case"thinking":return r&&r.type==="thinking"?n[n.length-1]={...r,text:r.text+t.text}:n.push({type:"thinking",text:t.text}),{...e,blocks:n};case"tool-use":return n.push({type:"tool",id:t.id,name:t.name,input:t.input||{},done:!1}),{...e,blocks:n};case"tool-result":{const l=n.findIndex(o=>o.type==="tool"&&o.id===t.id);return l>=0&&(n[l]={...n[l],result:t.content,isError:t.isError,done:!0}),{...e,blocks:n}}case"usage":return{...e,usage:t.usage,costUsd:(i=t.usage)==null?void 0:i.cost_usd,durationMs:t.durationMs};case"error":return n.push({type:"error",text:t.message||"agent error"}),{...e,blocks:n};default:return e}}function Gf(e,t,n){const r=e.findIndex(l=>l.id===t);if(r<0)return e;const i=e.slice();return i[r]=pE(i[r],n),i}function hE(e,t,n){if(!n||!n.trim())return e;const r=e.findIndex(l=>l.id===t);if(r<0)return e;const i=e.slice();return i[r]={...i[r],stderr:(i[r].stderr||"")+n},i}function mE({role:e,capabilities:t,mode:n,token:r}){const[i,l]=_.useState(()=>{try{const j=localStorage.getItem(il);if(j)return JSON.parse(j)}catch{}return[]}),[o,a]=_.useState(null),[s,u]=_.useState(!1),[f,c]=_.useState(""),[p,h]=_.useState(!1),m=_.useRef(null),v=_.useRef(null),k=_.useRef(!0);_.useEffect(()=>{try{localStorage.setItem(il,JSON.stringify(i.slice(-120)))}catch{}},[i]),_.useEffect(()=>{const j=v.current;j&&k.current&&(j.scrollTop=j.scrollHeight)},[i]);function x(){const j=v.current;j&&(k.current=j.scrollHeight-j.scrollTop-j.clientHeight<90)}_.useEffect(()=>{let j=!1,T=0,L=null,C=null;const R=()=>{if(j)return;const z=window.location.protocol==="https:"?"wss":"ws",$=new URL(`${z}://${window.location.host}/ws/chat`);r&&$.searchParams.set("t",r),C=new WebSocket($.toString()),m.current=C,C.onopen=()=>{T=0,h(!0)},C.onmessage=U=>{let M;try{M=JSON.parse(U.data)}catch{return}if(M.type==="chunk")l(V=>Gf(V,M.messageId,M.chunk));else if(M.type==="stderr")l(V=>hE(V,M.messageId,M.text));else if(M.type==="end")a(null),u(!1);else if(M.type==="error")a(null),u(!1),M.messageId?l(V=>Gf(V,M.messageId,{type:"error",message:M.message||"agent error"})):l(V=>[...V,{id:`err-${Date.now()}`,role:"error",text:M.message||"agent error"}]);else if(M.type==="turn-begin")l(V=>{if(V.some(F=>F.id===M.messageId))return V;const q=[];return M.userText&&q.push({id:`u-${M.messageId}`,role:"user",text:M.userText}),M.note&&q.push({id:`note-${M.messageId}`,role:"system",text:M.note}),q.push({id:M.messageId,role:"agent",blocks:[]}),[...V,...q]}),a(M.messageId),u(!0);else if(M.type==="reset"){l([]),a(null),u(!1);try{localStorage.removeItem(il)}catch{}}},C.onerror=()=>u(!1),C.onclose=()=>{if(h(!1),u(!1),a(null),j)return;const U=Math.min(1e3*2**T,1e4);T+=1,L=setTimeout(R,U)}};return R(),()=>{j=!0,clearTimeout(L);try{C==null||C.close()}catch{}}},[r]);function y(){if(!t.chatWrite)return;const j=f.trim();if(!j||s)return;const T=m.current,L=T==null?void 0:T.readyState;if(!T||L!==WebSocket.OPEN&&L!==WebSocket.CONNECTING){l(z=>[...z,{id:`err-${Date.now()}`,role:"error",text:"reconnecting to your agent — try again in a few seconds"}]);return}const C=`m-${Date.now()}`;k.current=!0,l(z=>[...z,{id:`u-${C}`,role:"user",text:j},{id:C,role:"agent",blocks:[]}]),a(C),u(!0),c("");const R=JSON.stringify({type:"send",text:j,mode:n,messageId:C});L===WebSocket.OPEN?T.send(R):T.addEventListener("open",()=>{try{T.send(R)}catch{}},{once:!0})}function g(){var j;((j=m.current)==null?void 0:j.readyState)===WebSocket.OPEN&&m.current.send(JSON.stringify({type:"cancel"})),u(!1),a(null)}function S(){var j;((j=m.current)==null?void 0:j.readyState)===WebSocket.OPEN&&m.current.send(JSON.stringify({type:"reset"})),l([]),a(null),u(!1);try{localStorage.removeItem(il)}catch{}}function N(j){j.key==="Enter"&&!j.shiftKey&&(j.preventDefault(),y())}const b=i.length===0;return d.jsxs(d.Fragment,{children:[!b&&t.chatWrite&&d.jsx("div",{className:"chat-toolbar","data-testid":"chat-toolbar",children:d.jsx("button",{className:"chat-newbtn",onClick:S,disabled:s,"data-testid":"new-chat-btn",title:"Start a fresh conversation — the agent forgets the current thread",children:"+ New chat"})}),d.jsxs("div",{className:"chat-messages",ref:v,onScroll:x,"data-testid":"chat-messages",children:[b&&d.jsx(kE,{role:e,connected:p}),i.map(j=>d.jsx(gE,{message:j,streaming:j.id===o},j.id))]}),t.chatWrite&&d.jsxs("div",{className:"chat-input","data-testid":"chat-input",children:[d.jsx("textarea",{"data-testid":"chat-textarea",placeholder:e==="client"?"Describe the change you’d like…":"Ask the agent to build, fix, or explain something — Enter to send",value:f,onChange:j=>c(j.target.value),onKeyDown:N,disabled:s,rows:2}),s?d.jsx("button",{onClick:g,"data-testid":"cancel-btn",children:"Stop"}):d.jsx("button",{className:"primary send-btn",onClick:y,disabled:!f.trim(),"data-testid":"send-btn",children:"Send"})]}),!t.chatWrite&&e==="viewer"&&d.jsx("div",{className:"chat-readonly-banner",children:"You’re viewing a shared workspace (read-only)."})]})}function gE({message:e,streaming:t}){if(e.role==="user")return d.jsx("div",{className:"chat-msg user","data-testid":"chat-msg-user",children:d.jsx("div",{className:"msg-plain",children:e.text})});if(e.role==="system")return d.jsx("div",{className:"chat-msg system","data-testid":"chat-msg-system",children:e.text});if(e.role==="error")return d.jsxs("div",{className:"chat-msg error","data-testid":"chat-msg-error",children:["⚠ ",e.text]});const n=e.blocks||[],r=n[n.length-1],i=t&&(r==null?void 0:r.type)==="text"?n.length-1:-1,l=t&&(n.length===0||(r==null?void 0:r.type)==="tool"&&r.done);return d.jsxs("div",{className:"chat-msg agent","data-testid":"chat-msg-agent",children:[n.map((o,a)=>d.jsx(yE,{block:o,cursor:a===i},o.id||`${o.type}-${a}`)),l&&d.jsx(xE,{}),e.usage&&d.jsx(vE,{usage:e.usage,costUsd:e.costUsd,durationMs:e.durationMs}),e.stderr&&d.jsxs("details",{className:"stderr-block",children:[d.jsx("summary",{children:"stderr"}),d.jsx("pre",{children:e.stderr})]})]})}function yE({block:e,cursor:t}){return e.type==="text"?d.jsxs("div",{className:"agent-text",children:[d.jsx(JS,{text:e.text}),t&&d.jsx("span",{className:"stream-cursor"})]}):e.type==="thinking"?d.jsxs("details",{className:"thinking-block",children:[d.jsx("summary",{children:"💭 Thought process"}),d.jsx("div",{className:"thinking-body",children:e.text})]}):e.type==="tool"?d.jsx(dE,{block:e}):e.type==="error"?d.jsxs("div",{className:"agent-error",children:["⚠ ",e.text]}):null}function xE(){return d.jsxs("div",{className:"agent-working","data-testid":"agent-working",children:[d.jsxs("span",{className:"dots",children:[d.jsx("i",{}),d.jsx("i",{}),d.jsx("i",{})]}),"working"]})}function vE({usage:e,costUsd:t,durationMs:n}){const r=typeof t=="number"?t:e==null?void 0:e.cost_usd;return d.jsxs("div",{className:"usage-footer","data-testid":"usage-footer",children:[(e==null?void 0:e.output_tokens)!=null&&d.jsxs("span",{title:"output tokens",children:["↑ ",e.output_tokens.toLocaleString()]}),typeof r=="number"&&r>0&&d.jsxs("span",{title:"cost (USD)",children:["$",r.toFixed(4)]}),n!=null&&d.jsxs("span",{children:[(n/1e3).toFixed(1),"s"]})]})}function kE({role:e,connected:t}){return d.jsxs("div",{className:"chat-empty","data-testid":"chat-empty",children:[d.jsx("div",{className:"chat-empty-mark",children:"✦"}),d.jsx("p",{className:"chat-empty-title",children:e==="client"?"Describe what you’d like changed":"Build something by chatting"}),d.jsx("p",{className:"chat-empty-sub",children:e==="client"?"The team’s AI agent will pick it up and you’ll see it here.":"The agent can read, write, and run code in this workspace. It streams back here — edits show up as diffs."}),e!=="client"&&d.jsxs("div",{className:"chat-empty-hints",children:[d.jsx("span",{children:"“What’s in this workspace?”"}),d.jsx("span",{children:"“Build a landing page in /web”"}),d.jsx("span",{children:"“Fix the failing test”"})]}),!t&&d.jsx("p",{className:"chat-empty-warn",children:"connecting to the agent bridge…"})]})}function wE({onCollapse:e,workspaceRoot:t,onOpen:n}){const[r,i]=_.useState(null),[l,o]=_.useState(null),[a,s]=_.useState(new Set);_.useEffect(()=>{fetch("/api/workspace/tree").then(c=>c.json()).then(c=>{if(c.error){o(c.error);return}i(c.entries||[])}).catch(c=>o(c.message||String(c)))},[]);function u(c){s(p=>{const h=new Set(p);return h.has(c)?h.delete(c):h.add(c),h})}function f(c,p=0){var v;const h=c.type==="dir",m=a.has(c.path);return d.jsxs("div",{children:[d.jsxs("div",{className:`tree-node ${h?"dir":""}`,onClick:()=>h?u(c.path):n==null?void 0:n(c.path),style:{paddingLeft:p*12+4},"data-testid":`tree-node-${c.path}`,children:[d.jsx("span",{style:{width:12,display:"inline-block"},children:h?m?"▾":"▸":" "}),d.jsx("span",{children:c.name})]}),h&&m&&((v=c.children)==null?void 0:v.map(k=>f(k,p+1)))]},c.path)}return d.jsxs("aside",{className:"tree-pane","data-testid":"file-tree",children:[d.jsxs("div",{className:"tree-header",children:[d.jsx("span",{children:"Files"}),d.jsx("button",{onClick:e,style:{padding:"2px 6px",fontSize:11},children:"⟨"})]}),d.jsxs("div",{className:"tree-list",children:[l&&d.jsx("div",{style:{color:"var(--error)",padding:4},children:l}),!r&&!l&&d.jsxs("div",{style:{color:"var(--text-muted)",padding:4},children:[d.jsx("span",{className:"spinner"})," loading…"]}),(r==null?void 0:r.length)===0&&d.jsx("div",{style:{color:"var(--text-muted)",padding:4},children:"Empty workspace"}),r==null?void 0:r.map(c=>f(c,0))]})]})}const bE=[{id:"preview",label:"Preview"},{id:"code",label:"Code"},{id:"logs",label:"Logs"},{id:"deploy",label:"Deploy"}];function SE({focus:e,workspaceRoot:t}){const[n,r]=_.useState("preview"),[i,l]=_.useState([]),[o,a]=_.useState(null),[s,u]=_.useState("3000"),[f,c]=_.useState(null);return _.useEffect(()=>{e&&(r("code"),fetch(`/api/workspace/file?path=${encodeURIComponent(e)}`).then(p=>p.json()).then(p=>c(p)))},[e]),_.useEffect(()=>{if(n!=="preview")return;let p=!1;async function h(){var x;const v=await fetch("/api/preview/ports").catch(()=>null);if(!v||p)return;const k=await v.json();p||(l(k.ports||[]),!o&&((x=k.ports)==null?void 0:x.length)>0&&a(k.ports[0].port))}h();const m=setInterval(h,4e3);return()=>{p=!0,clearInterval(m)}},[n,o]),d.jsxs("div",{style:{display:"flex",flexDirection:"column",height:"100%"},children:[d.jsxs("div",{className:"preview-tabs","data-testid":"preview-tabs",children:[bE.map(p=>d.jsx("button",{className:n===p.id?"active":"",onClick:()=>r(p.id),"data-testid":`preview-tab-${p.id}`,children:p.label},p.id)),d.jsx("span",{style:{flex:1}}),n==="preview"&&d.jsxs("div",{className:"preview-port-input",children:[d.jsx("input",{value:s,onChange:p=>u(p.target.value),placeholder:"port","data-testid":"preview-port-input"}),d.jsx("button",{onClick:()=>a(Number(s)),"data-testid":"preview-port-go",children:"Load"})]})]}),d.jsxs("div",{className:"preview-body",children:[n==="preview"&&(o?d.jsx("iframe",{className:"preview-iframe",src:`http://localhost:${o}`,title:`localhost:${o}`,"data-testid":"preview-iframe"},o):d.jsxs("div",{className:"preview-empty","data-testid":"preview-empty",children:[d.jsxs("p",{style:{maxWidth:320,lineHeight:1.6},children:["No dev server detected yet. Ask the agent to start one (e.g. ",d.jsx("code",{children:"bun dev"}),", ",d.jsx("code",{children:"npm run dev"}),"), then I’ll auto-detect it."]}),i.length>0&&d.jsxs("p",{style:{fontSize:12},children:["Detected: ",i.map(p=>`:${p.port}`).join(", ")]})]})),n==="code"&&d.jsx("div",{className:"preview-code-view","data-testid":"preview-code",children:f?d.jsxs(d.Fragment,{children:[d.jsxs("div",{style:{color:"var(--text-muted)",fontSize:11,marginBottom:8},children:[e," (",f.size," bytes",f.truncated?", truncated":"",")"]}),d.jsx("pre",{children:f.content})]}):d.jsx("div",{className:"preview-empty",children:"Click a file in the tree to inspect it."})}),n==="logs"&&d.jsx("div",{className:"preview-empty","data-testid":"preview-logs",children:"Logs panel — wires up to the agent's stderr stream and dev-server stdout (v1.x)."}),n==="deploy"&&d.jsxs("div",{className:"preview-empty","data-testid":"preview-deploy",children:["Deploy — Cloudflare Pages + custom-domain wizard ships in v1.1.",d.jsx("br",{}),"For now: run ",d.jsx("code",{children:"npx wrangler pages deploy"})," in the terminal."]})]})]})}function EE({onClose:e}){const[t,n]=_.useState("viewer"),[r,i]=_.useState(24),[l,o]=_.useState(""),[a,s]=_.useState(null),[u,f]=_.useState([]),[c,p]=_.useState(null),[h,m]=_.useState(!1);_.useEffect(()=>{v()},[]);async function v(){const g=await fetch("/api/share").catch(()=>null);if(!g||!g.ok)return;const S=await g.json();f(S.tokens||[])}async function k(){p(null),m(!0);try{const g=await fetch("/api/share",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({role:t,ttlSeconds:Math.max(60,Number(r)*3600),label:l})}),S=await g.json();if(!g.ok)throw new Error(S.error||`HTTP ${g.status}`);s(S),v()}catch(g){p(g.message||String(g))}finally{m(!1)}}async function x(g){await fetch(`/api/share/${encodeURIComponent(g)}`,{method:"DELETE"}),v()}async function y(g){try{await navigator.clipboard.writeText(g)}catch{}}return d.jsx("div",{className:"modal-backdrop",onClick:e,children:d.jsxs("div",{className:"modal",onClick:g=>g.stopPropagation(),"data-testid":"share-dialog",children:[d.jsx("h2",{children:"Share workspace"}),d.jsx("p",{className:"muted",children:"Issue a token-protected URL. Anyone with the link opens this workspace in their browser with the role you choose."}),d.jsx("label",{children:"Role"}),d.jsx("div",{className:"row",children:d.jsxs("label",{style:{display:"flex",alignItems:"center",gap:6,color:"var(--text)"},children:[d.jsx("input",{type:"radio",name:"role",value:"viewer",checked:t==="viewer",onChange:()=>n("viewer"),"data-testid":"share-role-viewer"}),d.jsx("span",{children:"Viewer — read-only chat + preview"})]})}),d.jsx("div",{className:"row",children:d.jsxs("label",{style:{display:"flex",alignItems:"center",gap:6,color:"var(--text)"},children:[d.jsx("input",{type:"radio",name:"role",value:"client",checked:t==="client",onChange:()=>n("client"),"data-testid":"share-role-client"}),d.jsx("span",{children:"Client — chat + request changes (for AI-installer GTM)"})]})}),d.jsxs("div",{className:"row",children:[d.jsxs("div",{children:[d.jsx("label",{children:"Expires in"}),d.jsx("input",{type:"number",min:"1",value:r,onChange:g=>i(g.target.value),style:{width:80,background:"var(--bg)",color:"var(--text)",border:"1px solid var(--border)",padding:"4px 8px",borderRadius:6},"data-testid":"share-ttl"}),d.jsx("span",{style:{marginLeft:6,color:"var(--text-muted)",fontSize:12},children:"hours"})]}),d.jsxs("div",{style:{flex:1},children:[d.jsx("label",{children:"Label (optional)"}),d.jsx("input",{value:l,onChange:g=>o(g.target.value),placeholder:"e.g. Mel preview",style:{width:"100%",background:"var(--bg)",color:"var(--text)",border:"1px solid var(--border)",padding:"4px 8px",borderRadius:6},"data-testid":"share-label"})]})]}),c&&d.jsx("div",{style:{color:"var(--error)",fontSize:12,marginBottom:8},"data-testid":"share-error",children:c}),a&&d.jsxs(d.Fragment,{children:[d.jsx("label",{children:"Share URL"}),d.jsx("div",{className:"url-output","data-testid":"share-url",children:a.shareUrl}),d.jsxs("div",{style:{display:"flex",gap:8},children:[d.jsx("button",{onClick:()=>y(a.shareUrl),"data-testid":"share-copy",children:"Copy"}),d.jsx("a",{href:a.shareUrl,target:"_blank",rel:"noreferrer",children:d.jsx("button",{children:"Open"})})]})]}),u.length>0&&d.jsxs("div",{style:{marginTop:16},children:[d.jsx("label",{children:"Active share tokens"}),d.jsx("div",{style:{maxHeight:140,overflow:"auto",border:"1px solid var(--border)",borderRadius:6},children:u.map(g=>d.jsxs("div",{style:{padding:"6px 10px",borderBottom:"1px solid var(--border)",display:"flex",justifyContent:"space-between",alignItems:"center",fontSize:12},"data-testid":`active-token-${g.sub}`,children:[d.jsxs("div",{children:[d.jsx("strong",{children:g.label||g.role})," ",d.jsxs("span",{style:{color:"var(--text-muted)"},children:["(",g.role,", expires ",new Date(g.exp*1e3).toLocaleString(),")"]})]}),d.jsx("button",{onClick:()=>x(g.sub),style:{fontSize:11},"data-testid":`revoke-${g.sub}`,children:"Revoke"})]},g.sub))})]}),d.jsxs("div",{className:"actions",children:[d.jsx("button",{onClick:e,"data-testid":"share-close",children:"Close"}),d.jsx("button",{className:"primary",onClick:k,disabled:h,"data-testid":"share-issue",children:h?d.jsx("span",{className:"spinner"}):"Issue URL"})]})]})})}function CE({onClose:e,token:t}){var R,z;const[n,r]=_.useState(null),[i,l]=_.useState(!1),[o,a]=_.useState(null),[s,u]=_.useState(!1),[f,c]=_.useState(""),[p,h]=_.useState(""),[m,v]=_.useState(168),[k,x]=_.useState(null);_.useEffect(()=>{g()},[]);function y($,U={}){const M={...U.headers||{}};return t&&(M.Authorization=`Bearer ${t}`),fetch($,{...U,headers:M})}async function g(){a(null),l(!1);const $=await y("/api/sync/status").catch(()=>null);if(!$||!$.ok){l(!0);return}r(await $.json())}async function S($){a(null),u(!0);try{await $()}catch(U){a(U.message||String(U))}finally{u(!1)}}const N=()=>S(async()=>{const $=await y("/api/sync/pair",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({inviteCode:f})}),U=await $.json();if(!$.ok)throw new Error(U.error||`HTTP ${$.status}`);c(""),await g()}),b=()=>S(async()=>{var M,V;const $=await y("/api/sync/invite",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({projectCode:(V=(M=n==null?void 0:n.workspaces)==null?void 0:M[0])==null?void 0:V.projectId,displayName:p,expiresHours:Math.max(1,Number(m)||168)})}),U=await $.json();if(!$.ok)throw new Error(U.error||`HTTP ${$.status}`);x(U.invite)}),j=()=>S(async()=>{var M,V;const $=await y("/api/sync/detach",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({workspaceId:(V=(M=n==null?void 0:n.workspaces)==null?void 0:M[0])==null?void 0:V.workspaceId})}),U=await $.json();if(!$.ok)throw new Error(U.error||`HTTP ${$.status}`);x(null),await g()});async function T($){try{await navigator.clipboard.writeText($)}catch{}}const L=((R=n==null?void 0:n.workspaces)==null?void 0:R[0])||null,C=!!n&&!((z=n.daemon)!=null&&z.running);return d.jsx("div",{className:"modal-backdrop",onClick:e,children:d.jsxs("div",{className:"modal",onClick:$=>$.stopPropagation(),"data-testid":"sync-dialog",children:[d.jsx("h2",{children:"Collaborate on this folder"}),d.jsx("p",{className:"muted",children:"bmo-sync keeps this workspace folder identical across machines — yours and a collaborator's. Both of you edit; changes flow both ways. Like a shared drive, but built for workspaces."}),!n&&!i&&d.jsxs("div",{className:"muted",children:[d.jsx("span",{className:"spinner"})," Checking sync status…"]}),i&&d.jsxs("div",{className:"sync-state warn","data-testid":"sync-load-failed",children:[d.jsx("strong",{children:"Couldn't load sync status."}),d.jsx("span",{className:"muted",children:"The workspace server didn't answer. Try again."})]}),C&&d.jsxs("div",{className:"sync-state warn","data-testid":"sync-daemon-down",children:[d.jsx("strong",{children:"The bmo-sync daemon isn't running."}),d.jsx("span",{className:"muted",children:"Folder sharing needs the background sync service running on this machine. Start it, then recheck."})]}),n&&!C&&!L&&d.jsxs(d.Fragment,{children:[d.jsxs("div",{className:"sync-state","data-testid":"sync-unpaired",children:[d.jsxs("strong",{children:[n.workspaceName," isn't shared yet."]}),d.jsx("span",{className:"muted",children:"Got an invite code from a collaborator? Paste it to sync this folder with theirs."})]}),d.jsx("label",{children:"Invite code"}),d.jsxs("div",{className:"row",children:[d.jsx("input",{className:"sync-input",style:{flex:1},value:f,onChange:$=>c($.target.value),placeholder:"e.g. CAFE-MILANO-A8X3","data-testid":"sync-invite-input"}),d.jsx("button",{className:"primary",onClick:N,disabled:s||!f.trim(),"data-testid":"sync-join",children:s?d.jsx("span",{className:"spinner"}):"Connect folder"})]})]}),n&&!C&&L&&d.jsxs(d.Fragment,{children:[d.jsxs("div",{className:"sync-state ok","data-testid":"sync-paired",children:[d.jsxs("strong",{children:["✓ Synced — ",L.projectName||L.projectId]}),d.jsxs("span",{className:"muted",children:["This folder is shared. Edits sync both ways automatically",L.conflictCount?` · ${L.conflictCount} conflict${L.conflictCount>1?"s":""} resolved`:"","."]})]}),n.canInvite?d.jsxs(d.Fragment,{children:[d.jsx("label",{children:"Invite a collaborator"}),d.jsxs("div",{className:"row",children:[d.jsx("input",{className:"sync-input",style:{flex:1},value:p,onChange:$=>h($.target.value),placeholder:"Their name — e.g. Mel","data-testid":"sync-invite-name"}),d.jsx("input",{className:"sync-input",style:{width:64},type:"number",min:"1",value:m,onChange:$=>v($.target.value),title:"Invite expires in (hours)"}),d.jsx("button",{onClick:b,disabled:s,"data-testid":"sync-create-invite",children:s?d.jsx("span",{className:"spinner"}):"Create invite"})]}),k&&d.jsxs(d.Fragment,{children:[d.jsx("label",{children:"Send this code to your collaborator"}),d.jsx("div",{className:"url-output","data-testid":"sync-invite-code",children:k.code}),d.jsxs("div",{className:"row",style:{marginBottom:0},children:[d.jsx("button",{onClick:()=>T(k.code),"data-testid":"sync-copy-code",children:"Copy code"}),k.expiresAt&&d.jsxs("span",{className:"muted",style:{margin:0},children:["expires ",new Date(k.expiresAt*1e3).toLocaleDateString()]})]})]})]}):d.jsxs("p",{className:"muted","data-testid":"sync-no-invite",children:["Inviting from here needs a bmo-sync admin key (set"," ",d.jsx("span",{className:"md-code-inline",children:"BMO_SYNC_ADMIN_KEY"}),"). Without it, anyone you've already invited can still join with their own code."]})]}),o&&d.jsx("div",{style:{color:"var(--error)",fontSize:12,marginTop:8},"data-testid":"sync-error",children:o}),d.jsxs("div",{className:"actions",children:[(C||i)&&d.jsx("button",{onClick:g,disabled:s,"data-testid":"sync-recheck",children:"Recheck"}),L&&!C&&d.jsx("button",{onClick:j,disabled:s,"data-testid":"sync-disconnect",children:"Disconnect"}),d.jsx("button",{onClick:e,"data-testid":"sync-close",children:"Close"})]})]})})}function NE({alert:e,onDismiss:t}){if(!e)return null;const n=e.kind==="fatal";return d.jsxs("div",{className:"conflict-banner","data-testid":"conflict-banner",role:"status",children:[d.jsx("span",{className:"ico",children:n?"⛔":"⚠️"}),n?d.jsxs("span",{children:[d.jsx("strong",{children:"Sync stopped."})," ",e.message||"The sync engine hit a fatal error."," Reconnect from the Collaborate dialog."]}):d.jsxs("span",{children:[d.jsx("strong",{children:"Sync conflict"})," on ",d.jsx("code",{children:e.path||"a file"})," — bmo-sync kept ",d.jsx("strong",{children:e.resolution||"the latest edit"}),e.user?` (also edited by ${e.user})`:"","."]}),d.jsx("span",{className:"spacer"}),d.jsx("button",{onClick:t,"data-testid":"conflict-dismiss",children:"Dismiss"})]})}function jE({urlToken:e,onClose:t}){const[n,r]=_.useState([]),[i,l]=_.useState(!0),[o,a]=_.useState(null),[s,u]=_.useState(null),[f,c]=_.useState({}),[p,h]=_.useState(null),m=e?{Authorization:`Bearer ${e}`}:{};function v(){l(!0),fetch("/api/conflicts",{headers:m}).then(g=>g.ok?g.json():{conflicts:[]}).then(g=>{r(Array.isArray(g==null?void 0:g.conflicts)?g.conflicts:[]),a(null)}).catch(g=>a(String((g==null?void 0:g.message)||g))).finally(()=>l(!1))}_.useEffect(v,[]);function k(g){return`${g.workspaceId}:${g.path}`}async function x(g){const S=k(g);if(f[S])return;const N=new URL("/api/conflicts/view",window.location.origin);N.searchParams.set("workspaceId",g.workspaceId),N.searchParams.set("path",g.path);const b=await fetch(N.toString(),{headers:m});if(!b.ok){a(`Could not load ${g.path} (HTTP ${b.status})`);return}const j=await b.json().catch(()=>null);j&&c(T=>({...T,[S]:{mine:qf(j.mine),theirs:qf(j.theirs),conflict:j.conflict}}))}async function y(g,S){const N=k(g);h(N);try{const b=await fetch("/api/conflicts/resolve",{method:"POST",headers:{"content-type":"application/json",...m},body:JSON.stringify({workspaceId:g.workspaceId,path:g.path,action:S})});if(!b.ok){const j=await b.json().catch(()=>({}));a((j==null?void 0:j.error)||`Resolve failed (HTTP ${b.status}).`);return}v(),u(null)}finally{h(null)}}return d.jsxs("div",{className:"conflict-panel","data-testid":"conflict-panel",role:"dialog","aria-label":"Open conflicts",children:[d.jsxs("header",{className:"conflict-panel-header",children:[d.jsx("strong",{children:"Open conflicts"})," ",d.jsxs("span",{className:"muted",children:["(",n.length,") · changes saved to the daemon back-office until you resolve"]}),d.jsx("span",{className:"spacer"}),d.jsx("button",{onClick:v,children:"Refresh"}),d.jsx("button",{onClick:t,children:"Close"})]}),i&&d.jsx("div",{className:"muted",children:"Loading…"}),o&&d.jsx("div",{className:"conflict-panel-error",children:o}),!i&&n.length===0&&d.jsx("div",{className:"muted",children:"No open conflicts."}),d.jsx("ul",{className:"conflict-list",children:n.map(g=>{const S=k(g),N=s===S,b=f[S];return d.jsxs("li",{className:"conflict-row",children:[d.jsxs("div",{className:"conflict-row-head",onClick:()=>{N?u(null):(u(S),g.resolution!=="remote_announced"&&x(g))},children:[d.jsx("code",{children:g.path}),d.jsxs("span",{className:"muted",children:["·"," ",g.resolution==="remote_announced"?`${g.conflictingUser||"peer"} flagged`:g.conflictingUser||"peer"," ","·"," ",new Date((g.detectedAt||0)*1e3).toLocaleTimeString()]}),d.jsx("span",{className:"spacer"}),d.jsx("span",{className:"muted",children:N?"▲":"▼"})]}),N&&g.resolution==="remote_announced"&&d.jsxs("div",{className:"conflict-row-body",children:[d.jsxs("div",{className:"muted",children:[g.conflictingUser||"A peer"," reported a conflict on this file. Your local copy is unchanged here — open it, decide whether to keep your version or pull theirs, save, and the next sync will reconcile."]}),d.jsx("div",{className:"conflict-actions",children:d.jsx("button",{disabled:p===S,onClick:()=>y(g,"keep_mine"),"data-testid":"conflict-acknowledge",children:"Acknowledge"})})]}),N&&g.resolution!=="remote_announced"&&d.jsxs("div",{className:"conflict-row-body",children:[!b&&d.jsx("div",{className:"muted",children:"Loading diff…"}),b&&d.jsxs(d.Fragment,{children:[d.jsxs("div",{className:"conflict-meta",children:[d.jsxs("span",{children:["mine: ",d.jsx("code",{children:ca(g.mineSha256)})]}),d.jsxs("span",{children:["theirs: ",d.jsx("code",{children:ca(g.theirsSha256)})]}),g.baseSha256&&d.jsxs("span",{children:["base: ",d.jsx("code",{children:ca(g.baseSha256)})]})]}),d.jsx(TE,{mine:b.mine,theirs:b.theirs}),d.jsxs("div",{className:"conflict-actions",children:[d.jsx("button",{disabled:p===S,onClick:()=>y(g,"keep_mine"),"data-testid":"conflict-keep-mine",children:"Keep mine"}),d.jsx("button",{disabled:p===S,onClick:()=>y(g,"take_theirs"),"data-testid":"conflict-take-theirs",children:"Take theirs"})]})]})]})]},S)})})]})}function qf(e){if(typeof e!="string")return null;try{const t=atob(e),n=new Uint8Array(t.length);for(let r=0;r<t.length;r++)n[r]=t.charCodeAt(r);return new TextDecoder().decode(n)}catch{return null}}function ca(e){return typeof e!="string"?"∅":e.length>12?`${e.slice(0,12)}…`:e}function TE({mine:e,theirs:t}){return e==null&&t==null?d.jsx("div",{className:"muted",children:"Both versions unavailable on disk."}):e==null||t==null?d.jsx("div",{className:"muted",children:"Only one side is available — the other was deleted before resolution."}):d.jsx(vl,{oldText:t,newText:e})}function IE({role:e,urlToken:t,onSyncAlert:n,onOpenConflicts:r}){const[i,l]=_.useState([]),[o,a]=_.useState({tokensIn:0,tokensOut:0,costUsd:0}),[s,u]=_.useState([]),[f,c]=_.useState(!1),[p,h]=_.useState(null),[m,v]=_.useState(0);_.useEffect(()=>{if(e!=="partner")return;const S=t?{Authorization:`Bearer ${t}`}:{};fetch("/api/sync/status",{headers:S}).then(N=>N.ok?N.json():null).then(N=>{var b;N&&h({paired:!!N.paired,daemonRunning:!!((b=N.daemon)!=null&&b.running),peer:null})}).catch(()=>{}),fetch("/api/conflicts",{headers:S}).then(N=>N.ok?N.json():{conflicts:[]}).then(N=>v(Array.isArray(N==null?void 0:N.conflicts)?N.conflicts.length:0)).catch(()=>{})},[e,t]),_.useEffect(()=>{const S=window.location.protocol==="https:"?"wss":"ws",N=new URL(`${S}://${window.location.host}/ws/activity`);t&&N.searchParams.set("t",t);const b=new WebSocket(N.toString());return b.onopen=()=>c(!0),b.onclose=()=>c(!1),b.onmessage=j=>{let T;try{T=JSON.parse(j.data)}catch{return}T.type==="snapshot"?(l(T.snapshot.presence||[]),a(T.snapshot.usage||{tokensIn:0,tokensOut:0,costUsd:0}),u(T.snapshot.recent||[])):(u(L=>[...L.slice(-49),T]),k(T))},()=>{try{b.close()}catch{}}},[t]);function k(S){S.type==="daemon-status"?h(N=>({...N||{paired:!1},daemonRunning:!!S.connected})):S.type==="sync-paired"?h(N=>({...N||{},paired:!0,daemonRunning:!0})):S.type==="sync-detached"?h(N=>({...N||{},paired:!1,peer:null})):S.type==="remote-presence"?h(N=>({...N||{paired:!0},peer:S.user||null})):S.type==="sync-conflict"?(n==null||n({kind:"conflict",path:S.path,resolution:S.resolution,user:S.conflictingUser}),(S.resolution==="local_pre_write_guard"||S.resolution==="remote_announced")&&v(N=>N+1)):S.type==="sync-conflict-resolved"?v(N=>Math.max(0,N-1)):S.type==="sync-fatal"&&(n==null||n({kind:"fatal",message:S.message}))}const x=[...s].reverse().find(S=>S.type==="chat-stream"||S.type==="chat-user"||S.type==="chat-end"),y=s.find(S=>S.type==="inbox-change");let g=null;return p!=null&&p.paired&&(g=p.daemonRunning?d.jsxs("span",{className:"pill synced","data-testid":"activity-sync",children:["⇄ Synced",p.peer?` · with ${p.peer}`:""]}):d.jsx("span",{className:"pill sync-off","data-testid":"activity-sync",children:"⇄ Sync paused"})),d.jsxs("footer",{className:"bottombar","data-testid":"activity-bar",children:[d.jsx("span",{className:`pill ${f?"live":""}`,"data-testid":"activity-connected",children:f?"live":"reconnecting…"}),d.jsxs("span",{className:"pill","data-testid":"activity-presence",children:["👥 ",i.length," viewing"]}),g,e==="partner"&&m>0&&d.jsxs("button",{type:"button",className:"pill conflict-badge","data-testid":"activity-conflicts",onClick:()=>r==null?void 0:r(),title:"Resolve open sync conflicts",children:["🛑 ",m," conflict",m===1?"":"s"]}),d.jsxs("span",{className:"pill","data-testid":"activity-usage",children:["↓",o.tokensIn," ↑",o.tokensOut," · $",(o.costUsd||0).toFixed(3)]}),x&&d.jsx("span",{className:"pill",style:{maxWidth:380,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},"data-testid":"activity-last",children:x.type==="chat-end"?"✓ agent finished":"… agent streaming"}),y&&d.jsx("span",{className:"pill","data-testid":"activity-inbox",children:"📦 inbox updated"}),d.jsx("span",{style:{flex:1}}),d.jsxs("span",{style:{fontSize:11},children:["v0.1.0 · ",e]})]})}function _E(){var r;const[e,t]=_.useState(null);if(_.useEffect(()=>{let i=!1;async function l(){try{const s=await(await fetch("/api/inbox")).json();i||t(s)}catch{}}l();const o=setInterval(l,5e3);return()=>{i=!0,clearInterval(o)}},[]),!e||!e.hasInbox||!((r=e.imports)!=null&&r.length))return null;const n=e.imports.filter(i=>{var o;const l=Object.keys(e.installed||{}).find(a=>a.startsWith(i.replace(/-v[\d.]+$/,"")));return l?((o=e.installed[l])==null?void 0:o.integrated)!==!0:!0});return n.length===0?null:d.jsxs("div",{className:"inbox-card","data-testid":"inbox-card",children:["📦 ",d.jsx("strong",{children:"Component inbox"}),": ",n.length," pending integration",n.length!==1?"s":"",": ",n.join(", "),".",d.jsxs("span",{className:"muted",children:["Ask the agent: ",d.jsxs("em",{children:["“walk me through integrating ",n[0],"”"]})]})]})}const AE=[{id:"concise",label:"Concise",sample:"Got it. Done."},{id:"playful",label:"Playful",sample:"Oh nice — let's go!"},{id:"formal",label:"Formal",sample:"Understood. Proceeding now."},{id:"dry",label:"Dry",sample:"Sure. If we must."}],Qf=["#22d3ee","#a78bfa","#f472b6","#facc15","#34d399","#fb7185"],PE=[{id:"drive",label:"Google Drive",note:"Files & docs"},{id:"notion",label:"Notion",note:"Wiki & notes"},{id:"gmail",label:"Gmail",note:"Inbox & threads"},{id:"calendar",label:"Calendar",note:"Schedule"},{id:"canva",label:"Canva",note:"Designs"},{id:"github",label:"GitHub",note:"Code & PRs"}],LE=new Set(["login","missing","subscribe"]),FE=new Set(["login","missing"]),Yf={missing:{title:"Almost there — I need Claude installed before I can think.",subtitle:"That's the brain I run on. Install it once, then come back here.",cmdLabel:"Run this in your terminal",cmd:"curl -fsSL https://claude.ai/install.sh | bash",after:"Then come back and press the button below.",retry:"Still don't see Claude on this machine. Make sure the install finished, then try again."},login:{title:"One quick thing before we dig in — sign me in to Claude.",subtitle:"It's a one-time sign-in to your own Claude account — it runs on your machine, on your plan. After this you won't be asked again.",cmdLabel:"Run this once in your terminal",cmd:"claude auth login",after:"A browser window opens — sign in with your Claude account, then come back and press the button.",retry:"Hmm — still not signed in. Finish the sign-in in your browser, then press it again."},subscribe:{title:"You're in — I just need an active Claude plan to start thinking.",subtitle:"Claude Code runs on a Claude Pro plan (or higher). Grab one on your own account, then come back — you won’t be asked again.",cmdLabel:"Get a plan, then come back",link:"https://claude.ai/upgrade",linkLabel:"Open claude.ai →",after:"Subscribe at claude.ai, then come back and press the button.",retry:"Still no active plan showing. Give it a moment after subscribing, then try again."}},RE=new Set(["md","txt","json","yml","yaml","toml","js","mjs","cjs","ts","tsx","jsx","py","rb","go","rs","java","kt","swift","c","h","cpp","cc","hpp","css","scss","html","sh","env","sql","xml","svg","csv"]);function OE(e){const t=e.lastIndexOf(".");return t>=0?e.slice(t+1).toLowerCase():""}function DE(e){if(!e)return null;try{const t=new URL(e);return t.hostname==="localhost"||t.hostname==="127.0.0.1"?null:t.protocol==="http:"&&(t.port===""||t.port==="80")||t.protocol==="https:"&&(t.port===""||t.port==="443")?t.hostname:`${t.hostname}:${t.port}`}catch{return null}}async function zE(e){const t=Array.from(e||[]).slice(0,200);if(t.length===0)return{folderName:null,files:[]};const n=(t[0].webkitRelativePath||t[0].name).split("/")[0]||"folder",r=40,i=600,l=[];for(const a of t){if(l.length>=r)break;const s=a.webkitRelativePath||a.name,u=OE(a.name);if(!RE.has(u)){l.push({path:s,head:""});continue}if(a.size>100*1024){l.push({path:s,head:""});continue}try{const p=await a.slice(0,i).text();l.push({path:s,head:p})}catch{l.push({path:s,head:""})}}const o=Math.max(t.length-l.length,0);return{folderName:n,files:l,totalFiles:t.length,untouched:o}}function ME({peekSummary:e,services:t,agentName:n}){const r=[],i=e!=null&&e.folderName?` (you just glanced at "${e.folderName}")`:"";return r.push({kind:"survey",title:"Tell me what's in my workspace",sub:e!=null&&e.folderName?"I'll read the workspace I'm running in and summarize.":"I'll read what's here and summarize the project."}),(t==null?void 0:t.size)>0?r.push({kind:"startup",title:"Help me start something new",sub:`${n} will ask one question to figure out what to build.`}):r.push({kind:"startup",title:"Help me start a project",sub:"Tell me the rough idea — I'll scaffold it."}),r.push({kind:"chat",title:"Just chat — find me work",sub:"No agenda yet. Get me into the workspace."}),r}function Kf(e,t=55,n=380){const[r,i]=_.useState(""),[l,o]=_.useState(!1),[a,s]=_.useState(!1);return _.useEffect(()=>{if(!e){i(""),o(!1),s(!1);return}i(""),o(!1),s(!1);let u=0,f=null;const c=setTimeout(()=>{s(!0),f=setInterval(()=>{u+=1,i(e.slice(0,u)),u>=e.length&&(clearInterval(f),o(!0))},t)},n);return()=>{clearTimeout(c),f&&clearInterval(f)}},[e,t,n]),[r,l,a]}function rn({children:e,from:t="agent",accent:n,streaming:r=!1}){return d.jsx("div",{className:`ob-bubble ob-bubble-${t}${r?" ob-bubble-streaming":""}`,style:t==="user"?{borderColor:n}:void 0,children:e})}function Yn(){return d.jsx("span",{className:"ob-caret","aria-hidden":"true",children:"▍"})}function Xf(e){const t=Math.random().toString(36).slice(2,8);return`${e}-${Date.now().toString(36)}-${t}`}function BE({session:e,token:t,onComplete:n}){var Pu,Lu,Fu,Ru;const[r,i]=_.useState(1),l=_.useMemo(()=>DE(e==null?void 0:e.shareBaseUrl),[e==null?void 0:e.shareBaseUrl]),[o,a]=_.useState(((Pu=e==null?void 0:e.identity)==null?void 0:Pu.name)||""),[s,u]=_.useState(((Lu=e==null?void 0:e.identity)==null?void 0:Lu.tone)||"concise"),[f,c]=_.useState(((Fu=e==null?void 0:e.identity)==null?void 0:Fu.color)||Qf[0]),[p,h]=_.useState(!1),[m,v]=_.useState(null),[k,x]=_.useState(null),[y,g]=_.useState(!1),[S,N]=_.useState(!1),[b,j]=_.useState(null),[T,L]=_.useState(""),[C,R]=_.useState(new Set(((Ru=e==null?void 0:e.identity)==null?void 0:Ru.connectedServices)||[])),[z,$]=_.useState(!1),[U,M]=_.useState(null),[V,q]=_.useState(null),[F,W]=_.useState(!1),w=_.useRef(null),K=_.useRef(null),te=_.useRef({}),[E,se]=_.useState({}),Ie=_.useCallback((P,Y,ie)=>{P&&se(pe=>{const _e=pe[P]||{text:"",done:!1,error:null},jn=Y==="text"?_e.text+(ie||""):_e.text,pt={...pe,[P]:{..._e,text:jn}};return te.current[P]=jn,pt})},[]),re=_.useCallback((P,Y)=>{P&&se(ie=>{const pe=ie[P]||{text:"",done:!1,error:null};return{...ie,[P]:{...pe,done:!0,error:Y||pe.error}}})},[]);_.useEffect(()=>{let P=!1,Y=0,ie=null,pe=null;const _e=()=>{if(P)return;const jn=window.location.protocol==="https:"?"wss":"ws",pt=new URL(`${jn}://${window.location.host}/ws/chat`);t&&pt.searchParams.set("t",t),pe=new WebSocket(pt.toString()),K.current=pe,pe.onmessage=$t=>{var Ut;let $e;try{$e=JSON.parse($t.data)}catch{return}$e.type==="chunk"&&((Ut=$e.chunk)==null?void 0:Ut.type)==="text"?Ie($e.messageId,"text",$e.chunk.text):$e.type==="end"?re($e.messageId,null):$e.type==="error"&&re($e.messageId,$e.message||"agent error")},pe.onopen=()=>{Y=0},pe.onclose=()=>{if(K.current=null,P)return;const $t=Math.min(1e3*2**Y,8e3);Y+=1,ie=setTimeout(_e,$t)},pe.onerror=()=>{}};return _e(),()=>{P=!0,clearTimeout(ie);try{pe==null||pe.close()}catch{}}},[t,Ie,re]),_.useEffect(()=>{document.documentElement.style.setProperty("--accent",f),document.documentElement.style.setProperty("--accent-hot",f)},[f]);const ce=_.useMemo(()=>{const P={"content-type":"application/json"};return t&&(P.Authorization=`Bearer ${t}`),P},[t]);async function qe(P){const Y=await fetch("/api/agent/identity",{method:"POST",headers:ce,body:JSON.stringify({name:o,tone:s,color:f,...P})});if(!Y.ok){const ie=await Y.json().catch(()=>({}));throw new Error(ie.error||`save failed (${Y.status})`)}return Y.json()}const Qe=_.useCallback(async(P=!1)=>{g(!0);try{const Y=await fetch(`/api/agent/readiness${P?"?fresh=1":""}`,{headers:ce});return Y.ok?await Y.json():{status:"unknown"}}catch{return{status:"unknown"}}finally{g(!1)}},[ce]);async function zt(){if(v(null),!o.trim()){v("Pick a name — even just one syllable.");return}h(!0);try{await qe({});const P=await Qe(!1);if(LE.has(P.status)){x(P);return}i(2)}catch(P){v(String(P.message||P))}finally{h(!1)}}async function Gn(){const P=await Qe(!0);if(N(!0),P.status==="login"||P.status==="missing"){x(P);return}x(null),i(2)}async function Pi(){const P=await Qe(!0);if(P.status==="ready"||P.status==="unknown"){j(null),x(null),i(2);return}j(null),x(P)}async function wo(){N(!1),j({status:"starting",url:null,error:null});try{const Y=await(await fetch("/api/agent/login/start",{method:"POST",headers:ce})).json();j(Y),Y.status==="success"&&Pi()}catch{j({status:"error",url:null,error:"Could not start sign-in."})}}async function bo(){const P=T.trim();if(P){L("");try{const Y=await fetch("/api/agent/login/code",{method:"POST",headers:{...ce,"content-type":"application/json"},body:JSON.stringify({code:P})});j(await Y.json())}catch{}}}_.useEffect(()=>{if(!(b&&["starting","awaiting-browser","awaiting-code"].includes(b.status)))return;let Y=!0;const ie=setInterval(async()=>{try{const _e=await(await fetch("/api/agent/login/status",{headers:ce})).json();if(!Y)return;j(_e),_e.status==="success"&&(clearInterval(ie),Pi())}catch{}},2e3);return()=>{Y=!1,clearInterval(ie)}},[b==null?void 0:b.status,ce]);const[Ir,Li]=_.useState(null),wt=Ir?E[Ir]:null;async function _r(P){if(q(null),!(!P||P.length===0)){$(!0);try{const Y=await zE(P);if(Y.files.length===0){q("That folder looks empty — try another?"),$(!1);return}M(Y);const ie=Xf("peek");Li(ie),se(_e=>({..._e,[ie]:{text:"",done:!1,error:null}}));const pe=await fetch("/api/onboarding/peek",{method:"POST",headers:ce,body:JSON.stringify({folderName:Y.folderName,files:Y.files,totalFiles:Y.totalFiles,messageId:ie})});if(!pe.ok){const _e=await pe.json().catch(()=>({}));throw new Error(_e.error||`peek failed (${pe.status})`)}}catch(Y){q(String(Y.message||Y))}finally{$(!1)}}}function Fi(P){R(Y=>{const ie=new Set(Y);return ie.has(P)?ie.delete(P):ie.add(P),ie})}const[Cn,Ar]=_.useState(null),[Au,Pr]=_.useState(null),[Ri,Oi]=_.useState(null),I=Cn?E[Cn]:null;async function B(P){Oi(null),Pr(P);const Y=Xf("job");Ar(Y),se(ie=>({...ie,[Y]:{text:"",done:!1,error:null}}));try{await qe({connectedServices:[...C]});const ie=await fetch("/api/onboarding/start-job",{method:"POST",headers:ce,body:JSON.stringify({kind:P,messageId:Y,peekFolderName:(U==null?void 0:U.folderName)||null})});if(!ie.ok){const pe=await ie.json().catch(()=>({}));throw new Error(pe.error||`start-job failed (${ie.status})`)}}catch(ie){Oi(String(ie.message||ie)),Ar(null),Pr(null)}}async function Q(){try{await qe({connectedServices:[...C]});const P=await fetch("/api/agent/onboarded",{method:"POST",headers:ce});if(!P.ok){const Y=await P.json().catch(()=>({}));throw new Error(Y.error||`mark-onboarded failed (${P.status})`)}n==null||n()}catch(P){v(String(P.message||P))}}const X=o.trim()||"your agent",oe="Hey. I'm new here.",rt="Before we start — what should I call myself?",[Mt,Ye]=Kf(r===1?oe:"",65,420),[Nn,Bt]=Kf(r===1&&Ye?rt:"",48,520),Be=r===1?Bt:!0,en=_.useMemo(()=>ME({peekSummary:U,services:C,agentName:X}),[U,C,X]),ke=k?Yf[k.status]||Yf.login:null,bm=k?FE.has(k.status):!1;return d.jsx("div",{className:"ob-root",children:d.jsxs("div",{className:"ob-stage",children:[d.jsxs("header",{className:"ob-header",children:[d.jsx("div",{className:"ob-step-dots","aria-label":`Step ${r} of 5`,children:[1,2,3,4,5].map(P=>d.jsx("span",{className:`ob-dot ${P===r?"active":""} ${P<r?"done":""}`},P))}),r>1&&!k&&d.jsx("button",{className:"ob-back",onClick:()=>i(r-1),children:"← Back"})]}),k&&ke&&d.jsxs("section",{className:"ob-step",children:[d.jsxs(rn,{children:[d.jsx("p",{className:"ob-bubble-text",children:ke.title}),d.jsx("p",{className:"ob-bubble-subtle",children:ke.subtitle})]}),d.jsxs("div",{className:"ob-card ob-reveal",children:[k.status==="login"?d.jsxs("div",{className:"ob-login-inapp",children:[(!b||b.status==="idle")&&d.jsx("button",{className:"ob-next primary",onClick:wo,children:"Sign in to Claude →"}),b&&(b.status==="starting"||b.status==="awaiting-browser")&&d.jsxs(d.Fragment,{children:[d.jsx("p",{className:"ob-bubble-text",children:"Opening Claude in your browser…"}),d.jsx("p",{className:"ob-bubble-subtle",children:"Sign in there with your Claude account — I'll detect it automatically, nothing to come back and click."}),b.url&&d.jsx("a",{className:"ob-login-link",href:b.url,target:"_blank",rel:"noreferrer",children:"If the browser didn't open, click here →"})]}),b&&b.status==="awaiting-code"&&d.jsxs("div",{className:"ob-login-code",children:[d.jsx("label",{className:"ob-label",children:"Paste the code from your browser"}),d.jsxs("div",{className:"ob-actions",children:[d.jsx("input",{className:"ob-input",value:T,onChange:P=>L(P.target.value),placeholder:"code from claude.ai"}),d.jsx("button",{className:"ob-next primary",onClick:bo,children:"Submit"})]})]}),b&&(b.status==="error"||b.status==="unsupported")&&d.jsxs(d.Fragment,{children:[d.jsxs("div",{className:"ob-error",children:[b.error||"I couldn't run the in-app sign-in."," You can sign in from a terminal instead:"]}),d.jsx("div",{className:"ob-login-cmd",children:d.jsx("code",{children:ke.cmd})}),d.jsx("p",{className:"ob-bubble-subtle",style:{marginTop:10},children:ke.after})]})]}):d.jsxs(d.Fragment,{children:[d.jsx("label",{className:"ob-label",children:ke.cmdLabel}),ke.cmd?d.jsx("div",{className:"ob-login-cmd",children:d.jsx("code",{children:ke.cmd})}):d.jsx("a",{className:"ob-login-link",href:ke.link,target:"_blank",rel:"noreferrer",children:ke.linkLabel}),d.jsx("p",{className:"ob-bubble-subtle",style:{marginTop:10},children:ke.after})]}),S&&d.jsx("div",{className:"ob-error",style:{marginTop:12},children:ke.retry}),d.jsx("div",{className:"ob-actions",children:d.jsx("button",{className:"ob-next primary",onClick:Gn,disabled:y,children:y?"Checking…":"I've done it — continue →"})}),!bm&&d.jsx("div",{className:"ob-actions",children:d.jsx("button",{className:"ob-skip",onClick:()=>{x(null),i(2)},children:"I already have a plan — continue"})})]})]}),r===1&&!k&&d.jsxs("section",{className:"ob-step",children:[d.jsxs(rn,{streaming:!Ye||!Bt,children:[d.jsxs("p",{className:"ob-bubble-text",children:[Mt,!Ye&&d.jsx(Yn,{})]}),Ye&&d.jsxs("p",{className:"ob-bubble-text",children:[Nn,!Bt&&d.jsx(Yn,{})]})]}),Be&&d.jsxs("div",{className:"ob-card ob-reveal",children:[d.jsx("label",{className:"ob-label",children:"Name"}),d.jsx("input",{autoFocus:!0,className:"ob-input",placeholder:"Echo, Pip, Atlas, Sage…",value:o,onChange:P=>a(P.target.value),onKeyDown:P=>{P.key==="Enter"&&zt()},maxLength:40}),d.jsx("label",{className:"ob-label",children:"Voice"}),d.jsx("div",{className:"ob-tone-grid",children:AE.map(P=>d.jsxs("button",{className:`ob-tone ${s===P.id?"selected":""}`,onClick:()=>u(P.id),children:[d.jsx("span",{className:"ob-tone-label",children:P.label}),d.jsx("span",{className:"ob-tone-sample",children:P.sample})]},P.id))}),d.jsx("label",{className:"ob-label",children:"Color"}),d.jsx("div",{className:"ob-color-row",children:Qf.map(P=>d.jsx("button",{className:`ob-color-swatch ${f===P?"selected":""}`,style:{background:P},onClick:()=>c(P),"aria-label":`Pick ${P}`},P))}),m&&d.jsx("div",{className:"ob-error",children:m}),d.jsx("div",{className:"ob-actions",children:d.jsx("button",{className:"ob-next primary",onClick:zt,disabled:p||!o.trim(),children:p?"Saving…":`I'm ${X}. Let's go →`})})]})]}),r===2&&d.jsxs("section",{className:"ob-step",children:[d.jsxs(rn,{children:[d.jsx("p",{className:"ob-bubble-text",children:"Cool. Now — show me something you're working on. Drag a folder onto me."}),d.jsx("p",{className:"ob-bubble-subtle",children:"I'll read filenames + a short sample of each text file (≤40 files, ≤600 bytes each) so I can react. Files stay on your machine; the sample goes to Anthropic the same way every chat turn does. Pick something you'd be OK talking about in chat."})]}),d.jsxs("div",{className:`ob-dropzone ${F?"over":""} ${z?"peeking":""}`,onDragOver:P=>{P.preventDefault(),W(!0)},onDragLeave:()=>W(!1),onDrop:async P=>{var ie,pe;P.preventDefault(),W(!1);const Y=(ie=P.dataTransfer)==null?void 0:ie.items;if(Y&&Y.length>0&&Y[0].webkitGetAsEntry){const _e=[],jn=async(pt,$t="")=>{if(pt.isFile)await new Promise($e=>pt.file(Ut=>{try{Object.defineProperty(Ut,"webkitRelativePath",{value:`${$t}${Ut.name}`})}catch{}_e.push(Ut),$e()},$e));else if(pt.isDirectory){const $e=pt.createReader();await new Promise(Ut=>$e.readEntries(async Sm=>{for(const Em of Sm)await jn(Em,`${$t}${pt.name}/`);Ut()},Ut))}};for(const pt of Y){const $t=pt.webkitGetAsEntry();$t&&await jn($t)}if(_e.length>0){await _r(_e);return}}await _r((pe=P.dataTransfer)==null?void 0:pe.files)},onClick:()=>{var P;return(P=w.current)==null?void 0:P.click()},children:[d.jsx("input",{ref:w,type:"file",webkitdirectory:"",directory:"",multiple:!0,style:{display:"none"},onChange:P=>_r(P.target.files)}),z?d.jsxs(d.Fragment,{children:[d.jsx("div",{className:"ob-drop-icon",children:"👀"}),d.jsx("div",{className:"ob-drop-title",children:"Looking…"})]}):U?d.jsxs(d.Fragment,{children:[d.jsx("div",{className:"ob-drop-icon",children:"📁"}),d.jsx("div",{className:"ob-drop-title",children:U.folderName}),d.jsxs("div",{className:"ob-drop-sub",children:[U.totalFiles," file",U.totalFiles===1?"":"s"," · sampled ",U.files.length]})]}):d.jsxs(d.Fragment,{children:[d.jsx("div",{className:"ob-drop-icon",children:"📂"}),d.jsx("div",{className:"ob-drop-title",children:"Drop a folder here"}),d.jsx("div",{className:"ob-drop-sub",children:"or click to pick one"})]})]}),wt&&d.jsxs(rn,{streaming:!wt.done,children:[wt.text?d.jsxs("p",{className:"ob-bubble-text",children:[wt.text,!wt.done&&d.jsx(Yn,{})]}):d.jsxs("p",{className:"ob-bubble-subtle",children:["👀 ",X," is reading…",!wt.done&&d.jsx(Yn,{})]}),wt.error&&d.jsx("p",{className:"ob-bubble-subtle ob-bubble-error",children:wt.error})]}),V&&d.jsx("div",{className:"ob-error",children:V}),d.jsxs("div",{className:"ob-actions",children:[d.jsx("button",{className:"ob-skip",onClick:()=>i(3),children:"Skip — I'd rather just chat"}),d.jsx("button",{className:"ob-next primary",onClick:()=>i(3),disabled:!U&&!V,children:"Next: connect data →"})]})]}),r===3&&d.jsxs("section",{className:"ob-step",children:[d.jsxs(rn,{children:[d.jsx("p",{className:"ob-bubble-text",children:"What should I have eyes on?"}),d.jsx("p",{className:"ob-bubble-subtle",children:"Pick anything I should know lives in your world. I'll wire them up later — this just plants the flags."})]}),d.jsx("div",{className:"ob-tile-grid",children:PE.map(P=>d.jsxs("button",{className:`ob-tile ${C.has(P.id)?"selected":""}`,onClick:()=>Fi(P.id),children:[d.jsx("span",{className:"ob-tile-label",children:P.label}),d.jsx("span",{className:"ob-tile-note",children:P.note}),C.has(P.id)&&d.jsx("span",{className:"ob-tile-check",children:"✓"})]},P.id))}),d.jsx("div",{className:"ob-soon",children:"Connection flows for these arrive in the next release — your picks here are saved so I'll prompt you when they go live."}),d.jsxs("div",{className:"ob-actions",children:[d.jsx("button",{className:"ob-skip",onClick:()=>i(4),children:"Skip →"}),d.jsx("button",{className:"ob-next primary",onClick:()=>i(4),children:"Next: pair another device →"})]})]}),r===4&&d.jsxs("section",{className:"ob-step",children:[d.jsxs(rn,{children:[d.jsx("p",{className:"ob-bubble-text",children:"I don't live on this Mac. I live with you."}),d.jsx("p",{className:"ob-bubble-subtle",children:l?d.jsxs(d.Fragment,{children:["Open ",d.jsx("code",{children:l})," on your phone with the same login and I'll follow."]}):d.jsx(d.Fragment,{children:"Sign in on any device with the same token and I'll show up there — same memory, same conversation."})})]}),d.jsxs("div",{className:"ob-qr-placeholder",children:[d.jsx("div",{className:"ob-qr-box",children:d.jsxs("div",{className:"ob-qr-soon",children:["QR pairing",d.jsx("br",{}),"coming soon"]})}),d.jsx("p",{className:"ob-bubble-subtle",style:{textAlign:"center",marginTop:16},children:"For now, sign in on any device with the same token and we're synced."})]}),d.jsxs("div",{className:"ob-actions",children:[d.jsx("button",{className:"ob-skip",onClick:()=>i(5),children:"Skip →"}),d.jsx("button",{className:"ob-next primary",onClick:()=>i(5),children:"Next: first job →"})]})]}),r===5&&d.jsxs("section",{className:"ob-step",children:[d.jsxs(rn,{children:[d.jsx("p",{className:"ob-bubble-text",children:"Right — what should I actually do for you first?"}),d.jsx("p",{className:"ob-bubble-subtle",children:"Pick one and I'll start now. Or close this and just talk to me."})]}),Cn?d.jsxs(d.Fragment,{children:[d.jsxs(rn,{streaming:!I||!I.done,children:[I!=null&&I.text?d.jsxs("p",{className:"ob-bubble-text",children:[I.text,!I.done&&d.jsx(Yn,{})]}):d.jsxs("p",{className:"ob-bubble-subtle",children:[X," is on it…",(!I||!I.done)&&d.jsx(Yn,{})]}),(I==null?void 0:I.error)&&d.jsx("p",{className:"ob-bubble-subtle ob-bubble-error",children:I.error})]}),d.jsxs("div",{className:"ob-actions",children:[d.jsx("button",{className:"ob-skip",onClick:()=>{Ar(null),Pr(null)},children:"← Pick a different job"}),d.jsx("button",{className:"ob-next primary",onClick:Q,disabled:!I||!I.done&&!I.error,children:I!=null&&I.done?"Open my workspace →":"Streaming…"})]})]}):d.jsx("div",{className:"ob-jobs",children:en.map(P=>d.jsxs("button",{className:"ob-job",onClick:()=>B(P.kind),children:[d.jsx("div",{className:"ob-job-title",children:P.title}),d.jsx("div",{className:"ob-job-sub",children:P.sub})]},P.kind))}),(m||Ri)&&d.jsx("div",{className:"ob-error",children:m||Ri}),!Cn&&d.jsx("div",{className:"ob-actions",children:d.jsx("button",{className:"ob-skip",onClick:Q,children:"Skip to chat →"})})]}),!k&&d.jsx("p",{className:"ob-consent-note",children:"I quietly share how I'm doing — what's working, what breaks, my setup health — with the Venturewild team so they can help if you ever get stuck. Never your words or files. You can turn this off anytime."})]})})}const $E={display:"grid",placeItems:"center",minHeight:"100vh",padding:24,textAlign:"center"},UE={maxWidth:420,display:"grid",gap:16,justifyItems:"center"},HE={fontSize:44,letterSpacing:"0.18em",fontWeight:700,fontFamily:"ui-monospace, SFMono-Regular, Menlo, monospace",color:"var(--accent, #22d3ee)"};function VE(e){return e&&e.length===6?`${e.slice(0,3)} ${e.slice(3)}`:e}function WE(){const[e,t]=_.useState("starting"),[n,r]=_.useState(null),[i,l]=_.useState(null),[o,a]=_.useState(0),[s,u]=_.useState(0),f=_.useRef({requestId:null,pollAfterMs:2500}),c=_.useCallback(async()=>{t("starting"),r(null);try{const p=await fetch("/api/auth/pair/start",{method:"POST",headers:{"content-type":"application/json"},body:"{}"});if(p.status===429){t("error"),r("Too many sign-in attempts right now. Wait a minute, then try again.");return}if(!p.ok){t("error"),r("Could not start sign-in. Please try again.");return}const h=await p.json();f.current={requestId:h.requestId,pollAfterMs:h.pollAfterMs||2500},l(h.code),a(h.expiresAt||0),t("waiting")}catch{t("error"),r("Network error. Check your connection and try again.")}},[]);return _.useEffect(()=>{c()},[c]),_.useEffect(()=>{if(e!=="waiting")return;let p=!1,h=null;const{requestId:m,pollAfterMs:v}=f.current;async function k(){try{const x=await fetch("/api/auth/pair/status",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({requestId:m})});if(p)return;const y=await x.json().catch(()=>({}));if(y.status==="approved"&&y.token){if((await fetch("/api/auth/exchange",{method:"POST",headers:{Authorization:`Bearer ${y.token}`}})).ok){window.location.reload();return}t("error"),r("Almost there — sign-in completed but the session could not be saved. Try again.");return}if(y.status==="denied"){t("error"),r("This device was denied.");return}if(y.status==="expired"){t("error"),r("The code expired before it was approved.");return}h=setTimeout(k,v)}catch{p||(h=setTimeout(k,v))}}return h=setTimeout(k,v),()=>{p=!0,clearTimeout(h)}},[e]),_.useEffect(()=>{if(e!=="waiting"||!o)return;const p=()=>u(Math.max(0,Math.round((o-Date.now())/1e3)));p();const h=setInterval(p,1e3);return()=>clearInterval(h)},[e,o]),d.jsx("div",{className:"app",style:{gridTemplateRows:"1fr"},children:d.jsx("div",{style:$E,children:d.jsxs("div",{style:UE,children:[d.jsx("h2",{style:{margin:0},children:"Sign in to this workspace"}),e==="starting"&&d.jsxs("p",{style:{color:"var(--text-muted)"},children:[d.jsx("span",{className:"spinner"})," Preparing…"]}),e==="waiting"&&d.jsxs(d.Fragment,{children:[d.jsx("p",{style:{color:"var(--text-muted)",margin:0},children:"On a device where you’re already signed in, open this workspace and approve this code:"}),d.jsx("div",{style:HE,"data-testid":"pair-code",children:VE(i)}),d.jsxs("p",{style:{color:"var(--text-muted)",fontSize:13,margin:0},children:["Waiting for approval",s?` · expires in ${s}s`:"","…"]})]}),e==="error"&&d.jsxs(d.Fragment,{children:[d.jsx("p",{style:{color:"var(--error)"},children:n}),d.jsx("button",{onClick:c,children:"Try again"})]})]})})})}const GE={position:"fixed",top:12,left:"50%",transform:"translateX(-50%)",zIndex:1e3,background:"var(--surface, #1e293b)",border:"1px solid var(--accent, #22d3ee)",borderRadius:10,padding:"12px 16px",boxShadow:"0 8px 24px rgba(0,0,0,0.35)",display:"grid",gap:8,maxWidth:460},qE={display:"flex",gap:8,alignItems:"center",flexWrap:"wrap"};function QE({token:e}){const[t,n]=_.useState([]),[r,i]=_.useState(""),[l,o]=_.useState(!1),[a,s]=_.useState(null),u=_.useRef(!0),f=_.useCallback((v={})=>e?{Authorization:`Bearer ${e}`,...v}:v,[e]),c=_.useCallback(async()=>{try{const v=await fetch("/api/auth/pair/requests",{headers:f()});if(!v.ok)return;const k=await v.json();u.current&&n(Array.isArray(k.requests)?k.requests:[])}catch{}},[f]);_.useEffect(()=>{u.current=!0,c();const v=setInterval(c,5e3);return()=>{u.current=!1,clearInterval(v)}},[c]);async function p(){const v=r.trim();if(!(!v||l)){o(!0),s(null);try{const k=await fetch("/api/auth/pair/approve",{method:"POST",headers:f({"content-type":"application/json"}),body:JSON.stringify({code:v})}),x=await k.json().catch(()=>({}));k.ok?(s({ok:!0,text:`Approved ${x.label||"the device"}.`}),i("")):x.error==="no_match"?s({ok:!1,text:"That code doesn’t match a waiting device."}):s({ok:!1,text:"Could not approve — try again."}),c()}catch{s({ok:!1,text:"Network error."})}finally{o(!1)}}}async function h(v){try{await fetch("/api/auth/pair/deny",{method:"POST",headers:f({"content-type":"application/json"}),body:JSON.stringify({requestId:v})}),c()}catch{}}if(t.length===0)return null;const m=t.map(v=>v.label).join(", ");return d.jsxs("div",{style:GE,"data-testid":"pair-approvals",children:[d.jsx("strong",{style:{fontSize:13},children:t.length===1?"A device wants to sign in":`${t.length} devices want to sign in`}),d.jsx("span",{style:{fontSize:12,color:"var(--text-muted)"},children:m}),d.jsx("span",{style:{fontSize:12,color:"var(--text-muted)"},children:"Enter the code shown on that device to approve it:"}),d.jsxs("div",{style:qE,children:[d.jsx("input",{value:r,onChange:v=>i(v.target.value.replace(/\D/g,"").slice(0,6)),placeholder:"000000",inputMode:"numeric","aria-label":"Sign-in code from the new device","data-testid":"pair-approve-code",style:{fontFamily:"ui-monospace, monospace",letterSpacing:"0.15em",width:110,textAlign:"center"},onKeyDown:v=>v.key==="Enter"&&p()}),d.jsx("button",{onClick:p,disabled:l||r.length<6,"data-testid":"pair-approve-btn",children:l?"Approving…":"Approve"}),t.map(v=>d.jsxs("button",{onClick:()=>h(v.requestId),title:`Deny ${v.label}`,style:{background:"transparent",color:"var(--text-muted)"},children:["Deny ",t.length>1?v.label:""]},v.requestId))]}),a&&d.jsx("span",{style:{fontSize:12,color:a.ok?"var(--accent, #22d3ee)":"var(--error)"},children:a.text})]})}const YE={partner:"Partner",viewer:"Viewer",client:"Client"};function KE(){var U,M;const[e,t]=_.useState(null),[n,r]=_.useState(null),[i,l]=_.useState(!1),[o,a]=_.useState(!1),[s,u]=_.useState(!1),[f,c]=_.useState(null),[p,h]=_.useState(!1),[m,v]=_.useState(!1),[k,x]=_.useState(!1),[y,g]=_.useState("build"),[S,N]=_.useState(null),b=_.useMemo(()=>typeof window>"u"?null:new URLSearchParams(window.location.search).get("t"),[]);if(_.useEffect(()=>{async function V(){try{const q={};b&&(q.Authorization=`Bearer ${b}`);const F=await fetch("/api/session",{headers:q});if(F.status===401){l(!0);return}if(!F.ok)throw new Error(`session fetch failed: ${F.status}`);const W=await F.json();t(W)}catch(q){r(q.message||String(q))}}V()},[b]),_.useEffect(()=>{var q;const V=(q=e==null?void 0:e.identity)==null?void 0:q.color;V&&(document.documentElement.style.setProperty("--accent",V),document.documentElement.style.setProperty("--accent-hot",V))},[(U=e==null?void 0:e.identity)==null?void 0:U.color]),i)return d.jsx(WE,{});if(n)return d.jsx("div",{className:"app",style:{gridTemplateRows:"1fr",alignItems:"center",justifyItems:"center"},children:d.jsxs("div",{style:{padding:24},children:[d.jsx("h2",{children:"wild-workspace"}),d.jsxs("p",{style:{color:"var(--error)"},children:["Could not load session: ",n]}),d.jsx("p",{style:{color:"var(--text-muted)",fontSize:13},children:"Verify the server is running and the share URL token is valid."})]})});if(!e)return d.jsx("div",{className:"app",style:{gridTemplateRows:"1fr",alignItems:"center",justifyItems:"center"},children:d.jsxs("div",{style:{padding:24,color:"var(--text-muted)"},children:[d.jsx("span",{className:"spinner"})," Loading workspace…"]})});if((M=e.capabilities)!=null&&M.chatWrite&&!e.onboarded)return d.jsx(BE,{session:e,token:b,onComplete:async()=>{try{const V={};b&&(V.Authorization=`Bearer ${b}`);const q=await fetch("/api/session",{headers:V});q.ok&&t(await q.json())}catch{t({...e,onboarded:!0})}}});const{role:j,capabilities:T,workspace:L,workspaceId:C,agent:R,identity:z}=e,$=["workspace"];return T.fileTree?m&&$.push("collapsed-tree"):$.push("no-tree"),T.preview||$.push("no-preview"),d.jsxs("div",{className:"app",children:[T.share&&d.jsx(QE,{token:b}),d.jsxs("header",{className:"topbar","data-testid":"topbar",children:[d.jsx("span",{className:"brand",children:"wild-workspace"}),d.jsx("span",{className:`badge ${j}`,"data-testid":"role-badge",children:YE[j]||j}),d.jsx("span",{className:"badge","data-testid":"workspace-name",children:(L==null?void 0:L.name)||C}),z!=null&&z.name?d.jsx("span",{className:"badge","data-testid":"agent-name",style:{borderColor:z.color,color:z.color},children:z.name}):R&&d.jsx("span",{className:"badge","data-testid":"agent-name",children:R.label||R.id}),d.jsx("span",{className:"spacer"}),T.sync&&d.jsx("button",{onClick:()=>u(!0),"data-testid":"collaborate-btn",children:"Collaborate"}),T.share&&d.jsx("button",{onClick:()=>a(!0),"data-testid":"share-btn",children:"Share"}),T.terminal&&d.jsx("button",{onClick:()=>x(V=>!V),"data-testid":"terminal-toggle",children:k?"Hide terminal":"Terminal"})]}),d.jsxs("main",{className:$.join(" "),children:[T.fileTree&&!m&&d.jsx(wE,{onCollapse:()=>v(!0),workspaceRoot:L==null?void 0:L.path,onOpen:N}),T.fileTree&&m&&d.jsx("aside",{className:"tree-collapsed-rail",children:d.jsx("button",{onClick:()=>v(!1),title:"Show file tree",children:"≡"})}),d.jsxs("section",{className:"chat-pane","data-testid":"chat-pane",children:[d.jsxs("div",{className:"chat-header","data-testid":"chat-header",children:[d.jsx("span",{style:{fontSize:12,color:"var(--text-muted)",textTransform:"uppercase",letterSpacing:"0.08em"},children:"Chat"}),T.chatWrite&&d.jsxs("span",{className:"chat-mode-toggle","data-testid":"mode-toggle",children:[d.jsx("button",{className:y==="build"?"active":"",onClick:()=>g("build"),"data-testid":"mode-build",children:"Build"}),d.jsx("button",{className:y==="plan"?"active":"",onClick:()=>g("plan"),"data-testid":"mode-plan",children:"Plan"})]}),d.jsx("span",{className:"spacer"}),d.jsx("span",{style:{fontSize:11,color:"var(--text-muted)"},children:R!=null&&R.available?`via ${R.label}`:"no agent installed — install Claude Code (npm i -g @anthropic-ai/claude-code)"})]}),T.inbox&&d.jsx(_E,{}),d.jsx(mE,{role:j,capabilities:T,mode:y,token:b})]}),T.preview&&d.jsx("section",{className:"preview-pane","data-testid":"preview-pane",children:d.jsx(SE,{focus:S,workspaceRoot:L==null?void 0:L.path})})]}),d.jsx(IE,{role:j,capabilities:T,session:e,urlToken:b,onSyncAlert:c,onOpenConflicts:()=>h(!0)}),d.jsx(NE,{alert:f,onDismiss:()=>c(null)}),p&&d.jsx(jE,{urlToken:b,onClose:()=>h(!1)}),o&&d.jsx(EE,{onClose:()=>a(!1),workspaceId:C}),s&&d.jsx(CE,{onClose:()=>u(!1),token:b}),k&&d.jsxs("div",{className:"terminal-overlay","data-testid":"terminal-overlay",children:[d.jsxs("header",{children:[d.jsx("strong",{children:"Terminal (preview)"}),d.jsx("button",{onClick:()=>x(!1),children:"close"})]}),d.jsx("p",{children:"Power-user terminal is a hidden-by-default toggle. The xterm.js + node-pty integration ships in v1.1. For now use your OS terminal — wild-workspace doesn't replace your shell, it surfaces the agent."})]})]})}function XE(e){const t=window.fetch.bind(window);window.fetch=(n,r={})=>{const i=typeof n=="string"?n:(n==null?void 0:n.url)||"";if(!(i.startsWith("/api/")||i.includes("://")===!1&&i.startsWith("api/")))return t(n,r);const o=new Headers(r.headers||{});return o.has("Authorization")||o.set("Authorization",`Bearer ${e}`),t(n,{...r,headers:o})}}function ZE(){fa.createRoot(document.getElementById("root")).render(d.jsx(Bm.StrictMode,{children:d.jsx(KE,{})}))}async function JE(){const t=typeof window<"u"&&window.fetch?new URLSearchParams(window.location.search).get("t"):null;if(t){let n=!1;try{const r=await window.fetch("/api/auth/exchange",{method:"POST",headers:{Authorization:`Bearer ${t}`}});n=r.ok&&(await r.json().catch(()=>({}))).cookie===!0}catch{}if(n){const r=new URLSearchParams(window.location.search);r.delete("t");const i=r.toString(),l=window.location.pathname+(i?`?${i}`:"")+window.location.hash;window.history.replaceState(null,"",l)}else XE(t)}ZE()}JE();
89
+ `).find(o=>o.trim())||(t?"error":"done");return d.jsxs("details",{className:`tool-result ${t?"err":""}`,open:t,children:[d.jsxs("summary",{children:[t?"✗ ":"",l.slice(0,140)]}),d.jsx("pre",{children:i})]})}function dE({block:e}){const{name:t,input:n={},result:r,done:i,isError:l}=e,o=sE[t]||{icon:"⚙",verb:t},a=uE(t,n),s=i?l?"error":"done":"running";return d.jsxs("div",{className:`tool-card ${s}`,"data-testid":"tool-card","data-tool":t,children:[d.jsxs("div",{className:"tool-head",children:[d.jsx("span",{className:"tool-icon",children:o.icon}),d.jsx("span",{className:"tool-verb",children:o.verb}),a&&d.jsx("code",{className:"tool-target",children:a}),t==="Edit"&&n.replace_all&&d.jsx("span",{className:"tool-tag",children:"all"}),d.jsx("span",{className:"tool-status","data-status":s,children:s==="running"?d.jsx("span",{className:"tool-spinner","aria-label":"running"}):s==="error"?"✗":"✓"})]}),d.jsx(cE,{name:t,input:n}),d.jsx(fE,{content:r,isError:l})]})}const il="wild-workspace.chat.v2";function pE(e,t){var i;const n=e.blocks?e.blocks.slice():[],r=n[n.length-1];switch(t.type){case"text":return r&&r.type==="text"?n[n.length-1]={...r,text:r.text+t.text}:n.push({type:"text",text:t.text}),{...e,blocks:n};case"thinking":return r&&r.type==="thinking"?n[n.length-1]={...r,text:r.text+t.text}:n.push({type:"thinking",text:t.text}),{...e,blocks:n};case"tool-use":return n.push({type:"tool",id:t.id,name:t.name,input:t.input||{},done:!1}),{...e,blocks:n};case"tool-result":{const l=n.findIndex(o=>o.type==="tool"&&o.id===t.id);return l>=0&&(n[l]={...n[l],result:t.content,isError:t.isError,done:!0}),{...e,blocks:n}}case"usage":return{...e,usage:t.usage,costUsd:(i=t.usage)==null?void 0:i.cost_usd,durationMs:t.durationMs};case"error":return n.push({type:"error",text:t.message||"agent error"}),{...e,blocks:n};default:return e}}function Gf(e,t,n){const r=e.findIndex(l=>l.id===t);if(r<0)return e;const i=e.slice();return i[r]=pE(i[r],n),i}function hE(e,t,n){if(!n||!n.trim())return e;const r=e.findIndex(l=>l.id===t);if(r<0)return e;const i=e.slice();return i[r]={...i[r],stderr:(i[r].stderr||"")+n},i}function mE({role:e,capabilities:t,mode:n,token:r}){const[i,l]=_.useState(()=>{try{const j=localStorage.getItem(il);if(j)return JSON.parse(j)}catch{}return[]}),[o,a]=_.useState(null),[s,u]=_.useState(!1),[f,c]=_.useState(""),[p,h]=_.useState(!1),m=_.useRef(null),v=_.useRef(null),k=_.useRef(!0);_.useEffect(()=>{try{localStorage.setItem(il,JSON.stringify(i.slice(-120)))}catch{}},[i]),_.useEffect(()=>{const j=v.current;j&&k.current&&(j.scrollTop=j.scrollHeight)},[i]);function x(){const j=v.current;j&&(k.current=j.scrollHeight-j.scrollTop-j.clientHeight<90)}_.useEffect(()=>{let j=!1,T=0,L=null,C=null;const R=()=>{if(j)return;const z=window.location.protocol==="https:"?"wss":"ws",$=new URL(`${z}://${window.location.host}/ws/chat`);r&&$.searchParams.set("t",r),C=new WebSocket($.toString()),m.current=C,C.onopen=()=>{T=0,h(!0)},C.onmessage=U=>{let M;try{M=JSON.parse(U.data)}catch{return}if(M.type==="chunk")l(V=>Gf(V,M.messageId,M.chunk));else if(M.type==="stderr")l(V=>hE(V,M.messageId,M.text));else if(M.type==="end")a(null),u(!1);else if(M.type==="error")a(null),u(!1),M.messageId?l(V=>Gf(V,M.messageId,{type:"error",message:M.message||"agent error"})):l(V=>[...V,{id:`err-${Date.now()}`,role:"error",text:M.message||"agent error"}]);else if(M.type==="turn-begin")l(V=>{if(V.some(F=>F.id===M.messageId))return V;const q=[];return M.userText&&q.push({id:`u-${M.messageId}`,role:"user",text:M.userText}),M.note&&q.push({id:`note-${M.messageId}`,role:"system",text:M.note}),q.push({id:M.messageId,role:"agent",blocks:[]}),[...V,...q]}),a(M.messageId),u(!0);else if(M.type==="reset"){l([]),a(null),u(!1);try{localStorage.removeItem(il)}catch{}}},C.onerror=()=>u(!1),C.onclose=()=>{if(h(!1),u(!1),a(null),j)return;const U=Math.min(1e3*2**T,1e4);T+=1,L=setTimeout(R,U)}};return R(),()=>{j=!0,clearTimeout(L);try{C==null||C.close()}catch{}}},[r]);function y(){if(!t.chatWrite)return;const j=f.trim();if(!j||s)return;const T=m.current,L=T==null?void 0:T.readyState;if(!T||L!==WebSocket.OPEN&&L!==WebSocket.CONNECTING){l(z=>[...z,{id:`err-${Date.now()}`,role:"error",text:"reconnecting to your agent — try again in a few seconds"}]);return}const C=`m-${Date.now()}`;k.current=!0,l(z=>[...z,{id:`u-${C}`,role:"user",text:j},{id:C,role:"agent",blocks:[]}]),a(C),u(!0),c("");const R=JSON.stringify({type:"send",text:j,mode:n,messageId:C});L===WebSocket.OPEN?T.send(R):T.addEventListener("open",()=>{try{T.send(R)}catch{}},{once:!0})}function g(){var j;((j=m.current)==null?void 0:j.readyState)===WebSocket.OPEN&&m.current.send(JSON.stringify({type:"cancel"})),u(!1),a(null)}function S(){var j;((j=m.current)==null?void 0:j.readyState)===WebSocket.OPEN&&m.current.send(JSON.stringify({type:"reset"})),l([]),a(null),u(!1);try{localStorage.removeItem(il)}catch{}}function N(j){j.key==="Enter"&&!j.shiftKey&&(j.preventDefault(),y())}const b=i.length===0;return d.jsxs(d.Fragment,{children:[!b&&t.chatWrite&&d.jsx("div",{className:"chat-toolbar","data-testid":"chat-toolbar",children:d.jsx("button",{className:"chat-newbtn",onClick:S,disabled:s,"data-testid":"new-chat-btn",title:"Start a fresh conversation — the agent forgets the current thread",children:"+ New chat"})}),d.jsxs("div",{className:"chat-messages",ref:v,onScroll:x,"data-testid":"chat-messages",children:[b&&d.jsx(kE,{role:e,connected:p}),i.map(j=>d.jsx(gE,{message:j,streaming:j.id===o},j.id))]}),t.chatWrite&&d.jsxs("div",{className:"chat-input","data-testid":"chat-input",children:[d.jsx("textarea",{"data-testid":"chat-textarea",placeholder:e==="client"?"Describe the change you’d like…":"Ask the agent to build, fix, or explain something — Enter to send",value:f,onChange:j=>c(j.target.value),onKeyDown:N,disabled:s,rows:2}),s?d.jsx("button",{onClick:g,"data-testid":"cancel-btn",children:"Stop"}):d.jsx("button",{className:"primary send-btn",onClick:y,disabled:!f.trim(),"data-testid":"send-btn",children:"Send"})]}),!t.chatWrite&&e==="viewer"&&d.jsx("div",{className:"chat-readonly-banner",children:"You’re viewing a shared workspace (read-only)."})]})}function gE({message:e,streaming:t}){if(e.role==="user")return d.jsx("div",{className:"chat-msg user","data-testid":"chat-msg-user",children:d.jsx("div",{className:"msg-plain",children:e.text})});if(e.role==="system")return d.jsx("div",{className:"chat-msg system","data-testid":"chat-msg-system",children:e.text});if(e.role==="error")return d.jsxs("div",{className:"chat-msg error","data-testid":"chat-msg-error",children:["⚠ ",e.text]});const n=e.blocks||[],r=n[n.length-1],i=t&&(r==null?void 0:r.type)==="text"?n.length-1:-1,l=t&&(n.length===0||(r==null?void 0:r.type)==="tool"&&r.done);return d.jsxs("div",{className:"chat-msg agent","data-testid":"chat-msg-agent",children:[n.map((o,a)=>d.jsx(yE,{block:o,cursor:a===i},o.id||`${o.type}-${a}`)),l&&d.jsx(xE,{}),e.usage&&d.jsx(vE,{usage:e.usage,costUsd:e.costUsd,durationMs:e.durationMs}),e.stderr&&d.jsxs("details",{className:"stderr-block",children:[d.jsx("summary",{children:"stderr"}),d.jsx("pre",{children:e.stderr})]})]})}function yE({block:e,cursor:t}){return e.type==="text"?d.jsxs("div",{className:"agent-text",children:[d.jsx(JS,{text:e.text}),t&&d.jsx("span",{className:"stream-cursor"})]}):e.type==="thinking"?d.jsxs("details",{className:"thinking-block",children:[d.jsx("summary",{children:"💭 Thought process"}),d.jsx("div",{className:"thinking-body",children:e.text})]}):e.type==="tool"?d.jsx(dE,{block:e}):e.type==="error"?d.jsxs("div",{className:"agent-error",children:["⚠ ",e.text]}):null}function xE(){return d.jsxs("div",{className:"agent-working","data-testid":"agent-working",children:[d.jsxs("span",{className:"dots",children:[d.jsx("i",{}),d.jsx("i",{}),d.jsx("i",{})]}),"working"]})}function vE({usage:e,costUsd:t,durationMs:n}){const r=typeof t=="number"?t:e==null?void 0:e.cost_usd;return d.jsxs("div",{className:"usage-footer","data-testid":"usage-footer",children:[(e==null?void 0:e.output_tokens)!=null&&d.jsxs("span",{title:"output tokens",children:["↑ ",e.output_tokens.toLocaleString()]}),typeof r=="number"&&r>0&&d.jsxs("span",{title:"cost (USD)",children:["$",r.toFixed(4)]}),n!=null&&d.jsxs("span",{children:[(n/1e3).toFixed(1),"s"]})]})}function kE({role:e,connected:t}){return d.jsxs("div",{className:"chat-empty","data-testid":"chat-empty",children:[d.jsx("div",{className:"chat-empty-mark",children:"✦"}),d.jsx("p",{className:"chat-empty-title",children:e==="client"?"Describe what you’d like changed":"Build something by chatting"}),d.jsx("p",{className:"chat-empty-sub",children:e==="client"?"The team’s AI agent will pick it up and you’ll see it here.":"The agent can read, write, and run code in this workspace. It streams back here — edits show up as diffs."}),e!=="client"&&d.jsxs("div",{className:"chat-empty-hints",children:[d.jsx("span",{children:"“What’s in this workspace?”"}),d.jsx("span",{children:"“Build a landing page in /web”"}),d.jsx("span",{children:"“Fix the failing test”"})]}),!t&&d.jsx("p",{className:"chat-empty-warn",children:"connecting to the agent bridge…"})]})}function wE({onCollapse:e,workspaceRoot:t,onOpen:n}){const[r,i]=_.useState(null),[l,o]=_.useState(null),[a,s]=_.useState(new Set);_.useEffect(()=>{fetch("/api/workspace/tree").then(c=>c.json()).then(c=>{if(c.error){o(c.error);return}i(c.entries||[])}).catch(c=>o(c.message||String(c)))},[]);function u(c){s(p=>{const h=new Set(p);return h.has(c)?h.delete(c):h.add(c),h})}function f(c,p=0){var v;const h=c.type==="dir",m=a.has(c.path);return d.jsxs("div",{children:[d.jsxs("div",{className:`tree-node ${h?"dir":""}`,onClick:()=>h?u(c.path):n==null?void 0:n(c.path),style:{paddingLeft:p*12+4},"data-testid":`tree-node-${c.path}`,children:[d.jsx("span",{style:{width:12,display:"inline-block"},children:h?m?"▾":"▸":" "}),d.jsx("span",{children:c.name})]}),h&&m&&((v=c.children)==null?void 0:v.map(k=>f(k,p+1)))]},c.path)}return d.jsxs("aside",{className:"tree-pane","data-testid":"file-tree",children:[d.jsxs("div",{className:"tree-header",children:[d.jsx("span",{children:"Files"}),d.jsx("button",{onClick:e,style:{padding:"2px 6px",fontSize:11},children:"⟨"})]}),d.jsxs("div",{className:"tree-list",children:[l&&d.jsx("div",{style:{color:"var(--error)",padding:4},children:l}),!r&&!l&&d.jsxs("div",{style:{color:"var(--text-muted)",padding:4},children:[d.jsx("span",{className:"spinner"})," loading…"]}),(r==null?void 0:r.length)===0&&d.jsx("div",{style:{color:"var(--text-muted)",padding:4},children:"Empty workspace"}),r==null?void 0:r.map(c=>f(c,0))]})]})}const bE=[{id:"preview",label:"Preview"},{id:"code",label:"Code"},{id:"logs",label:"Logs"},{id:"deploy",label:"Deploy"}];function SE({focus:e,workspaceRoot:t}){const[n,r]=_.useState("preview"),[i,l]=_.useState([]),[o,a]=_.useState(null),[s,u]=_.useState("3000"),[f,c]=_.useState(null);return _.useEffect(()=>{e&&(r("code"),fetch(`/api/workspace/file?path=${encodeURIComponent(e)}`).then(p=>p.json()).then(p=>c(p)))},[e]),_.useEffect(()=>{if(n!=="preview")return;let p=!1;async function h(){var x;const v=await fetch("/api/preview/ports").catch(()=>null);if(!v||p)return;const k=await v.json();p||(l(k.ports||[]),!o&&((x=k.ports)==null?void 0:x.length)>0&&a(k.ports[0].port))}h();const m=setInterval(h,4e3);return()=>{p=!0,clearInterval(m)}},[n,o]),d.jsxs("div",{style:{display:"flex",flexDirection:"column",height:"100%"},children:[d.jsxs("div",{className:"preview-tabs","data-testid":"preview-tabs",children:[bE.map(p=>d.jsx("button",{className:n===p.id?"active":"",onClick:()=>r(p.id),"data-testid":`preview-tab-${p.id}`,children:p.label},p.id)),d.jsx("span",{style:{flex:1}}),n==="preview"&&d.jsxs("div",{className:"preview-port-input",children:[d.jsx("input",{value:s,onChange:p=>u(p.target.value),placeholder:"port","data-testid":"preview-port-input"}),d.jsx("button",{onClick:()=>a(Number(s)),"data-testid":"preview-port-go",children:"Load"})]})]}),d.jsxs("div",{className:"preview-body",children:[n==="preview"&&(o?d.jsx("iframe",{className:"preview-iframe",src:`http://localhost:${o}`,title:`localhost:${o}`,"data-testid":"preview-iframe"},o):d.jsxs("div",{className:"preview-empty","data-testid":"preview-empty",children:[d.jsxs("p",{style:{maxWidth:320,lineHeight:1.6},children:["No dev server detected yet. Ask the agent to start one (e.g. ",d.jsx("code",{children:"bun dev"}),", ",d.jsx("code",{children:"npm run dev"}),"), then I’ll auto-detect it."]}),i.length>0&&d.jsxs("p",{style:{fontSize:12},children:["Detected: ",i.map(p=>`:${p.port}`).join(", ")]})]})),n==="code"&&d.jsx("div",{className:"preview-code-view","data-testid":"preview-code",children:f?d.jsxs(d.Fragment,{children:[d.jsxs("div",{style:{color:"var(--text-muted)",fontSize:11,marginBottom:8},children:[e," (",f.size," bytes",f.truncated?", truncated":"",")"]}),d.jsx("pre",{children:f.content})]}):d.jsx("div",{className:"preview-empty",children:"Click a file in the tree to inspect it."})}),n==="logs"&&d.jsx("div",{className:"preview-empty","data-testid":"preview-logs",children:"Logs panel — wires up to the agent's stderr stream and dev-server stdout (v1.x)."}),n==="deploy"&&d.jsxs("div",{className:"preview-empty","data-testid":"preview-deploy",children:["Deploy — Cloudflare Pages + custom-domain wizard ships in v1.1.",d.jsx("br",{}),"For now: run ",d.jsx("code",{children:"npx wrangler pages deploy"})," in the terminal."]})]})]})}function EE({onClose:e}){const[t,n]=_.useState("viewer"),[r,i]=_.useState(24),[l,o]=_.useState(""),[a,s]=_.useState(null),[u,f]=_.useState([]),[c,p]=_.useState(null),[h,m]=_.useState(!1);_.useEffect(()=>{v()},[]);async function v(){const g=await fetch("/api/share").catch(()=>null);if(!g||!g.ok)return;const S=await g.json();f(S.tokens||[])}async function k(){p(null),m(!0);try{const g=await fetch("/api/share",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({role:t,ttlSeconds:Math.max(60,Number(r)*3600),label:l})}),S=await g.json();if(!g.ok)throw new Error(S.error||`HTTP ${g.status}`);s(S),v()}catch(g){p(g.message||String(g))}finally{m(!1)}}async function x(g){await fetch(`/api/share/${encodeURIComponent(g)}`,{method:"DELETE"}),v()}async function y(g){try{await navigator.clipboard.writeText(g)}catch{}}return d.jsx("div",{className:"modal-backdrop",onClick:e,children:d.jsxs("div",{className:"modal",onClick:g=>g.stopPropagation(),"data-testid":"share-dialog",children:[d.jsx("h2",{children:"Share workspace"}),d.jsx("p",{className:"muted",children:"Issue a token-protected URL. Anyone with the link opens this workspace in their browser with the role you choose."}),d.jsx("label",{children:"Role"}),d.jsx("div",{className:"row",children:d.jsxs("label",{style:{display:"flex",alignItems:"center",gap:6,color:"var(--text)"},children:[d.jsx("input",{type:"radio",name:"role",value:"viewer",checked:t==="viewer",onChange:()=>n("viewer"),"data-testid":"share-role-viewer"}),d.jsx("span",{children:"Viewer — read-only chat + preview"})]})}),d.jsx("div",{className:"row",children:d.jsxs("label",{style:{display:"flex",alignItems:"center",gap:6,color:"var(--text)"},children:[d.jsx("input",{type:"radio",name:"role",value:"client",checked:t==="client",onChange:()=>n("client"),"data-testid":"share-role-client"}),d.jsx("span",{children:"Client — chat + request changes (for AI-installer GTM)"})]})}),d.jsxs("div",{className:"row",children:[d.jsxs("div",{children:[d.jsx("label",{children:"Expires in"}),d.jsx("input",{type:"number",min:"1",value:r,onChange:g=>i(g.target.value),style:{width:80,background:"var(--bg)",color:"var(--text)",border:"1px solid var(--border)",padding:"4px 8px",borderRadius:6},"data-testid":"share-ttl"}),d.jsx("span",{style:{marginLeft:6,color:"var(--text-muted)",fontSize:12},children:"hours"})]}),d.jsxs("div",{style:{flex:1},children:[d.jsx("label",{children:"Label (optional)"}),d.jsx("input",{value:l,onChange:g=>o(g.target.value),placeholder:"e.g. Mel preview",style:{width:"100%",background:"var(--bg)",color:"var(--text)",border:"1px solid var(--border)",padding:"4px 8px",borderRadius:6},"data-testid":"share-label"})]})]}),c&&d.jsx("div",{style:{color:"var(--error)",fontSize:12,marginBottom:8},"data-testid":"share-error",children:c}),a&&d.jsxs(d.Fragment,{children:[d.jsx("label",{children:"Share URL"}),d.jsx("div",{className:"url-output","data-testid":"share-url",children:a.shareUrl}),d.jsxs("div",{style:{display:"flex",gap:8},children:[d.jsx("button",{onClick:()=>y(a.shareUrl),"data-testid":"share-copy",children:"Copy"}),d.jsx("a",{href:a.shareUrl,target:"_blank",rel:"noreferrer",children:d.jsx("button",{children:"Open"})})]})]}),u.length>0&&d.jsxs("div",{style:{marginTop:16},children:[d.jsx("label",{children:"Active share tokens"}),d.jsx("div",{style:{maxHeight:140,overflow:"auto",border:"1px solid var(--border)",borderRadius:6},children:u.map(g=>d.jsxs("div",{style:{padding:"6px 10px",borderBottom:"1px solid var(--border)",display:"flex",justifyContent:"space-between",alignItems:"center",fontSize:12},"data-testid":`active-token-${g.sub}`,children:[d.jsxs("div",{children:[d.jsx("strong",{children:g.label||g.role})," ",d.jsxs("span",{style:{color:"var(--text-muted)"},children:["(",g.role,", expires ",new Date(g.exp*1e3).toLocaleString(),")"]})]}),d.jsx("button",{onClick:()=>x(g.sub),style:{fontSize:11},"data-testid":`revoke-${g.sub}`,children:"Revoke"})]},g.sub))})]}),d.jsxs("div",{className:"actions",children:[d.jsx("button",{onClick:e,"data-testid":"share-close",children:"Close"}),d.jsx("button",{className:"primary",onClick:k,disabled:h,"data-testid":"share-issue",children:h?d.jsx("span",{className:"spinner"}):"Issue URL"})]})]})})}function CE({onClose:e,token:t}){var R,z;const[n,r]=_.useState(null),[i,l]=_.useState(!1),[o,a]=_.useState(null),[s,u]=_.useState(!1),[f,c]=_.useState(""),[p,h]=_.useState(""),[m,v]=_.useState(168),[k,x]=_.useState(null);_.useEffect(()=>{g()},[]);function y($,U={}){const M={...U.headers||{}};return t&&(M.Authorization=`Bearer ${t}`),fetch($,{...U,headers:M})}async function g(){a(null),l(!1);const $=await y("/api/sync/status").catch(()=>null);if(!$||!$.ok){l(!0);return}r(await $.json())}async function S($){a(null),u(!0);try{await $()}catch(U){a(U.message||String(U))}finally{u(!1)}}const N=()=>S(async()=>{const $=await y("/api/sync/pair",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({inviteCode:f})}),U=await $.json();if(!$.ok)throw new Error(U.error||`HTTP ${$.status}`);c(""),await g()}),b=()=>S(async()=>{var M,V;const $=await y("/api/sync/invite",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({projectCode:(V=(M=n==null?void 0:n.workspaces)==null?void 0:M[0])==null?void 0:V.projectId,displayName:p,expiresHours:Math.max(1,Number(m)||168)})}),U=await $.json();if(!$.ok)throw new Error(U.error||`HTTP ${$.status}`);x(U.invite)}),j=()=>S(async()=>{var M,V;const $=await y("/api/sync/detach",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({workspaceId:(V=(M=n==null?void 0:n.workspaces)==null?void 0:M[0])==null?void 0:V.workspaceId})}),U=await $.json();if(!$.ok)throw new Error(U.error||`HTTP ${$.status}`);x(null),await g()});async function T($){try{await navigator.clipboard.writeText($)}catch{}}const L=((R=n==null?void 0:n.workspaces)==null?void 0:R[0])||null,C=!!n&&!((z=n.daemon)!=null&&z.running);return d.jsx("div",{className:"modal-backdrop",onClick:e,children:d.jsxs("div",{className:"modal",onClick:$=>$.stopPropagation(),"data-testid":"sync-dialog",children:[d.jsx("h2",{children:"Collaborate on this folder"}),d.jsx("p",{className:"muted",children:"bmo-sync keeps this workspace folder identical across machines — yours and a collaborator's. Both of you edit; changes flow both ways. Like a shared drive, but built for workspaces."}),!n&&!i&&d.jsxs("div",{className:"muted",children:[d.jsx("span",{className:"spinner"})," Checking sync status…"]}),i&&d.jsxs("div",{className:"sync-state warn","data-testid":"sync-load-failed",children:[d.jsx("strong",{children:"Couldn't load sync status."}),d.jsx("span",{className:"muted",children:"The workspace server didn't answer. Try again."})]}),C&&d.jsxs("div",{className:"sync-state warn","data-testid":"sync-daemon-down",children:[d.jsx("strong",{children:"The bmo-sync daemon isn't running."}),d.jsx("span",{className:"muted",children:"Folder sharing needs the background sync service running on this machine. Start it, then recheck."})]}),n&&!C&&!L&&d.jsxs(d.Fragment,{children:[d.jsxs("div",{className:"sync-state","data-testid":"sync-unpaired",children:[d.jsxs("strong",{children:[n.workspaceName," isn't shared yet."]}),d.jsx("span",{className:"muted",children:"Got an invite code from a collaborator? Paste it to sync this folder with theirs."})]}),d.jsx("label",{children:"Invite code"}),d.jsxs("div",{className:"row",children:[d.jsx("input",{className:"sync-input",style:{flex:1},value:f,onChange:$=>c($.target.value),placeholder:"e.g. CAFE-MILANO-A8X3","data-testid":"sync-invite-input"}),d.jsx("button",{className:"primary",onClick:N,disabled:s||!f.trim(),"data-testid":"sync-join",children:s?d.jsx("span",{className:"spinner"}):"Connect folder"})]})]}),n&&!C&&L&&d.jsxs(d.Fragment,{children:[d.jsxs("div",{className:"sync-state ok","data-testid":"sync-paired",children:[d.jsxs("strong",{children:["✓ Synced — ",L.projectName||L.projectId]}),d.jsxs("span",{className:"muted",children:["This folder is shared. Edits sync both ways automatically",L.conflictCount?` · ${L.conflictCount} conflict${L.conflictCount>1?"s":""} resolved`:"","."]})]}),n.canInvite?d.jsxs(d.Fragment,{children:[d.jsx("label",{children:"Invite a collaborator"}),d.jsxs("div",{className:"row",children:[d.jsx("input",{className:"sync-input",style:{flex:1},value:p,onChange:$=>h($.target.value),placeholder:"Their name — e.g. Mel","data-testid":"sync-invite-name"}),d.jsx("input",{className:"sync-input",style:{width:64},type:"number",min:"1",value:m,onChange:$=>v($.target.value),title:"Invite expires in (hours)"}),d.jsx("button",{onClick:b,disabled:s,"data-testid":"sync-create-invite",children:s?d.jsx("span",{className:"spinner"}):"Create invite"})]}),k&&d.jsxs(d.Fragment,{children:[d.jsx("label",{children:"Send this code to your collaborator"}),d.jsx("div",{className:"url-output","data-testid":"sync-invite-code",children:k.code}),d.jsxs("div",{className:"row",style:{marginBottom:0},children:[d.jsx("button",{onClick:()=>T(k.code),"data-testid":"sync-copy-code",children:"Copy code"}),k.expiresAt&&d.jsxs("span",{className:"muted",style:{margin:0},children:["expires ",new Date(k.expiresAt*1e3).toLocaleDateString()]})]})]})]}):d.jsxs("p",{className:"muted","data-testid":"sync-no-invite",children:["Inviting from here needs a bmo-sync admin key (set"," ",d.jsx("span",{className:"md-code-inline",children:"BMO_SYNC_ADMIN_KEY"}),"). Without it, anyone you've already invited can still join with their own code."]})]}),o&&d.jsx("div",{style:{color:"var(--error)",fontSize:12,marginTop:8},"data-testid":"sync-error",children:o}),d.jsxs("div",{className:"actions",children:[(C||i)&&d.jsx("button",{onClick:g,disabled:s,"data-testid":"sync-recheck",children:"Recheck"}),L&&!C&&d.jsx("button",{onClick:j,disabled:s,"data-testid":"sync-disconnect",children:"Disconnect"}),d.jsx("button",{onClick:e,"data-testid":"sync-close",children:"Close"})]})]})})}function NE({alert:e,onDismiss:t}){if(!e)return null;const n=e.kind==="fatal";return d.jsxs("div",{className:"conflict-banner","data-testid":"conflict-banner",role:"status",children:[d.jsx("span",{className:"ico",children:n?"⛔":"⚠️"}),n?d.jsxs("span",{children:[d.jsx("strong",{children:"Sync stopped."})," ",e.message||"The sync engine hit a fatal error."," Reconnect from the Collaborate dialog."]}):d.jsxs("span",{children:[d.jsx("strong",{children:"Sync conflict"})," on ",d.jsx("code",{children:e.path||"a file"})," — bmo-sync kept ",d.jsx("strong",{children:e.resolution||"the latest edit"}),e.user?` (also edited by ${e.user})`:"","."]}),d.jsx("span",{className:"spacer"}),d.jsx("button",{onClick:t,"data-testid":"conflict-dismiss",children:"Dismiss"})]})}function jE({urlToken:e,onClose:t}){const[n,r]=_.useState([]),[i,l]=_.useState(!0),[o,a]=_.useState(null),[s,u]=_.useState(null),[f,c]=_.useState({}),[p,h]=_.useState(null),m=e?{Authorization:`Bearer ${e}`}:{};function v(){l(!0),fetch("/api/conflicts",{headers:m}).then(g=>g.ok?g.json():{conflicts:[]}).then(g=>{r(Array.isArray(g==null?void 0:g.conflicts)?g.conflicts:[]),a(null)}).catch(g=>a(String((g==null?void 0:g.message)||g))).finally(()=>l(!1))}_.useEffect(v,[]);function k(g){return`${g.workspaceId}:${g.path}`}async function x(g){const S=k(g);if(f[S])return;const N=new URL("/api/conflicts/view",window.location.origin);N.searchParams.set("workspaceId",g.workspaceId),N.searchParams.set("path",g.path);const b=await fetch(N.toString(),{headers:m});if(!b.ok){a(`Could not load ${g.path} (HTTP ${b.status})`);return}const j=await b.json().catch(()=>null);j&&c(T=>({...T,[S]:{mine:qf(j.mine),theirs:qf(j.theirs),conflict:j.conflict}}))}async function y(g,S){const N=k(g);h(N);try{const b=await fetch("/api/conflicts/resolve",{method:"POST",headers:{"content-type":"application/json",...m},body:JSON.stringify({workspaceId:g.workspaceId,path:g.path,action:S})});if(!b.ok){const j=await b.json().catch(()=>({}));a((j==null?void 0:j.error)||`Resolve failed (HTTP ${b.status}).`);return}v(),u(null)}finally{h(null)}}return d.jsxs("div",{className:"conflict-panel","data-testid":"conflict-panel",role:"dialog","aria-label":"Open conflicts",children:[d.jsxs("header",{className:"conflict-panel-header",children:[d.jsx("strong",{children:"Open conflicts"})," ",d.jsxs("span",{className:"muted",children:["(",n.length,") · changes saved to the daemon back-office until you resolve"]}),d.jsx("span",{className:"spacer"}),d.jsx("button",{onClick:v,children:"Refresh"}),d.jsx("button",{onClick:t,children:"Close"})]}),i&&d.jsx("div",{className:"muted",children:"Loading…"}),o&&d.jsx("div",{className:"conflict-panel-error",children:o}),!i&&n.length===0&&d.jsx("div",{className:"muted",children:"No open conflicts."}),d.jsx("ul",{className:"conflict-list",children:n.map(g=>{const S=k(g),N=s===S,b=f[S];return d.jsxs("li",{className:"conflict-row",children:[d.jsxs("div",{className:"conflict-row-head",onClick:()=>{N?u(null):(u(S),g.resolution!=="remote_announced"&&x(g))},children:[d.jsx("code",{children:g.path}),d.jsxs("span",{className:"muted",children:["·"," ",g.resolution==="remote_announced"?`${g.conflictingUser||"peer"} flagged`:g.conflictingUser||"peer"," ","·"," ",new Date((g.detectedAt||0)*1e3).toLocaleTimeString()]}),d.jsx("span",{className:"spacer"}),d.jsx("span",{className:"muted",children:N?"▲":"▼"})]}),N&&g.resolution==="remote_announced"&&d.jsxs("div",{className:"conflict-row-body",children:[d.jsxs("div",{className:"muted",children:[g.conflictingUser||"A peer"," reported a conflict on this file. Your local copy is unchanged here — open it, decide whether to keep your version or pull theirs, save, and the next sync will reconcile."]}),d.jsx("div",{className:"conflict-actions",children:d.jsx("button",{disabled:p===S,onClick:()=>y(g,"keep_mine"),"data-testid":"conflict-acknowledge",children:"Acknowledge"})})]}),N&&g.resolution!=="remote_announced"&&d.jsxs("div",{className:"conflict-row-body",children:[!b&&d.jsx("div",{className:"muted",children:"Loading diff…"}),b&&d.jsxs(d.Fragment,{children:[d.jsxs("div",{className:"conflict-meta",children:[d.jsxs("span",{children:["mine: ",d.jsx("code",{children:ca(g.mineSha256)})]}),d.jsxs("span",{children:["theirs: ",d.jsx("code",{children:ca(g.theirsSha256)})]}),g.baseSha256&&d.jsxs("span",{children:["base: ",d.jsx("code",{children:ca(g.baseSha256)})]})]}),d.jsx(TE,{mine:b.mine,theirs:b.theirs}),d.jsxs("div",{className:"conflict-actions",children:[d.jsx("button",{disabled:p===S,onClick:()=>y(g,"keep_mine"),"data-testid":"conflict-keep-mine",children:"Keep mine"}),d.jsx("button",{disabled:p===S,onClick:()=>y(g,"take_theirs"),"data-testid":"conflict-take-theirs",children:"Take theirs"})]})]})]})]},S)})})]})}function qf(e){if(typeof e!="string")return null;try{const t=atob(e),n=new Uint8Array(t.length);for(let r=0;r<t.length;r++)n[r]=t.charCodeAt(r);return new TextDecoder().decode(n)}catch{return null}}function ca(e){return typeof e!="string"?"∅":e.length>12?`${e.slice(0,12)}…`:e}function TE({mine:e,theirs:t}){return e==null&&t==null?d.jsx("div",{className:"muted",children:"Both versions unavailable on disk."}):e==null||t==null?d.jsx("div",{className:"muted",children:"Only one side is available — the other was deleted before resolution."}):d.jsx(vl,{oldText:t,newText:e})}function IE({role:e,urlToken:t,onSyncAlert:n,onOpenConflicts:r}){const[i,l]=_.useState([]),[o,a]=_.useState({tokensIn:0,tokensOut:0,costUsd:0}),[s,u]=_.useState([]),[f,c]=_.useState(!1),[p,h]=_.useState(null),[m,v]=_.useState(0);_.useEffect(()=>{if(e!=="partner")return;const S=t?{Authorization:`Bearer ${t}`}:{};fetch("/api/sync/status",{headers:S}).then(N=>N.ok?N.json():null).then(N=>{var b;N&&h({paired:!!N.paired,daemonRunning:!!((b=N.daemon)!=null&&b.running),peer:null})}).catch(()=>{}),fetch("/api/conflicts",{headers:S}).then(N=>N.ok?N.json():{conflicts:[]}).then(N=>v(Array.isArray(N==null?void 0:N.conflicts)?N.conflicts.length:0)).catch(()=>{})},[e,t]),_.useEffect(()=>{const S=window.location.protocol==="https:"?"wss":"ws",N=new URL(`${S}://${window.location.host}/ws/activity`);t&&N.searchParams.set("t",t);const b=new WebSocket(N.toString());return b.onopen=()=>c(!0),b.onclose=()=>c(!1),b.onmessage=j=>{let T;try{T=JSON.parse(j.data)}catch{return}T.type==="snapshot"?(l(T.snapshot.presence||[]),a(T.snapshot.usage||{tokensIn:0,tokensOut:0,costUsd:0}),u(T.snapshot.recent||[])):(u(L=>[...L.slice(-49),T]),k(T))},()=>{try{b.close()}catch{}}},[t]);function k(S){S.type==="daemon-status"?h(N=>({...N||{paired:!1},daemonRunning:!!S.connected})):S.type==="sync-paired"?h(N=>({...N||{},paired:!0,daemonRunning:!0})):S.type==="sync-detached"?h(N=>({...N||{},paired:!1,peer:null})):S.type==="remote-presence"?h(N=>({...N||{paired:!0},peer:S.user||null})):S.type==="sync-conflict"?(n==null||n({kind:"conflict",path:S.path,resolution:S.resolution,user:S.conflictingUser}),(S.resolution==="local_pre_write_guard"||S.resolution==="remote_announced")&&v(N=>N+1)):S.type==="sync-conflict-resolved"?v(N=>Math.max(0,N-1)):S.type==="sync-fatal"&&(n==null||n({kind:"fatal",message:S.message}))}const x=[...s].reverse().find(S=>S.type==="chat-stream"||S.type==="chat-user"||S.type==="chat-end"),y=s.find(S=>S.type==="inbox-change");let g=null;return p!=null&&p.paired&&(g=p.daemonRunning?d.jsxs("span",{className:"pill synced","data-testid":"activity-sync",children:["⇄ Synced",p.peer?` · with ${p.peer}`:""]}):d.jsx("span",{className:"pill sync-off","data-testid":"activity-sync",children:"⇄ Sync paused"})),d.jsxs("footer",{className:"bottombar","data-testid":"activity-bar",children:[d.jsx("span",{className:`pill ${f?"live":""}`,"data-testid":"activity-connected",children:f?"live":"reconnecting…"}),d.jsxs("span",{className:"pill","data-testid":"activity-presence",children:["👥 ",i.length," viewing"]}),g,e==="partner"&&m>0&&d.jsxs("button",{type:"button",className:"pill conflict-badge","data-testid":"activity-conflicts",onClick:()=>r==null?void 0:r(),title:"Resolve open sync conflicts",children:["🛑 ",m," conflict",m===1?"":"s"]}),d.jsxs("span",{className:"pill","data-testid":"activity-usage",children:["↓",o.tokensIn," ↑",o.tokensOut," · $",(o.costUsd||0).toFixed(3)]}),x&&d.jsx("span",{className:"pill",style:{maxWidth:380,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},"data-testid":"activity-last",children:x.type==="chat-end"?"✓ agent finished":"… agent streaming"}),y&&d.jsx("span",{className:"pill","data-testid":"activity-inbox",children:"📦 inbox updated"}),d.jsx("span",{style:{flex:1}}),d.jsxs("span",{style:{fontSize:11},children:["v0.1.0 · ",e]})]})}function _E(){var r;const[e,t]=_.useState(null);if(_.useEffect(()=>{let i=!1;async function l(){try{const s=await(await fetch("/api/inbox")).json();i||t(s)}catch{}}l();const o=setInterval(l,5e3);return()=>{i=!0,clearInterval(o)}},[]),!e||!e.hasInbox||!((r=e.imports)!=null&&r.length))return null;const n=e.imports.filter(i=>{var o;const l=Object.keys(e.installed||{}).find(a=>a.startsWith(i.replace(/-v[\d.]+$/,"")));return l?((o=e.installed[l])==null?void 0:o.integrated)!==!0:!0});return n.length===0?null:d.jsxs("div",{className:"inbox-card","data-testid":"inbox-card",children:["📦 ",d.jsx("strong",{children:"Component inbox"}),": ",n.length," pending integration",n.length!==1?"s":"",": ",n.join(", "),".",d.jsxs("span",{className:"muted",children:["Ask the agent: ",d.jsxs("em",{children:["“walk me through integrating ",n[0],"”"]})]})]})}const AE=[{id:"concise",label:"Concise",sample:"Got it. Done."},{id:"playful",label:"Playful",sample:"Oh nice — let's go!"},{id:"formal",label:"Formal",sample:"Understood. Proceeding now."},{id:"dry",label:"Dry",sample:"Sure. If we must."}],Qf=["#22d3ee","#a78bfa","#f472b6","#facc15","#34d399","#fb7185"],PE=[{id:"drive",label:"Google Drive",note:"Files & docs"},{id:"notion",label:"Notion",note:"Wiki & notes"},{id:"gmail",label:"Gmail",note:"Inbox & threads"},{id:"calendar",label:"Calendar",note:"Schedule"},{id:"canva",label:"Canva",note:"Designs"},{id:"github",label:"GitHub",note:"Code & PRs"}],LE=new Set(["login","missing","subscribe"]),FE=new Set(["login","missing"]),Yf={missing:{title:"Almost there — I need Claude installed before I can think.",subtitle:"That's the brain I run on. Install it once, then come back here.",cmdLabel:"Run this in your terminal",cmd:"curl -fsSL https://claude.ai/install.sh | bash",after:"Then come back and press the button below.",retry:"Still don't see Claude on this machine. Make sure the install finished, then try again."},login:{title:"One quick thing before we dig in — sign me in to Claude.",subtitle:"It's a one-time sign-in to your own Claude account — it runs on your machine, on your plan. After this you won't be asked again.",cmdLabel:"Run this once in your terminal",cmd:"claude auth login",after:"A browser window opens — sign in with your Claude account, then come back and press the button.",retry:"Hmm — still not signed in. Finish the sign-in in your browser, then press it again."},subscribe:{title:"You're in — I just need an active Claude plan to start thinking.",subtitle:"Claude Code runs on a Claude Pro plan (or higher). Grab one on your own account, then come back — you won’t be asked again.",cmdLabel:"Get a plan, then come back",link:"https://claude.ai/upgrade",linkLabel:"Open claude.ai →",after:"Subscribe at claude.ai, then come back and press the button.",retry:"Still no active plan showing. Give it a moment after subscribing, then try again."}},RE=new Set(["md","txt","json","yml","yaml","toml","js","mjs","cjs","ts","tsx","jsx","py","rb","go","rs","java","kt","swift","c","h","cpp","cc","hpp","css","scss","html","sh","env","sql","xml","svg","csv"]);function OE(e){const t=e.lastIndexOf(".");return t>=0?e.slice(t+1).toLowerCase():""}function DE(e){if(!e)return null;try{const t=new URL(e);return t.hostname==="localhost"||t.hostname==="127.0.0.1"?null:t.protocol==="http:"&&(t.port===""||t.port==="80")||t.protocol==="https:"&&(t.port===""||t.port==="443")?t.hostname:`${t.hostname}:${t.port}`}catch{return null}}async function zE(e){const t=Array.from(e||[]).slice(0,200);if(t.length===0)return{folderName:null,files:[]};const n=(t[0].webkitRelativePath||t[0].name).split("/")[0]||"folder",r=40,i=600,l=[];for(const a of t){if(l.length>=r)break;const s=a.webkitRelativePath||a.name,u=OE(a.name);if(!RE.has(u)){l.push({path:s,head:""});continue}if(a.size>100*1024){l.push({path:s,head:""});continue}try{const p=await a.slice(0,i).text();l.push({path:s,head:p})}catch{l.push({path:s,head:""})}}const o=Math.max(t.length-l.length,0);return{folderName:n,files:l,totalFiles:t.length,untouched:o}}function ME({peekSummary:e,services:t,agentName:n}){const r=[],i=e!=null&&e.folderName?` (you just glanced at "${e.folderName}")`:"";return r.push({kind:"survey",title:"Tell me what's in my workspace",sub:e!=null&&e.folderName?"I'll read the workspace I'm running in and summarize.":"I'll read what's here and summarize the project."}),(t==null?void 0:t.size)>0?r.push({kind:"startup",title:"Help me start something new",sub:`${n} will ask one question to figure out what to build.`}):r.push({kind:"startup",title:"Help me start a project",sub:"Tell me the rough idea — I'll scaffold it."}),r.push({kind:"chat",title:"Just chat — find me work",sub:"No agenda yet. Get me into the workspace."}),r}function Kf(e,t=55,n=380){const[r,i]=_.useState(""),[l,o]=_.useState(!1),[a,s]=_.useState(!1);return _.useEffect(()=>{if(!e){i(""),o(!1),s(!1);return}i(""),o(!1),s(!1);let u=0,f=null;const c=setTimeout(()=>{s(!0),f=setInterval(()=>{u+=1,i(e.slice(0,u)),u>=e.length&&(clearInterval(f),o(!0))},t)},n);return()=>{clearTimeout(c),f&&clearInterval(f)}},[e,t,n]),[r,l,a]}function rn({children:e,from:t="agent",accent:n,streaming:r=!1}){return d.jsx("div",{className:`ob-bubble ob-bubble-${t}${r?" ob-bubble-streaming":""}`,style:t==="user"?{borderColor:n}:void 0,children:e})}function Yn(){return d.jsx("span",{className:"ob-caret","aria-hidden":"true",children:"▍"})}function Xf(e){const t=Math.random().toString(36).slice(2,8);return`${e}-${Date.now().toString(36)}-${t}`}function BE({session:e,token:t,onComplete:n}){var Pu,Lu,Fu,Ru;const[r,i]=_.useState(1),l=_.useMemo(()=>DE(e==null?void 0:e.shareBaseUrl),[e==null?void 0:e.shareBaseUrl]),[o,a]=_.useState(((Pu=e==null?void 0:e.identity)==null?void 0:Pu.name)||""),[s,u]=_.useState(((Lu=e==null?void 0:e.identity)==null?void 0:Lu.tone)||"concise"),[f,c]=_.useState(((Fu=e==null?void 0:e.identity)==null?void 0:Fu.color)||Qf[0]),[p,h]=_.useState(!1),[m,v]=_.useState(null),[k,x]=_.useState(null),[y,g]=_.useState(!1),[S,N]=_.useState(!1),[b,j]=_.useState(null),[T,L]=_.useState(""),[C,R]=_.useState(new Set(((Ru=e==null?void 0:e.identity)==null?void 0:Ru.connectedServices)||[])),[z,$]=_.useState(!1),[U,M]=_.useState(null),[V,q]=_.useState(null),[F,W]=_.useState(!1),w=_.useRef(null),K=_.useRef(null),te=_.useRef({}),[E,se]=_.useState({}),Ie=_.useCallback((P,Y,ie)=>{P&&se(pe=>{const _e=pe[P]||{text:"",done:!1,error:null},jn=Y==="text"?_e.text+(ie||""):_e.text,pt={...pe,[P]:{..._e,text:jn}};return te.current[P]=jn,pt})},[]),re=_.useCallback((P,Y)=>{P&&se(ie=>{const pe=ie[P]||{text:"",done:!1,error:null};return{...ie,[P]:{...pe,done:!0,error:Y||pe.error}}})},[]);_.useEffect(()=>{let P=!1,Y=0,ie=null,pe=null;const _e=()=>{if(P)return;const jn=window.location.protocol==="https:"?"wss":"ws",pt=new URL(`${jn}://${window.location.host}/ws/chat`);t&&pt.searchParams.set("t",t),pe=new WebSocket(pt.toString()),K.current=pe,pe.onmessage=$t=>{var Ut;let $e;try{$e=JSON.parse($t.data)}catch{return}$e.type==="chunk"&&((Ut=$e.chunk)==null?void 0:Ut.type)==="text"?Ie($e.messageId,"text",$e.chunk.text):$e.type==="end"?re($e.messageId,null):$e.type==="error"&&re($e.messageId,$e.message||"agent error")},pe.onopen=()=>{Y=0},pe.onclose=()=>{if(K.current=null,P)return;const $t=Math.min(1e3*2**Y,8e3);Y+=1,ie=setTimeout(_e,$t)},pe.onerror=()=>{}};return _e(),()=>{P=!0,clearTimeout(ie);try{pe==null||pe.close()}catch{}}},[t,Ie,re]),_.useEffect(()=>{document.documentElement.style.setProperty("--accent",f),document.documentElement.style.setProperty("--accent-hot",f)},[f]);const ce=_.useMemo(()=>{const P={"content-type":"application/json"};return t&&(P.Authorization=`Bearer ${t}`),P},[t]);async function qe(P){const Y=await fetch("/api/agent/identity",{method:"POST",headers:ce,body:JSON.stringify({name:o,tone:s,color:f,...P})});if(!Y.ok){const ie=await Y.json().catch(()=>({}));throw new Error(ie.error||`save failed (${Y.status})`)}return Y.json()}const Qe=_.useCallback(async(P=!1)=>{g(!0);try{const Y=await fetch(`/api/agent/readiness${P?"?fresh=1":""}`,{headers:ce});return Y.ok?await Y.json():{status:"unknown"}}catch{return{status:"unknown"}}finally{g(!1)}},[ce]);async function zt(){if(v(null),!o.trim()){v("Pick a name — even just one syllable.");return}h(!0);try{await qe({});const P=await Qe(!1);if(LE.has(P.status)){x(P);return}i(2)}catch(P){v(String(P.message||P))}finally{h(!1)}}async function Gn(){const P=await Qe(!0);if(N(!0),P.status==="login"||P.status==="missing"){x(P);return}x(null),i(2)}async function Pi(){const P=await Qe(!0);if(P.status==="ready"||P.status==="unknown"){j(null),x(null),i(2);return}j(null),x(P)}async function wo(){N(!1),j({status:"starting",url:null,error:null});try{const Y=await(await fetch("/api/agent/login/start",{method:"POST",headers:ce})).json();j(Y),Y.status==="success"&&Pi()}catch{j({status:"error",url:null,error:"Could not start sign-in."})}}async function bo(){const P=T.trim();if(P){L("");try{const Y=await fetch("/api/agent/login/code",{method:"POST",headers:{...ce,"content-type":"application/json"},body:JSON.stringify({code:P})});j(await Y.json())}catch{}}}_.useEffect(()=>{if(!(b&&["starting","awaiting-browser","awaiting-code"].includes(b.status)))return;let Y=!0;const ie=setInterval(async()=>{try{const _e=await(await fetch("/api/agent/login/status",{headers:ce})).json();if(!Y)return;j(_e),_e.status==="success"&&(clearInterval(ie),Pi())}catch{}},2e3);return()=>{Y=!1,clearInterval(ie)}},[b==null?void 0:b.status,ce]);const[Ir,Li]=_.useState(null),wt=Ir?E[Ir]:null;async function _r(P){if(q(null),!(!P||P.length===0)){$(!0);try{const Y=await zE(P);if(Y.files.length===0){q("That folder looks empty — try another?"),$(!1);return}M(Y);const ie=Xf("peek");Li(ie),se(_e=>({..._e,[ie]:{text:"",done:!1,error:null}}));const pe=await fetch("/api/onboarding/peek",{method:"POST",headers:ce,body:JSON.stringify({folderName:Y.folderName,files:Y.files,totalFiles:Y.totalFiles,messageId:ie})});if(!pe.ok){const _e=await pe.json().catch(()=>({}));throw new Error(_e.error||`peek failed (${pe.status})`)}}catch(Y){q(String(Y.message||Y))}finally{$(!1)}}}function Fi(P){R(Y=>{const ie=new Set(Y);return ie.has(P)?ie.delete(P):ie.add(P),ie})}const[Cn,Ar]=_.useState(null),[Au,Pr]=_.useState(null),[Ri,Oi]=_.useState(null),I=Cn?E[Cn]:null;async function B(P){Oi(null),Pr(P);const Y=Xf("job");Ar(Y),se(ie=>({...ie,[Y]:{text:"",done:!1,error:null}}));try{await qe({connectedServices:[...C]});const ie=await fetch("/api/onboarding/start-job",{method:"POST",headers:ce,body:JSON.stringify({kind:P,messageId:Y,peekFolderName:(U==null?void 0:U.folderName)||null})});if(!ie.ok){const pe=await ie.json().catch(()=>({}));throw new Error(pe.error||`start-job failed (${ie.status})`)}}catch(ie){Oi(String(ie.message||ie)),Ar(null),Pr(null)}}async function Q(){try{await qe({connectedServices:[...C]});const P=await fetch("/api/agent/onboarded",{method:"POST",headers:ce});if(!P.ok){const Y=await P.json().catch(()=>({}));throw new Error(Y.error||`mark-onboarded failed (${P.status})`)}n==null||n()}catch(P){v(String(P.message||P))}}const X=o.trim()||"your agent",oe="Hey. I'm new here.",rt="Before we start — what should I call myself?",[Mt,Ye]=Kf(r===1?oe:"",65,420),[Nn,Bt]=Kf(r===1&&Ye?rt:"",48,520),Be=r===1?Bt:!0,en=_.useMemo(()=>ME({peekSummary:U,services:C,agentName:X}),[U,C,X]),ke=k?Yf[k.status]||Yf.login:null,bm=k?FE.has(k.status):!1;return d.jsx("div",{className:"ob-root",children:d.jsxs("div",{className:"ob-stage",children:[d.jsxs("header",{className:"ob-header",children:[d.jsx("div",{className:"ob-step-dots","aria-label":`Step ${r} of 5`,children:[1,2,3,4,5].map(P=>d.jsx("span",{className:`ob-dot ${P===r?"active":""} ${P<r?"done":""}`},P))}),r>1&&!k&&d.jsx("button",{className:"ob-back",onClick:()=>i(r-1),children:"← Back"})]}),k&&ke&&d.jsxs("section",{className:"ob-step",children:[d.jsxs(rn,{children:[d.jsx("p",{className:"ob-bubble-text",children:ke.title}),d.jsx("p",{className:"ob-bubble-subtle",children:ke.subtitle})]}),d.jsxs("div",{className:"ob-card ob-reveal",children:[k.status==="login"?d.jsxs("div",{className:"ob-login-inapp",children:[(!b||b.status==="idle")&&d.jsx("button",{className:"ob-next primary",onClick:wo,children:"Sign in to Claude →"}),b&&(b.status==="starting"||b.status==="awaiting-browser")&&d.jsxs(d.Fragment,{children:[d.jsx("p",{className:"ob-bubble-text",children:"Opening Claude in your browser…"}),d.jsx("p",{className:"ob-bubble-subtle",children:"Sign in there with your Claude account — I'll detect it automatically, nothing to come back and click."}),b.url&&d.jsx("a",{className:"ob-login-link",href:b.url,target:"_blank",rel:"noreferrer",children:"If the browser didn't open, click here →"})]}),b&&b.status==="awaiting-code"&&d.jsxs("div",{className:"ob-login-code",children:[d.jsx("label",{className:"ob-label",children:"Paste the code from your browser"}),d.jsxs("div",{className:"ob-actions",children:[d.jsx("input",{className:"ob-input",value:T,onChange:P=>L(P.target.value),placeholder:"code from claude.ai"}),d.jsx("button",{className:"ob-next primary",onClick:bo,children:"Submit"})]})]}),b&&(b.status==="error"||b.status==="unsupported")&&d.jsxs(d.Fragment,{children:[d.jsxs("div",{className:"ob-error",children:[b.error||"I couldn't run the in-app sign-in."," You can sign in from a terminal instead:"]}),d.jsx("div",{className:"ob-login-cmd",children:d.jsx("code",{children:ke.cmd})}),d.jsx("p",{className:"ob-bubble-subtle",style:{marginTop:10},children:ke.after})]})]}):d.jsxs(d.Fragment,{children:[d.jsx("label",{className:"ob-label",children:ke.cmdLabel}),ke.cmd?d.jsx("div",{className:"ob-login-cmd",children:d.jsx("code",{children:ke.cmd})}):d.jsx("a",{className:"ob-login-link",href:ke.link,target:"_blank",rel:"noreferrer",children:ke.linkLabel}),d.jsx("p",{className:"ob-bubble-subtle",style:{marginTop:10},children:ke.after})]}),S&&d.jsx("div",{className:"ob-error",style:{marginTop:12},children:ke.retry}),d.jsx("div",{className:"ob-actions",children:d.jsx("button",{className:"ob-next primary",onClick:Gn,disabled:y,children:y?"Checking…":"I've done it — continue →"})}),!bm&&d.jsx("div",{className:"ob-actions",children:d.jsx("button",{className:"ob-skip",onClick:()=>{x(null),i(2)},children:"I already have a plan — continue"})})]})]}),r===1&&!k&&d.jsxs("section",{className:"ob-step",children:[d.jsxs(rn,{streaming:!Ye||!Bt,children:[d.jsxs("p",{className:"ob-bubble-text",children:[Mt,!Ye&&d.jsx(Yn,{})]}),Ye&&d.jsxs("p",{className:"ob-bubble-text",children:[Nn,!Bt&&d.jsx(Yn,{})]})]}),Be&&d.jsxs("div",{className:"ob-card ob-reveal",children:[d.jsx("label",{className:"ob-label",children:"Name"}),d.jsx("input",{autoFocus:!0,className:"ob-input",placeholder:"Echo, Pip, Atlas, Sage…",value:o,onChange:P=>a(P.target.value),onKeyDown:P=>{P.key==="Enter"&&zt()},maxLength:40}),d.jsx("label",{className:"ob-label",children:"Voice"}),d.jsx("div",{className:"ob-tone-grid",children:AE.map(P=>d.jsxs("button",{className:`ob-tone ${s===P.id?"selected":""}`,onClick:()=>u(P.id),children:[d.jsx("span",{className:"ob-tone-label",children:P.label}),d.jsx("span",{className:"ob-tone-sample",children:P.sample})]},P.id))}),d.jsx("label",{className:"ob-label",children:"Color"}),d.jsx("div",{className:"ob-color-row",children:Qf.map(P=>d.jsx("button",{className:`ob-color-swatch ${f===P?"selected":""}`,style:{background:P},onClick:()=>c(P),"aria-label":`Pick ${P}`},P))}),m&&d.jsx("div",{className:"ob-error",children:m}),d.jsx("div",{className:"ob-actions",children:d.jsx("button",{className:"ob-next primary",onClick:zt,disabled:p||!o.trim(),children:p?"Saving…":`I'm ${X}. Let's go →`})})]})]}),r===2&&d.jsxs("section",{className:"ob-step",children:[d.jsxs(rn,{children:[d.jsx("p",{className:"ob-bubble-text",children:"Cool. Now — show me something you're working on. Drag a folder onto me."}),d.jsx("p",{className:"ob-bubble-subtle",children:"I'll read filenames + a short sample of each text file (≤40 files, ≤600 bytes each) so I can react. Files stay on your machine; the sample goes to Anthropic the same way every chat turn does. Pick something you'd be OK talking about in chat."})]}),d.jsxs("div",{className:`ob-dropzone ${F?"over":""} ${z?"peeking":""}`,onDragOver:P=>{P.preventDefault(),W(!0)},onDragLeave:()=>W(!1),onDrop:async P=>{var ie,pe;P.preventDefault(),W(!1);const Y=(ie=P.dataTransfer)==null?void 0:ie.items;if(Y&&Y.length>0&&Y[0].webkitGetAsEntry){const _e=[],jn=async(pt,$t="")=>{if(pt.isFile)await new Promise($e=>pt.file(Ut=>{try{Object.defineProperty(Ut,"webkitRelativePath",{value:`${$t}${Ut.name}`})}catch{}_e.push(Ut),$e()},$e));else if(pt.isDirectory){const $e=pt.createReader();await new Promise(Ut=>$e.readEntries(async Sm=>{for(const Em of Sm)await jn(Em,`${$t}${pt.name}/`);Ut()},Ut))}};for(const pt of Y){const $t=pt.webkitGetAsEntry();$t&&await jn($t)}if(_e.length>0){await _r(_e);return}}await _r((pe=P.dataTransfer)==null?void 0:pe.files)},onClick:()=>{var P;return(P=w.current)==null?void 0:P.click()},children:[d.jsx("input",{ref:w,type:"file",webkitdirectory:"",directory:"",multiple:!0,style:{display:"none"},onChange:P=>_r(P.target.files)}),z?d.jsxs(d.Fragment,{children:[d.jsx("div",{className:"ob-drop-icon",children:"👀"}),d.jsx("div",{className:"ob-drop-title",children:"Looking…"})]}):U?d.jsxs(d.Fragment,{children:[d.jsx("div",{className:"ob-drop-icon",children:"📁"}),d.jsx("div",{className:"ob-drop-title",children:U.folderName}),d.jsxs("div",{className:"ob-drop-sub",children:[U.totalFiles," file",U.totalFiles===1?"":"s"," · sampled ",U.files.length]})]}):d.jsxs(d.Fragment,{children:[d.jsx("div",{className:"ob-drop-icon",children:"📂"}),d.jsx("div",{className:"ob-drop-title",children:"Drop a folder here"}),d.jsx("div",{className:"ob-drop-sub",children:"or click to pick one"})]})]}),wt&&d.jsxs(rn,{streaming:!wt.done,children:[wt.text?d.jsxs("p",{className:"ob-bubble-text",children:[wt.text,!wt.done&&d.jsx(Yn,{})]}):d.jsxs("p",{className:"ob-bubble-subtle",children:["👀 ",X," is reading…",!wt.done&&d.jsx(Yn,{})]}),wt.error&&d.jsx("p",{className:"ob-bubble-subtle ob-bubble-error",children:wt.error})]}),V&&d.jsx("div",{className:"ob-error",children:V}),d.jsxs("div",{className:"ob-actions",children:[d.jsx("button",{className:"ob-skip",onClick:()=>i(3),children:"Skip — I'd rather just chat"}),d.jsx("button",{className:"ob-next primary",onClick:()=>i(3),disabled:!U&&!V,children:"Next: connect data →"})]})]}),r===3&&d.jsxs("section",{className:"ob-step",children:[d.jsxs(rn,{children:[d.jsx("p",{className:"ob-bubble-text",children:"What should I have eyes on?"}),d.jsx("p",{className:"ob-bubble-subtle",children:"Pick anything I should know lives in your world. I'll wire them up later — this just plants the flags."})]}),d.jsx("div",{className:"ob-tile-grid",children:PE.map(P=>d.jsxs("button",{className:`ob-tile ${C.has(P.id)?"selected":""}`,onClick:()=>Fi(P.id),children:[d.jsx("span",{className:"ob-tile-label",children:P.label}),d.jsx("span",{className:"ob-tile-note",children:P.note}),C.has(P.id)&&d.jsx("span",{className:"ob-tile-check",children:"✓"})]},P.id))}),d.jsx("div",{className:"ob-soon",children:"Connection flows for these arrive in the next release — your picks here are saved so I'll prompt you when they go live."}),d.jsxs("div",{className:"ob-actions",children:[d.jsx("button",{className:"ob-skip",onClick:()=>i(4),children:"Skip →"}),d.jsx("button",{className:"ob-next primary",onClick:()=>i(4),children:"Next: pair another device →"})]})]}),r===4&&d.jsxs("section",{className:"ob-step",children:[d.jsxs(rn,{children:[d.jsx("p",{className:"ob-bubble-text",children:"I don't live on this Mac. I live with you."}),d.jsx("p",{className:"ob-bubble-subtle",children:l?d.jsxs(d.Fragment,{children:["Open ",d.jsx("code",{children:l})," on your phone with the same login and I'll follow."]}):d.jsx(d.Fragment,{children:"Sign in on any device with the same token and I'll show up there — same memory, same conversation."})})]}),d.jsxs("div",{className:"ob-qr-placeholder",children:[d.jsx("div",{className:"ob-qr-box",children:d.jsxs("div",{className:"ob-qr-soon",children:["QR pairing",d.jsx("br",{}),"coming soon"]})}),d.jsx("p",{className:"ob-bubble-subtle",style:{textAlign:"center",marginTop:16},children:"For now, sign in on any device with the same token and we're synced."})]}),d.jsxs("div",{className:"ob-actions",children:[d.jsx("button",{className:"ob-skip",onClick:()=>i(5),children:"Skip →"}),d.jsx("button",{className:"ob-next primary",onClick:()=>i(5),children:"Next: first job →"})]})]}),r===5&&d.jsxs("section",{className:"ob-step",children:[d.jsxs(rn,{children:[d.jsx("p",{className:"ob-bubble-text",children:"Right — what should I actually do for you first?"}),d.jsx("p",{className:"ob-bubble-subtle",children:"Pick one and I'll start now. Or close this and just talk to me."})]}),Cn?d.jsxs(d.Fragment,{children:[d.jsxs(rn,{streaming:!I||!I.done,children:[I!=null&&I.text?d.jsxs("p",{className:"ob-bubble-text",children:[I.text,!I.done&&d.jsx(Yn,{})]}):d.jsxs("p",{className:"ob-bubble-subtle",children:[X," is on it…",(!I||!I.done)&&d.jsx(Yn,{})]}),(I==null?void 0:I.error)&&d.jsx("p",{className:"ob-bubble-subtle ob-bubble-error",children:I.error})]}),d.jsxs("div",{className:"ob-actions",children:[d.jsx("button",{className:"ob-skip",onClick:()=>{Ar(null),Pr(null)},children:"← Pick a different job"}),d.jsx("button",{className:"ob-next primary",onClick:Q,disabled:!I||!I.done&&!I.error,children:I!=null&&I.done?"Open my workspace →":"Streaming…"})]})]}):d.jsx("div",{className:"ob-jobs",children:en.map(P=>d.jsxs("button",{className:"ob-job",onClick:()=>B(P.kind),children:[d.jsx("div",{className:"ob-job-title",children:P.title}),d.jsx("div",{className:"ob-job-sub",children:P.sub})]},P.kind))}),(m||Ri)&&d.jsx("div",{className:"ob-error",children:m||Ri}),!Cn&&d.jsx("div",{className:"ob-actions",children:d.jsx("button",{className:"ob-skip",onClick:Q,children:"Skip to chat →"})})]}),!k&&d.jsx("p",{className:"ob-consent-note",children:"I quietly share how I'm doing — what's working, what breaks, my setup health — with the Venturewild team so they can help if you ever get stuck. Never your words or files. You can turn this off anytime."})]})})}const $E={display:"grid",placeItems:"center",minHeight:"100vh",padding:24,textAlign:"center"},UE={maxWidth:420,display:"grid",gap:16,justifyItems:"center"},HE={fontSize:44,letterSpacing:"0.18em",fontWeight:700,fontFamily:"ui-monospace, SFMono-Regular, Menlo, monospace",color:"var(--accent, #22d3ee)"};function VE(e){return e&&e.length===6?`${e.slice(0,3)} ${e.slice(3)}`:e}function WE(){const[e,t]=_.useState("starting"),[n,r]=_.useState(null),[i,l]=_.useState(null),[o,a]=_.useState(0),[s,u]=_.useState(0),f=_.useRef({requestId:null,pollAfterMs:2500}),c=_.useCallback(async()=>{t("starting"),r(null);try{const p=await fetch("/api/auth/pair/start",{method:"POST",headers:{"content-type":"application/json"},body:"{}"});if(p.status===429){t("error"),r("Too many sign-in attempts right now. Wait a minute, then try again.");return}if(!p.ok){t("error"),r("Could not start sign-in. Please try again.");return}const h=await p.json();f.current={requestId:h.requestId,pollAfterMs:h.pollAfterMs||2500},l(h.code),a(h.expiresAt||0),t("waiting")}catch{t("error"),r("Network error. Check your connection and try again.")}},[]);return _.useEffect(()=>{c()},[c]),_.useEffect(()=>{if(e!=="waiting")return;let p=!1,h=null;const{requestId:m,pollAfterMs:v}=f.current;async function k(){try{const x=await fetch("/api/auth/pair/status",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({requestId:m})});if(p)return;const y=await x.json().catch(()=>({}));if(y.status==="approved"&&y.token){if((await fetch("/api/auth/exchange",{method:"POST",headers:{Authorization:`Bearer ${y.token}`}})).ok){window.location.reload();return}t("error"),r("Almost there — sign-in completed but the session could not be saved. Try again.");return}if(y.status==="denied"){t("error"),r("This device was denied.");return}if(y.status==="expired"){t("error"),r("The code expired before it was approved.");return}h=setTimeout(k,v)}catch{p||(h=setTimeout(k,v))}}return h=setTimeout(k,v),()=>{p=!0,clearTimeout(h)}},[e]),_.useEffect(()=>{if(e!=="waiting"||!o)return;const p=()=>u(Math.max(0,Math.round((o-Date.now())/1e3)));p();const h=setInterval(p,1e3);return()=>clearInterval(h)},[e,o]),d.jsx("div",{className:"app",style:{gridTemplateRows:"1fr"},children:d.jsx("div",{style:$E,children:d.jsxs("div",{style:UE,children:[d.jsx("h2",{style:{margin:0},children:"Sign in to this workspace"}),e==="starting"&&d.jsxs("p",{style:{color:"var(--text-muted)"},children:[d.jsx("span",{className:"spinner"})," Preparing…"]}),e==="waiting"&&d.jsxs(d.Fragment,{children:[d.jsx("p",{style:{color:"var(--text-muted)",margin:0},children:"On a device where you’re already signed in, open this workspace and approve this code:"}),d.jsx("div",{style:HE,"data-testid":"pair-code",children:VE(i)}),d.jsxs("p",{style:{color:"var(--text-muted)",fontSize:13,margin:0},children:["Waiting for approval",s?` · expires in ${s}s`:"","…"]})]}),e==="error"&&d.jsxs(d.Fragment,{children:[d.jsx("p",{style:{color:"var(--error)"},children:n}),d.jsx("button",{onClick:c,children:"Try again"})]})]})})})}const GE={position:"fixed",top:12,left:"50%",transform:"translateX(-50%)",zIndex:1e3,background:"var(--surface, #1e293b)",border:"1px solid var(--accent, #22d3ee)",borderRadius:10,padding:"12px 16px",boxShadow:"0 8px 24px rgba(0,0,0,0.35)",display:"grid",gap:10,maxWidth:460},qE={display:"flex",gap:10,alignItems:"center",flexWrap:"wrap"},QE={fontFamily:"ui-monospace, SFMono-Regular, Menlo, monospace",letterSpacing:"0.12em",fontWeight:700,color:"var(--accent, #22d3ee)"};function YE(e){return e&&e.length===6?`${e.slice(0,3)} ${e.slice(3)}`:e}function KE({token:e}){const[t,n]=_.useState([]),[r,i]=_.useState(null),[l,o]=_.useState(null),a=_.useRef(!0),s=_.useCallback((c={})=>e?{Authorization:`Bearer ${e}`,...c}:c,[e]),u=_.useCallback(async()=>{try{const c=await fetch("/api/auth/pair/requests",{headers:s()});if(!c.ok)return;const p=await c.json();a.current&&n(Array.isArray(p.requests)?p.requests:[])}catch{}},[s]);_.useEffect(()=>{a.current=!0,u();const c=setInterval(u,5e3);return()=>{a.current=!1,clearInterval(c)}},[u]);async function f(c,p,h){if(!r){i(c),o(null);try{const m=await fetch(`/api/auth/pair/${p}`,{method:"POST",headers:s({"content-type":"application/json"}),body:JSON.stringify({requestId:c})});m.ok&&p==="approve"?o({ok:!0,text:`Approved ${h}.`}):m.ok||o({ok:!1,text:"That request is no longer waiting."})}catch{o({ok:!1,text:"Network error."})}finally{i(null),u()}}}return t.length===0?null:d.jsxs("div",{style:GE,"data-testid":"pair-approvals",children:[d.jsx("strong",{style:{fontSize:13},children:t.length===1?"A device wants to sign in":`${t.length} devices want to sign in`}),d.jsx("span",{style:{fontSize:12,color:"var(--text-muted)"},children:"Check that the code matches the one on that device, then approve."}),t.map(c=>d.jsxs("div",{style:qE,children:[d.jsx("span",{style:{fontSize:13},children:c.label}),d.jsx("span",{style:QE,"data-testid":"pair-request-code",children:YE(c.code)}),d.jsx("span",{className:"spacer",style:{flex:1}}),d.jsx("button",{onClick:()=>f(c.requestId,"approve",c.label),disabled:r===c.requestId,"data-testid":"pair-approve-btn",children:r===c.requestId?"Approving…":"Approve"}),d.jsx("button",{onClick:()=>f(c.requestId,"deny",c.label),disabled:r===c.requestId,style:{background:"transparent",color:"var(--text-muted)"},children:"Deny"})]},c.requestId)),l&&d.jsx("span",{style:{fontSize:12,color:l.ok?"var(--accent, #22d3ee)":"var(--error)"},children:l.text})]})}const XE={partner:"Partner",viewer:"Viewer",client:"Client"};function ZE(){var U,M;const[e,t]=_.useState(null),[n,r]=_.useState(null),[i,l]=_.useState(!1),[o,a]=_.useState(!1),[s,u]=_.useState(!1),[f,c]=_.useState(null),[p,h]=_.useState(!1),[m,v]=_.useState(!1),[k,x]=_.useState(!1),[y,g]=_.useState("build"),[S,N]=_.useState(null),b=_.useMemo(()=>typeof window>"u"?null:new URLSearchParams(window.location.search).get("t"),[]);if(_.useEffect(()=>{async function V(){try{const q={};b&&(q.Authorization=`Bearer ${b}`);const F=await fetch("/api/session",{headers:q});if(F.status===401){l(!0);return}if(!F.ok)throw new Error(`session fetch failed: ${F.status}`);const W=await F.json();t(W)}catch(q){r(q.message||String(q))}}V()},[b]),_.useEffect(()=>{var q;const V=(q=e==null?void 0:e.identity)==null?void 0:q.color;V&&(document.documentElement.style.setProperty("--accent",V),document.documentElement.style.setProperty("--accent-hot",V))},[(U=e==null?void 0:e.identity)==null?void 0:U.color]),i)return d.jsx(WE,{});if(n)return d.jsx("div",{className:"app",style:{gridTemplateRows:"1fr",alignItems:"center",justifyItems:"center"},children:d.jsxs("div",{style:{padding:24},children:[d.jsx("h2",{children:"wild-workspace"}),d.jsxs("p",{style:{color:"var(--error)"},children:["Could not load session: ",n]}),d.jsx("p",{style:{color:"var(--text-muted)",fontSize:13},children:"Verify the server is running and the share URL token is valid."})]})});if(!e)return d.jsx("div",{className:"app",style:{gridTemplateRows:"1fr",alignItems:"center",justifyItems:"center"},children:d.jsxs("div",{style:{padding:24,color:"var(--text-muted)"},children:[d.jsx("span",{className:"spinner"})," Loading workspace…"]})});if((M=e.capabilities)!=null&&M.chatWrite&&!e.onboarded)return d.jsx(BE,{session:e,token:b,onComplete:async()=>{try{const V={};b&&(V.Authorization=`Bearer ${b}`);const q=await fetch("/api/session",{headers:V});q.ok&&t(await q.json())}catch{t({...e,onboarded:!0})}}});const{role:j,capabilities:T,workspace:L,workspaceId:C,agent:R,identity:z}=e,$=["workspace"];return T.fileTree?m&&$.push("collapsed-tree"):$.push("no-tree"),T.preview||$.push("no-preview"),d.jsxs("div",{className:"app",children:[T.share&&d.jsx(KE,{token:b}),d.jsxs("header",{className:"topbar","data-testid":"topbar",children:[d.jsx("span",{className:"brand",children:"wild-workspace"}),d.jsx("span",{className:`badge ${j}`,"data-testid":"role-badge",children:XE[j]||j}),d.jsx("span",{className:"badge","data-testid":"workspace-name",children:(L==null?void 0:L.name)||C}),z!=null&&z.name?d.jsx("span",{className:"badge","data-testid":"agent-name",style:{borderColor:z.color,color:z.color},children:z.name}):R&&d.jsx("span",{className:"badge","data-testid":"agent-name",children:R.label||R.id}),d.jsx("span",{className:"spacer"}),T.sync&&d.jsx("button",{onClick:()=>u(!0),"data-testid":"collaborate-btn",children:"Collaborate"}),T.share&&d.jsx("button",{onClick:()=>a(!0),"data-testid":"share-btn",children:"Share"}),T.terminal&&d.jsx("button",{onClick:()=>x(V=>!V),"data-testid":"terminal-toggle",children:k?"Hide terminal":"Terminal"})]}),d.jsxs("main",{className:$.join(" "),children:[T.fileTree&&!m&&d.jsx(wE,{onCollapse:()=>v(!0),workspaceRoot:L==null?void 0:L.path,onOpen:N}),T.fileTree&&m&&d.jsx("aside",{className:"tree-collapsed-rail",children:d.jsx("button",{onClick:()=>v(!1),title:"Show file tree",children:"≡"})}),d.jsxs("section",{className:"chat-pane","data-testid":"chat-pane",children:[d.jsxs("div",{className:"chat-header","data-testid":"chat-header",children:[d.jsx("span",{style:{fontSize:12,color:"var(--text-muted)",textTransform:"uppercase",letterSpacing:"0.08em"},children:"Chat"}),T.chatWrite&&d.jsxs("span",{className:"chat-mode-toggle","data-testid":"mode-toggle",children:[d.jsx("button",{className:y==="build"?"active":"",onClick:()=>g("build"),"data-testid":"mode-build",children:"Build"}),d.jsx("button",{className:y==="plan"?"active":"",onClick:()=>g("plan"),"data-testid":"mode-plan",children:"Plan"})]}),d.jsx("span",{className:"spacer"}),d.jsx("span",{style:{fontSize:11,color:"var(--text-muted)"},children:R!=null&&R.available?`via ${R.label}`:"no agent installed — install Claude Code (npm i -g @anthropic-ai/claude-code)"})]}),T.inbox&&d.jsx(_E,{}),d.jsx(mE,{role:j,capabilities:T,mode:y,token:b})]}),T.preview&&d.jsx("section",{className:"preview-pane","data-testid":"preview-pane",children:d.jsx(SE,{focus:S,workspaceRoot:L==null?void 0:L.path})})]}),d.jsx(IE,{role:j,capabilities:T,session:e,urlToken:b,onSyncAlert:c,onOpenConflicts:()=>h(!0)}),d.jsx(NE,{alert:f,onDismiss:()=>c(null)}),p&&d.jsx(jE,{urlToken:b,onClose:()=>h(!1)}),o&&d.jsx(EE,{onClose:()=>a(!1),workspaceId:C}),s&&d.jsx(CE,{onClose:()=>u(!1),token:b}),k&&d.jsxs("div",{className:"terminal-overlay","data-testid":"terminal-overlay",children:[d.jsxs("header",{children:[d.jsx("strong",{children:"Terminal (preview)"}),d.jsx("button",{onClick:()=>x(!1),children:"close"})]}),d.jsx("p",{children:"Power-user terminal is a hidden-by-default toggle. The xterm.js + node-pty integration ships in v1.1. For now use your OS terminal — wild-workspace doesn't replace your shell, it surfaces the agent."})]})]})}function JE(e){const t=window.fetch.bind(window);window.fetch=(n,r={})=>{const i=typeof n=="string"?n:(n==null?void 0:n.url)||"";if(!(i.startsWith("/api/")||i.includes("://")===!1&&i.startsWith("api/")))return t(n,r);const o=new Headers(r.headers||{});return o.has("Authorization")||o.set("Authorization",`Bearer ${e}`),t(n,{...r,headers:o})}}function eC(){fa.createRoot(document.getElementById("root")).render(d.jsx(Bm.StrictMode,{children:d.jsx(ZE,{})}))}async function tC(){const t=typeof window<"u"&&window.fetch?new URLSearchParams(window.location.search).get("t"):null;if(t){let n=!1;try{const r=await window.fetch("/api/auth/exchange",{method:"POST",headers:{Authorization:`Bearer ${t}`}});n=r.ok&&(await r.json().catch(()=>({}))).cookie===!0}catch{}if(n){const r=new URLSearchParams(window.location.search);r.delete("t");const i=r.toString(),l=window.location.pathname+(i?`?${i}`:"")+window.location.hash;window.history.replaceState(null,"",l)}else JE(t)}eC()}tC();
@@ -6,7 +6,7 @@
6
6
  <meta name="theme-color" content="#0a0a0c" />
7
7
  <title>wild-workspace</title>
8
8
  <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='14' fill='%230061FE'/%3E%3Ctext x='32' y='42' font-family='ui-sans-serif,system-ui' font-size='30' font-weight='700' text-anchor='middle' fill='%23fff'%3Ew%3C/text%3E%3C/svg%3E" />
9
- <script type="module" crossorigin src="/assets/index-CAzFAt7W.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-Dc6jo84c.js"></script>
10
10
  <link rel="stylesheet" crossorigin href="/assets/index-Bj-mdLGj.css">
11
11
  </head>
12
12
  <body>