claude-simple 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/bin/edge-ctl.js +260 -0
- package/client.js +7 -1
- package/dist/app.0.2.5.min.js +953 -0
- package/dist/index.html +2 -2
- package/dist/{style.0.2.3.min.css → style.0.2.5.min.css} +229 -32
- package/package.json +6 -2
- package/public/app.js +12 -2
- package/public/chat.js +271 -61
- package/public/index.html +33 -3
- package/public/mobile.js +72 -0
- package/public/pwa.js +32 -2
- package/public/shell/boot.js +2 -0
- package/public/shell/hub.js +5 -1
- package/public/shell/layout.js +11 -2
- package/public/shell/templates.js +37 -19
- package/public/shell.js +34 -4
- package/public/state.js +8 -1
- package/public/style.css +228 -31
- package/public/sw.js +2 -2
- package/src/app.js +21 -7
- package/dist/app.0.2.3.min.js +0 -924
package/dist/app.0.2.3.min.js
DELETED
|
@@ -1,924 +0,0 @@
|
|
|
1
|
-
var Bs=Object.defineProperty;var ja=Object.getOwnPropertyDescriptor;var Da=Object.getOwnPropertyNames;var _a=Object.prototype.hasOwnProperty;var X=(e,t)=>()=>(e&&(t=e(e=0)),t);var Os=(e,t)=>{for(var s in t)Bs(e,s,{get:t[s],enumerable:!0})},Rs=(e,t,s,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of Da(t))!_a.call(e,a)&&a!==s&&Bs(e,a,{get:()=>t[a],enumerable:!(o=ja(t,a))||o.enumerable});return e},zn=(e,t,s)=>(Rs(e,t,"default"),s&&Rs(s,t,"default"));var n={};import*as or from"https://cdn.jsdelivr.net/gh/forechoandlook/mini-react@v0.1.13/dist/mini-react.min.js";var C=X(()=>{zn(n,or)});var Gs={};Os(Gs,{AGENT_DEFAULT_MODEL:()=>Fs,AGENT_LABELS:()=>R,AGENT_MODELS:()=>Jn,LOW_TURN_THRESHOLD:()=>Vn,activityHits:()=>ue,activityLoading:()=>Me,addCustomModel:()=>Tt,agentFilter:()=>Q,agentsMeta:()=>it,chatDensity:()=>Te,collapsedFolders:()=>at,ctx:()=>f,currentAgent:()=>S,currentEffort:()=>pe,currentModel:()=>$,currentPermission:()=>Mt,currentProject:()=>y,currentTab:()=>j,expandedFolders:()=>ot,favoritesOnly:()=>Pe,filesPath:()=>A,filesRoot:()=>F,filteredSessions:()=>Re,getCustomModels:()=>tn,getDefaultModel:()=>re,getEffortsForModel:()=>Et,getModelsForAgent:()=>fe,getSessionMeta:()=>rt,gitRoot:()=>J,hubMachineReady:()=>Ee,hubMode:()=>k,isProcessing:()=>ie,machinesList:()=>D,metaKey:()=>B,projectGroups:()=>ze,removeCustomModel:()=>Qn,selectedMachineId:()=>M,sessionDisplayTitle:()=>Ge,sessionFilter:()=>O,sessionMetaMap:()=>v,sessionSearch:()=>N,sessionSort:()=>Us,sessionsData:()=>E,setAgent:()=>me,setChatDensity:()=>Yn,setSelectedMachine:()=>Z,showHiddenOnly:()=>Ne,sidebarView:()=>Le,timeRange:()=>te,viewingFile:()=>U,workspacesData:()=>P,wsStatus:()=>Se});function Z(e){let t=e||null;M.value=t,f.machineId=t,t?localStorage.setItem("machineId",t):localStorage.removeItem("machineId")}function B(e,t,s){let o=`${e||"claude"}:${t}`;return s||f?.machineId?`${s||f.machineId}:${o}`:o}function rt(e,t,s){return v.peek()?.[B(e,t,s)]||{favorite:!1,notes:"",title:"",hidden:!1}}function Ge(e,{preferSnippet:t=!1}={}){if(!e)return"Session";if(t&&e.snippet)return e.snippet;let o=(rt(e.agent,e.sessionId,e.machineId).title||"").trim();return o||e.display||e.sessionId?.slice(0,8)||"Session"}function Yn(e){let t=["clean","normal","full"].includes(e)?e:"normal";Te.value=t,localStorage.setItem("chatDensity",t),document.getElementById("messages")?.setAttribute("data-density",t)}function Ha(e){return e?typeof e=="string"?{value:e,label:e}:e.value?{value:String(e.value),label:e.label||e.value,...Array.isArray(e.efforts)&&e.efforts.length?{efforts:e.efforts.map(String)}:{},...e.defaultEffort?{defaultEffort:String(e.defaultEffort)}:{}}:null:null}function Xn(){try{let e=localStorage.getItem(qs);if(!e)return{};let t=JSON.parse(e);return t&&typeof t=="object"?t:{}}catch{return{}}}function Ws(e){localStorage.setItem(qs,JSON.stringify(e))}function tn(e){let t=(e||"claude").toLowerCase(),s=Xn()[t];return Array.isArray(s)?s.filter(Boolean).map(String):[]}function Tt(e,t){let s=(e||"claude").toLowerCase(),o=String(t||"").trim();if(!o)return!1;let a=Xn(),i=Array.isArray(a[s])?[...a[s]]:[];return i.includes(o)||i.push(o),a[s]=i,Ws(a),!0}function Qn(e,t){let s=(e||"claude").toLowerCase(),o=String(t||"").trim(),a=Xn(),i=Array.isArray(a[s])?a[s].filter(r=>r!==o):[];return a[s]=i,Ws(a),!0}function fe(e){let t=(e||"claude").toLowerCase(),s=it.peek()?.defaults?.[t],o=s?.models?.length?s.models.map(Ha).filter(Boolean):(Jn[t]||Jn.claude).map(r=>({...r})),a=tn(t),i=new Set(o.map(r=>r.value));for(let r of a){if(i.has(r)){o=o.map(c=>{if(c.value!==r)return c;let u=String(c.label||c.value);return{...c,custom:!0,label:u.includes("\u2605")?u:`${u} \u2605`}});continue}i.add(r),o.push({value:r,label:`${r} \u2605`,custom:!0})}return o}function re(e){let t=(e||"claude").toLowerCase(),s=it.peek()?.defaults?.[t];return s?.model?String(s.model):Fs[t]||fe(t)[0]?.value||"claude-sonnet-4-5"}function Et(e,t){let o=fe(e).find(i=>i.value===t);if(o?.efforts?.length)return o.efforts;let a=it.peek()?.defaults?.[(e||"claude").toLowerCase()];return a?.efforts?.length?a.efforts.map(String):["low","medium","high","xhigh","max"]}function me(e){let t=(e||"claude").toLowerCase();S.value=t,localStorage.setItem("agent",t);let s=fe(t),o=re(t);s.some(a=>a.value===$.peek())||($.value=o,localStorage.setItem("model",o))}function Pa(e,t){return!t||t==="all"?!0:(e.agent||"claude")===t}function Na(e,t){let s=parseInt(t||"0",10)||0;if(s<=0)return!0;let o=e.updatedAt||0;if(typeof o=="string"){let a=Number(o);o=Number.isFinite(a)&&a>0?a<1e12?a*1e3:a:Date.parse(o)||0}else typeof o=="number"&&o>0&&o<1e12&&(o=o*1e3);return o>=Date.now()-s*864e5}function Ra(e,t){if(!t)return!0;let s=v.value?.[B(e.agent,e.sessionId,e.machineId)];return[e.display,e.cwd,e.projectName,e.agent,e.sessionId,e.snippet,e.machineId,s?.notes,s?.title].filter(Boolean).join(" ").toLowerCase().includes(t)}function Kn(e){return Array.isArray(e)?e:e&&Array.isArray(e.results)?e.results:e&&Array.isArray(e.sessions)?e.sessions:[]}var E,P,O,N,Us,ot,at,A,U,y,j,ie,Se,S,$,pe,Mt,F,J,it,Le,Q,te,ue,Me,v,Pe,Ne,Vn,Te,k,D,M,Ee,f,R,Jn,Fs,qs,Re,ze,I=X(()=>{C();E=(0,n.signal)([]),P=(0,n.signal)([]),O=(0,n.signal)(null),N=(0,n.signal)(""),Us=(0,n.signal)("time"),ot=(0,n.signal)(new Set),at=(0,n.signal)(new Set),A=(0,n.signal)(""),U=(0,n.signal)(null),y=(0,n.signal)(null),j=(0,n.signal)("chat"),ie=(0,n.signal)(!1),Se=(0,n.signal)("idle"),S=(0,n.signal)(localStorage.getItem("agent")||"claude"),$=(0,n.signal)(localStorage.getItem("model")||"claude-sonnet-4-5"),pe=(0,n.signal)(""),Mt=(0,n.signal)("default"),F=(0,n.signal)(""),J=(0,n.signal)(""),it=(0,n.signal)(null),Le=(0,n.signal)(localStorage.getItem("sidebarView")||"projects"),Q=(0,n.signal)(localStorage.getItem("agentFilter")||"all"),te=(0,n.signal)(localStorage.getItem("timeRange")||"14"),ue=(0,n.signal)(null),Me=(0,n.signal)(!1),v=(0,n.signal)({}),Pe=(0,n.signal)(localStorage.getItem("favoritesOnly")==="1"),Ne=(0,n.signal)(localStorage.getItem("showHiddenOnly")==="1"),Vn=2,Te=(0,n.signal)(localStorage.getItem("chatDensity")||"normal"),k=(0,n.signal)(!1),D=(0,n.signal)([]),M=(0,n.signal)(null),Ee=(0,n.signal)(!1),f={token:localStorage.getItem("token"),sessionId:null,agent:null,machineId:null,ws:null,shellBubble:null,configDir:null};R={claude:"Claude",codex:"Codex",grok:"Grok"},Jn={claude:[{value:"claude-sonnet-4-5",label:"sonnet-4-5"},{value:"claude-sonnet-4-6",label:"sonnet-4-6"},{value:"claude-opus-4-5",label:"opus-4-5"},{value:"claude-haiku-4-5",label:"haiku-4-5"}],codex:[{value:"gpt-5.4",label:"gpt-5.4"},{value:"gpt-5.3",label:"gpt-5.3"},{value:"gpt-5.2",label:"gpt-5.2"},{value:"o3",label:"o3"},{value:"o4-mini",label:"o4-mini"},{value:"codex-mini-latest",label:"codex-mini"}],grok:[{value:"grok-4.5",label:"grok-4.5"},{value:"grok-4",label:"grok-4"},{value:"grok-3",label:"grok-3"},{value:"grok-3-mini",label:"grok-3-mini"}]},Fs={claude:"claude-sonnet-4-5",codex:"gpt-5.4",grok:"grok-4.5"};qs="customModelsByAgent";Re=(0,n.computed)(()=>{let e=O.value,t=N.value.toLowerCase().trim(),s=Us.value,o=Q.value,a=te.value,i=Pe.value,r=Ne.value,c=v.value,u=ue.value,l=u?.results?.length&&t?Kn(u.results):Kn(E.value);e&&(l=l.filter(p=>p&&(p.cwd===e||p.configDir===e)));let d=M.value;return k.value&&d&&(l=l.filter(p=>p&&(!p.machineId||p.machineId===d))),l=l.filter(p=>p&&Pa(p,o)&&Na(p,a)),r?l=l.filter(p=>p&&c?.[B(p.agent,p.sessionId,p.machineId)]?.hidden):l=l.filter(p=>p&&!c?.[B(p.agent,p.sessionId,p.machineId)]?.hidden),i&&(l=l.filter(p=>p&&c?.[B(p.agent,p.sessionId,p.machineId)]?.favorite)),t&&!(u?.results&&u.q===t)&&(l=l.filter(p=>p&&Ra(p,t))),l=[...l].sort((p,m)=>{let b=c?.[B(p.agent,p.sessionId,p.machineId)]?.favorite?1:0,g=c?.[B(m.agent,m.sessionId,m.machineId)]?.favorite?1:0;return b!==g?g-b:s==="project"&&(p.cwd||"").localeCompare(m.cwd||"")||m.updatedAt-p.updatedAt}),!e&&!t&&!i?l.slice(0,120):l}),ze=(0,n.computed)(()=>{let e=Kn(Re.value),t=new Map;for(let s of e){if(!s)continue;let o=s.machineId?`${s.machineId}::${s.cwd||"(no path)"}`:s.cwd||"(no path)";t.has(o)||t.set(o,{cwd:s.cwd||null,machineId:s.machineId||null,projectName:s.projectName||(s.cwd?s.cwd.split("/").filter(Boolean).pop():"(no path)"),updatedAt:0,agents:{},sessions:[],latest:null});let a=t.get(o);a.sessions.push(s),a.agents[s.agent||"claude"]=(a.agents[s.agent||"claude"]||0)+1,(s.updatedAt||0)>a.updatedAt&&(a.updatedAt=s.updatedAt,a.latest=s)}return[...t.values()].sort((s,o)=>o.updatedAt-s.updatedAt)})});function It(e={}){return e.machineId!==void 0?e.machineId:f.machineId||M.peek()||null}function Oa(e){let t=e.split("?")[0];return t==="/api/hub"||t==="/api/machines"||t.startsWith("/api/auth/")}async function h(e,t,s,o,a={}){let i=It(a),r={"Content-Type":"application/json",...f.token?{Authorization:`Bearer ${f.token}`}:{}};k.peek()&&i&&!Oa(t)&&(r["X-Machine-Id"]=i);let c=a.etag===!0?`${f.token||""}:${i||""}:${t}`:a.etag||null;if(e.toUpperCase()==="GET"&&c){let d=zs.get(c);d&&(r["If-None-Match"]=d)}let u=async()=>{let d=await fetch(t,{method:e,signal:o,headers:r,...s!==void 0?{body:JSON.stringify(s)}:{}});if(d.status===304)return Zn;let p=await d.json().catch(()=>({}));if(!d.ok)throw new Error(p.error||`HTTP ${d.status}`);if(c){let m=d.headers.get("ETag");m&&zs.set(c,m)}return p};if(e.toUpperCase()!=="GET"||o||a.dedupe===!1)return u();let l=`${f.token||""}:${i||""}:${t}`;return Ba.get(l,u)}function Ct(e){return f.ws?.readyState===WebSocket.OPEN?(f.ws.send(JSON.stringify(e)),!0):(e?.type==="ping"||e?.type==="pong"||e?.type==="subscribe"||nn.push(e),!1)}function Js(){for(;nn.length&&f.ws?.readyState===WebSocket.OPEN;)f.ws.send(JSON.stringify(nn.shift()))}function Ks(){nn.length=0}function Vs(e={}){let t=new URLSearchParams(e);f.token&&t.set("token",f.token);let s=It();return k.peek()&&s&&t.set("machine",s),t.toString()}function lt(e={}){let t={...e};f.token&&(t.Authorization=`Bearer ${f.token}`);let s=It();return k.peek()&&s&&(t["X-Machine-Id"]=s),t}async function ct(){try{let e=await fetch("/api/hub");if(!e.ok)return k.value=!1,!1;let t=await e.json();if(t?.hub)return k.value=!0,t}catch{}return k.value=!1,!1}var Ba,zs,Zn,nn,K=X(()=>{I();C();Ba=(0,n.createFetch)({cache:!1,dedupe:!0,retry:0}),zs=new Map,Zn=Symbol("not-modified");nn=[]});function Je(){try{let e=localStorage.getItem(eo);return e?JSON.parse(e):null}catch{return null}}function es(e){try{let s={...Je()||{},...e,updatedAt:Date.now()};localStorage.setItem(eo,JSON.stringify(s))}catch{}}var eo,sn=X(()=>{eo="lastSessionContext"});async function ts(){if(At&&!(typeof navigator>"u"||!navigator.wakeLock?.request)&&document.visibilityState==="visible"&&!Ke)try{Ke=await navigator.wakeLock.request("screen"),Ke.addEventListener("release",()=>{Ke=null,At&&document.visibilityState==="visible"&&setTimeout(()=>{ts().catch(()=>{})},500)})}catch{Ke=null}}async function to(){let e=Ke;if(Ke=null,!!e)try{await e.release()}catch{}}function no(e){At=!!e,At?ts().catch(()=>{}):to().catch(()=>{})}function so(){typeof document>"u"||document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&At?ts().catch(()=>{}):document.visibilityState==="hidden"&&to().catch(()=>{})})}var Ke,At,oo=X(()=>{Ke=null,At=!1});function _(e){if(e==null||e==="")return null;if(typeof e=="number")return!Number.isFinite(e)||e<=0?null:e<1e12?Math.round(e*1e3):Math.round(e);if(typeof e=="string"){let t=e.trim();if(/^\d+(\.\d+)?$/.test(t))return _(Number(t));let s=Date.parse(t);return Number.isNaN(s)?null:s}return null}function pt(e){let t=_(e);if(t==null)return"";let s=Date.now()-t;return s<0?new Date(t).toLocaleDateString():s<6e4?"just now":s<36e5?`${Math.floor(s/6e4)}m ago`:s<864e5?`${Math.floor(s/36e5)}h ago`:s<7*864e5?`${Math.floor(s/864e5)}d ago`:new Date(t).toLocaleDateString()}function Ve(e,t=!0){let s=e||"claude",a={claude:"bg-orange-500/15 text-orange-400",codex:"bg-emerald-500/15 text-emerald-400",grok:"bg-sky-500/15 text-sky-400"}[s]||"bg-base-300 text-base-content/50",i=t?s==="claude"?"CC":s==="codex"?"CX":s==="grok"?"GX":s.slice(0,2).toUpperCase():R[s]||s;return`<span class="text-[9px] px-1 py-0.5 rounded font-mono font-semibold flex-shrink-0 ${a}" title="${(0,n.esc)(R[s]||s)}">${i}</span>`}function ao(e){return Object.entries(e||{}).filter(([,t])=>t>0).map(([t,s])=>`${Ve(t)}<span class="text-[9px] text-base-content/40 -ml-0.5">${s}</span>`).join("")}function Ye(e){if(!e)return"";let t="",s=e.split("/").filter(Boolean);return s.length<=3?e:"\u2026/"+s.slice(-2).join("/")}var jt=X(()=>{C();I()});function Ua(){return R[S.peek()]||"Assistant"}function po(e){return e.scrollHeight-e.scrollTop-e.clientHeight<80}function se(e,t){e&&(t||po(e))&&(e.scrollTop=e.scrollHeight)}function Fa(){io||(io=!0,document.addEventListener("visibilitychange",()=>{if(document.visibilityState==="visible"&&(ge=400,on({probe:!0}),f.ws?.readyState===WebSocket.OPEN&&f.sessionId))try{f.ws.send(JSON.stringify({type:"subscribe",sessionId:f.sessionId}))}catch{}}),window.addEventListener("online",()=>{ge=400,on({force:!0})}),window.addEventListener("pageshow",e=>{e.persisted?on({force:!0}):on({probe:!0})}))}function Rt(){let e=(0,n.$)("conn-dot");if(!e)return;let t=Se.peek();e.classList.remove("bg-success","bg-warning","bg-error","bg-base-content/30","animate-pulse"),t==="open"?(e.classList.add("bg-success"),e.title="\u5DF2\u8FDE\u63A5"):t==="connecting"||t==="reconnecting"?(e.classList.add("bg-warning","animate-pulse"),e.title=t==="connecting"?"\u8FDE\u63A5\u4E2D\u2026":"\u91CD\u8FDE\u4E2D\u2026"):t==="offline"?(e.classList.add("bg-error"),e.title="\u79BB\u7EBF"):(e.classList.add("bg-base-content/30"),e.title="\u672A\u8FDE\u63A5")}function on(e={}){let t=f.ws?.readyState;if(e.force){ge=400,je({quiet:!0});return}if(t===WebSocket.OPEN){if(e.probe&&Ce&&Date.now()-Ce>45e3)try{f.ws.close()}catch{}else if(e.probe&&f.ws.readyState===WebSocket.OPEN)try{f.ws.send(JSON.stringify({type:"ping"}))}catch{}return}t!==WebSocket.CONNECTING&&(ge=Math.min(ge,800),je({quiet:!0}))}function qa(){Ae||(Se.value=typeof navigator<"u"&&navigator.onLine===!1?"offline":"reconnecting",Rt(),Ae=setTimeout(()=>{Ae=null,je({quiet:!0})},ge),ge=Math.min(Math.round(ge*1.8),15e3))}function uo(){an&&(clearInterval(an),an=null)}function Wa(e,t){uo(),an=setInterval(()=>{if(!(t!==_t||e.readyState!==WebSocket.OPEN)){if(Ce&&Date.now()-Ce>7e4){try{e.close()}catch{}return}if(!(typeof document<"u"&&document.visibilityState==="hidden")&&Ce&&Date.now()-Ce>4e4)try{e.send(JSON.stringify({type:"ping"}))}catch{}}},3e4)}function Xe(){let e=ie.peek()||!!y.peek()&&Se.peek()==="open";no(e)}function Ga(){ge=400,Ae&&(clearTimeout(Ae),Ae=null),ln=!1;let e=f.ws?.readyState;if(e===WebSocket.OPEN||e===WebSocket.CONNECTING)try{f.ws?.close()}catch{}je({quiet:!1}),x("\u6B63\u5728\u91CD\u65B0\u8FDE\u63A5\u5E76\u540C\u6B65\u2026")}function je(e={}){Fa(),Ae&&(clearTimeout(Ae),Ae=null),e.quiet&&(ln=!0);let t=f.machineId||(typeof localStorage<"u"?localStorage.getItem("machineId"):null);ro!==t&&(Ks(),ro=t);let s=++_t;try{f.ws?.close()}catch{}Se.value="connecting",Rt();let o=location.protocol==="https:"?"wss":"ws",a=new URLSearchParams;f.token&&a.set("token",f.token),t&&a.set("machine",t),f.sessionId&&a.set("sessionId",f.sessionId);let i=new WebSocket(`${o}://${location.host}/ws/chat?${a}`);f.ws=i,i.addEventListener("open",()=>{if(s===_t){if(ge=1e3,Ce=Date.now(),Se.value="open",Rt(),f.sessionId)try{i.send(JSON.stringify({type:"subscribe",sessionId:f.sessionId}))}catch{}Js(),Wa(i,s),Xe(),rn&&!ln&&x(t?`\u5DF2\u91CD\u8FDE \xB7 ${t}`:"\u5DF2\u91CD\u8FDE"),rn=!1,ln=!1}}),i.addEventListener("message",r=>{if(s!==_t)return;let c;try{c=JSON.parse(r.data)}catch{return}if(c.type==="pong"){Ce=Date.now();return}if(c.type==="ping"){if(Ce=Date.now(),i.readyState===WebSocket.OPEN)try{i.send(JSON.stringify({type:"pong"}))}catch{}return}if(c.type==="run-status"){c.running?ie.value=!0:ie.peek()&&(ie.value=!1,fo()),Xe();return}mo(c)}),i.addEventListener("close",()=>{s===_t&&(uo(),rn=!0,Se.value=typeof navigator<"u"&&navigator.onLine===!1?"offline":"reconnecting",Rt(),qa())}),i.addEventListener("error",()=>{})}function fo(){if(!Nt||ie.peek())return;let e=Nt;Nt=null;let t=(0,n.$)("chat-input");t&&(t.value=e,pn(t)),setTimeout(()=>hn(),0)}function Ht(){ie.value=!1,Xe(),fo()}function mo(e){if(e.type==="batch"&&Array.isArray(e.items)){for(let t of e.items)mo(t);return}if(e.type==="session-created"){f.sessionId=e.sessionId,e.agent&&me(e.agent),document.dispatchEvent(new CustomEvent("sessions-changed"));return}if(e.type==="result"){Pt(),Dt(),le();let t=e.usage;if(t){let s={input_tokens:t.inputTokens??t.input_tokens,output_tokens:t.outputTokens??t.output_tokens,cache_read_input_tokens:t.cacheReadInputTokens??t.cache_read_input_tokens,cache_creation_input_tokens:t.cacheCreationInputTokens??t.cache_creation_input_tokens,total_tokens:t.totalTokens??t.total_tokens,reasoning_tokens:t.reasoningTokens??t.reasoning_tokens,costUSD:t.costUSD??t.cost_usd};f.lastUsage=s,f.sessionId&&(f.lastUsageSessionId=f.sessionId),ut(s)}e.is_error||x("\u2713 Done"),Ht();return}if(e.type==="complete"){Pt(),Dt(),le(),!e.aborted&&e.agent==="claude"?x("\u2713 Done"):!e.aborted&&e.agent&&e.agent!=="claude"&&!e.error||(!e.aborted&&!e.agent?x("\u2713 Done"):e.aborted&&x("\u5DF2\u505C\u6B62")),Ht();return}if(e.type==="error"){Pt(),Dt(),Qe("error","Error",e.message),Ht();return}if(e.type==="shell-start"){f.shellBubble=null;return}if(e.type==="shell-output"){f.shellBubble||(f.shellBubble=oi(e.stream==="stderr"?"shell-err":"shell")),ai(f.shellBubble,e.data);return}if(e.type==="shell-exit"){f.shellBubble=null,x(e.code===0?"\u2713 Exit 0":`\u2717 Exit ${e.code}${e.error?": "+e.error:""}`),Ht();return}if(e.type==="assistant"){Dt();for(let t of e.message?.content||[])t.type==="text"&&t.text?(le(),Qe("assistant",Ua(),t.text,{ts:e.ts||Date.now()})):t.type==="tool_use"&&os(t,{ts:e.ts||Date.now()});return}if(e.type==="assistant_stream_start"){ho(e.ts);return}if(e.type==="assistant_delta"){za(e.data||"");return}if(e.type==="assistant_stream_end"){Dt();return}if(e.type==="thought_stream_start"){bo();return}if(e.type==="thought_delta"){Ja(e.data||"");return}if(e.type==="thought_stream_end"){Pt();return}if(e.type==="status"){let t=(0,n.$)("stream-status");if(t)t.textContent=e.message||"";else{let s=(0,n.$)("messages");s&&e.message&&(s.insertAdjacentHTML("beforeend",`<div id="stream-status" class="text-center text-[10px] text-base-content/35 py-0.5">${(0,n.esc)(e.message)}</div>`),se(s))}return}if(e.type==="permission_resolved"){let t=e.requestId||e.request_id;t&&(0,n.$)(`perm-${t}`)?.remove();return}(e.type==="permission_request"||e.request_id&&e.tool_name)&&li(e)}function ho(e){let t=(0,n.$)("messages");if(!t||(le(),Pt(),be&&t.contains(be)))return;let s=un(e||Date.now()),o=fn(e||Date.now());t.insertAdjacentHTML("beforeend",`
|
|
2
|
-
<div class="msg-row assistant px-2 live-stream" data-ts="${(0,n.esc)(o)}">
|
|
3
|
-
<div class="max-w-[88%] rounded-2xl rounded-tl-sm px-3 py-2 text-sm bg-base-200 break-words md">
|
|
4
|
-
<div class="msg-stream-body whitespace-pre-wrap"></div>
|
|
5
|
-
</div>
|
|
6
|
-
${s?`<div class="msg-ts" title="${(0,n.esc)(o)}">${(0,n.esc)(s)}</div>`:""}
|
|
7
|
-
</div>`),be=t.lastElementChild,ne=be.querySelector(".msg-stream-body"),(0,n.$)("stream-status")?.remove(),se(t,!0)}function za(e){e&&(ne||ho(Date.now()),ne&&(ne.textContent+=e,se((0,n.$)("messages"),!0)))}function Dt(){if(!be||!ne){be=null,ne=null;return}let e=ne.textContent||"";e.trim()?(ne.classList.remove("whitespace-pre-wrap"),ne.classList.add("md"),ne.innerHTML=cn(e),vo(be)):be.remove(),be=null,ne=null,(0,n.$)("stream-status")?.remove()}function bo(){let e=(0,n.$)("messages");e&&(Be&&e.contains(Be)||(e.insertAdjacentHTML("beforeend",`
|
|
8
|
-
<div class="msg-row assistant px-2 thought-stream">
|
|
9
|
-
<div class="max-w-[88%] rounded-lg border border-base-300/60 bg-base-200/50 px-2.5 py-1.5 text-[11px] text-base-content/45">
|
|
10
|
-
<div class="font-medium text-base-content/35 mb-0.5">thinking\u2026</div>
|
|
11
|
-
<div class="thought-body font-mono whitespace-pre-wrap max-h-24 overflow-y-auto opacity-80"></div>
|
|
12
|
-
</div>
|
|
13
|
-
</div>`),Be=e.lastElementChild,xe=Be.querySelector(".thought-body"),se(e,!0)))}function Ja(e){e&&(xe||bo(),xe&&(xe.textContent+=e,xe.scrollTop=xe.scrollHeight,se((0,n.$)("messages"),!0)))}function Pt(){if(Be&&xe){let e=(xe.textContent||"").length;Be.innerHTML=`
|
|
14
|
-
<div class="text-[10px] text-base-content/30 px-1 select-none">\u{1F4AD} thought ${e?`(${e} chars)`:""}</div>`}Be=null,xe=null}function Va(e){let[t,...s]=e.slice(1).trim().split(/\s+/),o=s.join(" ");switch(t.toLowerCase()){case"agent":{if(!o)return x(`Current agent: ${S.peek()} \xB7 agents: claude | codex | grok`),!0;let a=o.toLowerCase();return["claude","codex","grok"].includes(a)?(a!==S.peek()?(f.sessionId=null,me(a),$.value=re(a),localStorage.setItem("model",$.peek()),document.dispatchEvent(new CustomEvent("agent-changed")),x(`Agent \u2192 ${R[a]} \xB7 model ${$.peek()} \xB7 new session`)):x(`Agent already ${R[a]}`),!0):(x(`Unknown agent "${o}" \xB7 valid: claude | codex | grok`),!0)}case"model":{if(!o){let c=S.peek(),u=fe(c).map(l=>l.value).join(" | ");return x(`Current model: ${$.peek()} (${c})${u?`
|
|
15
|
-
Available: ${u}`:""}`),!0}let a=Ka[o.toLowerCase()]||o,i=S.peek()||"claude",r=fe(i).some(c=>c.value===a);return r||Tt(i,a),$.value=a,localStorage.setItem("model",a),x(`Model set to ${a}${r?"":" (saved as custom \u2605)"}`),document.dispatchEvent(new CustomEvent("agent-changed")),!0}case"effort":{let a=Et(S.peek(),$.peek());return o?a.includes(o.toLowerCase())?(pe.value=o.toLowerCase(),x(`Effort set to ${o.toLowerCase()}`),!0):(x(`Unknown effort level "${o}" \xB7 valid: ${a.join(" | ")}`),!0):(x(`Current effort: ${pe.peek()||"(default)"} \xB7 levels: ${a.join(" | ")}`),!0)}case"clear":return ft(),f.sessionId=null,f.lastUsage=null,x("Cleared \xB7 new session"),!0;case"usage":return Ya(),!0;case"help":return x(`Available slash commands:
|
|
16
|
-
/agent [claude|codex|grok]
|
|
17
|
-
/model [haiku|sonnet|opus|<model-id>]
|
|
18
|
-
/effort [low|medium|high|xhigh|max]
|
|
19
|
-
/usage
|
|
20
|
-
/clear
|
|
21
|
-
/help`),!0;default:return!1}}function V(e){if(e==null||Number.isNaN(Number(e)))return"\u2014";let t=Number(e);return t>=1e6?`${(t/1e6).toFixed(2)}M`:t>=1e3?`${(t/1e3).toFixed(1)}k`:String(Math.round(t))}async function Ya(){let e=S.peek()||"claude",t=$.peek()||"\u2014",s=f.sessionId,o=[`Agent: ${R[e]||e}`,`Model: ${t}`,`Session: ${s?s.slice(0,8)+"\u2026":"(new \u2014 send a message first)"}`];if(f.lastUsage&&(!s||f.lastUsageSessionId===s)){let a=f.lastUsage;o.push(`Last turn: \u2191${V(a.input_tokens)} \u2193${V(a.output_tokens)}`+(a.cache_read_input_tokens?` \xB7 ${V(a.cache_read_input_tokens)} cached`:"")+(a.reasoning_tokens?` \xB7 ${V(a.reasoning_tokens)} reason`:"")+(a.total_tokens?` \xB7 \u03A3${V(a.total_tokens)}`:"")+(a.costUSD!=null?` \xB7 $${Number(a.costUSD).toFixed(4)}`:""))}if(!s){x(o.join(`
|
|
22
|
-
`)),ns("\u25C8 /usage \xB7 no session yet");return}x("Loading usage\u2026");try{let a=new URLSearchParams({sessionId:s,agent:e}),r=(await h("GET",`/api/usage?${a}`)).context||{};if(r.model&&(o[1]=`Model: ${r.model}`),r.title&&o.push(`Title: ${r.title}`),r.tokensUsed!=null||r.windowTokens!=null||r.usagePercent!=null){let u=r.tokensUsed!=null?V(r.tokensUsed):"\u2014",l=r.windowTokens!=null?V(r.windowTokens):"\u2014",d=r.usagePercent!=null?`${r.usagePercent}%`:"\u2014";o.push(`Context window: ${u} / ${l} (${d})`)}if(r.turnCount!=null&&o.push(`Turns: ${r.turnCount}`),r.toolCallCount!=null&&o.push(`Tools: ${r.toolCallCount}`),r.sessionDurationSeconds!=null){let u=Math.floor(r.sessionDurationSeconds/60),l=Math.round(r.sessionDurationSeconds%60);o.push(`Duration: ${u}m ${l}s`)}if(r.sessionTotals){let u=r.sessionTotals;o.push(`Session totals: \u2191${V(u.input_tokens)} \u2193${V(u.output_tokens)}`+(u.cache_read_input_tokens?` \xB7 ${V(u.cache_read_input_tokens)} cached`:"")+(u.total_tokens?` \xB7 \u03A3${V(u.total_tokens)}`:"")+(u.costUSD?` \xB7 $${Number(u.costUSD).toFixed(4)}`:""))}if(r.lastTurn&&!f.lastUsage){let u=r.lastTurn;o.push(`Last completed turn: \u2191${V(u.input_tokens)} \u2193${V(u.output_tokens)}`+(u.cache_read_input_tokens?` \xB7 ${V(u.cache_read_input_tokens)} cached`:"")+(u.costUSD!=null?` \xB7 $${Number(u.costUSD).toFixed(4)}`:""))}r.note&&o.push(r.note);let c=(0,n.$)("messages");if(c){let u=[...c.children];for(let l=u.length-1;l>=0;l--)if(u[l].textContent==="Loading usage\u2026"){u[l].remove();break}}x(o.join(`
|
|
23
|
-
`)),(r.tokensUsed!=null||r.windowTokens!=null)&&ss({tokensUsed:r.tokensUsed,windowTokens:r.windowTokens,usagePercent:r.usagePercent,model:r.model||t,turnCount:r.turnCount,toolCallCount:r.toolCallCount}),r.sessionTotals?ut(r.sessionTotals):r.lastTurn?ut(r.lastTurn):f.lastUsage&&ut(f.lastUsage)}catch(a){let i=(0,n.$)("messages");if(i){let r=[...i.children];for(let c=r.length-1;c>=0;c--)if(r[c].textContent==="Loading usage\u2026"){r[c].remove();break}}x(`${o.join(`
|
|
24
|
-
`)}
|
|
25
|
-
(usage fetch failed: ${a.message})`)}}function hn(){let e=(0,n.$)("chat-input"),t=e?.value.trim();if(!t||!y.peek())return;if(ie.peek()){Nt=t,e.value="",pn(e),dn(""),x(`\u5DF2\u6392\u961F\u4E0B\u4E00\u6761\uFF0C\u5F53\u524D\u56DE\u590D\u7ED3\u675F\u540E\u53D1\u9001\uFF1A${t.length>80?t.slice(0,80)+"\u2026":t}`);return}if(e.value="",pn(e),dn(""),t.startsWith("/")&&Va(t))return;ie.value=!0,Xe();let s=Date.now();if(t.startsWith("!"))Qe("shell","$ Shell",t.slice(1).trim(),{ts:s}),Ct({type:"command",cmd:"shell.exec",command:t.slice(1).trim(),cwd:y.peek().path})||x("\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u547D\u4EE4\u5DF2\u5165\u961F\uFF0C\u91CD\u8FDE\u540E\u53D1\u9001");else{le(),Qe("user","You",t,{ts:s});let o=pe.peek(),a=Mt.peek(),i=S.peek()||"claude";Ct({type:"command",cmd:"turn.start",agent:i,command:t,sessionId:f.sessionId,options:{agent:i,cwd:y.peek().path,sessionId:f.sessionId,configDir:f.configDir,model:$.peek(),...o&&{effort:o},...a&&a!=="default"&&{permissionMode:a},...a==="bypassPermissions"&&{allowDangerouslySkipPermissions:!0}}})||x("\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u6D88\u606F\u5DF2\u5165\u961F\uFF0C\u91CD\u8FDE\u540E\u53D1\u9001")}}function xo(){f.sessionId&&Ct({type:"command",cmd:"turn.interrupt",sessionId:f.sessionId}),Nt=null,Ht()}function dn(e){let t=e.startsWith("!"),s=(0,n.$)("chat-input"),o=(0,n.$)("send-btn");if(!s||!o)return;s.classList.toggle("textarea-warning",t),s.style.fontFamily=t?"monospace":"",o.classList.toggle("btn-warning",t),o.classList.toggle("btn-primary",!t),o.textContent=t?"Run":"Send";let a=R[S.peek()]||"agent";s.placeholder=t?"Shell command (without !)\u2026":`Ask ${a}\u2026 or !cmd for shell`}function Xa(){return typeof window<"u"&&window.matchMedia("(max-width: 640px)").matches?96:140}function pn(e){e.style.height="auto",e.style.height=Math.min(e.scrollHeight,Xa())+"px"}function un(e){let t=_(e);if(t==null)return"";let s=new Date(t);if(Number.isNaN(s.getTime()))return"";let o=new Date,a=s.toDateString()===o.toDateString(),i=s.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1});return a?i:`${s.toLocaleDateString(void 0,{month:"numeric",day:"numeric"})} ${i}`}function fn(e){let t=_(e);return t==null?"":new Date(t).toISOString()}function ei(e,t,s){if(e==="system")return t;let o=un(s),a=fn(s),i=o?`<div class="msg-ts" title="${(0,n.esc)(a)}">${(0,n.esc)(o)}</div>`:"";return`<div class="msg-row ${e==="user"?"user":e==="shell"||e==="shell-err"?"assistant":e} px-2" data-ts="${(0,n.esc)(a)}">${t}${i}</div>`}function ti(e,t,s){let o='<button class="msg-expand-btn text-[10px] text-primary/70 mt-1.5 block cursor-pointer hover:text-primary">\u25BC Show more</button>',a;switch(e){case"user":{let r=/!\[.*?\]\(.*?\)/.test(t)?cn(t):null,c=t.length>Qa,u=c?`<div class="msg-body">${r??(0,n.esc)(t.trimEnd())}</div>${o}`:r??(0,n.esc)(t.trimEnd());a=`<div class="max-w-[80%] rounded-2xl rounded-tr-sm px-3 py-2 text-sm bg-primary/15 border border-primary/20 text-base-content break-words${c?" msg-card":""}${r?" md":""}"${r?"":' style="white-space:pre-wrap"'}>${u}</div>`;break}case"assistant":{let i=t.length>Za,r=i?`<div class="msg-body">${cn(t)}</div>${o}`:cn(t);a=`<div class="max-w-[min(92%,52rem)] rounded-2xl rounded-tl-sm px-3.5 py-2.5 text-sm bg-base-200 border border-base-300/40 break-words md leading-relaxed${i?" msg-card":""}">${r}</div>`;break}case"error":a=`<div class="max-w-[88%] rounded-2xl rounded-tl-sm px-3 py-2 text-sm bg-error/10 border border-error/20 text-error break-words" style="white-space:pre-wrap">${(0,n.esc)(t)}</div>`;break;case"shell":case"shell-err":a=`<div class="w-full max-w-[96%] rounded-lg code-surface px-3 py-2 text-sm font-mono ${e==="shell-err"?"text-error":""} break-words" style="white-space:pre-wrap">${(0,n.esc)(t)}</div>`;break;case"system":return`<div class="text-center text-[11px] text-base-content/30 py-0.5 select-none">${(0,n.esc)(t)}</div>`;default:a=`<div class="rounded-lg px-3 py-2 text-sm bg-base-300 break-words" style="white-space:pre-wrap">${(0,n.esc)(t)}</div>`}return ei(e,a,s)}function Qe(e,t,s,o={}){let a=(0,n.$)("messages");if(!a)return;let i=_(o.ts)??(e==="system"?null:Date.now());a.insertAdjacentHTML("beforeend",ti(e,s,i)),vo(a.lastElementChild),se(a,e==="user")}function x(e){Qe("system","",e)}async function ni(e,t){let s=await fetch("/api/upload-image",{method:"POST",headers:{Authorization:`Bearer ${f.token}`,"x-filename":t},body:e});if(!s.ok)throw new Error(`Upload failed: ${s.status}`);return(await s.json()).path}function si(e,t){let s=e.selectionStart,o=e.selectionEnd;e.value=e.value.slice(0,s)+t+e.value.slice(o),e.selectionStart=e.selectionEnd=s+t.length,e.dispatchEvent(new Event("input"))}function go(){document.addEventListener("paste",async e=>{let t=(0,n.$)("chat-input");if(!t||document.activeElement!==t)return;let o=[...e.clipboardData?.items||[]].find(c=>c.type.startsWith("image/"));if(!o)return;e.preventDefault();let a=o.getAsFile(),i=o.type.split("/")[1]||"png",r=`paste-${Date.now()}.${i}`;t.disabled=!0;try{let c=await ni(a,r);si(t,``)}catch(c){x(`Image paste failed: ${c.message}`)}finally{t.disabled=!1,t.focus()}})}function Ie(e){return e>=1e3?`${(e/1e3).toFixed(1)}k`:String(e)}function ns(e){let t=(0,n.$)("messages");t&&(t.insertAdjacentHTML("beforeend",`<div class="flex items-center gap-1.5 px-1 py-0.5 text-[10px] font-mono text-base-content/30 select-none">
|
|
26
|
-
<span class="text-base-content/20">\u25C8</span>${(0,n.esc)(e)}
|
|
27
|
-
</div>`),se(t))}function ut(e){if(!e)return;let t=e.input_tokens??e.inputTokens??0,s=e.output_tokens??e.outputTokens??0,o=e.total_tokens??e.totalTokens??t+s,a=e.cache_read_input_tokens??e.cachedReadTokens?` \xB7 ${Ie(e.cache_read_input_tokens??e.cachedReadTokens)} cached`:"",i=e.cache_creation_input_tokens??e.cacheCreationInputTokens?` \xB7 ${Ie(e.cache_creation_input_tokens??e.cacheCreationInputTokens)} cache-write`:"",r=e.reasoning_tokens??e.reasoningTokens?` \xB7 ${Ie(e.reasoning_tokens??e.reasoningTokens)} reason`:"",c=e.costUSD??e.cost_usd,u=c!=null?` \xB7 $${Number(c).toFixed(4)}`:"",l=o?` \xB7 \u03A3${Ie(o)}`:"";ns(`\u2191${Ie(t)} \u2193${Ie(s)}${a}${i}${r}${l}${u}`)}function ss(e){if(!e)return;let t=e.tokensUsed??e.contextTokensUsed,s=e.windowTokens??e.contextWindowTokens,o=e.usagePercent??e.contextWindowUsage;if(t==null&&s==null&&o==null)return;let a=[];t!=null&&s!=null?a.push(`context ${Ie(t)} / ${Ie(s)}`):t!=null&&a.push(`context ${Ie(t)}`),o!=null&&a.push(`${o}%`),e.model&&a.push(e.model),e.turnCount!=null&&a.push(`${e.turnCount} turns`),e.toolCallCount!=null&&a.push(`${e.toolCallCount} tools`),ns(`\u25A3 ${a.join(" \xB7 ")}`)}function ft(){let e=(0,n.$)("messages"),t=(0,n.$)("permission-requests");e&&(e.innerHTML=""),t&&(t.innerHTML=""),f.shellBubble=null,he=null,be=ne=null,Be=xe=null}function oi(e){let t=(0,n.$)("messages");if(!t)return null;let s=e==="shell-err"?" text-error":"";return t.insertAdjacentHTML("beforeend",`<div class="mx-2 rounded-lg code-surface px-3 py-2 text-sm font-mono${s} whitespace-pre-wrap break-words"></div>`),se(t),t.lastElementChild}function ai(e,t){e&&(e.textContent+=t,se(e.closest("#messages")))}function ii(e){let t=typeof e.input=="object"?JSON.stringify(e.input,null,2):String(e.input||""),o=((typeof e.input=="object"?Object.values(e.input).find(a=>typeof a=="string"&&a.trim()):t)||"").replace(/\n/g," ").slice(0,Te.peek()==="full"?120:80);return{inputStr:t,preview:o}}function lo(e,{inBatch:t=!1}={}){let{inputStr:s,preview:o}=ii(e);return`<div class="tool-card${t?" tool-batch-item":""} cursor-pointer select-none px-2 py-1">
|
|
28
|
-
<div class="flex items-center gap-1.5 text-xs font-mono">
|
|
29
|
-
<span class="text-warning">\u26A1</span>
|
|
30
|
-
<span class="text-warning font-semibold">${(0,n.esc)(e.name||"tool")}</span>
|
|
31
|
-
<span class="text-base-content/50 truncate flex-1">${(0,n.esc)(o)}</span>
|
|
32
|
-
<span class="tool-chevron text-[10px] text-base-content/40">\u25B6</span>
|
|
33
|
-
</div>
|
|
34
|
-
<div class="tool-detail">${(0,n.esc)(s)}</div>
|
|
35
|
-
</div>`}function ri(e){let t=[...new Set(e.names)],s=t.slice(0,4).join(", ")+(t.length>4?"\u2026":""),o=e.el.querySelector(".tool-batch-summary");o&&(o.innerHTML=`<span class="text-warning">\u26A1</span>
|
|
36
|
-
<span class="font-semibold text-base-content/70">${e.count} tool${e.count===1?"":"s"}</span>
|
|
37
|
-
<span class="text-base-content/40 truncate flex-1">${(0,n.esc)(s)}</span>
|
|
38
|
-
<span class="tool-chevron text-[10px] text-base-content/40">\u25B6</span>`)}function le(){he=null}function os(e,t={}){let s=(0,n.$)("messages");if(!s)return;let o=Te.peek()||"normal",a=_(t.ts);if(o==="clean"){if(!he||!s.contains(he.el)){let u=un(a),l=fn(a);s.insertAdjacentHTML("beforeend",`
|
|
39
|
-
<div class="msg-row assistant px-2" data-ts="${(0,n.esc)(l)}">
|
|
40
|
-
<div class="tool-batch w-full max-w-[96%] px-2 py-1.5">
|
|
41
|
-
<div class="tool-batch-summary flex items-center gap-1.5 text-xs font-mono"></div>
|
|
42
|
-
<div class="tool-batch-list"></div>
|
|
43
|
-
</div>
|
|
44
|
-
${u?`<div class="msg-ts" title="${(0,n.esc)(l)}">${(0,n.esc)(u)}</div>`:""}
|
|
45
|
-
</div>`),he={el:s.lastElementChild.querySelector(".tool-batch"),names:[],count:0}}he.names.push(e.name||"tool"),he.count++,he.el.querySelector(".tool-batch-list")?.insertAdjacentHTML("beforeend",lo(e,{inBatch:!0})),ri(he),se(s);return}he=null;let i=un(a),r=fn(a);s.insertAdjacentHTML("beforeend",`
|
|
46
|
-
<div class="msg-row assistant px-2" data-ts="${(0,n.esc)(r)}">
|
|
47
|
-
<div class="w-full max-w-[96%]">${lo(e)}</div>
|
|
48
|
-
${i?`<div class="msg-ts" title="${(0,n.esc)(r)}">${(0,n.esc)(i)}</div>`:""}
|
|
49
|
-
</div>`),se(s)}function mn(){let e=(0,n.$)("messages");e&&e.setAttribute("data-density",Te.peek()||"normal")}function li(e){let t=e.request_id||e.requestId;if(!t||(0,n.$)(`perm-${t}`))return;let s=e.tool_input?JSON.stringify(e.tool_input).slice(0,200):"";(0,n.$)("permission-requests")?.insertAdjacentHTML("beforeend",`
|
|
50
|
-
<div id="perm-${t}" class="perm-banner mx-3 mb-2 rounded-lg border border-warning/25 bg-warning/8 p-3">
|
|
51
|
-
<div class="text-xs text-warning font-semibold mb-1">\u26A0\uFE0F Permission Required</div>
|
|
52
|
-
<div class="font-mono text-xs text-base-content/60 break-all mb-2">
|
|
53
|
-
<strong class="text-base-content">${(0,n.esc)(e.tool_name||"")}</strong> ${(0,n.esc)(s)}
|
|
54
|
-
</div>
|
|
55
|
-
<div class="flex gap-2">
|
|
56
|
-
<button class="btn btn-success btn-xs" data-perm-id="${t}" data-perm-allow="1">Allow</button>
|
|
57
|
-
<button class="btn btn-error btn-xs" data-perm-id="${t}" data-perm-allow="0">Deny</button>
|
|
58
|
-
</div>
|
|
59
|
-
</div>`),(0,n.$)("messages").scrollTop=999999}function ci(){if(co||typeof marked>"u")return;let e=o=>String(o??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"),t=(o,a)=>{let i=String(a||"").trim().split(/\s+/)[0]||"",r=i||"text",c=i?`language-${(0,n.esc)(i)}`:"";return`<div class="code-block">
|
|
60
|
-
<div class="code-block-header">
|
|
61
|
-
<span class="code-block-lang">${(0,n.esc)(r)}</span>
|
|
62
|
-
<button type="button" class="code-copy" data-copy="1" title="Copy code">Copy</button>
|
|
63
|
-
</div>
|
|
64
|
-
<pre><code class="hljs ${c}">${e(o)}</code></pre>
|
|
65
|
-
</div>`},s={code(o,a){return o&&typeof o=="object"&&o.text!=null?t(o.text,o.lang||a):t(o,a)},table(o,a){return o&&typeof o=="object"&&!Array.isArray(o)&&o.header?!1:`<div class="table-wrap"><table><thead>${o}</thead><tbody>${a}</tbody></table></div>`}};try{if(typeof marked.use=="function")marked.use({gfm:!0,breaks:!0,renderer:s});else if(typeof marked.setOptions=="function"){let o=new marked.Renderer;o.code=s.code,o.table=s.table,marked.setOptions({gfm:!0,breaks:!0,renderer:o})}}catch(o){console.warn("[md] marked setup failed",o)}co=!0}function cn(e){if(typeof marked>"u")return(0,n.esc)(e);try{return ci(),marked.parse(String(e??""),{breaks:!0,gfm:!0})}catch{return(0,n.esc)(e)}}function vo(e){!e||typeof hljs>"u"||(e.querySelectorAll("pre code:not(.hljs)").forEach(t=>{try{hljs.highlightElement(t)}catch{}}),e.querySelectorAll("pre code.hljs").forEach(t=>{t.classList.add("hljs")}))}function yo(){mn(),so(),(0,n.watch)(Se,o=>{Rt(),Xe();let a=(0,n.$)("ws-banner"),i=(0,n.$)("ws-banner-text");if(!a)return;if(o==="open"||o==="idle"){a.classList.add("hidden"),i&&(i.textContent="");return}if(o==="connecting"&&!rn){a.classList.add("hidden");return}a.classList.remove("hidden");let r=o==="offline"?"\u7F51\u7EDC\u5DF2\u65AD\u5F00\uFF0C\u6062\u590D\u540E\u4F1A\u81EA\u52A8\u91CD\u8FDE":o==="connecting"?"\u6B63\u5728\u8FDE\u63A5\u2026":"\u8FDE\u63A5\u4E2D\u65AD\uFF0C\u6B63\u5728\u91CD\u8FDE\u2026\uFF08\u7184\u5C4F\u6216\u5207\u7F51\u65F6\u5E38\u89C1\uFF09";i?i.textContent=r:a.textContent=r}),(0,n.watch)(y,()=>Xe()),n.delegate.on("click","#btn-refresh-conn, #ws-banner-retry",o=>{o.preventDefault(),Ga()}),(0,n.watch)(ie,o=>{(0,n.$)("send-btn")?.classList.toggle("hidden",o),(0,n.$)("stop-btn")?.classList.toggle("hidden",!o);let a=(0,n.$)("chat-input");a&&(a.disabled=!1,o?a.placeholder="\u56DE\u590D\u4E2D\u2026 \u53EF\u5148\u5199\u597D\u4E0B\u4E00\u6761\uFF0C\u7ED3\u675F\u540E\u81EA\u52A8\u53D1\u9001 \xB7 \u6216\u70B9 \u25A0 \u505C\u6B62":dn(a.value||""));let i=(0,n.$)("messages");if(!i)return;let r=(0,n.$)("typing-indicator");o&&!r?(i.insertAdjacentHTML("beforeend",'<div id="typing-indicator" class="flex justify-start px-2"><div class="flex items-center gap-1.5 rounded-2xl rounded-tl-sm px-3 py-2 bg-base-200 text-xs text-base-content/40"><span class="dot"></span><span class="dot"></span><span class="dot"></span></div></div>'),se(i)):o||(r?.remove(),le()),Xe()}),(0,n.watch)(Te,()=>mn()),n.delegate.on("keydown","#chat-input",o=>{o.key==="Enter"&&(o.metaKey||o.ctrlKey)&&(o.preventDefault(),hn())}),n.delegate.on("input","#chat-input",(o,a)=>{pn(a),dn(a.value)}),n.delegate.on("click","[data-perm-id]",(o,a)=>{Ct({type:"command",cmd:"approval.respond",requestId:a.dataset.permId,allow:a.dataset.permAllow==="1",sessionId:f.sessionId}),a.closest(".perm-banner")?.remove()}),n.delegate.on("click",".tool-card",(o,a)=>{let i=a.closest(".tool-batch");if(i&&!i.classList.contains("open")&&!a.classList.contains("tool-batch-item"))return;o.stopPropagation();let r=(0,n.$)("messages"),c=a.getBoundingClientRect().top;if(a.classList.toggle("open"),r){let u=a.getBoundingClientRect().top;r.scrollTop+=u-c}}),n.delegate.on("click",".tool-batch",(o,a)=>{if(o.target.closest(".tool-card")&&a.classList.contains("open"))return;let i=(0,n.$)("messages"),r=a.getBoundingClientRect().top;if(a.classList.toggle("open"),i){let c=a.getBoundingClientRect().top;i.scrollTop+=c-r}});let e=(0,n.$)("messages"),t=(0,n.$)("scroll-top-btn"),s=(0,n.$)("scroll-bottom-btn");if(e&&t&&s){let o=()=>{let a=e.scrollHeight-e.clientHeight>200;t.classList.toggle("hidden",!a||e.scrollTop<200),s.classList.toggle("hidden",!a||po(e))};e.addEventListener("scroll",o,{passive:!0}),new ResizeObserver(o).observe(e),new MutationObserver(o).observe(e,{childList:!0,subtree:!0}),t.addEventListener("click",()=>e.scrollTo({top:0,behavior:"smooth"})),s.addEventListener("click",()=>e.scrollTo({top:e.scrollHeight,behavior:"smooth"})),o()}n.delegate.on("click",".msg-expand-btn",(o,a)=>{o.stopPropagation();let r=a.closest(".msg-card").classList.toggle("open");a.textContent=r?"\u25B2 Show less":"\u25BC Show more"}),n.delegate.on("click",".code-copy",async(o,a)=>{o.preventDefault(),o.stopPropagation();let c=a.closest(".code-block")?.querySelector("pre code")?.textContent??"";try{await navigator.clipboard.writeText(c),a.textContent="Copied",a.classList.add("copied"),setTimeout(()=>{a.textContent="Copy",a.classList.remove("copied")},1400)}catch{a.textContent="Failed",setTimeout(()=>{a.textContent="Copy"},1400)}})}var _t,Ae,ge,Ce,an,io,rn,ro,ln,Nt,be,ne,Be,xe,Ka,Qa,Za,he,co,mt=X(()=>{C();I();K();oo();jt();jt();_t=0,Ae=null,ge=1e3,Ce=0,an=null,io=!1,rn=!1,ro=null,ln=!1,Nt=null;be=null,ne=null,Be=null,xe=null;Ka={haiku:"claude-haiku-4-5",sonnet:"claude-sonnet-4-6",opus:"claude-opus-4-5",sonnet4:"claude-sonnet-4-6",haiku4:"claude-haiku-4-5"};Qa=200,Za=500;he=null;co=!1});function ko(e,t){wo=e,as=t,window.addEventListener("hashchange",()=>{if(is){is=!1;return}So(location.hash.slice(1)||"/")}),(0,n.watch)(j,s=>{f.sessionId&&bn(`/session/${f.sessionId}/${s}`)})}function rs(e){let t=(e||"").match(/^\/session\/([^/]+)(?:\/([^/]+))?$/);return t?{id:t[1],tab:t[2]||null}:null}function $o(){So(location.hash.slice(1)||"/")}function So(e){let t=rs(e);if(!t){document.dispatchEvent(new CustomEvent("router:home"));return}let{id:s,tab:o}=t;!wo||!as||(s!==f.sessionId?document.dispatchEvent(new CustomEvent("router:session",{detail:{id:s,tab:o}})):o&&o!==j.peek()&&as(o))}function bn(e){is=!0,location.hash=e}function xn(){f.sessionId&&bn(`/session/${f.sessionId}/${j.peek()}`)}var Lr,wo,as,is,gn=X(()=>{C();I();Lr=(0,n.createRouter)({"/":()=>{},"/session/*":()=>{},"/session/*/*":()=>{},"*":()=>{}}),wo=null,as=null,is=!1});async function yn(){try{return await vn.get("sessions")??null}catch{return null}}async function Lo(e){try{await vn.set("sessions",e,{ttl:di})}catch{}}async function Mo(){try{return await vn.get("workspaces")??null}catch{return null}}async function Bt(e){try{await vn.set("workspaces",e,{ttl:3600*1e3})}catch{}}var vn,di,wn=X(()=>{C();vn=(0,n.idb)("claude-ui"),di=300*1e3});function En(e){return Array.isArray(e)?e:e&&Array.isArray(e.results)?e.results:e&&Array.isArray(e.sessions)?e.sessions:[]}function ve(){let e=En(E.peek()),t=M.peek();k.peek()&&t&&(e=e.filter(o=>o&&(!o.machineId||o.machineId===t)));let s=Q.peek();return s&&s!=="all"&&(e=e.filter(o=>o&&(o.agent||"claude")===s)),e}function qo(e){let t=e.getFullYear(),s=String(e.getMonth()+1).padStart(2,"0"),o=String(e.getDate()).padStart(2,"0");return`${t}-${s}-${o}`}function wi(e,t){let s=[],a=new Date(e,t,1).getDay(),r=new Date(e,t+1,0).getDate(),c=new Date(e,t,0).getDate();for(let l=a-1;l>=0;l--)s.push({date:new Date(e,t-1,c-l),isCurrentMonth:!1});for(let l=1;l<=r;l++)s.push({date:new Date(e,t,l),isCurrentMonth:!0});let u=7-s.length%7;if(u<7)for(let l=1;l<=u;l++)s.push({date:new Date(e,t+1,l),isCurrentMonth:!1});return s}function Wo(e,t){let s=new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime(),o=s+1440*60*1e3;return t.filter(a=>{let i=_(a.updatedAt);return i!=null&&i>=s&&i<o})}function Go(e){return e==null||isNaN(e)?"0":e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:String(e)}function nt(e){let t=(0,n.$)("calendar-grid");if(!t)return;e=En(e??ve());let s=(0,n.$)("calendar-month-year");if(s){let d=new Date(Tn,tt,1);s.textContent=d.toLocaleDateString([],{month:"long",year:"numeric"})}let o=e.length,a=new Set(e.map(d=>d.cwd).filter(Boolean)).size,i=e.reduce((d,p)=>d+(Number(p.totalTokens)||0),0),r=(0,n.$)("stats-total-sessions"),c=(0,n.$)("stats-total-projects"),u=(0,n.$)("stats-total-tokens");r&&(r.textContent=String(o)),c&&(c.textContent=String(a)),u&&(u.textContent=Go(i));let l=wi(Tn,tt);qt||(qt=qo(new Date)),t.innerHTML=l.map(d=>{let p=d.date,m=qo(p),g=Wo(p,e).length,W=p.toDateString()===new Date().toDateString(),Fe=m===qt,de="",qe="text-base-content/65",$e="border border-transparent",w="cursor-default",G=d.isCurrentMonth?"":"opacity-25";g>0?(w="cursor-pointer",g<=2?(de="bg-primary/15",qe="text-primary font-bold",$e="border border-primary/30"):g<=5?(de="bg-primary/30",qe="text-primary font-bold",$e="border border-primary/50"):(de="bg-primary/60",qe="text-primary-content font-bold",$e="border border-primary/80")):(de="bg-base-300/25",d.isCurrentMonth&&(w="hover:bg-base-300/50")),W?$e="border-2 border-primary":Fe&&($e="border-2 border-accent");let We=p.getDate()===1?`<span class="absolute top-0.5 left-1 text-[8px] opacity-60 uppercase">${p.toLocaleDateString([],{month:"short"})}</span>`:"";return`
|
|
66
|
-
<div class="aspect-square rounded-xl flex flex-col items-center justify-center p-2 relative transition-all min-h-[50px] md:min-h-[60px] ${de} ${qe} ${$e} ${w} ${G}"
|
|
67
|
-
data-cal-date="${m}" title="${p.toDateString()}: ${g} session(s)">
|
|
68
|
-
${We}
|
|
69
|
-
<span class="text-sm font-semibold">${p.getDate()}</span>
|
|
70
|
-
${g>0?`<span class="text-[10px] mt-0.5 opacity-80 font-medium whitespace-nowrap">${g}s</span>`:""}
|
|
71
|
-
</div>`}).join(""),ki(e),Si(e)}function ki(e){let t=(0,n.$)("selected-day-title"),s=(0,n.$)("selected-day-sessions");if(!t||!s)return;let[o,a,i]=qt.split("-").map(Number),r=new Date(o,a-1,i);t.textContent=`Activity on ${r.toLocaleDateString([],{weekday:"short",month:"short",day:"numeric",year:"numeric"})}`,e=En(e);let c=Wo(r,e),u=c.reduce((d,p)=>d+(Number(p.totalTokens)||0),0),l=(0,n.$)("stats-selected-tokens");if(l&&(l.textContent=Go(u)),!c.length){s.innerHTML='<div class="text-center text-xs text-base-content/40 my-auto py-6">No sessions active on this day</div>';return}c.sort((d,p)=>(_(p.updatedAt)||0)-(_(d.updatedAt)||0)),s.innerHTML=c.map(d=>{let p=_(d.updatedAt),m=p!=null?new Date(p).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}):"";return`
|
|
72
|
-
<div class="flex items-center gap-2.5 p-2.5 rounded-lg bg-base-300/40 hover:bg-base-300 border border-base-300/50 cursor-pointer text-left transition-all ${f.sessionId===d.sessionId?"border-primary bg-primary/5":""}"
|
|
73
|
-
data-session-id="${(0,n.esc)(d.sessionId)}"
|
|
74
|
-
data-session-cwd="${(0,n.esc)(d.cwd||"")}"
|
|
75
|
-
data-session-config-dir="${(0,n.esc)(d.configDir||"")}"
|
|
76
|
-
data-session-agent="${(0,n.esc)(d.agent||"claude")}"
|
|
77
|
-
data-session-machine="${(0,n.esc)(d.machineId||"")}">
|
|
78
|
-
<div class="flex-shrink-0">${Ve(d.agent)}</div>
|
|
79
|
-
<div class="flex-1 min-w-0">
|
|
80
|
-
<div class="text-xs font-semibold truncate text-base-content/90">${(0,n.esc)(Ge(d))}</div>
|
|
81
|
-
<div class="text-[10px] text-base-content/40 truncate mt-0.5">${(0,n.esc)(d.projectName||Ye(d.cwd)||"No workspace")}</div>
|
|
82
|
-
</div>
|
|
83
|
-
<div class="text-[9px] text-base-content/45 font-mono whitespace-nowrap bg-base-300 px-1.5 py-0.5 rounded">${m}</div>
|
|
84
|
-
</div>`}).join("")}function zo(e,t){return[...En(e)].filter(s=>s&&s.sessionId).sort((s,o)=>(_(o.updatedAt)||0)-(_(s.updatedAt)||0)).slice(0,t)}function Si(e){let t=(0,n.$)("recent-sessions-list");if(!t)return;let s=zo(e,6);if(!s.length){t.innerHTML='<div class="col-span-full text-center text-xs text-base-content/40 py-4">No recent sessions</div>';return}t.innerHTML=s.map(o=>{let a=pt(o.updatedAt);return`
|
|
85
|
-
<div class="flex items-center gap-2.5 p-3 rounded-xl bg-base-200 hover:bg-base-300 border border-base-300/80 cursor-pointer text-left transition-all ${f.sessionId===o.sessionId?"border-primary bg-primary/5":""}"
|
|
86
|
-
data-session-id="${(0,n.esc)(o.sessionId)}"
|
|
87
|
-
data-session-cwd="${(0,n.esc)(o.cwd||"")}"
|
|
88
|
-
data-session-config-dir="${(0,n.esc)(o.configDir||"")}"
|
|
89
|
-
data-session-agent="${(0,n.esc)(o.agent||"claude")}"
|
|
90
|
-
data-session-machine="${(0,n.esc)(o.machineId||"")}">
|
|
91
|
-
<div class="flex-shrink-0">${Ve(o.agent)}</div>
|
|
92
|
-
<div class="flex-1 min-w-0">
|
|
93
|
-
<div class="text-xs font-bold truncate text-base-content/95">${(0,n.esc)(Ge(o))}</div>
|
|
94
|
-
<div class="text-[10px] text-base-content/40 truncate mt-1">${(0,n.esc)(o.projectName||Ye(o.cwd)||"No workspace")}</div>
|
|
95
|
-
</div>
|
|
96
|
-
<div class="text-[9px] text-base-content/40 whitespace-nowrap self-start mt-0.5">${a}</div>
|
|
97
|
-
</div>`}).join("")}function gt(){let e=(0,n.$)("recent-menu");e&&(e.classList.add("hidden"),e.classList.remove("recent-menu-open"),e.style.top="",e.style.left="",e.style.right="",e.style.width="",e.style.maxHeight=""),(0,n.$)("recent-menu-backdrop")?.classList.add("hidden")}function fs(){let e=(0,n.$)("recent-menu"),t=(0,n.$)("topbar");if(!e||!t)return;let s=t.getBoundingClientRect(),a=Math.round(s.bottom+6),i=window.matchMedia("(max-width: 640px)").matches?8:null;if(e.style.top=`${a}px`,i!=null)e.style.left=`${i}px`,e.style.right=`${i}px`;else{let u=(0,n.$)("btn-recent-sessions")?.getBoundingClientRect();if(u){let l=Math.min(320,window.innerWidth-16),d=u.left;d+l>window.innerWidth-8&&(d=window.innerWidth-l-8),d<8&&(d=8),e.style.left=`${Math.round(d)}px`,e.style.right="auto",e.style.width=`${l}px`}}let r=Math.max(160,window.innerHeight-a-12);e.style.maxHeight=`${Math.min(r,Math.round(window.innerHeight*.7))}px`}function Li(e){let t=(0,n.$)("recent-menu");t&&(Mi(e??ve()),t.classList.remove("hidden"),t.classList.add("recent-menu-open"),fs(),(0,n.$)("recent-menu-backdrop")?.classList.remove("hidden"))}function Jo(e){let t=(0,n.$)("recent-menu");t&&(t.classList.contains("hidden")?Li(e):gt())}function Mi(e){let t=(0,n.$)("recent-menu-list"),s=(0,n.$)("recent-menu-empty"),o=(0,n.$)("recent-menu-count");if(!t)return;let a=zo(e??ve(),$i);if(o&&(o.textContent=a.length?`${a.length}`:""),!a.length){t.innerHTML="",s?.classList.remove("hidden");return}s?.classList.add("hidden"),t.innerHTML=a.map(i=>{let r=pt(i.updatedAt),c=f.sessionId===i.sessionId&&(!i.machineId||!f.machineId||i.machineId===f.machineId),u=i.machineId?`<span class="text-[9px] font-mono text-base-content/35 truncate max-w-[4.5rem]" title="${(0,n.esc)(i.machineId)}">@${(0,n.esc)(i.machineId)}</span>`:"";return`
|
|
98
|
-
<button type="button"
|
|
99
|
-
class="recent-menu-item w-full text-left px-2 py-1.5 rounded-md flex items-start gap-2
|
|
100
|
-
${c?"bg-primary/15 text-primary":"hover:bg-base-200"}"
|
|
101
|
-
data-session-id="${(0,n.esc)(i.sessionId)}"
|
|
102
|
-
data-session-cwd="${(0,n.esc)(i.cwd||"")}"
|
|
103
|
-
data-session-config-dir="${(0,n.esc)(i.configDir||"")}"
|
|
104
|
-
data-session-agent="${(0,n.esc)(i.agent||"claude")}"
|
|
105
|
-
data-session-machine="${(0,n.esc)(i.machineId||"")}">
|
|
106
|
-
<span class="flex-shrink-0 mt-0.5">${Ve(i.agent)}</span>
|
|
107
|
-
<span class="flex-1 min-w-0">
|
|
108
|
-
<span class="block text-xs font-semibold truncate leading-snug">${(0,n.esc)(Ge(i))}</span>
|
|
109
|
-
<span class="block text-[10px] text-base-content/40 truncate mt-0.5">${(0,n.esc)(i.projectName||Ye(i.cwd)||"\u2014")}</span>
|
|
110
|
-
</span>
|
|
111
|
-
<span class="flex flex-col items-end gap-0.5 flex-shrink-0 self-start">
|
|
112
|
-
<span class="text-[9px] text-base-content/40 whitespace-nowrap">${(0,n.esc)(r)}</span>
|
|
113
|
-
${u}
|
|
114
|
-
${c?'<span class="text-[9px] opacity-70">\u5F53\u524D</span>':""}
|
|
115
|
-
</span>
|
|
116
|
-
</button>`}).join("")}function Ko(e){qt=e}function ms(e){tt+=e,tt<0?(tt=11,Tn--):tt>11&&(tt=0,Tn++)}var Tn,tt,qt,$i,hs=X(()=>{C();I();jt();Tn=new Date().getFullYear(),tt=new Date().getMonth(),qt=null;$i=15});function Vo(e){return Array.isArray(e)?e:e&&Array.isArray(e.results)?e.results:e&&Array.isArray(e.sessions)?e.sessions:[]}function Ti(){let e=parseInt(te.peek()||"14",10),t=new URLSearchParams;e>0&&t.set("days",String(e)),t.set("limit","400");let s=t.toString();return s?`/api/sessions?${s}`:"/api/sessions"}async function ye({waitFresh:e=!0}={}){let t=Vo(await yn());if(t.length){let o=M.peek();if(k.peek()&&o){let a=t.filter(i=>!i.machineId||i.machineId===o);E.value=a.length?a:t}else E.value=t}let s=async()=>{let o=Ti(),a=await h("GET",o,void 0,void 0,{etag:!0});if(a===Zn)return E.peek();let i=Vo(a);return E.value=i,Lo(i),i};if(e||!t.length)try{await s()}catch(o){if(!t.length){let a=(0,n.$)("session-list");a&&(a.innerHTML=`<div class="px-3 py-3 text-xs text-error">${(0,n.esc)(o.message)}</div>`)}}else s().catch(()=>{})}async function Wt({waitFresh:e=!0}={}){let t=await Mo();t&&(P.value=t);let s=async()=>{let o=await h("GET","/api/workspaces");P.value=o,Bt(o)};if(e||!t)try{await s()}catch{}else s().catch(()=>{})}function Qo(e,{showProject:t=!1}={}){let s=f.sessionId===e.sessionId&&(f.agent||S.peek())===(e.agent||"claude"),o=v.peek()?.[B(e.agent,e.sessionId,e.machineId)]||{},a=!!(o.title&&String(o.title).trim()),i=e.snippet&&N.peek()?e.snippet:Ge(e),r=!!o.favorite,c=!!o.hidden,u=!!(o.notes&&String(o.notes).trim()),l=Number(e.turnCount)||0,d=l>0&&l<=Vn,p=t?`<div class="text-[10px] text-base-content/40 font-mono truncate" title="${(0,n.esc)(e.cwd||"")}">${(0,n.esc)(e.projectName||Ye(e.cwd)||"\u2014")}</div>`:"",m=[d?`<span class="session-thin-badge" title="Only ${l} turn${l===1?"":"s"}">thin\xB7${l}</span>`:"",a?`<span class="session-renamed-badge" title="Custom name (original: ${(0,n.esc)(e.display||"")})">renamed</span>`:"",c?'<span class="session-hidden-badge" title="Hidden">hidden</span>':"",u?`<span class="session-note-dot" title="${(0,n.esc)(o.notes)}">\u{1F4DD}</span>`:""].filter(Boolean).join(""),b=e.machineId?`<span class="text-[9px] px-1 py-0.5 rounded bg-base-300 text-base-content/50 font-mono flex-shrink-0" title="Machine">${(0,n.esc)(e.machineId)}</span>`:"",g=a?`${i}
|
|
117
|
-
(original: ${e.display||e.sessionId})`:e.display||"";return`<div class="session-row px-2.5 py-1.5 cursor-pointer hover:bg-base-300 border-l-2
|
|
118
|
-
${s?"border-primary bg-primary/5":"border-transparent"} ${r?"session-fav":""} ${c?"session-hidden-row":""} ${d?"session-thin":""}"
|
|
119
|
-
data-session-id="${(0,n.esc)(e.sessionId)}"
|
|
120
|
-
data-session-cwd="${(0,n.esc)(e.cwd||"")}"
|
|
121
|
-
data-session-config-dir="${(0,n.esc)(e.configDir||"")}"
|
|
122
|
-
data-session-agent="${(0,n.esc)(e.agent||"claude")}"
|
|
123
|
-
data-session-machine="${(0,n.esc)(e.machineId||"")}"
|
|
124
|
-
data-session-display="${(0,n.esc)(e.display||"")}">
|
|
125
|
-
<div class="flex items-start gap-1.5">
|
|
126
|
-
<button type="button" class="session-fav-btn flex-shrink-0 leading-none mt-0.5 ${r?"is-fav":""}"
|
|
127
|
-
data-fav-toggle="1"
|
|
128
|
-
data-fav-session-id="${(0,n.esc)(e.sessionId)}"
|
|
129
|
-
data-fav-session-agent="${(0,n.esc)(e.agent||"claude")}"
|
|
130
|
-
data-fav-session-machine="${(0,n.esc)(e.machineId||"")}"
|
|
131
|
-
title="${r?"Unfavorite":"Favorite"}">${r?"\u2605":"\u2606"}</button>
|
|
132
|
-
${Ve(e.agent)}
|
|
133
|
-
<div class="min-w-0 flex-1">
|
|
134
|
-
<div class="session-title-wrap flex items-center gap-1 min-w-0"
|
|
135
|
-
data-rename-session-id="${(0,n.esc)(e.sessionId)}"
|
|
136
|
-
data-rename-session-agent="${(0,n.esc)(e.agent||"claude")}"
|
|
137
|
-
data-rename-session-machine="${(0,n.esc)(e.machineId||"")}"
|
|
138
|
-
data-rename-session-display="${(0,n.esc)(e.display||"")}">
|
|
139
|
-
<div class="session-title-text text-[11px] leading-snug truncate flex-1 min-w-0 ${s?"text-primary font-medium":"text-base-content/85"} ${a?"session-title-renamed":""}"
|
|
140
|
-
title="${(0,n.esc)(g)}">${(0,n.esc)(i)}</div>
|
|
141
|
-
<button type="button" class="session-rename-btn flex-shrink-0"
|
|
142
|
-
data-rename-session="1"
|
|
143
|
-
title="Rename session">\u270E</button>
|
|
144
|
-
<button type="button" class="session-hide-btn flex-shrink-0 ${c?"is-hidden":""}"
|
|
145
|
-
data-hide-toggle="1"
|
|
146
|
-
data-hide-session-id="${(0,n.esc)(e.sessionId)}"
|
|
147
|
-
data-hide-session-agent="${(0,n.esc)(e.agent||"claude")}"
|
|
148
|
-
data-hide-session-machine="${(0,n.esc)(e.machineId||"")}"
|
|
149
|
-
title="${c?"Unhide session":"Hide session"}">${c?"\u{1F441}":"\u{1F648}"}</button>
|
|
150
|
-
</div>
|
|
151
|
-
${p}
|
|
152
|
-
${m||b?`<div class="flex items-center gap-1 mt-0.5 flex-wrap">${b}${m}</div>`:""}
|
|
153
|
-
</div>
|
|
154
|
-
<span class="text-[9px] text-base-content/30 flex-shrink-0 mt-0.5">${pt(e.updatedAt)}</span>
|
|
155
|
-
</div>
|
|
156
|
-
</div>`}async function Gt(){try{let e=await h("GET","/api/agents");it.value=e;let t=S.peek()||"claude",s=fe(t),o=$.peek();if(o&&!s.some(a=>a.value===o)){let a=re(t);$.value=a,localStorage.setItem("model",a)}return we(),e}catch(e){return console.warn("[agents] failed to load model list",e),null}}function bs(){let e=(0,n.$)("sel-effort");if(!e)return;let t=S.peek()||"claude",s=$.peek(),o=Et(t,s),a=pe.peek()||"",i=['<option value="">effort: off</option>'].concat(o.map(r=>`<option value="${(0,n.esc)(r)}" ${r===a?"selected":""}>${(0,n.esc)(r)}</option>`));e.innerHTML=i.join(""),a&&!o.includes(a)?(pe.value="",e.value=""):e.value=a}function we(){let e=(0,n.$)("sel-model");if(!e)return;let t=S.peek()||"claude",s=fe(t),o=$.peek();if(o&&!s.some(i=>i.value===o)&&(s=[{value:o,label:`${o} \u2605`,custom:!0},...s]),e.innerHTML=s.map(i=>`<option value="${(0,n.esc)(i.value)}" ${i.value===o?"selected":""}>${(0,n.esc)(i.label)}</option>`).join(""),s.some(i=>i.value===o))e.value=o;else{let i=re(t)||s[0]?.value;$.value=i,localStorage.setItem("model",i),e.value=i,o=i}let a=(0,n.$)("sel-agent");a&&(a.value=t),bs()}function xs({editCurrent:e=!1}={}){let t=S.peek()||"claude",s=e&&$.peek()||"",o=e?`Edit model id for ${t}:`:`Add model id for ${t} (saved in this browser):`,a=window.prompt(o,s);if(a==null)return;let i=a.trim();i&&(e&&s&&s!==i&&tn(t).includes(s)&&Qn(t,s),Tt(t,i),$.value=i,localStorage.setItem("model",i),we())}function Ei(e){if(!Array.isArray(e)||!e.length)return`<div class="px-3 py-6 text-center text-xs text-base-content/40">
|
|
157
|
-
No projects in this range.<br>
|
|
158
|
-
<span class="text-base-content/30">Try \u201CAll\u201D time or another agent.</span>
|
|
159
|
-
</div>`;let t=ot.value,s=at.value,o=!!N.value.trim(),a=e[0]?.cwd||"(no path)";return e.map(i=>{let r=i.cwd||"(no path)",c=!1;s.has(r)?c=!1:t.has(r)?c=!0:c=o||r===a||e.length<=3;let u=i.latest?.display||i.latest?.snippet||"",l=i.machineId?`<span class="text-[9px] font-mono text-base-content/40 flex-shrink-0">@${(0,n.esc)(i.machineId)}</span>`:"",d=`
|
|
160
|
-
<div class="project-header px-2.5 py-2 cursor-pointer hover:bg-base-300/80 select-none border-b border-base-300/40"
|
|
161
|
-
data-folder="${(0,n.esc)(r)}" data-project-cwd="${(0,n.esc)(i.cwd||"")}" data-project-machine="${(0,n.esc)(i.machineId||"")}">
|
|
162
|
-
<div class="flex items-center gap-1.5">
|
|
163
|
-
<span class="text-[10px] text-base-content/40 w-3 flex-shrink-0">${c?"\u25BE":"\u25B8"}</span>
|
|
164
|
-
<span class="text-[12px] font-semibold truncate flex-1" title="${(0,n.esc)(i.cwd||"")}">${(0,n.esc)(i.projectName)}</span>
|
|
165
|
-
${l}
|
|
166
|
-
<span class="text-[9px] text-base-content/30 flex-shrink-0">${pt(i.updatedAt)}</span>
|
|
167
|
-
</div>
|
|
168
|
-
<div class="flex items-center gap-1.5 mt-0.5 pl-4">
|
|
169
|
-
<span class="text-[10px] text-base-content/35 font-mono truncate flex-1" title="${(0,n.esc)(i.cwd||"")}">${(0,n.esc)(Ye(i.cwd))}</span>
|
|
170
|
-
<span class="flex items-center gap-0.5 flex-shrink-0">${ao(i.agents)}</span>
|
|
171
|
-
</div>
|
|
172
|
-
${u&&!c?`<div class="pl-4 mt-0.5 text-[10px] text-base-content/45 truncate" title="${(0,n.esc)(u)}">\u21B3 ${(0,n.esc)(u)}</div>`:""}
|
|
173
|
-
</div>`,p=i.sessions.slice(0,40),m=i.sessions.length>40?`<div class="px-3 py-1 text-[10px] text-base-content/30">+${i.sessions.length-40} more</div>`:"",b=c?`<div class="pb-1 border-b border-base-300/30">${p.map(g=>Qo(g)).join("")}${m}</div>`:"";return d+b}).join("")}function Ii(e){if(!Array.isArray(e)||!e.length)return`<div class="px-3 py-6 text-center text-xs text-base-content/40">
|
|
174
|
-
No recent activity.<br>
|
|
175
|
-
<span class="text-base-content/30">Widen the time range or clear filters.</span>
|
|
176
|
-
</div>`;let t=new Map;for(let s of e){let a=(s.updatedAt?new Date(s.updatedAt):new Date).toLocaleDateString(void 0,{weekday:"short",month:"short",day:"numeric"});t.has(a)||t.set(a,[]),t.get(a).push(s)}return[...t.entries()].map(([s,o])=>`
|
|
177
|
-
<div class="px-2.5 pt-2.5 pb-1 text-[10px] uppercase tracking-wide text-base-content/35 font-medium sticky top-0 bg-base-200/95 backdrop-blur-sm z-[1]">${(0,n.esc)(s)}</div>
|
|
178
|
-
${o.map(a=>Qo(a,{showProject:!0})).join("")}
|
|
179
|
-
`).join("")}function Zo(){let e=Le.value,t=Array.isArray(Re.value)?Re.value:[],s=Array.isArray(ze.value)?ze.value:[];return e==="timeline"?Ii(t):Ei(s)}function ea(){document.querySelectorAll("[data-view]").forEach(i=>{let r=i.dataset.view===Le.peek();i.classList.toggle("bg-primary/15",r),i.classList.toggle("text-primary",r),i.classList.toggle("font-medium",r),i.classList.toggle("text-base-content/50",!r)}),document.querySelectorAll("[data-agent-filter]").forEach(i=>{i.classList.toggle("active",i.dataset.agentFilter===Q.peek())});let e=(0,n.$)("time-range");e&&e.value!==te.peek()&&(e.value=te.peek());let t=(0,n.$)("search-status");if(!t)return;let s=ue.peek(),o=Array.isArray(s?.results)?s.results:null,a=(N.peek()||"").trim();if(Me.peek())t.classList.remove("hidden"),t.textContent="Searching content\u2026";else if(a&&o)t.classList.remove("hidden"),t.textContent=`${o.length} hit${o.length===1?"":"s"} \xB7 ${te.peek()==="0"?"all time":te.peek()+"d"}${s.deep===!1?"":" (incl. content)"}`;else if(a)t.classList.remove("hidden"),t.textContent="Local filter only \u2014 type to deep-search";else{let i=Re.peek(),r=ze.peek(),c=Array.isArray(i)?i.length:0,u=Array.isArray(r)?r.length:0;t.classList.remove("hidden"),t.textContent=Le.peek()==="projects"?`${u} project${u===1?"":"s"} \xB7 ${c} session${c===1?"":"s"}`:`${c} recent session${c===1?"":"s"}`}}async function gs(e){let t=(e||"").trim();if(!t){ue.value=null,Me.value=!1;return}Me.value=!0;try{let s=new URLSearchParams({q:t,days:te.peek()||"14",limit:"50",deep:"1"});Q.peek()&&Q.peek()!=="all"&&s.set("agent",Q.peek());let o=await h("GET",`/api/activity?${s}`);N.peek().trim()===t&&(ue.value={q:t.toLowerCase(),results:o.results||[],deep:!0})}catch(s){N.peek().trim()===t&&(ue.value={q:t.toLowerCase(),results:[],error:s.message})}finally{Me.value=!1}}function In(e){clearTimeout(Yo);let t=(e||"").trim();if(!t){ue.value=null,Me.value=!1;return}Yo=setTimeout(()=>gs(t),350)}function Xo(e){if(!e)return"";if(typeof marked>"u")return`<pre class="whitespace-pre-wrap text-xs">${(0,n.esc)(e)}</pre>`;try{return marked.parse(String(e),{breaks:!0,gfm:!0})}catch{return`<pre class="whitespace-pre-wrap text-xs">${(0,n.esc)(e)}</pre>`}}async function vs(){let e=(0,n.$)("memory-area");if(e){if(!f.sessionId){e.innerHTML='<div class="p-4 text-xs text-base-content/40">Open or resume a session to view its memory.</div>';return}e.innerHTML='<div class="p-4 text-xs text-base-content/40">Loading\u2026</div>';try{let t=new URLSearchParams({agent:f.agent||S.peek()||"claude"}),s=await h("GET",`/api/sessions/${f.sessionId}/memory?${t}`),o=[];s.index&&o.push(`<div class="mb-4"><div class="text-[11px] uppercase tracking-wide text-base-content/40 mb-1">Index (MEMORY.md)</div>
|
|
180
|
-
<div class="md">${Xo(s.index)}</div></div>`);for(let a of s.files||[])o.push(`<div class="mb-3 border border-base-300 rounded-lg overflow-hidden">
|
|
181
|
-
<div class="px-3 py-1.5 bg-base-300/50 text-xs font-mono text-base-content/70">${(0,n.esc)(a.name)}</div>
|
|
182
|
-
<div class="px-3 py-2 md">${Xo(a.content)}</div>
|
|
183
|
-
</div>`);o.length||o.push('<div class="text-base-content/40 text-xs">No memory saved for this project yet.</div>'),e.innerHTML=`<div class="p-4">${o.join("")}</div>`}catch(t){e.innerHTML=`<div class="p-4 text-error text-xs">${(0,n.esc)(t?.message??String(t))}</div>`}}}var Yo,vt=X(()=>{C();I();K();wn();jt();Yo=null});function ta(e,t){Cn=e||"",An=t||""}async function na(e){let t=(0,n.$)("project-notes-bar");if(t)try{let s=await h("GET",`/api/projects/notes?root=${encodeURIComponent(e)}`);Cn=s.goal||"",An=s.notes||"",yt(),t.style.display="flex"}catch(s){console.error("Error fetching project notes:",s),t.style.display="none"}}function yt(){let e=(0,n.$)("project-notes-bar");if(!e)return;let t=Cn||"No goal yet \u2014 click Edit",s=An||"";e.innerHTML=`
|
|
184
|
-
<div class="flex items-start gap-2 w-full">
|
|
185
|
-
<div class="flex-1 min-w-0">
|
|
186
|
-
<div class="truncate">
|
|
187
|
-
<span class="font-bold text-base-content/40 uppercase mr-1">Goal:</span>
|
|
188
|
-
<span id="project-goal-text" class="text-base-content/80 cursor-pointer hover:underline" title="Click to edit">${(0,n.esc)(t)}</span>
|
|
189
|
-
</div>
|
|
190
|
-
${s?`<div class="mt-0.5 text-base-content/55 line-clamp-2 whitespace-pre-wrap" id="project-notes-text" title="${(0,n.esc)(s)}"><span class="font-bold text-base-content/35 uppercase mr-1">Notes:</span>${(0,n.esc)(s)}</div>`:'<div class="mt-0.5 text-base-content/35 italic text-[11px]" id="project-notes-text">No project notes</div>'}
|
|
191
|
-
</div>
|
|
192
|
-
<button id="btn-edit-project-notes" class="btn btn-ghost btn-xs px-1.5 hover:bg-base-300 flex-shrink-0">Edit</button>
|
|
193
|
-
</div>
|
|
194
|
-
`}function sa(){let e=(0,n.$)("project-notes-bar");e&&(e.innerHTML=`
|
|
195
|
-
<div class="flex flex-col gap-1.5 w-full">
|
|
196
|
-
<div class="flex items-center gap-2">
|
|
197
|
-
<span class="text-[10px] font-bold text-base-content/40 uppercase w-12 flex-shrink-0">Goal</span>
|
|
198
|
-
<input id="project-goal-input" type="text" class="input input-xs input-bordered flex-1 text-xs"
|
|
199
|
-
placeholder="One-line project goal\u2026" value="${(0,n.esc)(Cn)}">
|
|
200
|
-
</div>
|
|
201
|
-
<div class="flex items-start gap-2">
|
|
202
|
-
<span class="text-[10px] font-bold text-base-content/40 uppercase w-12 flex-shrink-0 mt-1">Notes</span>
|
|
203
|
-
<textarea id="project-notes-input" rows="2" class="textarea textarea-xs textarea-bordered flex-1 text-xs leading-snug"
|
|
204
|
-
placeholder="Longer project notes (stack, links, status\u2026)">${(0,n.esc)(An)}</textarea>
|
|
205
|
-
</div>
|
|
206
|
-
<div class="flex items-center gap-1 justify-end">
|
|
207
|
-
<button id="btn-save-project-notes" class="btn btn-primary btn-xs px-2">Save</button>
|
|
208
|
-
<button id="btn-cancel-project-notes" class="btn btn-ghost btn-xs px-2">Cancel</button>
|
|
209
|
-
</div>
|
|
210
|
-
</div>
|
|
211
|
-
`,(0,n.$)("project-goal-input")?.focus())}async function zt(){try{let e=await h("GET","/api/sessions/meta");v.value=e||{}}catch(e){console.warn("session meta load failed",e)}}function ce(){let e=(0,n.$)("session-notes-bar");if(!e)return;let t=f.sessionId,s=f.agent||S.peek()||"claude";if(!t){e.style.display="none",e.innerHTML="";return}if(e.querySelector(".session-rename-input"))return;let o=rt(s,t),a=(o.notes||"").trim(),i=!!o.favorite,r=!!o.hidden,u=(o.title||"").trim()||t.slice(0,8);e.style.display="flex",e.innerHTML=`
|
|
212
|
-
<div class="flex-1 min-w-0 flex items-center gap-2">
|
|
213
|
-
<button type="button" class="session-fav-btn ${i?"is-fav":""}" data-fav-toggle="1"
|
|
214
|
-
data-fav-session-id="${(0,n.esc)(t)}" data-fav-session-agent="${(0,n.esc)(s)}"
|
|
215
|
-
title="${i?"Unfavorite":"Favorite"}">${i?"\u2605":"\u2606"}</button>
|
|
216
|
-
<span id="session-title-display"
|
|
217
|
-
class="session-title-inline max-w-[40%] truncate font-semibold text-base-content/85"
|
|
218
|
-
title="${(0,n.esc)(u)}">${(0,n.esc)(u)}</span>
|
|
219
|
-
<button type="button" class="session-rename-btn flex-shrink-0 opacity-70 hover:opacity-100"
|
|
220
|
-
data-rename-bar="1" title="Rename session">\u270E</button>
|
|
221
|
-
<button type="button" class="session-hide-btn flex-shrink-0 ${r?"is-hidden":""}"
|
|
222
|
-
data-hide-toggle="1"
|
|
223
|
-
data-hide-session-id="${(0,n.esc)(t)}"
|
|
224
|
-
data-hide-session-agent="${(0,n.esc)(s)}"
|
|
225
|
-
title="${r?"Unhide session":"Hide session"}">${r?"\u{1F441}":"\u{1F648}"}</button>
|
|
226
|
-
<span class="font-bold text-base-content/40 uppercase flex-shrink-0">Note:</span>
|
|
227
|
-
<span class="truncate text-base-content/70 ${a?"":"italic text-base-content/40"}"
|
|
228
|
-
id="session-notes-text" title="${(0,n.esc)(a||"Add a note for this session")}">${(0,n.esc)(a||"No session note")}</span>
|
|
229
|
-
</div>
|
|
230
|
-
<button id="btn-edit-session-notes" class="btn btn-ghost btn-xs px-1.5">Note</button>
|
|
231
|
-
`}function oa(){let e=(0,n.$)("session-notes-bar");if(!e||!f.sessionId)return;let t=f.agent||S.peek()||"claude",s=rt(t,f.sessionId);e.innerHTML=`
|
|
232
|
-
<input id="session-notes-input" type="text" class="input input-xs input-bordered flex-1 text-xs"
|
|
233
|
-
placeholder="Note for this session\u2026" value="${(0,n.esc)(s.notes||"")}">
|
|
234
|
-
<button id="btn-save-session-notes" class="btn btn-primary btn-xs px-2">Save</button>
|
|
235
|
-
<button id="btn-cancel-session-notes" class="btn btn-ghost btn-xs px-2">Cancel</button>
|
|
236
|
-
`;let o=(0,n.$)("session-notes-input");o?.focus(),o?.select()}function jn(){return{favorite:!1,notes:"",title:"",hidden:!1}}function Dn(e,t){return{favorite:t.favorite!==void 0?!!t.favorite:!!e.favorite,notes:t.notes!==void 0?String(t.notes||""):String(e.notes||""),title:t.title!==void 0?String(t.title||"").trim():String(e.title||"").trim(),hidden:t.hidden!==void 0?!!t.hidden:!!e.hidden,updatedAt:Date.now()}}function aa(e){return!e?.favorite&&!e?.hidden&&!(e?.notes||"").trim()&&!(e?.title||"").trim()}async function ia(e,t,{machineId:s}={}){let o=s||f.machineId||null,a=B(t,e,o),i=v.peek()?.[a]||jn(),r=!i.favorite;v.value={...v.peek(),[a]:{...i,favorite:r,updatedAt:Date.now()}};try{let c=await h("PUT","/api/sessions/meta",{sessionId:e,agent:t,favorite:r,machineId:o||void 0},void 0,{machineId:o||void 0});v.value={...v.peek(),[a]:Dn(i,{favorite:c.favorite??r,notes:c.notes,title:c.title,hidden:c.hidden})}}catch(c){v.value={...v.peek(),[a]:i},alert(`Favorite failed: ${c.message}`)}f.sessionId===e&&ce()}async function ra(e,t,{machineId:s}={}){if(!e)return!1;let o=s||f.machineId||null,a=B(t,e,o),i=v.peek()?.[a]||jn(),r=!i.hidden;v.value={...v.peek(),[a]:{...i,hidden:r,updatedAt:Date.now()}};try{let c=await h("PUT","/api/sessions/meta",{sessionId:e,agent:t,hidden:r,machineId:o||void 0},void 0,{machineId:o||void 0}),u=Dn(i,{favorite:c.favorite,notes:c.notes,title:c.title,hidden:c.hidden??r});if(aa(u)){let l={...v.peek()};delete l[a],v.value=l}else v.value={...v.peek(),[a]:u}}catch(c){return v.value={...v.peek(),[a]:i},alert(`Hide failed: ${c.message}`),!1}return f.sessionId===e&&ce(),!0}async function ys(e,t,s,{machineId:o}={}){let a=o||f.machineId||null,i=B(t,e,a),r=v.peek()?.[i]||jn();try{let c=await h("PUT","/api/sessions/meta",{sessionId:e,agent:t,notes:s,machineId:a||void 0},void 0,{machineId:a||void 0});v.value={...v.peek(),[i]:Dn(r,{favorite:c.favorite,notes:c.notes??s,title:c.title,hidden:c.hidden})}}catch(c){return alert(`Save note failed: ${c.message}`),!1}return!0}async function Ci(e,t,s,{machineId:o}={}){if(!e)return!1;let a=o||f.machineId||null,i=B(t,e,a),r=v.peek()?.[i]||jn(),c=String(s||"").trim().slice(0,200);v.value={...v.peek(),[i]:{...r,title:c,updatedAt:Date.now()}};try{let u=await h("PUT","/api/sessions/meta",{sessionId:e,agent:t,title:c,machineId:a||void 0},void 0,{machineId:a||void 0}),l=Dn(r,{favorite:u.favorite,notes:u.notes,title:u.title!==void 0?u.title:c,hidden:u.hidden});if(aa(l)){let d={...v.peek()};delete d[i],v.value=d}else v.value={...v.peek(),[i]:l}}catch(u){return v.value={...v.peek(),[i]:r},alert(`Rename failed: ${u.message}`),!1}return f.sessionId===e&&ce(),!0}function ws({sessionId:e,agent:t="claude",machineId:s=null,fallbackDisplay:o="",mountEl:a}={}){if(!e||!a)return;document.querySelectorAll(".session-rename-input").forEach(m=>{m.dispatchEvent(new CustomEvent("session-rename-cancel",{bubbles:!0}))});let r=(rt(t,e,s).title||"").trim()||o||e.slice(0,8),c=a.innerHTML,u=a.className;a.classList.add("session-rename-active"),a.innerHTML=`<input type="text" class="session-rename-input input input-xs input-bordered w-full min-w-0 text-xs font-mono"
|
|
237
|
-
value="${(0,n.esc)(r)}"
|
|
238
|
-
placeholder="Session name (empty = original)"
|
|
239
|
-
maxlength="200"
|
|
240
|
-
data-rename-input="1"
|
|
241
|
-
data-rename-session-id="${(0,n.esc)(e)}"
|
|
242
|
-
data-rename-session-agent="${(0,n.esc)(t)}"
|
|
243
|
-
data-rename-session-machine="${(0,n.esc)(s||"")}"
|
|
244
|
-
autocomplete="off" spellcheck="false" />`;let l=a.querySelector(".session-rename-input");if(!l)return;let d=!1,p=async m=>{if(d)return;d=!0;let b=m?l.value:r;if(a.className=u,!m){a.innerHTML=c;return}await Ci(e,t,b,{machineId:s}),document.querySelectorAll(".session-rename-input").forEach(g=>g.remove()),f.sessionId===e&&ce(),v.value={...v.peek()}};l.addEventListener("keydown",m=>{m.stopPropagation(),m.key==="Enter"?(m.preventDefault(),p(!0)):m.key==="Escape"&&(m.preventDefault(),p(!1))}),l.addEventListener("click",m=>m.stopPropagation()),l.addEventListener("mousedown",m=>m.stopPropagation()),l.addEventListener("session-rename-cancel",()=>p(!1)),l.addEventListener("blur",()=>{setTimeout(()=>p(!0),0)}),requestAnimationFrame(()=>{l.focus(),l.select()})}var Cn,An,Jt=X(()=>{C();I();K();Cn="",An=""});var ca={};Os(ca,{goHome:()=>Pn,openProject:()=>ks,resumeSession:()=>ke,switchTab:()=>He});function Hn(e){for(let t of["files-machine-hint","git-machine-hint"]){let s=(0,n.$)(t);s&&(e?(s.textContent=`@${e}`,s.classList.remove("hidden"),s.title=`Paths are on machine ${e}`):(s.textContent="",s.classList.add("hidden"),s.title=""))}}function ks(e){let t=e?.path||"";(0,n.batch)(()=>{y.value=e,O.value=t,t&&(F.value=t,J.value=t,A.value="",U.value=null)}),f.sessionId=null,(0,n.$)("topbar-project").textContent=t||"Project";let s=(0,n.$)("files-root-input");s&&t&&(s.value=t);let o=(0,n.$)("git-root-input");o&&t&&(o.value=t),Hn(e?.machineId||M.peek()||f.machineId),(0,n.$)("welcome").classList.add("hidden");let a=(0,n.$)("project-view");a.classList.remove("hidden"),a.style.display="flex",ft(),je(),He("chat")}function Pn(){y.value=null,O.value=null,F.value="",J.value="",A.value="",U.value=null,f.sessionId=null,Hn(null),document.documentElement.classList.remove("boot-restore");let e=(0,n.$)("topbar-project");e&&(e.textContent="Select a session");let t=(0,n.$)("welcome");t&&(t.classList.remove("hidden"),t.style.display="",nt(ve()));let s=(0,n.$)("project-view");s&&(s.classList.add("hidden"),s.style.display="none"),bn("/")}async function ke(e,t,s,o,a,i={}){let r=i.tab&&["chat","files","git","shell","memory"].includes(i.tab)?i.tab:"chat",c=i.skipHistory??(r!=="chat"&&r!=="memory"),u=E.peek().find(w=>w.sessionId===e&&(!a||w.machineId===a||!w.machineId)),l=Je(),d=l?.sessionId===e?l:null,p=o||u?.agent||d?.agent||"claude",m=a||u?.machineId||d?.machineId||M.peek()||null,b=t||u?.cwd||d?.cwd||null,g=s||u?.configDir||d?.configDir||null;if(m&&m!==f.machineId){Z(m);try{f.ws?.close()}catch{}f.ws=null}f.sessionId=e,f.configDir=g,f.agent=p,me(p),we();let W=b;la(W,m,e),je(),W&&h("POST","/api/resolve-path",{path:W},void 0,{machineId:m||void 0}).then(w=>{f.sessionId!==e||!w?.path||w.path===W||(W=w.path,la(W,m,e),es({sessionId:e,cwd:W}))}).catch(()=>{}),ft(),He(r);{let w=E.peek();E.value=Array.isArray(w)?[...w]:[]}es({sessionId:e,cwd:W||b,configDir:g,agent:p,machineId:m,tab:r});let Fe=R[p]||p,de=`${m||""}:${p}:${e}`,qe=(w,G,We={})=>{if(f.sessionId!==e)return;if((0,n.$)("messages").innerHTML="",!w.length){x(`${Fe} \xB7 ${e.slice(0,8)}\u2026 \u2014 no history`);return}le();let z=40,ae=0,$t=()=>{if(f.sessionId!==e)return;let en=Math.min(ae+z,w.length);for(;ae<en;ae++){let L=w[ae],Ps=_(L.ts??L.timestamp),Gn=L.type||(L.role?"text":null);if(Gn==="text"||L.role&&L.content!=null&&!L.name){(L.role==="assistant"||L.role==="user")&&le();let Ns=typeof L.content=="string"?L.content:Array.isArray(L.content)?L.content.map(Aa=>Aa?.text||"").filter(Boolean).join(`
|
|
245
|
-
`):String(L.content??"");Ns&&Qe(L.role==="user"?"user":"assistant",L.role==="user"?"You":Fe,Ns,{ts:Ps})}else Gn==="tool_use"||L.name?os({name:L.name||"tool",input:L.input??L.arguments??{}},{ts:Ps}):(Gn==="token_usage"||L.usage)&&(le(),ut(L.usage||L))}if(ae<w.length){requestAnimationFrame($t);return}le(),G&&ss(G);let St=w.filter(L=>L.type==="token_usage").length||w.filter(L=>L.role==="user").length,Lt=We.truncated?` (showing last ${w.length} of ${We.total||"?"})`:"";x(`\u2500\u2500 ${Fe} history \xB7 ${St} turn${St!==1?"s":""}${Lt} \u2500\u2500`)};$t()},$e=async()=>{let w=_n.get(de);if(w&&w.msgs?.length&&Date.now()-w.at<Ai){qe(w.msgs,w.context,w.meta);return}x(`Loading ${Fe} history\u2026`);try{let G=m||M.peek()||f.machineId||null;if(k.peek()&&!G)throw new Error("\u672A\u9009\u62E9\u673A\u5668\uFF0C\u65E0\u6CD5\u52A0\u8F7D\u5386\u53F2");G&&f.machineId!==G&&Z(G);let We=async St=>{let Lt=new URLSearchParams({tail:"0",compact:"1"});return St&&Lt.set("agent",St),G&&Lt.set("machine",G),h("GET",`/api/sessions/${e}/messages?${Lt}`,void 0,void 0,{machineId:G||void 0,dedupe:!1})},z=await We(p),ae=Array.isArray(z)?z:z?.messages||[];!ae.length&&p&&(z=await We(null),ae=Array.isArray(z)?z:z?.messages||[]);let $t=Array.isArray(z)?null:z?.context||null,en=Array.isArray(z)?{}:{truncated:z?.truncated,total:z?.total};ae.length?_n.set(de,{msgs:ae,context:$t,meta:en,at:Date.now()}):_n.delete(de),qe(ae,$t,en)}catch(G){if(f.sessionId!==e)return;_n.delete(de),(0,n.$)("messages").innerHTML="",x(`${Fe} \xB7 ${e.slice(0,8)}\u2026 (history unavailable: ${G.message})`)}};c?$e().catch(()=>{}):(await new Promise(w=>requestAnimationFrame(w)),$e().catch(()=>{})),xn(),ce()}function la(e,t,s){if(e){let o=e.split("/").filter(Boolean).pop()||e;(0,n.batch)(()=>{y.value={id:o,name:o,path:e,machineId:t},O.value=e,F.value=e,J.value=e,A.value="",U.value=null}),(0,n.$)("topbar-project").textContent=t?`${e} @${t}`:e;let a=(0,n.$)("files-root-input");a&&(a.value=e);let i=(0,n.$)("git-root-input");i&&(i.value=e),Hn(t),(0,n.$)("welcome")?.classList.add("hidden");let r=(0,n.$)("project-view");r&&(r.classList.remove("hidden"),r.style.display="flex",r.style.flexDirection="column",r.style.flex="1",r.style.minHeight="0",r.style.overflow="hidden")}else{(0,n.$)("welcome")?.classList.add("hidden");let o=(0,n.$)("project-view");o&&(o.classList.remove("hidden"),o.style.display="flex",o.style.flexDirection="column",o.style.flex="1",o.style.minHeight="0"),Hn(t)}}function He(e){j.value=e}var _n,Ai,Kt=X(()=>{C();I();K();sn();mt();gn();vt();Jt();hs();_n=new Map,Ai=180*1e3});I();K();C();I();K();var dt=null;function Ys(){return window.matchMedia("(display-mode: standalone)").matches||window.navigator.standalone===!0||document.referrer.includes("android-app://")}function Xs(){document.getElementById("btn-pwa-install")?.classList.remove("hidden")}function Qs(){Ys()&&document.documentElement.classList.add("pwa-standalone"),window.addEventListener("beforeinstallprompt",s=>{s.preventDefault(),dt=s,document.dispatchEvent(new CustomEvent("pwa-installable")),Xs()}),window.addEventListener("appinstalled",()=>{dt=null,document.getElementById("btn-pwa-install")?.classList.add("hidden")});let e=/iPad|iPhone|iPod/.test(navigator.userAgent)||navigator.platform==="MacIntel"&&navigator.maxTouchPoints>1;if(!Ys()&&(e||/Android/i.test(navigator.userAgent))&&requestAnimationFrame(()=>Xs()),!("serviceWorker"in navigator))return;let t=()=>{navigator.serviceWorker.register("/sw.js",{scope:"/"}).then(s=>{s.addEventListener("updatefound",()=>{let o=s.installing;o&&o.addEventListener("statechange",()=>{o.state==="installed"&&navigator.serviceWorker.controller&&console.info("[pwa] New version ready \u2014 reload to apply")})})}).catch(s=>console.warn("[pwa] SW register failed",s))};document.readyState==="complete"?setTimeout(t,0):window.addEventListener("load",()=>setTimeout(t,0))}async function Zs(){if(!dt)return!1;dt.prompt();let e=await dt.userChoice.catch(()=>null);return dt=null,document.getElementById("btn-pwa-install")?.classList.add("hidden"),e?.outcome==="accepted"}sn();mt();gn();C();K();I();wn();function To(){document.getElementById("root").insertAdjacentHTML("beforeend",`
|
|
246
|
-
<dialog id="modal-settings" class="modal">
|
|
247
|
-
<div class="modal-box max-w-lg">
|
|
248
|
-
<h3 class="font-bold text-base mb-4">\u2699\uFE0F Settings \u2014 Workspaces</h3>
|
|
249
|
-
<div id="ws-list" class="flex flex-col gap-2 mb-4"></div>
|
|
250
|
-
<div class="border-t border-base-300 pt-4">
|
|
251
|
-
<div class="text-xs uppercase tracking-wider text-base-content/50 mb-2">Add workspace</div>
|
|
252
|
-
<div class="flex gap-2 mb-2">
|
|
253
|
-
<input id="ws-new-name" type="text" placeholder="Name (e.g. personal)" class="input input-sm input-bordered flex-1">
|
|
254
|
-
<input id="ws-new-dir" type="text" placeholder="~/.claude-personal" class="input input-sm input-bordered flex-[2]">
|
|
255
|
-
<button id="ws-add-btn" class="btn btn-primary btn-sm">Add</button>
|
|
256
|
-
</div>
|
|
257
|
-
<div id="ws-error" class="alert alert-error text-xs py-1.5 hidden"></div>
|
|
258
|
-
</div>
|
|
259
|
-
<div class="modal-action">
|
|
260
|
-
<form method="dialog"><button class="btn btn-ghost btn-sm">Close</button></form>
|
|
261
|
-
</div>
|
|
262
|
-
</div>
|
|
263
|
-
<form method="dialog" class="modal-backdrop"><button>close</button></form>
|
|
264
|
-
</dialog>`),kn(),document.getElementById("ws-add-btn").addEventListener("click",ls),document.getElementById("ws-new-name").addEventListener("keydown",e=>{e.key==="Enter"&&ls()}),document.getElementById("ws-new-dir").addEventListener("keydown",e=>{e.key==="Enter"&&ls()})}function kn(){let e=(0,n.$)("ws-list");if(!e)return;let t=P.peek();if(!t.length){e.innerHTML='<div class="text-xs text-base-content/40">No workspaces configured</div>';return}e.innerHTML=t.map(s=>`
|
|
265
|
-
<div class="flex items-center gap-2 px-3 py-2 bg-base-200 rounded-lg">
|
|
266
|
-
<div class="flex-1 min-w-0">
|
|
267
|
-
<div class="text-sm font-medium">${(0,n.esc)(s.name)}</div>
|
|
268
|
-
<div class="text-xs font-mono text-base-content/50 truncate">${(0,n.esc)(s.configDir)}</div>
|
|
269
|
-
</div>
|
|
270
|
-
${t.length>1?`<button class="btn btn-ghost btn-xs text-error" data-ws-delete="${(0,n.esc)(s.id)}">\u2715</button>`:'<span class="text-[10px] text-base-content/30">default</span>'}
|
|
271
|
-
</div>`).join(""),e.querySelectorAll("[data-ws-delete]").forEach(s=>{s.addEventListener("click",()=>pi(s.dataset.wsDelete))})}async function ls(){let e=(0,n.$)("ws-new-name").value.trim(),t=(0,n.$)("ws-new-dir").value.trim(),s=(0,n.$)("ws-error");if(s.classList.add("hidden"),!e||!t){s.textContent="Name and path are required",s.classList.remove("hidden");return}try{let o=await h("POST","/api/workspaces",{name:e,configDir:t});P.value=[...P.peek(),o],Bt(P.peek()),(0,n.$)("ws-new-name").value="",(0,n.$)("ws-new-dir").value="",kn()}catch(o){s.textContent=o.message,s.classList.remove("hidden")}}async function pi(e){try{await h("DELETE",`/api/workspaces/${e}`),P.value=P.peek().filter(t=>t.id!==e),Bt(P.peek()),kn()}catch(t){alert(t.message)}}function Eo(){kn(),(0,n.$)("modal-settings")?.showModal()}C();K();I();var Mn=!1,Oe="chat",ht=!1,q=null,ee=null,Ze=[],Io=0,$n=null,cs=localStorage.getItem("ai_last_auto_report")||"",Ln=[],Do="",Ue="ma_last_session_id";function bt(e){if(typeof marked<"u")try{return marked.parse(String(e??""),{breaks:!0,gfm:!0})}catch{}return(0,n.esc)(e).replace(/\n/g,"<br>")}function xt(e){!e||typeof hljs>"u"||e.querySelectorAll("pre code").forEach(t=>{try{hljs.highlightElement(t)}catch{}})}async function ds(e,t,s){let o=lt({"Content-Type":"application/json"}),a=await fetch(e,{method:"POST",headers:o,body:JSON.stringify(t||{})});if(!a.ok){let l=await a.json().catch(()=>({}));throw new Error(l.error||`HTTP ${a.status}`)}let i=a.body.getReader(),r=new TextDecoder,c="",u="message";for(;;){let{done:l,value:d}=await i.read();if(l)break;c+=r.decode(d,{stream:!0});let p=c.split(`
|
|
272
|
-
`);c=p.pop()||"";for(let m of p)if(m.startsWith("event:"))u=m.slice(6).trim();else if(m.startsWith("data:")){let b=m.slice(5).trim();if(!b)continue;let g;try{g=JSON.parse(b)}catch{continue}s[u]?.(g),s.any?.(u,g),u="message"}else m.trim()===""&&(u="message")}}function ui(){return`
|
|
273
|
-
<div id="meta-agent-panel" class="meta-agent-panel hidden" aria-hidden="true">
|
|
274
|
-
<div class="meta-agent-backdrop" data-ma-close="1"></div>
|
|
275
|
-
<div class="meta-agent-sheet">
|
|
276
|
-
<header class="meta-agent-header">
|
|
277
|
-
<div class="flex items-center gap-2 min-w-0">
|
|
278
|
-
<span class="text-base font-bold">\u2726 Meta Agent</span>
|
|
279
|
-
<span class="text-[10px] text-base-content/40 hidden sm:inline">\u62A5\u544A \xB7 \u95EE\u7B54 \xB7 VLM \xB7 \u4EE3\u7801</span>
|
|
280
|
-
</div>
|
|
281
|
-
<div class="flex items-center gap-1">
|
|
282
|
-
<button type="button" id="ma-btn-chat" class="btn btn-xs btn-ghost border border-primary/40 text-primary">\u5BF9\u8BDD</button>
|
|
283
|
-
<button type="button" id="ma-btn-history" class="btn btn-xs btn-ghost border border-base-300" title="\u5386\u53F2\u5BF9\u8BDD">\u5386\u53F2</button>
|
|
284
|
-
<button type="button" id="ma-btn-report" class="btn btn-xs btn-ghost border border-base-300">\u62A5\u544A</button>
|
|
285
|
-
<button type="button" id="ma-btn-config" class="btn btn-xs btn-ghost border border-base-300" title="AI \u914D\u7F6E">\u2699</button>
|
|
286
|
-
<button type="button" id="ma-btn-close" class="btn btn-xs btn-ghost" data-ma-close="1">\u2715</button>
|
|
287
|
-
</div>
|
|
288
|
-
</header>
|
|
289
|
-
|
|
290
|
-
<div id="ma-mode-chat" class="meta-agent-body flex flex-col min-h-0 flex-1">
|
|
291
|
-
<div class="flex items-center gap-1 px-2 py-1 border-b border-base-300 flex-shrink-0 overflow-x-auto">
|
|
292
|
-
<button type="button" id="ma-new-chat" class="btn btn-ghost btn-xs">\uFF0B \u65B0\u5BF9\u8BDD</button>
|
|
293
|
-
<button type="button" id="ma-open-history" class="btn btn-ghost btn-xs" title="\u5386\u53F2\u5217\u8868">\u2630 \u5386\u53F2</button>
|
|
294
|
-
<span id="ma-session-title" class="text-[11px] text-base-content/55 truncate flex-1 min-w-0 px-1"></span>
|
|
295
|
-
<button type="button" id="ma-del-session" class="btn btn-ghost btn-xs text-error" title="\u5220\u9664\u5F53\u524D\u4F1A\u8BDD">\u{1F5D1}</button>
|
|
296
|
-
<button type="button" id="ma-quick-today" class="btn btn-ghost btn-xs text-[10px]">\u4ECA\u5929\u505A\u4E86\u5565</button>
|
|
297
|
-
<button type="button" id="ma-quick-projects" class="btn btn-ghost btn-xs text-[10px]">\u9879\u76EE\u6982\u51B5</button>
|
|
298
|
-
</div>
|
|
299
|
-
<div id="ma-resume-bar" class="hidden px-3 py-1.5 border-b border-primary/30 bg-primary/10 text-[11px] flex items-center gap-2 flex-shrink-0">
|
|
300
|
-
<span id="ma-resume-text" class="flex-1 min-w-0 truncate text-base-content/70"></span>
|
|
301
|
-
<button type="button" id="ma-resume-focus" class="btn btn-primary btn-xs">\u7EE7\u7EED\u804A</button>
|
|
302
|
-
</div>
|
|
303
|
-
<div id="ma-messages" class="flex-1 overflow-y-auto p-3 flex flex-col gap-2"></div>
|
|
304
|
-
<div id="ma-attach-bar" class="hidden px-3 pt-1 flex flex-wrap gap-1"></div>
|
|
305
|
-
<div class="flex gap-2 items-end px-3 py-2 border-t border-base-300 bg-base-200 flex-shrink-0">
|
|
306
|
-
<label class="btn btn-ghost btn-sm px-2 cursor-pointer" title="\u6DFB\u52A0\u56FE\u7247 (VLM)" style="height:40px">
|
|
307
|
-
\u{1F5BC}
|
|
308
|
-
<input type="file" id="ma-image-input" accept="image/*" multiple class="hidden">
|
|
309
|
-
</label>
|
|
310
|
-
<textarea id="ma-input" rows="1" placeholder="\u7EE7\u7EED\u63D0\u95EE\u2026 (\u2318\u21B5 \u53D1\u9001) \xB7 \u5386\u53F2\u4F1A\u8BDD\u4F1A\u5E26\u7740\u4E0A\u4E0B\u6587"
|
|
311
|
-
class="textarea textarea-bordered flex-1 text-sm resize-none leading-relaxed"
|
|
312
|
-
style="min-height:40px;max-height:120px"
|
|
313
|
-
name="meta-agent-message"
|
|
314
|
-
autocomplete="off"
|
|
315
|
-
autocorrect="off"
|
|
316
|
-
autocapitalize="off"
|
|
317
|
-
spellcheck="false"
|
|
318
|
-
data-1p-ignore="true"
|
|
319
|
-
data-lpignore="true"
|
|
320
|
-
data-form-type="other"
|
|
321
|
-
data-bwignore="true"></textarea>
|
|
322
|
-
<button type="button" id="ma-send" class="btn btn-primary btn-sm" style="height:40px">\u53D1\u9001</button>
|
|
323
|
-
</div>
|
|
324
|
-
</div>
|
|
325
|
-
|
|
326
|
-
<div id="ma-mode-history" class="meta-agent-body hidden flex-col min-h-0 flex-1">
|
|
327
|
-
<div class="flex items-center gap-2 px-3 py-2 border-b border-base-300 flex-shrink-0">
|
|
328
|
-
<input id="ma-history-search" type="search" placeholder="\u641C\u7D22\u6807\u9898 / \u9884\u89C8\u2026"
|
|
329
|
-
class="input input-xs input-bordered flex-1 text-xs" autocomplete="off">
|
|
330
|
-
<button type="button" id="ma-history-refresh" class="btn btn-ghost btn-xs">\u21BB</button>
|
|
331
|
-
<button type="button" id="ma-history-new" class="btn btn-primary btn-xs">\uFF0B \u65B0\u5BF9\u8BDD</button>
|
|
332
|
-
</div>
|
|
333
|
-
<div id="ma-history-list" class="flex-1 overflow-y-auto p-2 flex flex-col gap-1"></div>
|
|
334
|
-
<p class="text-[10px] text-base-content/40 px-3 py-2 border-t border-base-300 flex-shrink-0">
|
|
335
|
-
\u70B9\u51FB\u4F1A\u8BDD\u53EF\u67E5\u770B\u5B8C\u6574\u8BB0\u5F55\uFF1B\u53D1\u9001\u65B0\u6D88\u606F\u5373 resume\uFF08\u4FDD\u7559 tool / VLM \u4E0A\u4E0B\u6587\uFF09\u3002
|
|
336
|
-
</p>
|
|
337
|
-
</div>
|
|
338
|
-
|
|
339
|
-
<div id="ma-mode-report" class="meta-agent-body hidden flex-col min-h-0 flex-1">
|
|
340
|
-
<div class="flex items-center gap-2 px-3 py-2 border-b border-base-300 flex-shrink-0 flex-wrap">
|
|
341
|
-
<select id="ma-report-days" class="select select-xs select-bordered">
|
|
342
|
-
<option value="1">\u4ECA\u5929</option>
|
|
343
|
-
<option value="3">\u8FD1 3 \u5929</option>
|
|
344
|
-
<option value="7">\u8FD1 7 \u5929</option>
|
|
345
|
-
</select>
|
|
346
|
-
<button type="button" id="ma-gen-report" class="btn btn-primary btn-xs">\u751F\u6210\u62A5\u544A</button>
|
|
347
|
-
<button type="button" id="ma-load-reports" class="btn btn-ghost btn-xs">\u5386\u53F2</button>
|
|
348
|
-
<label class="flex items-center gap-1 text-[11px] ml-auto cursor-pointer select-none">
|
|
349
|
-
<input type="checkbox" id="ma-auto-report" class="checkbox checkbox-xs">
|
|
350
|
-
\u81EA\u52A8\u65E5\u62A5
|
|
351
|
-
</label>
|
|
352
|
-
<select id="ma-auto-hour" class="select select-xs select-bordered w-16" title="\u81EA\u52A8\u751F\u6210\u65F6\u523B">
|
|
353
|
-
${Array.from({length:24},(e,t)=>`<option value="${t}">${String(t).padStart(2,"0")}:00</option>`).join("")}
|
|
354
|
-
</select>
|
|
355
|
-
</div>
|
|
356
|
-
<div id="ma-report-list" class="hidden border-b border-base-300 max-h-28 overflow-y-auto px-2 py-1"></div>
|
|
357
|
-
<div id="ma-report-body" class="flex-1 overflow-y-auto p-4">
|
|
358
|
-
<div class="text-sm text-base-content/45 text-center py-10">
|
|
359
|
-
\u70B9\u51FB\u300C\u751F\u6210\u62A5\u544A\u300D\u6C47\u603B\u4F1A\u8BDD\u4E0E\u9879\u76EE\u6D3B\u52A8\uFF0C\u6216\u5728\u5BF9\u8BDD\u91CC\u95EE\u300C\u6211\u4ECA\u5929\u505A\u4E86\u4EC0\u4E48\u300D\u3002
|
|
360
|
-
</div>
|
|
361
|
-
</div>
|
|
362
|
-
</div>
|
|
363
|
-
|
|
364
|
-
<div id="ma-mode-config" class="meta-agent-body hidden flex-col min-h-0 flex-1 overflow-y-auto p-4 gap-3">
|
|
365
|
-
<p class="text-xs text-base-content/50">OpenAI \u517C\u5BB9 API\uFF08DeepSeek / OpenAI / \u672C\u5730\u7F51\u5173\u7B49\uFF09\u3002Key \u4FDD\u5B58\u5728\u670D\u52A1\u7AEF <code>.ai_config.json</code>\u3002</p>
|
|
366
|
-
<label class="form-control">
|
|
367
|
-
<span class="label-text text-xs">API URL</span>
|
|
368
|
-
<input id="ma-cfg-url" class="input input-sm input-bordered font-mono text-xs" placeholder="https://api.deepseek.com/chat/completions">
|
|
369
|
-
</label>
|
|
370
|
-
<label class="form-control">
|
|
371
|
-
<span class="label-text text-xs">API Key</span>
|
|
372
|
-
<input id="ma-cfg-key" type="password" class="input input-sm input-bordered" placeholder="\u7559\u7A7A\u5219\u4E0D\u4FEE\u6539">
|
|
373
|
-
</label>
|
|
374
|
-
<label class="form-control">
|
|
375
|
-
<span class="label-text text-xs">\u4E3B\u6A21\u578B</span>
|
|
376
|
-
<input id="ma-cfg-model" class="input input-sm input-bordered font-mono text-xs" placeholder="deepseek-chat">
|
|
377
|
-
</label>
|
|
378
|
-
<div class="divider text-xs my-1">VLM \u591A\u6A21\u6001\uFF08\u53EF\u7559\u7A7A\u5171\u7528\u4E0A\u65B9\uFF09</div>
|
|
379
|
-
<label class="form-control">
|
|
380
|
-
<span class="label-text text-xs">VLM URL</span>
|
|
381
|
-
<input id="ma-cfg-vlm-url" class="input input-sm input-bordered font-mono text-xs" placeholder="\u53EF\u9009">
|
|
382
|
-
</label>
|
|
383
|
-
<label class="form-control">
|
|
384
|
-
<span class="label-text text-xs">VLM Key</span>
|
|
385
|
-
<input id="ma-cfg-vlm-key" type="password" class="input input-sm input-bordered" placeholder="\u53EF\u9009">
|
|
386
|
-
</label>
|
|
387
|
-
<label class="form-control">
|
|
388
|
-
<span class="label-text text-xs">VLM \u6A21\u578B</span>
|
|
389
|
-
<input id="ma-cfg-vlm-model" class="input input-sm input-bordered font-mono text-xs" placeholder="gpt-4o-mini / \u2026">
|
|
390
|
-
</label>
|
|
391
|
-
<label class="form-control">
|
|
392
|
-
<span class="label-text text-xs">System Prompt</span>
|
|
393
|
-
<textarea id="ma-cfg-system" class="textarea textarea-bordered textarea-sm text-xs min-h-[80px]"></textarea>
|
|
394
|
-
</label>
|
|
395
|
-
<div class="flex gap-2">
|
|
396
|
-
<button type="button" id="ma-cfg-save" class="btn btn-primary btn-sm">\u4FDD\u5B58</button>
|
|
397
|
-
<span id="ma-cfg-status" class="text-xs text-base-content/50 self-center"></span>
|
|
398
|
-
</div>
|
|
399
|
-
</div>
|
|
400
|
-
</div>
|
|
401
|
-
</div>`}function oe(e){Oe=e;let t=(0,n.$)("ma-mode-chat"),s=(0,n.$)("ma-mode-history"),o=(0,n.$)("ma-mode-report"),a=(0,n.$)("ma-mode-config");for(let[r,c]of[[t,"chat"],[s,"history"],[o,"report"],[a,"config"]]){if(!r)continue;let u=e===c;r.classList.toggle("hidden",!u),r.classList.toggle("flex",u)}let i=(r,c)=>{let u=(0,n.$)(r);u&&(u.classList.toggle("border-primary/40",c),u.classList.toggle("text-primary",c))};i("ma-btn-chat",e==="chat"),i("ma-btn-history",e==="history"),i("ma-btn-report",e==="report"),e==="history"&&et()}function Ot(){let e=(0,n.$)("ma-session-title"),t=(0,n.$)("ma-resume-bar"),s=(0,n.$)("ma-resume-text");if(!q){e&&(e.textContent="\u65B0\u5BF9\u8BDD"),t?.classList.add("hidden");return}let o=ee?.title||q.slice(0,8),a=No(ee?.updatedAt);if(e&&(e.textContent=a?`${o} \xB7 ${a}`:o),t&&s){t.classList.remove("hidden");let i=ee?.messageCount;s.textContent=i?`\u5DF2\u52A0\u8F7D\u5386\u53F2 \xB7 ${i} \u6761\u6D88\u606F \xB7 \u53D1\u9001\u5373\u53EF\u7EE7\u7EED`:"\u5DF2\u52A0\u8F7D\u5386\u53F2\u4F1A\u8BDD \xB7 \u53D1\u9001\u5373\u53EF\u7EE7\u7EED"}}function ps(e="chat"){let t=(0,n.$)("meta-agent-panel");if(t){if(t.classList.remove("hidden"),t.setAttribute("aria-hidden","false"),Mn=!0,oe(e),e==="chat"){De();let s=localStorage.getItem(Ue);!q&&s?_e(s,{silent:!0}).catch(()=>{localStorage.removeItem(Ue)}):Ot()}e==="history"&&De().then(()=>et()),e==="report"&&Ro(),e==="config"&&Bo(),setTimeout(()=>{Oe==="chat"&&(0,n.$)("ma-input")?.focus(),Oe==="history"&&(0,n.$)("ma-history-search")?.focus()},50)}}function _o(){let e=(0,n.$)("meta-agent-panel");e&&(e.classList.add("hidden"),e.setAttribute("aria-hidden","true"),Mn=!1)}function Ho(){Mn?_o():ps(Oe==="config"?"chat":Oe)}function H(e,t,s={}){let o=(0,n.$)("ma-messages");if(!o)return null;let a=document.createElement("div");return a.className=`ma-msg ma-msg-${e}`,e==="user"?a.innerHTML=`<div class="ma-bubble user">${(0,n.esc)(t)}</div>`:e==="assistant"?(a.innerHTML=`<div class="ma-bubble assistant md">${bt(t)}</div>`,xt(a)):e==="error"?a.innerHTML=`<div class="ma-bubble error">${(0,n.esc)(t)}</div>`:e==="system"?a.innerHTML=`<div class="ma-sys">${(0,n.esc)(t)}</div>`:e==="tool"&&(a.innerHTML=`<div class="ma-tool" data-tool-id="${(0,n.esc)(s.id||"")}">
|
|
402
|
-
<span class="ma-tool-name">${(0,n.esc)(s.name||"tool")}</span>
|
|
403
|
-
<span class="ma-tool-phase">${(0,n.esc)(s.phase||"")}</span>
|
|
404
|
-
<pre class="ma-tool-args">${(0,n.esc)(JSON.stringify(s.args||{},null,0).slice(0,200))}</pre>
|
|
405
|
-
</div>`),o.appendChild(a),o.scrollTop=o.scrollHeight,a}function Co(e,t){let s=e?.querySelector(".ma-bubble");s&&(s.innerHTML=bt(t),xt(s),(0,n.$)("ma-messages").scrollTop=(0,n.$)("ma-messages").scrollHeight)}function Po(e,t,s){let o=(0,n.$)("ma-messages");if(!o)return;let a=o.querySelector(`[data-tool-id="${CSS.escape(e)}"]`);if(!a)return;a.classList.toggle("done",t==="done"),a.classList.toggle("error",t==="error");let i=a.querySelector(".ma-tool-phase");if(i&&(i.textContent=t),s&&t!=="start"){let r=a.querySelector(".ma-tool-result");r||(r=document.createElement("pre"),r.className="ma-tool-result",a.appendChild(r)),r.textContent=JSON.stringify(s,null,0).slice(0,500)}}function fi(e){let t=(0,n.$)("ma-messages");if(!t)return;let s=document.createElement("div");s.className="ma-approval",s.dataset.approvalId=e.id;let o=e.args?.command??"";s.innerHTML=`
|
|
406
|
-
<div class="ma-approval-label">\u26A0 Agent \u60F3\u8981\u6267\u884C\u547D\u4EE4\uFF0C\u662F\u5426\u5141\u8BB8\uFF1F</div>
|
|
407
|
-
<pre class="ma-approval-cmd">${(0,n.esc)(o)}</pre>
|
|
408
|
-
<div class="ma-approval-actions">
|
|
409
|
-
<button type="button" class="btn btn-xs btn-error" data-approve="0">\u62D2\u7EDD</button>
|
|
410
|
-
<button type="button" class="btn btn-xs btn-success" data-approve="1">\u5141\u8BB8\u6267\u884C</button>
|
|
411
|
-
</div>`,s.querySelectorAll("[data-approve]").forEach(a=>{a.addEventListener("click",async()=>{let i=a.dataset.approve==="1";s.querySelectorAll("button").forEach(r=>{r.disabled=!0}),s.querySelector(".ma-approval-label").textContent=i?"\u2713 \u5DF2\u5141\u8BB8\uFF0C\u6B63\u5728\u6267\u884C\u2026":"\u2717 \u5DF2\u62D2\u7EDD";try{await h("POST","/api/ai/approve",{id:e.id,approved:i})}catch(r){s.querySelector(".ma-approval-label").textContent=`\u786E\u8BA4\u5931\u8D25\uFF1A${r.message}`}})}),t.appendChild(s),t.scrollTop=t.scrollHeight}function mi(){return Io+=1,`img${Io}`}function hi(e,t){if(!e)return;let s=e.selectionStart??e.value.length,o=e.selectionEnd??e.value.length;e.value=e.value.slice(0,s)+t+e.value.slice(o),e.selectionStart=e.selectionEnd=s+t.length,e.dispatchEvent(new Event("input"))}async function Ao(e,t){let s=lt({"x-filename":t||`paste-${Date.now()}.png`});delete s["Content-Type"];let o=await fetch("/api/upload-image",{method:"POST",headers:s,body:e});if(!o.ok){let l=await o.json().catch(()=>({}));throw new Error(l.error||`Upload failed: ${o.status}`)}let{path:a}=await o.json();if(!a)throw new Error("upload returned no path");let i=mi(),r=`[${i}](${a})`,c=(0,n.$)("ma-input");hi(c,(c?.value&&!c.value.endsWith(`
|
|
412
|
-
`)&&c.value.length?`
|
|
413
|
-
`:"")+r+`
|
|
414
|
-
`);let u="";try{u=URL.createObjectURL(e)}catch{}return Ze.push({path:a,previewUrl:u,label:i,ref:r}),us(),{path:a,ref:r,label:i}}function us(){let e=(0,n.$)("ma-attach-bar");if(e){if(!Ze.length){e.classList.add("hidden"),e.innerHTML="";return}e.classList.remove("hidden"),e.innerHTML=Ze.map((t,s)=>`
|
|
415
|
-
<div class="ma-thumb relative" title="${(0,n.esc)(t.ref||t.path)}">
|
|
416
|
-
${t.previewUrl?`<img src="${t.previewUrl}" alt="${(0,n.esc)(t.label||"img")}">`:`<span class="ma-thumb-path">${(0,n.esc)(t.label||"img")}</span>`}
|
|
417
|
-
<span class="ma-thumb-label">${(0,n.esc)(t.label||"")}</span>
|
|
418
|
-
<button type="button" class="ma-thumb-x" data-rm-img="${s}">\u2715</button>
|
|
419
|
-
</div>`).join(""),e.querySelectorAll("[data-rm-img]").forEach(t=>{t.addEventListener("click",()=>{let s=parseInt(t.dataset.rmImg,10),o=Ze[s];if(o?.previewUrl)try{URL.revokeObjectURL(o.previewUrl)}catch{}let a=(0,n.$)("ma-input");a&&o?.ref&&(a.value=a.value.split(o.ref).join("").replace(/\n{3,}/g,`
|
|
420
|
-
|
|
421
|
-
`),a.dispatchEvent(new Event("input"))),Ze.splice(s,1),us()})})}}function No(e){if(!e)return"";let t=new Date(e);if(Number.isNaN(t.getTime()))return"";let s=String(t.getMonth()+1).padStart(2,"0"),o=String(t.getDate()).padStart(2,"0"),a=String(t.getHours()).padStart(2,"0"),i=String(t.getMinutes()).padStart(2,"0");return`${s}-${o} ${a}:${i}`}async function De(){try{Ln=(await h("GET","/api/ai/sessions")).sessions||[]}catch{Ln=[]}return Ln}function et(){let e=(0,n.$)("ma-history-list");if(!e)return;let t=(Do||"").trim().toLowerCase(),s=Ln;if(t&&(s=s.filter(o=>`${o.title||""} ${o.preview||""} ${o.id||""}`.toLowerCase().includes(t))),!s.length){e.innerHTML=`<div class="text-center text-xs text-base-content/40 py-10">
|
|
422
|
-
${t?"\u6CA1\u6709\u5339\u914D\u7684\u4F1A\u8BDD":"\u8FD8\u6CA1\u6709\u5386\u53F2\u5BF9\u8BDD\u3002\u53D1\u4E00\u6761\u6D88\u606F\u540E\u4F1A\u51FA\u73B0\u5728\u8FD9\u91CC\u3002"}
|
|
423
|
-
</div>`;return}e.innerHTML=s.map(o=>{let a=o.id===q,i=No(o.updatedAt)||"",r=o.messageCount||0;return`
|
|
424
|
-
<div class="ma-hist-item ${a?"active":""}" data-hist-id="${(0,n.esc)(o.id)}">
|
|
425
|
-
<div class="flex items-start gap-2">
|
|
426
|
-
<button type="button" class="ma-hist-open flex-1 min-w-0 text-left" data-hist-open="${(0,n.esc)(o.id)}">
|
|
427
|
-
<div class="font-medium text-xs truncate">${(0,n.esc)(o.title||o.id.slice(0,8))}</div>
|
|
428
|
-
<div class="text-[10px] text-base-content/45 mt-0.5 line-clamp-2">${(0,n.esc)(o.preview||"\uFF08\u65E0\u9884\u89C8\uFF09")}</div>
|
|
429
|
-
<div class="text-[10px] text-base-content/35 mt-1">${(0,n.esc)(i)}${r?` \xB7 ${r} \u6761`:""}${a?" \xB7 \u5F53\u524D":""}</div>
|
|
430
|
-
</button>
|
|
431
|
-
<div class="flex flex-col gap-0.5 flex-shrink-0">
|
|
432
|
-
<button type="button" class="btn btn-ghost btn-xs px-1" data-hist-resume="${(0,n.esc)(o.id)}" title="\u6253\u5F00\u5E76\u7EE7\u7EED">\u21B5</button>
|
|
433
|
-
<button type="button" class="btn btn-ghost btn-xs px-1 text-error" data-hist-del="${(0,n.esc)(o.id)}" title="\u5220\u9664">\u2715</button>
|
|
434
|
-
</div>
|
|
435
|
-
</div>
|
|
436
|
-
</div>`}).join(""),e.querySelectorAll("[data-hist-open]").forEach(o=>{o.addEventListener("click",async()=>{await _e(o.dataset.histOpen),oe("chat"),(0,n.$)("ma-input")?.focus()})}),e.querySelectorAll("[data-hist-resume]").forEach(o=>{o.addEventListener("click",async a=>{a.stopPropagation(),await _e(o.dataset.histResume),oe("chat"),(0,n.$)("ma-input")?.focus()})}),e.querySelectorAll("[data-hist-del]").forEach(o=>{o.addEventListener("click",async a=>{a.stopPropagation();let i=o.dataset.histDel;if(confirm("\u5220\u9664\u8BE5\u5386\u53F2\u4F1A\u8BDD\uFF1F\u4E0D\u53EF\u6062\u590D\u3002"))try{await h("DELETE",`/api/ai/sessions/${i}`),q===i&&(q=null,ee=null,localStorage.removeItem(Ue),_e(null)),await De(),et()}catch(r){alert(r.message)}})})}function jo(e){return typeof e=="string"?e:Array.isArray(e)&&e.map(t=>typeof t=="string"?t:t?.type==="text"?t.text||"":t?.type==="image_url"?"[\u56FE\u7247]":"").filter(Boolean).join(`
|
|
437
|
-
`)||"[\u2026]"}function bi(e){let t=(0,n.$)("ma-messages");if(t){if(t.innerHTML="",!e?.length){H("system","\u6B64\u4F1A\u8BDD\u6682\u65E0\u6D88\u606F\u3002");return}for(let s of e){if(s.role==="user"){H("user",jo(s.content));continue}if(s.role==="assistant"){if(s.tool_calls?.length)for(let o of s.tool_calls){let a=o.function?.name||o.name||"tool",i={};try{i=JSON.parse(o.function?.arguments||o.arguments||"{}")}catch{}H("tool","",{name:a,args:i,phase:"history",id:o.id||a})}s.content&&H("assistant",typeof s.content=="string"?s.content:jo(s.content));continue}if(s.role==="tool"){let o=s.content;try{o=JSON.parse(s.content)}catch{}let a=s.tool_call_id||"";if(a)Po(a,"done",o);else{H("tool","",{name:"tool_result",args:{},phase:"done",id:`tr_${Math.random().toString(36).slice(2,8)}`});let i=t.querySelectorAll(".ma-tool"),r=i[i.length-1];if(r){r.classList.add("done");let c=r.querySelector(".ma-tool-result");c||(c=document.createElement("pre"),c.className="ma-tool-result",r.appendChild(c)),c.textContent=typeof o=="string"?o.slice(0,500):JSON.stringify(o,null,0).slice(0,500)}}}}t.scrollTop=t.scrollHeight}}async function xi(){if(!q){await _e(null);return}if(confirm("\u5220\u9664\u5F53\u524D Meta Agent \u4F1A\u8BDD\uFF1F\u6B64\u64CD\u4F5C\u4E0D\u53EF\u6062\u590D\u3002"))try{await h("DELETE",`/api/ai/sessions/${q}`),q=null,ee=null,localStorage.removeItem(Ue),await De(),await _e(null),Oe==="history"&&et()}catch(e){H("error",e.message)}}async function _e(e,t={}){if(!e){q=null,ee=null,localStorage.removeItem(Ue);let s=(0,n.$)("ma-messages");return s&&(s.innerHTML=""),H("system","\u65B0\u5BF9\u8BDD \u2014 \u8D34\u56FE \u2192 [imgN](\u8DEF\u5F84)\uFF1B\u9700\u8981\u770B\u56FE\u65F6\u8C03\u7528 VLM\u3002\u5386\u53F2\u5728\u300C\u5386\u53F2\u300D\u9875\uFF0C\u53EF\u968F\u65F6 resume\u3002"),Ot(),null}try{let s=await h("GET",`/api/ai/sessions/${e}`);return q=s.id,ee={id:s.id,title:s.title,updatedAt:s.updatedAt,messageCount:(s.messages||[]).length},localStorage.setItem(Ue,s.id),bi(s.messages||[]),t.silent||H("system","\u2191 \u5386\u53F2\u5DF2\u52A0\u8F7D\u3002\u5728\u4E0B\u65B9\u7EE7\u7EED\u8F93\u5165\u5373\u53EF resume\uFF08\u670D\u52A1\u7AEF\u4FDD\u7559\u5B8C\u6574 tool / VLM \u4E0A\u4E0B\u6587\uFF09\u3002"),Ot(),Oe==="history"&&et(),s}catch(s){throw t.silent||H("error",s.message),s}}async function Sn(){if(ht)return;let e=(0,n.$)("ma-input"),t=(e?.value||"").trim();if(!t)return;ht=!0,(0,n.$)("ma-send").disabled=!0,H("user",t),e.value="",e.style.height="";for(let a of Ze)if(a.previewUrl)try{URL.revokeObjectURL(a.previewUrl)}catch{}Ze=[],us();let s=null,o="";try{await ds("/api/ai/chat",{message:t,sessionId:q},{session:a=>{q=a.id,ee={...ee||{},id:a.id,title:a.title||ee?.title||t.slice(0,40),updatedAt:Date.now()},localStorage.setItem(Ue,a.id),Ot()},content:a=>{o=a.content||"",s||(s=H("assistant","")),Co(s,o)},tool:a=>{a.phase==="start"?H("tool","",{name:a.name,args:a.args,phase:"start",id:a.id}):Po(a.id,a.phase,a.result)},approval:a=>fi(a),done:a=>{a.sessionId&&(q=a.sessionId,localStorage.setItem(Ue,a.sessionId)),(a.title||a.sessionId)&&(ee={id:q,title:a.title||ee?.title||"\u5BF9\u8BDD",updatedAt:Date.now(),messageCount:(ee?.messageCount||0)+2}),a.content&&s&&Co(s,a.content),Ot(),De()},error:a=>{H("error",a.message||"error")}})}catch(a){H("error",a.message)}finally{ht=!1,(0,n.$)("ma-send").disabled=!1}}async function gi(){if(ht)return;ht=!0;let e=parseInt((0,n.$)("ma-report-days")?.value||"1",10),t=(0,n.$)("ma-report-body");t.innerHTML=`<div class="text-sm text-base-content/50 py-6 text-center">\u6B63\u5728\u6C47\u603B\u8FD1 ${e} \u5929\u6D3B\u52A8\u2026</div>`,(0,n.$)("ma-gen-report").disabled=!0;let s="",o=document.createElement("div");o.className="ma-report-tools flex flex-col gap-1 mb-3";try{await ds("/api/ai/report",{days:e},{content:a=>{s=a.content||"",t.innerHTML="",t.appendChild(o);let i=document.createElement("div");i.className="md ma-report-md",i.innerHTML=bt(s),xt(i),t.appendChild(i)},tool:a=>{if(a.phase==="start"){let i=document.createElement("div");i.className="ma-tool",i.dataset.toolId=a.id||a.name,i.innerHTML=`<span class="ma-tool-name">${(0,n.esc)(a.name)}</span> <span class="ma-tool-phase">start</span>`,o.appendChild(i)}else{let i=o.querySelector(`[data-tool-id="${CSS.escape(a.id||a.name)}"]`);i&&(i.classList.add(a.phase==="error"?"error":"done"),i.querySelector(".ma-tool-phase").textContent=a.phase)}},done:a=>{if(a.content){t.innerHTML="",t.appendChild(o);let i=document.createElement("div");i.className="md ma-report-md",i.innerHTML=bt(a.content),xt(i),t.appendChild(i)}},error:a=>{t.innerHTML=`<div class="alert alert-error text-sm">${(0,n.esc)(a.message)}</div>`}})}catch(a){t.innerHTML=`<div class="alert alert-error text-sm">${(0,n.esc)(a.message)}</div>`}finally{ht=!1,(0,n.$)("ma-gen-report").disabled=!1}}async function Ro(){try{let e=new Date,t=`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,"0")}-${String(e.getDate()).padStart(2,"0")}`,s=await h("GET",`/api/ai/reports/${t}`);if(s?.markdown){let o=(0,n.$)("ma-report-body");o.innerHTML=`
|
|
438
|
-
<div class="text-[10px] text-base-content/40 mb-2">${(0,n.esc)(s.title||t)} \xB7 ${(0,n.esc)(s.generatedAt||"")}</div>
|
|
439
|
-
<div class="md ma-report-md">${bt(s.markdown)}</div>`,xt(o)}}catch{}}async function vi(){let e=(0,n.$)("ma-report-list");try{let t=await h("GET","/api/ai/reports");e.classList.remove("hidden");let s=t.reports||[];if(!s.length){e.innerHTML='<div class="text-xs text-base-content/40 px-2 py-1">\u6682\u65E0\u5386\u53F2\u62A5\u544A</div>';return}e.innerHTML=s.map(o=>`
|
|
440
|
-
<button type="button" class="ma-report-item w-full text-left px-2 py-1 rounded hover:bg-base-200 text-xs"
|
|
441
|
-
data-day="${(0,n.esc)(o.day)}">
|
|
442
|
-
<span class="font-semibold">${(0,n.esc)(o.day)}</span>
|
|
443
|
-
<span class="text-base-content/45 ml-1">${(0,n.esc)(o.title||"")}</span>
|
|
444
|
-
</button>`).join(""),e.querySelectorAll("[data-day]").forEach(o=>{o.addEventListener("click",async()=>{try{let a=await h("GET",`/api/ai/reports/${o.dataset.day}`),i=(0,n.$)("ma-report-body");i.innerHTML=`
|
|
445
|
-
<div class="text-[10px] text-base-content/40 mb-2">${(0,n.esc)(a.title||a.day)} \xB7 ${(0,n.esc)(a.generatedAt||"")}</div>
|
|
446
|
-
<div class="md ma-report-md">${bt(a.markdown)}</div>`,xt(i)}catch(a){(0,n.$)("ma-report-body").innerHTML=`<div class="alert alert-error text-sm">${(0,n.esc)(a.message)}</div>`}})})}catch(t){e.classList.remove("hidden"),e.innerHTML=`<div class="text-xs text-error px-2">${(0,n.esc)(t.message)}</div>`}}async function Bo(){try{let e=await h("GET","/api/ai/config");(0,n.$)("ma-cfg-url").value=e.url||"",(0,n.$)("ma-cfg-model").value=e.model||"",(0,n.$)("ma-cfg-key").placeholder=e.hasKey?"\u5DF2\u914D\u7F6E \xB7 \u7559\u7A7A\u4E0D\u4FEE\u6539":"API Key",(0,n.$)("ma-cfg-key").value="",(0,n.$)("ma-cfg-vlm-url").value=e.vlm_url||"",(0,n.$)("ma-cfg-vlm-model").value=e.vlm_model||"",(0,n.$)("ma-cfg-vlm-key").value="",(0,n.$)("ma-cfg-vlm-key").placeholder=e.hasVlmKey?"\u5DF2\u914D\u7F6E \xB7 \u7559\u7A7A\u4E0D\u4FEE\u6539":"\u53EF\u9009",(0,n.$)("ma-cfg-system").value=e.system||"",(0,n.$)("ma-auto-report")&&((0,n.$)("ma-auto-report").checked=!!e.auto_report),(0,n.$)("ma-auto-hour")&&((0,n.$)("ma-auto-hour").value=String(e.auto_report_hour??18)),Ut(e)}catch(e){(0,n.$)("ma-cfg-status").textContent=e.message}}async function yi(){let e=(0,n.$)("ma-cfg-status");e.textContent="\u4FDD\u5B58\u4E2D\u2026";try{let t={url:(0,n.$)("ma-cfg-url").value.trim(),model:(0,n.$)("ma-cfg-model").value.trim(),vlm_url:(0,n.$)("ma-cfg-vlm-url").value.trim(),vlm_model:(0,n.$)("ma-cfg-vlm-model").value.trim(),system:(0,n.$)("ma-cfg-system").value,auto_report:!!(0,n.$)("ma-auto-report")?.checked,auto_report_hour:parseInt((0,n.$)("ma-auto-hour")?.value||"18",10)},s=(0,n.$)("ma-cfg-key").value.trim(),o=(0,n.$)("ma-cfg-vlm-key").value.trim();s&&(t.key=s),o&&(t.vlm_key=o);let a=await h("PUT","/api/ai/config",t);e.textContent=a.hasKey?"\u5DF2\u4FDD\u5B58 \u2713":"\u5DF2\u4FDD\u5B58\uFF08\u672A\u8BBE\u7F6E Key\uFF09",(0,n.$)("ma-cfg-key").value="",(0,n.$)("ma-cfg-vlm-key").value="",Ut(a)}catch(t){e.textContent=t.message}}function Ut(e){if($n&&(clearInterval($n),$n=null),!e?.auto_report)return;let t=e.auto_report_hour??18;$n=setInterval(async()=>{if(!f.token)return;let s=new Date;if(s.getHours()!==t)return;let o=`${s.getFullYear()}-${String(s.getMonth()+1).padStart(2,"0")}-${String(s.getDate()).padStart(2,"0")}`;if(cs!==o&&!(s.getMinutes()>10))try{cs=o,localStorage.setItem("ai_last_auto_report",o),await ds("/api/ai/report",{days:e.auto_report_days||1},{done:()=>{console.info("[meta-agent] auto report saved for",o)},error:a=>console.warn("[meta-agent] auto report",a.message)})}catch(a){console.warn("[meta-agent] auto report failed",a),cs="",localStorage.removeItem("ai_last_auto_report")}},60*1e3)}function Oo(){(0,n.$)("meta-agent-panel")||(document.getElementById("root")?.insertAdjacentHTML("beforeend",ui()),document.querySelectorAll("[data-ma-close]").forEach(e=>{e.addEventListener("click",_o)}),(0,n.$)("ma-btn-chat")?.addEventListener("click",()=>oe("chat")),(0,n.$)("ma-btn-history")?.addEventListener("click",async()=>{await De(),oe("history")}),(0,n.$)("ma-open-history")?.addEventListener("click",async()=>{await De(),oe("history")}),(0,n.$)("ma-btn-report")?.addEventListener("click",()=>{oe("report"),Ro()}),(0,n.$)("ma-btn-config")?.addEventListener("click",()=>{oe("config"),Bo()}),(0,n.$)("ma-send")?.addEventListener("click",Sn),(0,n.$)("ma-input")?.addEventListener("keydown",e=>{(e.metaKey||e.ctrlKey)&&e.key==="Enter"&&(e.preventDefault(),Sn())}),(0,n.$)("ma-input")?.addEventListener("input",()=>{let e=(0,n.$)("ma-input");e.style.height="auto",e.style.height=Math.min(e.scrollHeight,120)+"px"}),(0,n.$)("ma-new-chat")?.addEventListener("click",()=>{_e(null),oe("chat")}),(0,n.$)("ma-del-session")?.addEventListener("click",xi),(0,n.$)("ma-resume-focus")?.addEventListener("click",()=>{(0,n.$)("ma-input")?.focus()}),(0,n.$)("ma-history-refresh")?.addEventListener("click",async()=>{await De(),et()}),(0,n.$)("ma-history-new")?.addEventListener("click",()=>{_e(null),oe("chat")}),(0,n.$)("ma-history-search")?.addEventListener("input",e=>{Do=e.target.value||"",et()}),(0,n.$)("ma-quick-today")?.addEventListener("click",()=>{oe("chat"),(0,n.$)("ma-input").value="\u6839\u636E\u6211\u6700\u8FD1\u7684\u4F1A\u8BDD\uFF0C\u603B\u7ED3\u4ECA\u5929\u6211\u505A\u4E86\u4EC0\u4E48\uFF1F\u6309\u9879\u76EE\u5217\u51FA\u4EAE\u70B9\u4E0E\u672A\u5B8C\u6210\u4E8B\u9879\u3002",Sn()}),(0,n.$)("ma-quick-projects")?.addEventListener("click",()=>{oe("chat"),(0,n.$)("ma-input").value="\u5217\u51FA\u6700\u8FD1 14 \u5929\u6D3B\u8DC3\u7684\u9879\u76EE\uFF0C\u6BCF\u4E2A\u9879\u76EE\u4E00\u53E5\u8BDD\u8BF4\u660E\u6700\u8FD1\u5728\u505A\u4EC0\u4E48\u3002",Sn()}),(0,n.$)("ma-gen-report")?.addEventListener("click",gi),(0,n.$)("ma-load-reports")?.addEventListener("click",vi),(0,n.$)("ma-cfg-save")?.addEventListener("click",yi),(0,n.$)("ma-auto-report")?.addEventListener("change",async()=>{try{let e=await h("PUT","/api/ai/config",{auto_report:!!(0,n.$)("ma-auto-report").checked,auto_report_hour:parseInt((0,n.$)("ma-auto-hour")?.value||"18",10)});Ut(e)}catch{}}),(0,n.$)("ma-auto-hour")?.addEventListener("change",async()=>{try{let e=await h("PUT","/api/ai/config",{auto_report:!!(0,n.$)("ma-auto-report")?.checked,auto_report_hour:parseInt((0,n.$)("ma-auto-hour").value||"18",10)});Ut(e)}catch{}}),(0,n.$)("ma-image-input")?.addEventListener("change",async e=>{let t=[...e.target.files||[]],s=(0,n.$)("ma-input");s&&(s.disabled=!0);try{for(let o of t)if(o.type.startsWith("image/")){if(o.size>12*1024*1024){H("error",`${o.name} \u8FC7\u5927 (>12MB)`);continue}await Ao(o,o.name||`upload-${Date.now()}.png`)}}catch(o){H("error",`\u56FE\u7247\u4E0A\u4F20\u5931\u8D25: ${o.message}`)}finally{e.target.value="",s&&(s.disabled=!1,s.focus())}}),document.addEventListener("paste",async e=>{if(!Mn||Oe!=="chat")return;let t=(0,n.$)("ma-input"),s=(0,n.$)("meta-agent-panel"),o=document.activeElement;if(!s||!o||!s.contains(o))return;let i=[...e.clipboardData?.items||[]].filter(r=>r.type.startsWith("image/"));if(i.length){e.preventDefault(),t&&(t.disabled=!0);try{for(let r of i){let c=r.getAsFile();if(!c)continue;let u=(r.type.split("/")[1]||"png").replace(/[^a-z0-9]/gi,"")||"png";await Ao(c,`paste-${Date.now()}.${u}`)}}catch(r){H("error",`\u7C98\u8D34\u56FE\u7247\u5931\u8D25: ${r.message}`)}finally{t&&(t.disabled=!1,t.focus())}}}),_e(null,{silent:!0}))}function Ft(){f.token&&(k.peek()&&!It()||(De(),h("GET","/api/ai/config").then(Ut).catch(()=>{})))}I();var Uo=()=>`
|
|
447
|
-
<div id="auth-screen" class="${f.token?"hidden":""} flex items-center justify-center h-full p-4" ${f.token?'style="display:none"':""}>
|
|
448
|
-
<div class="card bg-base-200 border border-base-300 w-full max-w-sm shadow-xl">
|
|
449
|
-
<div class="card-body gap-3 p-8">
|
|
450
|
-
<h1 class="text-xl font-bold">\u{1F916} Agent UI</h1>
|
|
451
|
-
<p id="auth-subtitle" class="text-sm text-base-content/60 mb-1">Claude \xB7 Codex \xB7 Grok</p>
|
|
452
|
-
<label class="form-control">
|
|
453
|
-
<div class="label py-1"><span class="label-text text-xs uppercase tracking-wide">Username</span></div>
|
|
454
|
-
<input id="auth-username" type="text" class="input input-bordered input-sm" placeholder="username" autocomplete="username">
|
|
455
|
-
</label>
|
|
456
|
-
<label class="form-control">
|
|
457
|
-
<div class="label py-1"><span class="label-text text-xs uppercase tracking-wide">Password</span></div>
|
|
458
|
-
<input id="auth-password" type="password" class="input input-bordered input-sm" placeholder="\u2022\u2022\u2022\u2022\u2022\u2022" autocomplete="current-password">
|
|
459
|
-
</label>
|
|
460
|
-
<button id="auth-btn" class="btn btn-primary btn-sm mt-1" data-mode="login">Sign In</button>
|
|
461
|
-
<div id="auth-error" class="alert alert-error text-xs py-2 hidden"></div>
|
|
462
|
-
</div>
|
|
463
|
-
</div>
|
|
464
|
-
</div>`,Fo=(e={})=>{let t=!!e.restoreSession,s=f.token?"":"hidden",o=f.token?"display:flex":"display:none";return`
|
|
465
|
-
<div id="sidebar-overlay" class="hidden fixed inset-0 bg-black/50 z-[9]"></div>
|
|
466
|
-
<div id="app" class="${s} flex-col flex-1 overflow-hidden" style="${o}">
|
|
467
|
-
<div id="topbar" class="flex items-center gap-2 px-3 bg-base-200 border-b border-base-300 flex-shrink-0" style="height:44px">
|
|
468
|
-
<button id="hamburger" class="btn btn-ghost btn-sm px-2 text-lg">\u2630</button>
|
|
469
|
-
<span id="btn-home" class="font-semibold text-sm hidden sm:inline cursor-pointer hover:text-primary transition-colors select-none">\u{1F916} Agent UI</span>
|
|
470
|
-
<span class="text-base-300 hidden sm:inline">/</span>
|
|
471
|
-
<div id="machine-menu-wrap" class="relative hidden flex-shrink-0">
|
|
472
|
-
<button type="button" id="btn-machine-menu" class="btn btn-ghost btn-xs border border-base-300 font-mono gap-1 max-w-[11rem]"
|
|
473
|
-
title="\u673A\u5668\u5217\u8868 / \u5207\u6362">
|
|
474
|
-
<span id="topbar-machine-dot" class="w-1.5 h-1.5 rounded-full bg-success flex-shrink-0"></span>
|
|
475
|
-
<span id="topbar-machine-label" class="truncate">\u673A\u5668</span>
|
|
476
|
-
<span class="text-[10px] opacity-50">\u25BE</span>
|
|
477
|
-
</button>
|
|
478
|
-
<div id="machine-menu" class="hidden absolute left-0 top-full mt-1 z-50 w-72 max-h-80 overflow-y-auto
|
|
479
|
-
rounded-lg border border-base-300 bg-base-100 shadow-xl p-2">
|
|
480
|
-
<div class="flex items-center justify-between px-1 pb-1.5 mb-1 border-b border-base-300">
|
|
481
|
-
<span class="text-[10px] uppercase tracking-wide text-base-content/45 font-semibold">Machines</span>
|
|
482
|
-
<button type="button" id="btn-refresh-machines-menu" class="btn btn-ghost btn-xs px-1 text-[10px]">\u21BB</button>
|
|
483
|
-
</div>
|
|
484
|
-
<div id="machine-menu-list" class="flex flex-col gap-1"></div>
|
|
485
|
-
<p id="machine-menu-empty" class="text-xs text-base-content/40 px-1 py-2 hidden">\u6682\u65E0\u673A\u5668\u5728\u7EBF</p>
|
|
486
|
-
</div>
|
|
487
|
-
</div>
|
|
488
|
-
<div id="recent-menu-wrap" class="relative flex-shrink-0">
|
|
489
|
-
<button type="button" id="btn-recent-sessions" class="btn btn-ghost btn-xs border border-base-300 gap-1 min-h-8 px-2"
|
|
490
|
-
title="\u6700\u8FD1\u4F1A\u8BDD / \u5FEB\u901F\u8DF3\u8F6C" aria-haspopup="listbox" aria-expanded="false">
|
|
491
|
-
<span>\u6700\u8FD1</span>
|
|
492
|
-
<span class="text-[10px] opacity-50">\u25BE</span>
|
|
493
|
-
</button>
|
|
494
|
-
</div>
|
|
495
|
-
<span id="topbar-project" class="text-sm text-base-content/50 flex-1 truncate min-w-0">Select a session</span>
|
|
496
|
-
<span id="conn-dot" class="w-2 h-2 rounded-full bg-base-content/30 flex-shrink-0" title="\u8FDE\u63A5\u72B6\u6001"></span>
|
|
497
|
-
<div class="flex gap-2 topbar-actions">
|
|
498
|
-
<button id="btn-refresh-conn" class="btn btn-ghost btn-xs border border-base-300" title="\u91CD\u65B0\u8FDE\u63A5\u5E76\u540C\u6B65">\u21BB</button>
|
|
499
|
-
<button id="btn-pwa-install" class="btn btn-ghost btn-xs border border-base-300 hidden" title="\u5B89\u88C5\u5230\u4E3B\u5C4F\u5E55">\u5B89\u88C5</button>
|
|
500
|
-
<button id="btn-meta-agent" class="btn btn-ghost btn-xs border border-primary/40 text-primary" title="Meta Agent \u2014 \u62A5\u544A / \u95EE\u7B54 / VLM">\u2726 Agent</button>
|
|
501
|
-
<button id="btn-new-session" class="btn btn-ghost btn-xs border border-base-300">\uFF0B New</button>
|
|
502
|
-
<button id="btn-theme" class="btn btn-ghost btn-xs border border-base-300">\u{1F319}</button>
|
|
503
|
-
<button id="btn-settings" class="btn btn-ghost btn-xs border border-base-300">\u2699\uFE0F</button>
|
|
504
|
-
<button id="btn-logout" class="btn btn-ghost btn-xs border border-base-300">Sign out</button>
|
|
505
|
-
</div>
|
|
506
|
-
</div>
|
|
507
|
-
<!-- Fixed panel + backdrop: siblings of topbar so flex layout never clips them -->
|
|
508
|
-
<div id="recent-menu-backdrop" class="hidden fixed inset-0 z-[55] bg-black/40" aria-hidden="true"></div>
|
|
509
|
-
<div id="recent-menu" class="hidden fixed z-[60] w-80 max-h-[min(24rem,70vh)] overflow-y-auto
|
|
510
|
-
rounded-lg border border-base-300 bg-base-100 shadow-xl p-2"
|
|
511
|
-
role="listbox" aria-label="\u6700\u8FD1\u4F1A\u8BDD">
|
|
512
|
-
<div class="flex items-center justify-between px-1 pb-1.5 mb-1 border-b border-base-300 sticky top-0 bg-base-100 z-[1]">
|
|
513
|
-
<span class="text-[10px] uppercase tracking-wide text-base-content/45 font-semibold">\u6700\u8FD1\u4F1A\u8BDD</span>
|
|
514
|
-
<span id="recent-menu-count" class="text-[10px] text-base-content/35 font-mono"></span>
|
|
515
|
-
</div>
|
|
516
|
-
<div id="recent-menu-list" class="flex flex-col gap-0.5"></div>
|
|
517
|
-
<p id="recent-menu-empty" class="text-xs text-base-content/40 px-1 py-2 hidden">\u6682\u65E0\u6700\u8FD1\u4F1A\u8BDD</p>
|
|
518
|
-
</div>
|
|
519
|
-
<!-- Hub: only when no remembered machine (or last one offline) -->
|
|
520
|
-
<div id="machine-picker" class="hidden fixed inset-0 z-[60] bg-base-100 flex items-center justify-center p-4">
|
|
521
|
-
<div class="w-full max-w-md">
|
|
522
|
-
<h2 class="text-xl font-bold mb-1">\u9009\u62E9\u673A\u5668</h2>
|
|
523
|
-
<p class="text-sm text-base-content/55 mb-4">\u9996\u6B21\u4F7F\u7528\u6216\u4E0A\u6B21\u9009\u62E9\u7684\u673A\u5668\u4E0D\u5728\u7EBF\u65F6\u9700\u8981\u9009\u62E9\u3002\u4E4B\u540E\u4F1A\u8BB0\u4F4F\u4F60\u7684\u9009\u62E9\uFF0C\u4E5F\u53EF\u968F\u65F6\u5728\u9876\u680F\u5207\u6362\u3002</p>
|
|
524
|
-
<div id="machine-picker-list" class="flex flex-col gap-2 mb-4"></div>
|
|
525
|
-
<p id="machine-picker-empty" class="text-sm text-base-content/40 hidden">\u6682\u65E0\u5728\u7EBF\u673A\u5668\u3002\u8BF7\u5728\u4E1A\u52A1\u673A\u4E0A\u8FD0\u884C <code class="text-xs">npm run client</code> \u540E\u70B9\u5237\u65B0\u3002</p>
|
|
526
|
-
<div class="flex items-center gap-2">
|
|
527
|
-
<button id="btn-refresh-machines" class="btn btn-ghost btn-sm border border-base-300">\u21BB \u5237\u65B0</button>
|
|
528
|
-
<span id="machine-picker-status" class="text-xs text-base-content/40"></span>
|
|
529
|
-
</div>
|
|
530
|
-
</div>
|
|
531
|
-
</div>
|
|
532
|
-
<div class="flex flex-1 overflow-hidden relative">
|
|
533
|
-
<div id="sidebar" class="bg-base-200 border-r border-base-300 flex flex-col flex-shrink-0 overflow-hidden" style="width:280px;min-width:180px;max-width:520px">
|
|
534
|
-
<div class="flex items-center justify-between px-3 py-2 border-b border-base-300 flex-shrink-0">
|
|
535
|
-
<span class="text-xs uppercase tracking-wider text-base-content/50 font-medium">Projects</span>
|
|
536
|
-
<div class="flex items-center gap-1">
|
|
537
|
-
<span id="session-filter-badge" class="hidden text-[10px] text-primary cursor-pointer">\xD7 filter</span>
|
|
538
|
-
<button id="sidebar-refresh" class="btn btn-ghost btn-xs px-1 text-[10px] text-base-content/40" title="Refresh">\u21BB</button>
|
|
539
|
-
</div>
|
|
540
|
-
</div>
|
|
541
|
-
<div id="ws-tabs" class="flex overflow-x-auto border-b border-base-300 flex-shrink-0" style="scrollbar-width:none"></div>
|
|
542
|
-
<div class="px-2 py-1.5 border-b border-base-300 flex-shrink-0 flex flex-col gap-1.5">
|
|
543
|
-
<input id="session-search" type="text" placeholder="Search projects & recent work\u2026"
|
|
544
|
-
class="input input-xs input-bordered w-full text-xs" autocomplete="off">
|
|
545
|
-
<div class="flex items-center gap-1 flex-wrap">
|
|
546
|
-
<div id="view-tabs" class="flex rounded-md border border-base-300 overflow-hidden text-[10px]">
|
|
547
|
-
<button data-view="projects" class="px-2 py-0.5 bg-primary/15 text-primary font-medium">Projects</button>
|
|
548
|
-
<button data-view="timeline" class="px-2 py-0.5 text-base-content/50 hover:bg-base-300">Recent</button>
|
|
549
|
-
</div>
|
|
550
|
-
<select id="time-range" class="select select-xs select-bordered text-[10px] h-6 min-h-0 py-0 pl-1 pr-6" title="Time range">
|
|
551
|
-
<option value="1">Today</option>
|
|
552
|
-
<option value="7">7d</option>
|
|
553
|
-
<option value="14" selected>14d</option>
|
|
554
|
-
<option value="30">30d</option>
|
|
555
|
-
<option value="0">All</option>
|
|
556
|
-
</select>
|
|
557
|
-
</div>
|
|
558
|
-
<div id="agent-filter" class="flex gap-1 flex-wrap">
|
|
559
|
-
<button data-agent-filter="all" class="agent-pill active">All</button>
|
|
560
|
-
<button data-agent-filter="claude" class="agent-pill">Claude</button>
|
|
561
|
-
<button data-agent-filter="codex" class="agent-pill">Codex</button>
|
|
562
|
-
<button data-agent-filter="grok" class="agent-pill">Grok</button>
|
|
563
|
-
<button id="btn-fav-filter" class="agent-pill" title="Show only favorites">\u2605 Fav</button>
|
|
564
|
-
<button id="btn-hidden-filter" class="agent-pill" title="Show only hidden sessions">\u{1F648} Hidden</button>
|
|
565
|
-
</div>
|
|
566
|
-
<div id="search-status" class="hidden text-[10px] text-base-content/40 px-0.5"></div>
|
|
567
|
-
</div>
|
|
568
|
-
<div id="session-list" class="overflow-y-auto flex-1"></div>
|
|
569
|
-
</div>
|
|
570
|
-
<div class="flex flex-col flex-shrink-0 relative" style="width:12px">
|
|
571
|
-
<div id="sidebar-resize" class="absolute inset-0 bg-transparent hover:bg-primary/30 transition-colors cursor-col-resize"></div>
|
|
572
|
-
<button id="sidebar-toggle" title="Toggle sidebar"
|
|
573
|
-
class="absolute z-10 flex items-center justify-center w-5 h-8 bg-base-200 border border-base-300 rounded-r-md text-[10px] text-base-content/40 hover:text-base-content cursor-pointer"
|
|
574
|
-
style="left:0;top:50%;transform:translateY(-50%)">\u25C0</button>
|
|
575
|
-
</div>
|
|
576
|
-
<div id="main" class="flex-1 flex flex-col overflow-hidden min-w-0">
|
|
577
|
-
<div id="welcome" class="${t?"hidden flex-1 flex-col items-center justify-start overflow-y-auto p-4 md:p-6 gap-5":"flex-1 flex flex-col items-center justify-start overflow-y-auto p-4 md:p-6 gap-5"}">
|
|
578
|
-
<div class="max-w-4xl w-full text-center flex flex-col items-center gap-2 mt-2 md:mt-4">
|
|
579
|
-
<h2 class="text-3xl font-extrabold tracking-tight bg-gradient-to-r from-primary to-accent bg-clip-text text-transparent select-none">\u{1F916} Agent UI</h2>
|
|
580
|
-
<p class="text-sm text-base-content/65 max-w-md">Claude \xB7 Codex \xB7 Grok \u2014 Select a session from the sidebar or click a day on the calendar to view activity.</p>
|
|
581
|
-
<div class="flex gap-2 mt-0.5">
|
|
582
|
-
<button id="btn-new-session-welcome" class="btn btn-primary btn-sm shadow-md">\uFF0B New Session</button>
|
|
583
|
-
<button id="btn-meta-agent-welcome" class="btn btn-outline btn-primary btn-sm">\u2726 Meta Agent</button>
|
|
584
|
-
</div>
|
|
585
|
-
</div>
|
|
586
|
-
|
|
587
|
-
<!-- Stats Banner -->
|
|
588
|
-
<div class="max-w-4xl w-full grid grid-cols-2 md:grid-cols-4 gap-4 text-center select-none mt-1">
|
|
589
|
-
<div class="bg-base-200 border border-base-300 rounded-2xl p-3 shadow-md flex flex-col justify-center items-center">
|
|
590
|
-
<span class="text-[10px] uppercase tracking-wider text-base-content/40 font-bold">Total Sessions</span>
|
|
591
|
-
<span id="stats-total-sessions" class="text-xl md:text-2xl font-extrabold text-primary mt-0.5">0</span>
|
|
592
|
-
</div>
|
|
593
|
-
<div class="bg-base-200 border border-base-300 rounded-2xl p-3 shadow-md flex flex-col justify-center items-center">
|
|
594
|
-
<span class="text-[10px] uppercase tracking-wider text-base-content/40 font-bold">Active Projects</span>
|
|
595
|
-
<span id="stats-total-projects" class="text-xl md:text-2xl font-extrabold text-accent mt-0.5">0</span>
|
|
596
|
-
</div>
|
|
597
|
-
<div class="bg-base-200 border border-base-300 rounded-2xl p-3 shadow-md flex flex-col justify-center items-center">
|
|
598
|
-
<span class="text-[10px] uppercase tracking-wider text-base-content/40 font-bold">Total Tokens</span>
|
|
599
|
-
<span id="stats-total-tokens" class="text-xl md:text-2xl font-extrabold text-secondary mt-0.5">0</span>
|
|
600
|
-
</div>
|
|
601
|
-
<div class="bg-base-200 border border-base-300 rounded-2xl p-3 shadow-md flex flex-col justify-center items-center">
|
|
602
|
-
<span class="text-[10px] uppercase tracking-wider text-base-content/40 font-bold">Selected Day Tokens</span>
|
|
603
|
-
<span id="stats-selected-tokens" class="text-xl md:text-2xl font-extrabold text-success mt-0.5">0</span>
|
|
604
|
-
</div>
|
|
605
|
-
</div>
|
|
606
|
-
<div class="max-w-4xl w-full grid grid-cols-1 md:grid-cols-12 gap-5 items-start text-left">
|
|
607
|
-
<!-- Calendar Card -->
|
|
608
|
-
<div class="card bg-base-200 border border-base-300 md:col-span-7 shadow-xl">
|
|
609
|
-
<div class="card-body p-4">
|
|
610
|
-
<div class="flex items-center justify-between mb-3">
|
|
611
|
-
<button id="btn-cal-prev" class="btn btn-ghost btn-sm text-base font-bold">\u25C0</button>
|
|
612
|
-
<h3 id="calendar-month-year" class="text-base font-extrabold text-base-content select-none"></h3>
|
|
613
|
-
<button id="btn-cal-next" class="btn btn-ghost btn-sm text-base font-bold">\u25B6</button>
|
|
614
|
-
</div>
|
|
615
|
-
|
|
616
|
-
<div class="grid grid-cols-7 gap-2 text-center text-[10px] font-bold text-base-content/40 mb-1">
|
|
617
|
-
<div>Sun</div><div>Mon</div><div>Tue</div><div>Wed</div><div>Thu</div><div>Fri</div><div>Sat</div>
|
|
618
|
-
</div>
|
|
619
|
-
|
|
620
|
-
<div id="calendar-grid" class="grid grid-cols-7 gap-2">
|
|
621
|
-
<!-- JS Rendered -->
|
|
622
|
-
</div>
|
|
623
|
-
</div>
|
|
624
|
-
</div>
|
|
625
|
-
|
|
626
|
-
<!-- Details Card -->
|
|
627
|
-
<div class="card bg-base-200 border border-base-300 md:col-span-5 shadow-xl min-h-[290px] flex flex-col">
|
|
628
|
-
<div class="card-body p-4 flex flex-col flex-1">
|
|
629
|
-
<h3 id="selected-day-title" class="card-title text-xs font-bold uppercase tracking-wider text-base-content/50 mb-3 select-none">Daily Activity</h3>
|
|
630
|
-
<div id="selected-day-sessions" class="flex-1 overflow-y-auto flex flex-col gap-2 max-h-[250px]">
|
|
631
|
-
<div class="text-center text-xs text-base-content/40 my-auto py-6">Select a date on the calendar to view its sessions.</div>
|
|
632
|
-
</div>
|
|
633
|
-
</div>
|
|
634
|
-
</div>
|
|
635
|
-
</div>
|
|
636
|
-
|
|
637
|
-
<!-- Recent Sessions Section -->
|
|
638
|
-
<div class="max-w-4xl w-full border-t border-base-300/60 pt-4 mt-1 text-left">
|
|
639
|
-
<h3 class="text-xs font-bold uppercase tracking-wider text-base-content/50 mb-3 select-none">Recent Workspaces & Sessions</h3>
|
|
640
|
-
<div id="recent-sessions-list" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
|
|
641
|
-
<!-- JS Rendered -->
|
|
642
|
-
</div>
|
|
643
|
-
</div>
|
|
644
|
-
</div>
|
|
645
|
-
<div id="project-view" class="${t?"flex flex-col flex-1 overflow-hidden min-h-0":"hidden flex-col flex-1 overflow-hidden min-h-0"}" style="${t?"display:flex;flex-direction:column;flex:1;min-height:0":""}">
|
|
646
|
-
<div class="flex bg-base-200 border-b border-base-300 flex-shrink-0 overflow-x-auto" style="scrollbar-width:none">
|
|
647
|
-
<button class="px-4 py-2.5 text-sm border-b-2 flex-shrink-0 text-primary border-primary" data-tab="chat">Chat</button>
|
|
648
|
-
<button class="px-4 py-2.5 text-sm border-b-2 flex-shrink-0 text-base-content/50 border-transparent" data-tab="files">Files</button>
|
|
649
|
-
<button class="px-4 py-2.5 text-sm border-b-2 flex-shrink-0 text-base-content/50 border-transparent" data-tab="git">Git</button>
|
|
650
|
-
<button class="px-4 py-2.5 text-sm border-b-2 flex-shrink-0 text-base-content/50 border-transparent" data-tab="shell">Shell</button>
|
|
651
|
-
<button class="px-4 py-2.5 text-sm border-b-2 flex-shrink-0 text-base-content/50 border-transparent" data-tab="memory">\u{1F9E0} Memory</button>
|
|
652
|
-
</div>
|
|
653
|
-
<div id="tab-chat" class="flex flex-col flex-1 overflow-hidden relative">
|
|
654
|
-
<div id="project-notes-bar" class="bg-base-200 border-b border-base-300 px-3 py-1.5 text-xs flex flex-col gap-1" style="display:none"></div>
|
|
655
|
-
<div id="session-notes-bar" class="bg-base-200/80 border-b border-base-300 px-3 py-1 text-xs flex justify-between items-center gap-2" style="display:none"></div>
|
|
656
|
-
<div id="ws-banner" class="hidden px-3 py-1.5 text-[11px] text-center bg-warning/15 text-warning border-b border-warning/20 flex items-center justify-center gap-2">
|
|
657
|
-
<span id="ws-banner-text"></span>
|
|
658
|
-
<button type="button" id="ws-banner-retry" class="btn btn-ghost btn-xs h-6 min-h-0 px-2 text-warning border border-warning/30">\u91CD\u8FDE</button>
|
|
659
|
-
</div>
|
|
660
|
-
<div id="messages" class="flex-1 overflow-y-auto p-3 flex flex-col gap-2"></div>
|
|
661
|
-
<div id="scroll-nav" class="absolute right-3 bottom-24 flex flex-col gap-1.5 z-10">
|
|
662
|
-
<button id="scroll-top-btn" class="scroll-fab hidden" title="Jump to top">\u2191</button>
|
|
663
|
-
<button id="scroll-bottom-btn" class="scroll-fab hidden" title="Jump to bottom">\u2193</button>
|
|
664
|
-
</div>
|
|
665
|
-
<div id="permission-requests"></div>
|
|
666
|
-
<div id="chat-composer" class="flex flex-col border-t border-base-300 bg-base-200 flex-shrink-0">
|
|
667
|
-
<div id="chat-opts" class="hidden flex items-center gap-1.5 px-3 pt-2 flex-wrap">
|
|
668
|
-
<select id="sel-agent" class="select select-xs select-bordered text-xs font-semibold" title="Agent">
|
|
669
|
-
<option value="claude">Claude</option>
|
|
670
|
-
<option value="codex">Codex</option>
|
|
671
|
-
<option value="grok">Grok</option>
|
|
672
|
-
</select>
|
|
673
|
-
<select id="sel-convert-agent" class="select select-xs select-bordered text-xs font-semibold" title="Convert session to another agent">
|
|
674
|
-
<option value="" disabled selected>Convert to...</option>
|
|
675
|
-
<option value="claude">Convert to Claude</option>
|
|
676
|
-
<option value="codex">Convert to Codex</option>
|
|
677
|
-
<option value="grok">Convert to Grok</option>
|
|
678
|
-
</select>
|
|
679
|
-
<div class="flex items-center gap-0.5">
|
|
680
|
-
<select id="sel-model" class="select select-xs select-bordered font-mono text-xs max-w-[11rem]" title="Model">
|
|
681
|
-
<option value="claude-sonnet-4-5">sonnet-4-5</option>
|
|
682
|
-
<option value="claude-sonnet-4-6">sonnet-4-6</option>
|
|
683
|
-
<option value="claude-opus-4-5">opus-4-5</option>
|
|
684
|
-
<option value="claude-haiku-4-5">haiku-4-5</option>
|
|
685
|
-
</select>
|
|
686
|
-
<button type="button" id="btn-model-add" class="btn btn-ghost btn-xs px-1.5 font-mono" title="Add / set custom model id">+</button>
|
|
687
|
-
<button type="button" id="btn-model-edit" class="btn btn-ghost btn-xs px-1.5" title="Edit current model id">\u270E</button>
|
|
688
|
-
</div>
|
|
689
|
-
<select id="sel-effort" class="select select-xs select-bordered text-xs" title="Effort">
|
|
690
|
-
<option value="">effort: off</option>
|
|
691
|
-
<option value="low">low</option>
|
|
692
|
-
<option value="medium">medium</option>
|
|
693
|
-
<option value="high">high</option>
|
|
694
|
-
<option value="xhigh">xhigh</option>
|
|
695
|
-
<option value="max">max</option>
|
|
696
|
-
</select>
|
|
697
|
-
<select id="sel-permission" class="select select-xs select-bordered text-xs" title="Permissions">
|
|
698
|
-
<option value="default">perms: default</option>
|
|
699
|
-
<option value="acceptEdits">acceptEdits</option>
|
|
700
|
-
<option value="auto">auto</option>
|
|
701
|
-
<option value="plan">plan</option>
|
|
702
|
-
<option value="bypassPermissions">\u26A0 bypass all</option>
|
|
703
|
-
</select>
|
|
704
|
-
<select id="sel-density" class="select select-xs select-bordered text-xs" title="Chat density \u2014 how tools are shown">
|
|
705
|
-
<option value="clean">density: clean</option>
|
|
706
|
-
<option value="normal">density: normal</option>
|
|
707
|
-
<option value="full">density: full</option>
|
|
708
|
-
</select>
|
|
709
|
-
</div>
|
|
710
|
-
<div id="composer-row" class="flex gap-2 items-end px-3 pt-2 pb-2">
|
|
711
|
-
<button id="btn-opts" class="btn btn-ghost btn-sm px-2 text-base-content/30 hover:text-base-content flex-shrink-0" title="Options">\u2699</button>
|
|
712
|
-
<textarea id="chat-input" rows="1" placeholder="Ask\u2026 (!cmd shell \xB7 \u2318\u21B5 send)"
|
|
713
|
-
class="chat-input flex-1 text-sm resize-none leading-relaxed"
|
|
714
|
-
name="agent-chat-message"
|
|
715
|
-
autocomplete="off"
|
|
716
|
-
autocorrect="off"
|
|
717
|
-
autocapitalize="off"
|
|
718
|
-
spellcheck="false"
|
|
719
|
-
data-1p-ignore="true"
|
|
720
|
-
data-lpignore="true"
|
|
721
|
-
data-form-type="other"
|
|
722
|
-
data-bwignore="true"></textarea>
|
|
723
|
-
<button id="send-btn" class="btn btn-primary btn-sm flex-shrink-0">Send</button>
|
|
724
|
-
<button id="stop-btn" class="btn btn-error btn-sm hidden flex-shrink-0">\u25A0</button>
|
|
725
|
-
</div>
|
|
726
|
-
</div>
|
|
727
|
-
</div>
|
|
728
|
-
<div id="tab-files" class="hidden flex-col flex-1 overflow-hidden">
|
|
729
|
-
<div class="flex items-center gap-2 px-3 py-1.5 border-b border-base-300 flex-shrink-0 bg-base-200">
|
|
730
|
-
<span class="text-xs text-base-content/40 flex-shrink-0">Root</span>
|
|
731
|
-
<span id="files-machine-hint" class="hidden text-[10px] font-mono font-semibold text-primary flex-shrink-0"></span>
|
|
732
|
-
<input id="files-root-input" type="text" placeholder="Absolute path on the target machine"
|
|
733
|
-
class="input input-xs flex-1 font-mono text-xs bg-transparent border-0 focus:outline-none px-1" autocomplete="off">
|
|
734
|
-
<button id="files-root-btn" class="btn btn-ghost btn-xs text-xs">Go</button>
|
|
735
|
-
</div>
|
|
736
|
-
<div id="files-breadcrumb" class="flex items-center flex-wrap gap-1 px-4 py-2 border-b border-base-300 text-xs text-base-content/60 flex-shrink-0"></div>
|
|
737
|
-
<div id="files-area" class="flex-1 overflow-y-auto"></div>
|
|
738
|
-
</div>
|
|
739
|
-
<div id="tab-shell" class="hidden flex-col flex-1 overflow-hidden bg-[#0d1117]">
|
|
740
|
-
<div class="flex items-center gap-2 px-3 py-1.5 border-b border-base-300 bg-base-200 flex-shrink-0">
|
|
741
|
-
<span class="text-xs text-base-content/40 font-mono flex-1" id="term-cwd"></span>
|
|
742
|
-
<button id="term-reconnect" class="btn btn-ghost btn-xs text-xs text-base-content/40">Reconnect</button>
|
|
743
|
-
</div>
|
|
744
|
-
<div id="terminal-container" class="flex-1 p-2 overflow-hidden"></div>
|
|
745
|
-
</div>
|
|
746
|
-
<div id="tab-git" class="hidden flex-col flex-1 overflow-hidden">
|
|
747
|
-
<div class="flex items-center gap-2 px-3 py-1.5 border-b border-base-300 flex-shrink-0 bg-base-200">
|
|
748
|
-
<span class="text-xs text-base-content/40 flex-shrink-0">Root</span>
|
|
749
|
-
<span id="git-machine-hint" class="hidden text-[10px] font-mono font-semibold text-primary flex-shrink-0"></span>
|
|
750
|
-
<input id="git-root-input" type="text" placeholder="Absolute path on the target machine"
|
|
751
|
-
class="input input-xs flex-1 font-mono text-xs bg-transparent border-0 focus:outline-none px-1" autocomplete="off">
|
|
752
|
-
<button id="git-root-btn" class="btn btn-ghost btn-xs text-xs">Go</button>
|
|
753
|
-
</div>
|
|
754
|
-
<div id="git-area" class="flex-1 overflow-y-auto"></div>
|
|
755
|
-
</div>
|
|
756
|
-
<div id="tab-memory" class="hidden flex-col flex-1 overflow-hidden">
|
|
757
|
-
<div class="flex items-center gap-2 px-3 py-1.5 border-b border-base-300 flex-shrink-0 bg-base-200">
|
|
758
|
-
<span class="text-xs text-base-content/40 flex-shrink-0">\u{1F9E0} Project memory</span>
|
|
759
|
-
<span class="flex-1"></span>
|
|
760
|
-
<button id="memory-refresh" class="btn btn-ghost btn-xs text-xs">Refresh</button>
|
|
761
|
-
</div>
|
|
762
|
-
<div id="memory-area" class="flex-1 overflow-y-auto text-sm"></div>
|
|
763
|
-
</div>
|
|
764
|
-
</div>
|
|
765
|
-
</div>
|
|
766
|
-
</div>
|
|
767
|
-
</div>
|
|
768
|
-
<dialog id="modal-new-session" class="modal">
|
|
769
|
-
<div class="modal-box max-w-sm">
|
|
770
|
-
<h3 class="font-bold text-base mb-4">\uFF0B New Session</h3>
|
|
771
|
-
<div class="flex flex-col gap-3">
|
|
772
|
-
<label class="form-control">
|
|
773
|
-
<div class="label py-1"><span class="label-text text-xs uppercase tracking-wide">Agent</span></div>
|
|
774
|
-
<select id="new-session-agent" class="select select-bordered select-sm">
|
|
775
|
-
<option value="claude">Claude Code</option>
|
|
776
|
-
<option value="codex">OpenAI Codex</option>
|
|
777
|
-
<option value="grok">Grok</option>
|
|
778
|
-
</select>
|
|
779
|
-
</label>
|
|
780
|
-
<label class="form-control" id="new-session-machine-wrap" style="display:none">
|
|
781
|
-
<div class="label py-1"><span class="label-text text-xs uppercase tracking-wide">Machine (edge server)</span></div>
|
|
782
|
-
<select id="new-session-machine" class="select select-bordered select-sm"></select>
|
|
783
|
-
</label>
|
|
784
|
-
<label class="form-control">
|
|
785
|
-
<div class="label py-1"><span class="label-text text-xs uppercase tracking-wide">Working Directory</span></div>
|
|
786
|
-
<input id="new-session-path" type="text" class="input input-bordered input-sm font-mono"
|
|
787
|
-
placeholder="~/projects/my-app" autocomplete="off">
|
|
788
|
-
</label>
|
|
789
|
-
<label class="form-control" id="new-session-ws-wrap">
|
|
790
|
-
<div class="label py-1"><span class="label-text text-xs uppercase tracking-wide">Claude Workspace</span></div>
|
|
791
|
-
<select id="new-session-workspace" class="select select-bordered select-sm"></select>
|
|
792
|
-
</label>
|
|
793
|
-
<div id="new-session-error" class="alert alert-error text-xs py-2 hidden"></div>
|
|
794
|
-
</div>
|
|
795
|
-
<div class="modal-action">
|
|
796
|
-
<button id="new-session-cancel" class="btn btn-ghost btn-sm">Cancel</button>
|
|
797
|
-
<button id="new-session-start" class="btn btn-primary btn-sm">Start</button>
|
|
798
|
-
</div>
|
|
799
|
-
</div>
|
|
800
|
-
<form method="dialog" class="modal-backdrop"><button>close</button></form>
|
|
801
|
-
</dialog>`};hs();vt();Kt();C();I();K();wn();sn();vt();Jt();Kt();C();I();K();vt();Jt();async function wt(){try{let e=await h("GET","/api/machines");D.value=Array.isArray(e)?e:[]}catch{try{let e=await ct();e?.machines&&(D.value=e.machines)}catch{}}return D.peek()||[]}function $s(){let e=(0,n.$)("machine-picker-list"),t=(0,n.$)("machine-picker-empty");if(!e)return;let s=D.peek()||[];if(!s.length){e.innerHTML="",t?.classList.remove("hidden");return}t?.classList.add("hidden"),e.innerHTML=s.map(o=>{let a=o.online!==!1,i=[o.hostname,o.platform].filter(Boolean).join(" \xB7 ");return`
|
|
802
|
-
<button type="button" class="machine-pick-card w-full text-left px-4 py-3 rounded-xl border border-base-300
|
|
803
|
-
hover:border-primary hover:bg-primary/5 transition-colors ${a?"":"opacity-50"}"
|
|
804
|
-
data-pick-machine="${(0,n.esc)(o.id)}" ${a?"":"disabled"}>
|
|
805
|
-
<div class="flex items-center gap-2">
|
|
806
|
-
<span class="w-2 h-2 rounded-full flex-shrink-0 ${a?"bg-success":"bg-error"}"></span>
|
|
807
|
-
<span class="font-semibold text-sm font-mono">${(0,n.esc)(o.id)}</span>
|
|
808
|
-
</div>
|
|
809
|
-
${i?`<div class="text-xs text-base-content/45 mt-1 pl-4">${(0,n.esc)(i)}</div>`:""}
|
|
810
|
-
</button>`}).join("")}function kt(){let e=(0,n.$)("machine-picker");if(!e)return;e.classList.remove("hidden"),Ee.value=!1,$s();let t=(0,n.$)("machine-picker-status");if(t){let s=(D.peek()||[]).filter(o=>o.online!==!1).length;t.textContent=`${s} online`}}function Ss(){(0,n.$)("machine-picker")?.classList.add("hidden"),Ee.value=!0}function Vt(){(0,n.$)("machine-menu")?.classList.add("hidden")}function Nn(){let e=(0,n.$)("machine-menu-list"),t=(0,n.$)("machine-menu-empty");if(!e)return;let s=D.peek()||[],o=M.peek()||"";if(!s.length){e.innerHTML="",t?.classList.remove("hidden");return}t?.classList.add("hidden"),e.innerHTML=s.map(a=>{let i=a.online!==!1,r=a.id===o,c=[a.hostname,a.platform].filter(Boolean).join(" \xB7 "),u=a.connectedAt?new Date(a.connectedAt).toLocaleTimeString():"";return`
|
|
811
|
-
<button type="button"
|
|
812
|
-
class="machine-menu-item w-full text-left px-2 py-1.5 rounded-md text-xs
|
|
813
|
-
${r?"bg-primary/15 text-primary":"hover:bg-base-200"}
|
|
814
|
-
${i?"":"opacity-50"}"
|
|
815
|
-
data-pick-machine="${(0,n.esc)(a.id)}"
|
|
816
|
-
${i?"":"disabled"}>
|
|
817
|
-
<div class="flex items-center gap-1.5">
|
|
818
|
-
<span class="w-1.5 h-1.5 rounded-full flex-shrink-0 ${i?"bg-success":"bg-error"}"></span>
|
|
819
|
-
<span class="font-mono font-semibold truncate flex-1">${(0,n.esc)(a.id)}</span>
|
|
820
|
-
${r?'<span class="text-[9px] opacity-70">\u5F53\u524D</span>':""}
|
|
821
|
-
<span class="text-[9px] ${i?"text-success":"text-error"}">${i?"online":"offline"}</span>
|
|
822
|
-
</div>
|
|
823
|
-
${c||u?`<div class="text-[10px] text-base-content/40 pl-3.5 mt-0.5 truncate">${(0,n.esc)(c)}${u?" \xB7 "+(0,n.esc)(u):""}</div>`:""}
|
|
824
|
-
</button>`}).join("")}function st(){let e=(0,n.$)("machine-menu-wrap"),t=(0,n.$)("topbar-machine-label"),s=(0,n.$)("topbar-machine-dot");if(!k.peek()){e?.classList.add("hidden");return}e?.classList.remove("hidden");let o=M.peek()||"",i=(D.peek()||[]).find(c=>c.id===o),r=i?i.online!==!1:!1;t&&(t.textContent=o||"\u9009\u62E9\u673A\u5668"),s&&(s.classList.toggle("bg-success",!!o&&r),s.classList.toggle("bg-warning",!!o&&!r),s.classList.toggle("bg-base-content/30",!o)),Nn()}var da=null;function pa(){da||(da=setInterval(async()=>{if(!k.peek()||!f.token)return;await wt(),st();let e=M.peek();if(e){let t=(D.peek()||[]).find(s=>s.id===e);if(t&&t.online===!1){let s=(0,n.$)("topbar-machine-label");s&&(s.textContent=`${e} (offline)`)}}},15e3))}async function ua(e,{forceReload:t=!1}={}){if(!e)return;let s=M.peek(),o=s&&s!==e;if(Z(e),Ss(),Vt(),st(),o||t){try{f.ws?.close()}catch{}f.ws=null,f.sessionId=null,(0,n.batch)(()=>{y.value=null,O.value=null,U.value=null});try{let{filesRoot:i,gitRoot:r,filesPath:c}=await Promise.resolve().then(()=>(I(),Gs));i.value="",r.value="",c.value=""}catch{}if(o){let{goHome:i}=await Promise.resolve().then(()=>(Kt(),ca));i()}}let a=(0,n.$)("topbar-project");a&&!y.peek()&&(a.textContent=`Machine \xB7 ${e}`),Ft(),await Promise.all([ye(),Wt(),zt(),Gt().catch(()=>{})]),Ee.value=!0}async function Rn(e={}){(0,n.$)("auth-screen").style.display="none";let t=(0,n.$)("app");t.classList.remove("hidden"),t.style.display="flex";let s=e.hub;s===void 0?s=await ct():s?(k.value=!0,s.machines&&(D.value=s.machines)):k.value=!1;let o=e.deepLink||null,a=Je();if(!!(o?.id||(location.hash||"").startsWith("#/session/"))){let c=(0,n.$)("welcome");c&&(c.classList.add("hidden"),c.style.display="none");let u=(0,n.$)("project-view");u&&(u.classList.remove("hidden"),u.style.display="flex",u.style.flexDirection="column",u.style.flex="1",u.style.minHeight="0")}if(k.peek()){let c=localStorage.getItem("machineId")||a?.machineId||"";c&&(Z(c),Ss(),Ee.value=!0,Ft());let u=wt().then(p=>(st(),c&&!p.some(b=>b.id===c&&b.online!==!1)&&p.length&&(p.some(g=>g.id===c)||(Z(null),kt())),p));pa(),st();let l=Promise.all([ye({waitFresh:!1}),Wt({waitFresh:!1}),zt().catch(()=>{}),Gt().catch(()=>{})]);if(o?.id){let p=E.peek().find(m=>m.sessionId===o.id)||(a?.sessionId===o.id?a:null);await ke(o.id,p?.cwd||a?.cwd||null,p?.configDir||a?.configDir||null,p?.agent||a?.agent||null,p?.machineId||a?.machineId||c||null,{tab:o.tab||"chat"}),u.catch(()=>{}),l.catch(()=>{});return}if(!c){if(await u,!(D.peek()||[]).length){kt(),l.catch(()=>{});return}kt(),l.catch(()=>{});return}await l;let d=(0,n.$)("topbar-project");d&&d.textContent==="Select a session"&&(d.textContent=`Machine \xB7 ${c}`);return}Ft();let r=Promise.all([ye({waitFresh:!1}),Wt({waitFresh:!1}),zt().catch(()=>{}),Gt().catch(()=>{})]);if(o?.id){await yn().then(u=>{Array.isArray(u)?E.value=u:u&&Array.isArray(u.sessions)&&(E.value=u.sessions)}).catch(()=>{});let c=E.peek().find(u=>u.sessionId===o.id)||(a?.sessionId===o.id?a:null);await ke(o.id,c?.cwd||a?.cwd||null,c?.configDir||a?.configDir||null,c?.agent||a?.agent||null,null,{tab:o.tab||"chat"}),r.catch(()=>{});return}await r}Jt();C();I();K();mt();vt();Kt();function fa(){(0,n.$)("sidebar").classList.add("open"),(0,n.$)("sidebar-overlay").classList.remove("hidden")}function Bn(){(0,n.$)("sidebar").classList.remove("open"),(0,n.$)("sidebar-overlay").classList.add("hidden")}function ma(){window.innerWidth<=640&&Bn()}function ha(){let e=(0,n.$)("sidebar-toggle"),t=(0,n.$)("sidebar"),s=!1,o=t.style.width||"260px";e.addEventListener("click",()=>{s=!s,s?(o=t.style.width||"260px",t.style.width="0",t.style.minWidth="0",t.style.overflow="hidden"):(t.style.width=o,t.style.minWidth="",t.style.overflow=""),e.textContent=s?"\u25B6":"\u25C0"})}function ba(){let e=(0,n.$)("sidebar-resize"),t=(0,n.$)("sidebar"),s=!1;e.addEventListener("mousedown",o=>{s=!0,document.body.style.userSelect="none",document.body.style.cursor="col-resize",o.preventDefault()}),document.addEventListener("mousemove",o=>{if(!s)return;let a=Math.min(Math.max(o.clientX,160),520);t.style.width=a+"px",t.style.minWidth=""}),document.addEventListener("mouseup",()=>{s&&(s=!1,document.body.style.userSelect="",document.body.style.cursor="")})}function Ls(e){document.documentElement.setAttribute("data-theme",e),localStorage.setItem("theme",e);let t=(0,n.$)("btn-theme");t&&(t.textContent=e==="dark"||e==="night"||e==="black"?"\u{1F319}":"\u2600\uFE0F");let s=document.getElementById("hljs-theme");if(s||(s=document.querySelector('link[href*="highlight"][href*="styles"]'),s&&(s.id="hljs-theme")),s){let o=e==="dark"||e==="night"||e==="black";s.href=o?"https://cdn.jsdelivr.net/npm/@highlightjs/cdn-assets@11/styles/github-dark.min.css":"https://cdn.jsdelivr.net/npm/@highlightjs/cdn-assets@11/styles/github.min.css"}}function xa(){let e=document.documentElement.getAttribute("data-theme")||"dark";Ls(e==="dark"?"light":"dark")}function ga(){let e=(0,n.$)("new-session-agent");e&&(e.value=S.peek()||"claude");let t=(0,n.$)("new-session-workspace");if(t){let a=P.peek();t.innerHTML=a.map(i=>`<option value="${(0,n.esc)(i.configDir)}">${(0,n.esc)(i.name)} \u2014 ${(0,n.esc)(i.configDir)}</option>`).join(""),f.configDir&&(t.value=f.configDir)}let s=(0,n.$)("new-session-machine-wrap"),o=(0,n.$)("new-session-machine");if(s&&o)if(k.peek()){s.style.display="";let a=D.peek()||[],i=M.peek()||"";o.innerHTML=a.length?a.map(r=>`<option value="${(0,n.esc)(r.id)}">${(0,n.esc)(r.id)}${r.hostname?" \u2014 "+(0,n.esc)(r.hostname):""}</option>`).join(""):'<option value="">(no machines online)</option>',i&&(o.value=i),o.disabled=!0}else s.style.display="none",o&&(o.disabled=!1);Ms(),(0,n.$)("new-session-path").value=y.peek()?.path||"",(0,n.$)("new-session-error").classList.add("hidden"),(0,n.$)("modal-new-session").showModal(),setTimeout(()=>(0,n.$)("new-session-path").focus(),50)}function Ms(){let e=(0,n.$)("new-session-agent")?.value||"claude",t=(0,n.$)("new-session-ws-wrap");t&&(t.style.display=e==="claude"?"":"none")}async function Ts(){let e=(0,n.$)("new-session-path").value.trim(),t=(0,n.$)("new-session-agent")?.value||"claude",s=t==="claude"?(0,n.$)("new-session-workspace").value:null,o=(0,n.$)("new-session-error");if(o.classList.add("hidden"),!e){o.textContent="Working directory is required",o.classList.remove("hidden");return}if(k.peek()){let a=M.peek()||(0,n.$)("new-session-machine")?.value||D.peek()?.[0]?.id;if(!a){o.textContent="\u8BF7\u5148\u9009\u62E9\u4E00\u53F0\u673A\u5668",o.classList.remove("hidden"),kt();return}Z(a)}try{let a=await h("POST","/api/resolve-path",{path:e});if(!a.isDir)throw new Error("Path is not a directory");(0,n.$)("modal-new-session").close();let i=a.path.split("/").filter(Boolean).pop()||a.path;f.sessionId=null,f.configDir=s||null,f.agent=t,me(t),$.value=re(t)||$.peek(),localStorage.setItem("model",$.peek()),we(),(0,n.batch)(()=>{y.value={id:i,name:i,path:a.path,machineId:M.peek()||f.machineId},O.value=a.path,F.value=a.path,J.value=a.path,A.value="",U.value=null}),(0,n.$)("topbar-project").textContent=a.path;let r=(0,n.$)("files-root-input");r&&(r.value=a.path);let c=(0,n.$)("git-root-input");c&&(c.value=a.path),(0,n.$)("welcome").classList.add("hidden");let u=(0,n.$)("project-view");u.classList.remove("hidden"),u.style.display="flex",ft(),je(),He("chat"),x(`New ${R[t]||t} session \xB7 ${a.path}`)}catch(a){o.textContent=a.message,o.classList.remove("hidden")}}function ji(){let e=/iPad|iPhone|iPod/.test(navigator.userAgent)||navigator.platform==="MacIntel"&&navigator.maxTouchPoints>1,t=/Android/i.test(navigator.userAgent),s;e?s=`<ol class="list-decimal pl-4 space-y-2 text-sm text-base-content/80">
|
|
825
|
-
<li>\u70B9\u5E95\u90E8\u5206\u4EAB\u6309\u94AE <strong>\u5206\u4EAB</strong>\uFF08\u65B9\u6846+\u5411\u4E0A\u7BAD\u5934\uFF09</li>
|
|
826
|
-
<li>\u4E0B\u6ED1\u627E\u5230\u5E76\u70B9 <strong>\u6DFB\u52A0\u5230\u4E3B\u5C4F\u5E55</strong></li>
|
|
827
|
-
<li>\u786E\u8BA4\u540D\u79F0\u540E\u70B9 <strong>\u6DFB\u52A0</strong></li>
|
|
828
|
-
</ol>
|
|
829
|
-
<p class="text-xs text-base-content/50 mt-3">\u9700\u7528 <strong>Safari</strong> \u6253\u5F00\u672C\u7AD9\uFF1B\u5FAE\u4FE1/Chrome \u5185\u7F6E\u6D4F\u89C8\u5668\u901A\u5E38\u6CA1\u6709\u300C\u6DFB\u52A0\u5230\u4E3B\u5C4F\u5E55\u300D\u3002</p>`:t?s=`<ol class="list-decimal pl-4 space-y-2 text-sm text-base-content/80">
|
|
830
|
-
<li>\u70B9\u53F3\u4E0A\u89D2\u83DC\u5355 <strong>\u22EE</strong></li>
|
|
831
|
-
<li>\u9009\u62E9 <strong>\u5B89\u88C5\u5E94\u7528</strong> \u6216 <strong>\u6DFB\u52A0\u5230\u4E3B\u5C4F\u5E55</strong></li>
|
|
832
|
-
</ol>
|
|
833
|
-
<p class="text-xs text-base-content/50 mt-3">\u82E5\u83DC\u5355\u91CC\u6CA1\u6709\u8BE5\u9879\uFF1A\u8BF7\u7528 <strong>Chrome</strong> \u6253\u5F00\uFF0C\u5E76\u5148\u63A5\u53D7\u672C\u7AD9\u8BC1\u4E66\u63D0\u793A\u3002\u81EA\u7B7E HTTPS \u4E0B\u90E8\u5206\u673A\u578B\u4E0D\u5F39\u51FA\u4E00\u952E\u5B89\u88C5\u3002</p>`:s=`<p class="text-sm text-base-content/80">\u8BF7\u5728\u6D4F\u89C8\u5668\u83DC\u5355\u4E2D\u9009\u62E9 <strong>\u5B89\u88C5\u5E94\u7528 / \u5B89\u88C5\u7F51\u9875\u5E94\u7528 / \u6DFB\u52A0\u5230\u4E3B\u5C4F\u5E55</strong>\u3002</p>
|
|
834
|
-
<p class="text-xs text-base-content/50 mt-3">Chrome / Edge \u5730\u5740\u680F\u53F3\u4FA7\u6709\u65F6\u4E5F\u4F1A\u51FA\u73B0\u5B89\u88C5\u56FE\u6807\u3002</p>`;let o=document.getElementById("modal-pwa-install");o||(o=document.createElement("dialog"),o.id="modal-pwa-install",o.className="modal",document.body.appendChild(o)),o.innerHTML=`
|
|
835
|
-
<div class="modal-box max-w-sm">
|
|
836
|
-
<h3 class="font-bold text-base mb-3">\u5B89\u88C5\u5230\u4E3B\u5C4F\u5E55</h3>
|
|
837
|
-
${s}
|
|
838
|
-
<div class="modal-action">
|
|
839
|
-
<form method="dialog"><button class="btn btn-sm">\u77E5\u9053\u4E86</button></form>
|
|
840
|
-
</div>
|
|
841
|
-
</div>
|
|
842
|
-
<form method="dialog" class="modal-backdrop"><button>close</button></form>`,o.showModal()}async function Es(){let e=(0,n.$)("auth-screen");e&&(e.classList.remove("hidden"),e.style.display="flex");let t=(0,n.$)("app");t&&(t.classList.add("hidden"),t.style.display="none");let s=await h("GET","/api/auth/status"),o=(0,n.$)("auth-btn");s.needsSetup?((0,n.$)("auth-subtitle").textContent="Create your account to get started",o.textContent="Create Account",o.dataset.mode="register"):o.dataset.mode="login"}async function va(){let e=(0,n.$)("auth-username").value.trim(),t=(0,n.$)("auth-password").value,s=(0,n.$)("auth-error");s.classList.add("hidden");try{let o=await h("POST",`/api/auth/${(0,n.$)("auth-btn").dataset.mode||"login"}`,{username:e,password:t});f.token=o.token,localStorage.setItem("token",o.token),Rn()}catch(o){s.textContent=o.message,s.classList.remove("hidden")}}function Is(){return f.token?/^#\/session\//.test(location.hash||""):!1}function ya(e={}){if(Ls(localStorage.getItem("theme")||"dark"),!document.getElementById("xterm-css")){let l=document.createElement("link");l.id="xterm-css",l.rel="stylesheet",l.href="/xterm.min.css",document.head.appendChild(l)}if(!document.getElementById("xterm-js")){let l=document.createElement("script");l.id="xterm-js",l.src="/xterm.min.js",document.head.appendChild(l)}if(!document.getElementById("xterm-fit-js")){let l=document.createElement("script");l.id="xterm-fit-js",l.src="/addon-fit.min.js",document.head.appendChild(l)}let t=e.restoreSession??Is(),s=document.getElementById("root");if(s.style.cssText="display:flex;flex-direction:column;flex:1;height:100%",s.innerHTML=Uo()+Fo({restoreSession:t}),t){let l=(0,n.$)("session-list");l&&(l.innerHTML='<div class="px-3 py-3 text-[10px] text-base-content/35">Loading sessions\u2026</div>')}(0,n.effect)(()=>{let l=P.value,d=(0,n.$)("ws-tabs");if(!d)return;if(l.length<=1){d.innerHTML="";return}let p=O.peek();d.innerHTML=[{id:null,name:"All"},...l].map(m=>`<button class="px-3 py-1.5 text-[11px] flex-shrink-0 border-b-2 whitespace-nowrap
|
|
843
|
-
${(m.id===null?!p:p===m.configDir)?"border-primary text-primary":"border-transparent text-base-content/50 hover:text-base-content"}"
|
|
844
|
-
data-ws-tab="${(0,n.esc)(m.configDir||"")}">
|
|
845
|
-
${(0,n.esc)(m.name)}
|
|
846
|
-
</button>`).join("")}),(0,n.effect)(()=>{Re.value,ze.value,Le.value,Me.value,ue.value,v.value;let l=(0,n.$)("session-list");l&&(l.querySelector(".session-rename-input")||(l.innerHTML=Zo(),ea()))}),(0,n.effect)(()=>{E.value,Q.value,M.value,k.value;let l=(0,n.$)("welcome");l&&(document.documentElement.classList.contains("boot-restore")||document.documentElement.classList.contains("boot-pending")||l.classList.contains("hidden")||l.style.display==="none"||nt())}),(0,n.watch)(O,l=>{let d=(0,n.$)("session-filter-badge");d&&(d.style.display=l?"inline":"none",d.title=l||"")}),(0,n.effect)(()=>{let l=y.value;if(l&&l.path)na(l.path);else{let d=(0,n.$)("project-notes-bar");d&&(d.style.display="none")}}),(0,n.watch)(j,l=>{document.querySelectorAll("[data-tab]").forEach(p=>{let m=p.dataset.tab===l;p.classList.toggle("text-primary",m),p.classList.toggle("border-primary",m),p.classList.toggle("text-base-content/50",!m),p.classList.toggle("border-transparent",!m)});let d=(0,n.$)("project-view");d&&!d.classList.contains("hidden")&&(d.style.display="flex",d.style.flexDirection="column",d.style.minHeight="0",d.style.flex="1"),["chat","files","git","shell","memory"].forEach(p=>{let m=(0,n.$)(`tab-${p}`);if(!m)return;let b=p===l;m.classList.toggle("hidden",!b),b?(m.style.display="flex",m.style.flexDirection="column",m.style.flex="1 1 0",m.style.minHeight="0",m.style.overflow="hidden"):m.style.display="none"}),l==="memory"&&vs(),l==="shell"&&requestAnimationFrame(()=>{document.dispatchEvent(new CustomEvent("shell-tab-shown"))})}),ba(),ha();let o=null;document.addEventListener("sessions-changed",()=>{o&&clearTimeout(o),o=setTimeout(()=>{o=null,ye({waitFresh:!1})},1200)}),document.addEventListener("router:home",Pn),document.addEventListener("router:session",async({detail:{id:l,tab:d}})=>{let p=E.peek().find(b=>b.sessionId===l),m=Je();await ke(l,p?.cwd||(m?.sessionId===l?m.cwd:null)||null,p?.configDir||(m?.sessionId===l?m.configDir:null)||null,p?.agent||(m?.sessionId===l?m.agent:null)||null,p?.machineId||(m?.sessionId===l?m.machineId:null)||null,{tab:d||"chat"})}),n.delegate.on("click","#btn-home",Pn),n.delegate.on("click","#auth-btn",va),n.delegate.on("keydown","#auth-username, #auth-password",l=>{l.key==="Enter"&&va()}),n.delegate.on("click","#hamburger",()=>(0,n.$)("sidebar").classList.contains("open")?Bn():fa()),n.delegate.on("click","#sidebar-overlay",Bn),n.delegate.on("input","#session-search",(l,d)=>{N.value=d.value,In(d.value)}),n.delegate.on("click","#session-filter-badge",()=>{O.value=null}),n.delegate.on("click","[data-ws-tab]",(l,d)=>{O.value=d.dataset.wsTab||null}),n.delegate.on("click","#sidebar-refresh",()=>{ye(),N.peek().trim()&&gs(N.peek())}),n.delegate.on("click","[data-view]",(l,d)=>{Le.value=d.dataset.view,localStorage.setItem("sidebarView",d.dataset.view)}),n.delegate.on("click","[data-agent-filter]",(l,d)=>{Q.value=d.dataset.agentFilter,localStorage.setItem("agentFilter",d.dataset.agentFilter),N.peek().trim()&&In(N.peek())}),n.delegate.on("change","#time-range",(l,d)=>{te.value=d.value,localStorage.setItem("timeRange",d.value),ye({waitFresh:!0}),N.peek().trim()&&In(N.peek())}),n.delegate.on("dblclick","[data-project-cwd]",(l,d)=>{let p=d.dataset.projectCwd;if(!p)return;let m=p.split("/").filter(Boolean).pop()||p;ks({id:m,name:m,path:p}),x(`Project \xB7 ${p}`)}),n.delegate.on("click","#btn-pwa-install",async()=>{await Zs()||ji()}),document.addEventListener("pwa-installable",()=>{document.getElementById("btn-pwa-install")?.classList.remove("hidden")}),n.delegate.on("click","#btn-settings",Eo),n.delegate.on("click","#btn-meta-agent",()=>Ho()),n.delegate.on("click","#btn-meta-agent-welcome",()=>ps("chat")),n.delegate.on("click","#btn-theme",xa),n.delegate.on("click","#btn-cal-prev",()=>{ms(-1),nt(ve())}),n.delegate.on("click","#btn-cal-next",()=>{ms(1),nt(ve())}),n.delegate.on("click","[data-cal-date]",(l,d)=>{Ko(d.dataset.calDate),nt(ve())}),n.delegate.on("click","[data-session-id]",(l,d)=>{l.target.closest("[data-fav-toggle]")||l.target.closest("[data-hide-toggle]")||l.target.closest("[data-rename-session]")||l.target.closest(".session-rename-input")||l.target.closest(".session-title-wrap.session-rename-active")||(ke(d.dataset.sessionId,d.dataset.sessionCwd||null,d.dataset.sessionConfigDir||null,d.dataset.sessionAgent||"claude",d.dataset.sessionMachine||null),ma())}),n.delegate.on("click","[data-rename-session]",(l,d)=>{l.preventDefault(),l.stopPropagation();let p=d.closest(".session-title-wrap")||d,m=p.dataset.renameSessionId||p.dataset.sessionId;m&&ws({sessionId:m,agent:p.dataset.renameSessionAgent||"claude",machineId:p.dataset.renameSessionMachine||null,fallbackDisplay:p.dataset.renameSessionDisplay||"",mountEl:p})}),n.delegate.on("click","[data-rename-bar]",(l,d)=>{if(l.preventDefault(),l.stopPropagation(),!f.sessionId)return;let p=document.getElementById("session-title-display")||d;ws({sessionId:f.sessionId,agent:f.agent||S.peek()||"claude",machineId:f.machineId||null,mountEl:p})}),n.delegate.on("click","[data-folder]",(l,d)=>{let p=d.dataset.folder,b=d.querySelector("span")?.textContent.trim()==="\u25BE",g=new Set(ot.peek()),W=new Set(at.peek());b?(g.delete(p),W.add(p)):(g.add(p),W.delete(p)),(0,n.batch)(()=>{ot.value=g,at.value=W})}),n.delegate.on("click","#memory-refresh",vs),n.delegate.on("click","[data-tab]",(l,d)=>He(d.dataset.tab));let a=async(l,d)=>{let p=(0,n.$)(l),m=p?.value.trim();if(!m){d.value="";return}try{let b=await h("POST","/api/resolve-path",{path:m});if(b.isDir===!1)throw new Error("Path is not a directory on the target machine");d.value=b.path,p.value=b.path;let g=y.peek();g&&(y.value={...g,path:b.path})}catch(b){p.style.color="oklch(var(--er))",p.title=b.message,setTimeout(()=>{p.style.color="",p.title=""},2e3)}};n.delegate.on("click","#files-root-btn",()=>a("files-root-input",F)),n.delegate.on("click","#git-root-btn",()=>a("git-root-input",J)),n.delegate.on("keydown","#files-root-input",l=>{l.key==="Enter"&&a("files-root-input",F)}),n.delegate.on("keydown","#git-root-input",l=>{l.key==="Enter"&&a("git-root-input",J)}),n.delegate.on("click","#btn-opts",()=>{(0,n.$)("chat-opts").classList.toggle("hidden")}),n.delegate.on("change","#sel-agent",(l,d)=>{let p=S.peek(),m=d.value;p!==m&&(f.sessionId=null,me(m),$.value=re(m)||$.peek(),localStorage.setItem("model",$.peek()),we(),x(`Agent \u2192 ${R[m]||m} \xB7 model ${$.peek()} \xB7 new session`))}),n.delegate.on("change","#sel-convert-agent",async(l,d)=>{let p=d.value;if(!p)return;let m=f.sessionId;if(!m){alert("No active session to convert."),d.value="";return}let b=f.agent||S.peek()||"claude";if(b===p){alert("Cannot convert to the same agent."),d.value="";return}d.disabled=!0,x(`Converting session ${m.slice(0,8)} from ${b} to ${p}...`);try{let g=await h("POST",`/api/sessions/${m}/convert`,{targetAgent:p,sourceAgent:b});g&&g.success?(x(`\u2713 Converted successfully. New session: ${g.targetSessionId.slice(0,8)}`),await ye(),await ke(g.targetSessionId,y.peek()?.path||null,null,g.targetAgent),xn()):x("Failed to convert session.")}catch(g){x(`Error converting session: ${g.message}`),alert(`Error converting session: ${g.message}`)}finally{d.disabled=!1,d.value=""}}),n.delegate.on("change","#sel-model",(l,d)=>{$.value=d.value,localStorage.setItem("model",d.value),bs()}),n.delegate.on("click","#btn-model-add",()=>xs({editCurrent:!1})),n.delegate.on("click","#btn-model-edit",()=>xs({editCurrent:!0})),n.delegate.on("change","#sel-effort",(l,d)=>{pe.value=d.value}),n.delegate.on("change","#sel-permission",(l,d)=>{Mt.value=d.value}),n.delegate.on("change","#sel-density",(l,d)=>{Yn(d.value),x(`Density \u2192 ${d.value} (applies to new tool calls; reload session to re-group history)`)}),document.addEventListener("agent-changed",we),n.delegate.on("click","#btn-new-session, #btn-new-session-welcome",ga),n.delegate.on("click","#new-session-cancel",()=>(0,n.$)("modal-new-session").close()),n.delegate.on("click","#new-session-start",Ts),n.delegate.on("change","#new-session-agent",Ms),n.delegate.on("keydown","#new-session-path",l=>{l.key==="Enter"&&Ts()}),n.delegate.on("click","#send-btn",hn),n.delegate.on("click","#stop-btn",xo),n.delegate.on("click","#btn-logout",()=>{f.token=null,localStorage.removeItem("token"),Z(null),Ee.value=!1,location.reload()}),n.delegate.on("click","[data-pick-machine]",async(l,d)=>{let p=d.dataset.pickMachine;if(!p||p===M.peek()){Vt();return}await ua(p,{forceReload:!0})});let i=async()=>{let l=(0,n.$)("machine-picker-status");if(l&&(l.textContent="\u5237\u65B0\u4E2D\u2026"),await wt(),$s(),Nn(),st(),l){let d=(D.peek()||[]).filter(p=>p.online!==!1).length;l.textContent=`${d} online`}};n.delegate.on("click","#btn-refresh-machines",i),n.delegate.on("click","#btn-refresh-machines-menu",async l=>{l.stopPropagation(),await i()}),n.delegate.on("click","#btn-machine-menu",async l=>{l.stopPropagation(),gt();let d=(0,n.$)("machine-menu");d&&(d.classList.contains("hidden")?(await wt(),Nn(),d.classList.remove("hidden")):d.classList.add("hidden"))}),n.delegate.on("click","#btn-recent-sessions",l=>{l.preventDefault(),l.stopPropagation(),Vt(),Jo(ve());let d=!(0,n.$)("recent-menu")?.classList.contains("hidden"),p=(0,n.$)("btn-recent-sessions");p&&p.setAttribute("aria-expanded",d?"true":"false")}),n.delegate.on("click","#recent-menu-backdrop",l=>{l.preventDefault(),l.stopPropagation(),gt(),(0,n.$)("btn-recent-sessions")?.setAttribute("aria-expanded","false")}),document.addEventListener("click",l=>{let d=(0,n.$)("machine-menu-wrap");d&&!d.contains(l.target)&&Vt();let p=(0,n.$)("recent-menu"),m=(0,n.$)("btn-recent-sessions"),b=l.target;if(p&&!p.classList.contains("hidden")){if(p.contains(b)||m?.contains(b))return;gt(),m?.setAttribute("aria-expanded","false")}}),window.addEventListener("resize",()=>{(0,n.$)("recent-menu")&&!(0,n.$)("recent-menu").classList.contains("hidden")&&fs()}),n.delegate.on("click","#recent-menu [data-session-id]",()=>{gt(),(0,n.$)("btn-recent-sessions")?.setAttribute("aria-expanded","false")}),n.delegate.on("click","#btn-edit-project-notes, #project-goal-text, #project-notes-text",()=>{sa()}),n.delegate.on("click","#btn-cancel-project-notes",()=>{yt()});let r=async()=>{let l=(0,n.$)("project-goal-input")?.value?.trim()??"",d=(0,n.$)("project-notes-input")?.value?.trim()??"",p=y.peek();if(!(!p||!p.path))try{let m=await h("POST","/api/projects/notes",{root:p.path,goal:l,notes:d});ta(m.goal||"",m.notes||""),yt()}catch(m){alert(`Error saving project notes: ${m.message}`)}};n.delegate.on("click","#btn-save-project-notes",r),n.delegate.on("keydown","#project-goal-input",l=>{l.key==="Enter"&&r(),l.key==="Escape"&&yt()}),n.delegate.on("keydown","#project-notes-input",l=>{l.key==="Escape"&&yt()}),n.delegate.on("click","[data-fav-toggle]",(l,d)=>{l.stopPropagation(),l.preventDefault();let p=d.dataset.favSessionMachine||f.machineId;p&&(f.machineId=p,localStorage.setItem("machineId",p)),ia(d.dataset.favSessionId,d.dataset.favSessionAgent||"claude",{machineId:p||null})}),n.delegate.on("click","[data-hide-toggle]",(l,d)=>{l.stopPropagation(),l.preventDefault();let p=d.dataset.hideSessionMachine||f.machineId;p&&(f.machineId=p,localStorage.setItem("machineId",p)),ra(d.dataset.hideSessionId,d.dataset.hideSessionAgent||"claude",{machineId:p||null})}),n.delegate.on("click","#btn-edit-session-notes, #session-notes-text",()=>{oa()}),n.delegate.on("click","#btn-cancel-session-notes",()=>ce()),n.delegate.on("click","#btn-save-session-notes",async()=>{let l=(0,n.$)("session-notes-input")?.value?.trim()||"";if(!f.sessionId)return;let d=f.agent||S.peek()||"claude";await ys(f.sessionId,d,l)&&ce()}),n.delegate.on("keydown","#session-notes-input",async l=>{if(l.key==="Escape"&&ce(),l.key==="Enter"){let d=l.target.value.trim();if(!f.sessionId)return;let p=f.agent||S.peek()||"claude";await ys(f.sessionId,p,d)&&ce()}}),n.delegate.on("click","#btn-fav-filter",()=>{let l=!Pe.peek();Pe.value=l,localStorage.setItem("favoritesOnly",l?"1":"0"),(0,n.$)("btn-fav-filter")?.classList.toggle("active",l),l&&Ne.peek()&&(Ne.value=!1,localStorage.setItem("showHiddenOnly","0"),(0,n.$)("btn-hidden-filter")?.classList.remove("active"))}),n.delegate.on("click","#btn-hidden-filter",()=>{let l=!Ne.peek();Ne.value=l,localStorage.setItem("showHiddenOnly",l?"1":"0"),(0,n.$)("btn-hidden-filter")?.classList.toggle("active",l),l&&Pe.peek()&&(Pe.value=!1,localStorage.setItem("favoritesOnly","0"),(0,n.$)("btn-fav-filter")?.classList.remove("active"))}),we(),mn();let c=(0,n.$)("sel-density");c&&(c.value=Te.peek()||"normal");let u=(0,n.$)("time-range");u&&(u.value=te.peek()||"14"),document.querySelectorAll("[data-agent-filter]").forEach(l=>{l.classList.toggle("active",l.dataset.agentFilter===Q.peek())}),(0,n.$)("btn-fav-filter")?.classList.toggle("active",Pe.peek()),(0,n.$)("btn-hidden-filter")?.classList.toggle("active",Ne.peek()),document.querySelectorAll("[data-view]").forEach(l=>{let d=l.dataset.view===Le.peek();l.classList.toggle("bg-primary/15",d),l.classList.toggle("text-primary",d),l.classList.toggle("font-medium",d),l.classList.toggle("text-base-content/50",!d)})}mt();C();I();K();function Di(e){let t=e.split(".").pop()?.toLowerCase();return{js:"\u{1F4C4}",ts:"\u{1F4C4}",jsx:"\u{1F4C4}",tsx:"\u{1F4C4}",py:"\u{1F40D}",md:"\u{1F4DD}",json:"\u2699\uFE0F",html:"\u{1F310}",css:"\u{1F3A8}",sh:"\u26A1",env:"\u{1F511}",docx:"\u{1F4D8}",pptx:"\u{1F4D9}",xlsx:"\u{1F4D7}",pdf:"\u{1F4D5}"}[t]||"\u{1F4C4}"}function _i(e){let t=(e||"").split("/").pop()||"",s=t.lastIndexOf(".");return s>=0?t.slice(s+1).toLowerCase():""}function wa(e){let t=_i(e);return t==="docx"||t==="pptx"||t==="xlsx"||t==="pdf"?t:null}function Hi(e){return e==null?"":e>1024*1024?`${(e/(1024*1024)).toFixed(1)} MB`:e>1024?`${(e/1024).toFixed(1)} KB`:`${e} B`}function Pi(e){let t=atob(e),s=new Uint8Array(t.length);for(let o=0;o<t.length;o++)s[o]=t.charCodeAt(o);return s.buffer}var Cs=new Map;function On(e){if(Cs.has(e))return Cs.get(e);let t=new Promise((s,o)=>{let a=document.querySelector(`script[data-src="${e}"]`);if(a){if(a.dataset.loaded==="1")return s();a.addEventListener("load",()=>s()),a.addEventListener("error",()=>o(new Error(`Failed to load ${e}`)));return}let i=document.createElement("script");i.src=e,i.async=!0,i.dataset.src=e,i.onload=()=>{i.dataset.loaded="1",s()},i.onerror=()=>o(new Error(`Failed to load ${e}`)),document.head.appendChild(i)});return Cs.set(e,t),t}function Ni(e){let s=((e||"").split("/").pop()||"").toLowerCase(),o={dockerfile:"dockerfile",makefile:"makefile",gemfile:"ruby",rakefile:"ruby",procfile:"yaml","cmakelists.txt":"cmake"};if(o[s])return o[s];let a=s.includes(".")?s.split(".").pop():"";return{js:"javascript",mjs:"javascript",cjs:"javascript",jsx:"javascript",ts:"typescript",tsx:"typescript",mts:"typescript",cts:"typescript",py:"python",pyw:"python",pyi:"python",rb:"ruby",go:"go",rs:"rust",java:"java",kt:"kotlin",kts:"kotlin",c:"c",h:"c",cpp:"cpp",cc:"cpp",cxx:"cpp",hpp:"cpp",hh:"cpp",cs:"csharp",swift:"swift",php:"php",scala:"scala",sh:"bash",bash:"bash",zsh:"bash",fish:"bash",ps1:"powershell",psm1:"powershell",html:"xml",htm:"xml",xhtml:"xml",xml:"xml",svg:"xml",css:"css",scss:"scss",less:"less",json:"json",jsonc:"json",json5:"json",yml:"yaml",yaml:"yaml",toml:"ini",ini:"ini",conf:"ini",env:"bash",md:"markdown",mdx:"markdown",markdown:"markdown",sql:"sql",graphql:"graphql",gql:"graphql",vue:"xml",svelte:"xml",r:"r",lua:"lua",pl:"perl",pm:"perl",dart:"dart",ex:"elixir",exs:"elixir",erl:"erlang",zig:"zig",nim:"nim",clj:"clojure",cljs:"clojure",diff:"diff",patch:"diff",dockerfile:"dockerfile"}[a]||""}function Ri(e){!e||typeof hljs>"u"||e.querySelectorAll("pre code.file-preview-code").forEach(t=>{try{delete t.dataset.highlighted,hljs.highlightElement(t)}catch{}})}function Yt(e,t,s){let o=Hi(t);return`
|
|
847
|
-
<div class="flex items-center gap-2 px-4 py-1.5 border-b border-base-300 flex-shrink-0 bg-base-200">
|
|
848
|
-
<span class="text-xs text-base-content/40 flex-1 truncate">${(0,n.esc)(e)} \xB7 ${(0,n.esc)(o)}${s?` \xB7 <span class="text-primary/70">${(0,n.esc)(s)}</span>`:""}</span>
|
|
849
|
-
<button type="button" class="btn btn-ghost btn-xs" data-download-path="${(0,n.esc)(e)}">\u2193 Download</button>
|
|
850
|
-
</div>`}function Bi(e){return!Array.isArray(e)||!e.length?'<div class="p-4 text-xs text-base-content/40">Empty directory</div>':(e.sort((t,s)=>s.isDir-t.isDir||t.name.localeCompare(s.name)),e.map(t=>`
|
|
851
|
-
<div class="flex items-center gap-2 px-4 py-1.5 hover:bg-base-300 group"
|
|
852
|
-
${t.isDir?`data-dir-path="${(0,n.esc)(t.path)}"`:`data-file-path="${(0,n.esc)(t.path)}"`}>
|
|
853
|
-
<span class="cursor-pointer flex-shrink-0">${t.isDir?"\u{1F4C1}":Di(t.name)}</span>
|
|
854
|
-
<span class="text-sm flex-1 truncate cursor-pointer ${t.isDir?"text-primary":""}">${(0,n.esc)(t.name)}</span>
|
|
855
|
-
${t.isDir?"":`
|
|
856
|
-
<button type="button" class="file-row-action btn btn-ghost btn-xs px-1.5 text-base-content/50 hover:text-base-content"
|
|
857
|
-
data-download-path="${(0,n.esc)(t.path)}" title="Download" aria-label="Download">\u2193</button>
|
|
858
|
-
<button type="button" class="file-row-action btn btn-ghost btn-xs px-1.5 text-error/60 hover:text-error"
|
|
859
|
-
data-delete-path="${(0,n.esc)(t.path)}" title="Delete" aria-label="Delete">\u2715</button>`}
|
|
860
|
-
</div>`).join(""))}function Oi(e,t,s){let o=/[\x00-\x08\x0e-\x1f]/.test(t.slice(0,512)),a=Ni(e),i=a?`language-${(0,n.esc)(a)}`:"",r=o?`<div class="p-4 text-xs text-base-content/40">Binary file \u2014 <button class="text-primary hover:underline" data-download-path="${(0,n.esc)(e)}">Download</button></div>`:`<pre class="file-preview p-4 font-mono text-xs leading-relaxed overflow-auto flex-1 m-0"><code class="hljs file-preview-code ${i}">${(0,n.esc)(t)}</code></pre>`;return Yt(e,s,o?"binary":a||"text")+r}async function Ui(e){if(await On("https://cdn.jsdelivr.net/npm/mammoth@1.8.0/mammoth.browser.min.js"),typeof mammoth>"u")throw new Error("mammoth failed to load");let t=await mammoth.convertToHtml({arrayBuffer:e});return`<div class="office-preview office-docx overflow-auto flex-1 p-4 md">${t.messages?.length?`<div class="office-warn text-xs text-warning/80 px-3 py-1">${t.messages.length} conversion note(s)</div>`:""}<div class="office-docx-body">${t.value||'<p class="text-base-content/40">Empty document</p>'}</div></div>`}async function Fi(e){if(await On("https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js"),typeof XLSX>"u")throw new Error("SheetJS failed to load");let t=XLSX.read(new Uint8Array(e),{type:"array"}),s=t.SheetNames||[];if(!s.length)return'<div class="p-4 text-xs text-base-content/40">Empty workbook</div>';let o=s.map((i,r)=>`<button type="button" class="office-sheet-tab${r===0?" active":""}" data-sheet-idx="${r}">${(0,n.esc)(i)}</button>`).join(""),a=s.map((i,r)=>{let c=t.Sheets[i],u;try{u=XLSX.utils.sheet_to_html(c,{id:`xlsx-sheet-${r}`,editable:!1})}catch{u='<p class="text-base-content/40 text-xs p-2">Unable to render this sheet</p>'}return`<div class="office-sheet-pane${r===0?"":" hidden"}" data-sheet-pane="${r}">${u}</div>`}).join("");return`
|
|
861
|
-
<div class="office-preview office-xlsx flex flex-col flex-1 min-h-0 overflow-hidden">
|
|
862
|
-
<div class="office-sheet-tabs flex gap-1 px-2 py-1.5 border-b border-base-300 overflow-x-auto flex-shrink-0 bg-base-200">${o}</div>
|
|
863
|
-
<div class="office-sheet-body flex-1 overflow-auto p-2">${a}</div>
|
|
864
|
-
</div>`}function qi(e){let t=[],s=/<a:p[\s>][\s\S]*?<\/a:p>/g,o=/<a:t(?:\s[^>]*)?>([\s\S]*?)<\/a:t>/g,a,i=e.match(s)||[e];for(let r of i){let c=[];for(o.lastIndex=0;(a=o.exec(r))!==null;){let u=a[1].replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&").replace(/"/g,'"').replace(/'/g,"'");u&&c.push(u)}c.length&&t.push(c.join(""))}return t}async function Wi(e){if(await On("https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js"),typeof JSZip>"u")throw new Error("JSZip failed to load");let t=await JSZip.loadAsync(e),s=Object.keys(t.files).filter(a=>/^ppt\/slides\/slide\d+\.xml$/i.test(a)).sort((a,i)=>{let r=parseInt(a.match(/slide(\d+)/i)?.[1]||"0",10),c=parseInt(i.match(/slide(\d+)/i)?.[1]||"0",10);return r-c});if(!s.length)return'<div class="p-4 text-xs text-base-content/40">No slides found in this presentation</div>';let o=[];for(let a=0;a<s.length;a++){let i=await t.files[s[a]].async("string"),r=qi(i),c=r.length?r.map(u=>`<p>${(0,n.esc)(u)}</p>`).join(""):'<p class="text-base-content/40 italic">\uFF08\u6B64\u9875\u65E0\u53EF\u63D0\u53D6\u6587\u672C / \u53EF\u80FD\u4EE5\u56FE\u7247\u4E3A\u4E3B\uFF09</p>';o.push(`
|
|
865
|
-
<article class="office-slide-card">
|
|
866
|
-
<header class="office-slide-num">Slide ${a+1}</header>
|
|
867
|
-
<div class="office-slide-body">${c}</div>
|
|
868
|
-
</article>`)}return`
|
|
869
|
-
<div class="office-preview office-pptx overflow-auto flex-1 p-3 space-y-3">
|
|
870
|
-
<p class="text-xs text-base-content/50 px-1">\u6587\u672C\u9884\u89C8\uFF08\u7248\u5F0F/\u56FE\u7247\u4E0D\u8FD8\u539F\uFF09\xB7 ${s.length} slide(s)</p>
|
|
871
|
-
${o.join("")}
|
|
872
|
-
</div>`}async function Gi(){if(await On("https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"),typeof pdfjsLib>"u")throw new Error("pdf.js failed to load");return pdfjsLib.GlobalWorkerOptions.workerSrc="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js",pdfjsLib}async function zi(e){let s=await(await Gi()).getDocument({data:e}).promise,o=s.numPages,a=Math.min(o,40),i=[];for(let c=1;c<=a;c++){let u=await s.getPage(c),l=u.getViewport({scale:1}),d=Math.min(1.5,820/l.width),p=u.getViewport({scale:d}),m=document.createElement("canvas");m.width=Math.floor(p.width),m.height=Math.floor(p.height),m.className="office-pdf-page",m.dataset.page=String(c),await u.render({canvasContext:m.getContext("2d"),viewport:p}).promise,i.push(`
|
|
873
|
-
<figure class="office-pdf-figure">
|
|
874
|
-
<figcaption class="office-pdf-cap">Page ${c} / ${o}</figcaption>
|
|
875
|
-
${m.outerHTML}
|
|
876
|
-
</figure>`)}let r=o>a?`<p class="text-xs text-base-content/50 px-2">Showing first ${a} of ${o} pages \u2014 download for full file.</p>`:"";return`
|
|
877
|
-
<div class="office-preview office-pdf overflow-auto flex-1 p-3 space-y-3">
|
|
878
|
-
<p class="text-xs text-base-content/50 px-1">PDF preview \xB7 ${o} page(s)</p>
|
|
879
|
-
${r}
|
|
880
|
-
${i.join("")}
|
|
881
|
-
</div>`}async function Ji(e,t,s){let o=wa(e),a={docx:"Word",pptx:"PowerPoint",xlsx:"Excel",pdf:"PDF"},i=Pi(t),r;if(o==="docx")r=await Ui(i);else if(o==="xlsx")r=await Fi(i);else if(o==="pptx")r=await Wi(i);else if(o==="pdf")r=await zi(i);else throw new Error("Unsupported office type");return Yt(e,s,a[o]||o)+r}function Ki(e,t){let s=e?e.split("/"):[],o=t?s.slice(0,-1):s,a='<span class="cursor-pointer text-primary hover:underline" data-dir-path="">root</span>',i="";for(let r of o)i=i?`${i}/${r}`:r,a+=` <span class="text-base-content/40">/</span> <span class="cursor-pointer text-primary hover:underline" data-dir-path="${(0,n.esc)(i)}">${(0,n.esc)(r)}</span>`;return t&&(a+=` <span class="text-base-content/40">/</span> <span>${(0,n.esc)(s.at(-1))}</span>`),a}function Vi(){let e=y.peek(),t=F.peek()||e?.path;if(!t)return;let s=document.createElement("input");s.type="file",s.multiple=!0,s.addEventListener("change",async()=>{let o=A.peek(),a=e?.id||"_",i=[...s.files],r=!1;for(let c of i){let u=o?`${o}/${c.name}`:c.name,l=`/api/projects/${a}/file?root=${encodeURIComponent(t)}&path=${encodeURIComponent(u)}`;try{let d=await fetch(l,{method:"POST",headers:lt(),body:c,duplex:"half"});if(!d.ok){let p=await d.json().catch(()=>({}));throw new Error(p.error||`HTTP ${d.status}`)}}catch(d){alert(`Upload failed (${c.name}): ${d.message}`),r=!0;break}}if(!r){let c=A.peek();A.value=c+"\0",A.value=c}}),s.click()}async function Yi(e,t){let s=F.peek()||e?.path||"";if(!s){alert("No path selected \u2014 set root first");return}let o=e?.id||"_",a=(t||"").split("/").pop()||"download",i=new URLSearchParams({root:s,path:t,download:"true"}),r=`/api/projects/${o}/file?${i}`;try{let c=await fetch(r,{headers:lt(),credentials:"same-origin"});if(!c.ok){let p=await c.json().catch(()=>({}));throw new Error(p.error||`HTTP ${c.status}`)}let u=await c.blob(),l=URL.createObjectURL(u),d=document.createElement("a");d.href=l,d.download=a,d.rel="noopener",d.style.display="none",document.body.appendChild(d),d.click(),d.remove(),setTimeout(()=>URL.revokeObjectURL(l),6e4)}catch(c){try{let u=`/api/projects/${o}/file?${Vs({root:s,path:t,download:"true"})}`,l=document.createElement("a");l.href=u,l.download=a,l.rel="noopener",l.target="_blank",document.body.appendChild(l),l.click(),l.remove()}catch{alert(`Download failed: ${c.message}`)}}}async function Xi(e,t){if(!confirm(`Delete "${t.split("/").pop()}"?`))return;let s=F.peek()||e?.path||"",o=e?.id||"_";try{await h("DELETE",`/api/projects/${o}/file?root=${encodeURIComponent(s)}&path=${encodeURIComponent(t)}`),A.value=A.peek()+" ",A.value=A.peek().trimEnd()}catch(a){alert(`Delete failed: ${a.message}`)}}async function Qi(e,t,s,o){let a=s.size??(typeof s.content=="string"?s.content.length:0),i=wa(t);if(i&&s.encoding==="base64"){e.innerHTML=Yt(t,a,i)+`<div class="p-4 text-xs text-base-content/40">Parsing ${i.toUpperCase()}\u2026</div>`;try{let r=await Ji(t,s.content,a);if(o.aborted)return;e.innerHTML=r}catch(r){if(o.aborted)return;e.innerHTML=Yt(t,a,i)+`<div class="p-4 text-xs text-error">Preview failed: ${(0,n.esc)(r.message)} \u2014 <button class="text-primary hover:underline" data-download-path="${(0,n.esc)(t)}">Download</button></div>`}return}if(s.encoding==="base64"){e.innerHTML=Yt(t,a,"binary")+`<div class="p-4 text-xs text-base-content/40">Binary file \u2014 <button class="text-primary hover:underline" data-download-path="${(0,n.esc)(t)}">Download</button></div>`;return}e.innerHTML=Oi(t,s.content||"",a),Ri(e)}function ka(){(0,n.effect)(()=>{let e=(0,n.$)("files-breadcrumb");if(!e)return;let t=U.value!==null;e.innerHTML=`
|
|
882
|
-
${Ki(U.value??A.value,t)}
|
|
883
|
-
<div class="flex-1"></div>
|
|
884
|
-
${t?"":'<button id="btn-upload" class="btn btn-ghost btn-xs text-base-content/50 hover:text-base-content">\u2191 Upload</button>'}`}),(0,n.asyncEffect)(async e=>{let t=j.value,s=y.value,o=(F.value||s?.path||"").trim(),a=U.value,i=A.value,r=(0,n.$)("files-area");if(!r||t!=="files")return;if(!o){r.innerHTML='<div class="p-4 text-xs text-base-content/40">No path yet \u2014 open a session (uses that machine's cwd) or type an absolute path and press Go.</div>';return}let c=s?.id||"_";r.innerHTML='<div class="p-4 text-xs text-base-content/40">Loading\u2026</div>';try{if(a!==null){let d=await h("GET",`/api/projects/${c}/file?root=${encodeURIComponent(o)}&path=${encodeURIComponent(a)}`,void 0,e);e.aborted||await Qi(r,a,d,e);return}let u=await h("GET",`/api/projects/${c}/files?root=${encodeURIComponent(o)}&path=${encodeURIComponent(i)}`,void 0,e);if(e.aborted)return;let l=Array.isArray(u)?u:Array.isArray(u?.files)?u.files:null;if(!l){r.innerHTML=`<div class="p-4 text-xs text-error">${(0,n.esc)(u?.error||"Invalid directory listing")}</div>`;return}r.innerHTML=Bi(l)}catch(u){!e.aborted&&u?.name!=="AbortError"&&(r.innerHTML=`<div class="p-4 text-xs text-error">${(0,n.esc)(u.message)}</div>`)}}),n.delegate.on("click","[data-dir-path]",(e,t)=>{U.value=null,A.value=t.dataset.dirPath}),n.delegate.on("click","[data-file-path]",(e,t)=>{U.value=t.dataset.filePath}),n.delegate.on("click","#btn-upload",()=>Vi()),n.delegate.on("click","[data-download-path]",(e,t)=>{e.preventDefault(),e.stopPropagation();let s=t.dataset.downloadPath;s&&Yi(y.peek(),s)}),n.delegate.on("click","[data-delete-path]",(e,t)=>{e.preventDefault(),e.stopPropagation();let s=t.dataset.deletePath;s&&Xi(y.peek(),s)}),n.delegate.on("click",".office-sheet-tab",(e,t)=>{let s=t.closest(".office-xlsx");if(!s)return;let o=t.dataset.sheetIdx;s.querySelectorAll(".office-sheet-tab").forEach(a=>a.classList.toggle("active",a===t)),s.querySelectorAll(".office-sheet-pane").forEach(a=>{a.classList.toggle("hidden",a.dataset.sheetPane!==o)})})}C();I();K();var $a=(0,n.signal)(!1);function Zi(e){return e?e.split(`
|
|
885
|
-
`).map(t=>(0,n.esc)(t).replace(/\b([0-9a-f]{7})\b/,'<span class="gh">$1</span>').replace(/\(([^)]*)\)/,'<span style="color:oklch(var(--wa))">($1)</span>')).join(`
|
|
886
|
-
`):'<span class="text-base-content/40">No commits</span>'}function er(e){if(!e)return"";if(!e.isGit)return'<div class="p-4 text-sm text-base-content/50">Not a git repository</div>';let t={M:"text-warning",D:"text-error"};return`
|
|
887
|
-
<div class="px-4 py-2 text-xs text-base-content/50 border-b border-base-300">
|
|
888
|
-
Branch: <span class="text-success font-semibold">${(0,n.esc)(e.branch)}</span>
|
|
889
|
-
</div>
|
|
890
|
-
${e.files.length?e.files.map(s=>`
|
|
891
|
-
<div class="flex items-center gap-3 px-4 py-1 text-sm">
|
|
892
|
-
<span class="font-mono text-xs w-5 flex-shrink-0 ${t[s.status]||"text-success"}">${(0,n.esc)(s.status)}</span>
|
|
893
|
-
<span class="break-all">${(0,n.esc)(s.file)}</span>
|
|
894
|
-
</div>`).join(""):'<div class="px-4 py-2 text-sm text-base-content/50">Working tree clean</div>'}
|
|
895
|
-
<div class="border-t border-base-300 p-4">
|
|
896
|
-
<div class="flex gap-4 text-xs mb-3">
|
|
897
|
-
<span id="diff-unstaged" class="cursor-pointer text-primary">Unstaged</span>
|
|
898
|
-
<span id="diff-staged" class="cursor-pointer text-base-content/50">Staged</span>
|
|
899
|
-
</div>
|
|
900
|
-
<div id="diff-content" class="font-mono text-xs overflow-auto max-h-[30vh] whitespace-pre"></div>
|
|
901
|
-
</div>
|
|
902
|
-
<div class="border-t border-base-300">
|
|
903
|
-
<div class="flex items-center justify-between px-4 py-2 text-xs text-base-content/50 cursor-pointer select-none" id="git-graph-toggle">
|
|
904
|
-
<span class="uppercase tracking-wider font-medium">Git Graph</span>
|
|
905
|
-
<span id="git-graph-chevron" class="text-[10px]">\u25B6</span>
|
|
906
|
-
</div>
|
|
907
|
-
<div id="git-graph-panel" class="hidden px-4 pb-4">
|
|
908
|
-
<div id="git-graph-content" class="git-graph overflow-x-auto max-h-[35vh] overflow-y-auto">
|
|
909
|
-
<span class="text-base-content/40">Loading\u2026</span>
|
|
910
|
-
</div>
|
|
911
|
-
</div>
|
|
912
|
-
</div>`}function tr(e){return e.split(`
|
|
913
|
-
`).map(t=>t.startsWith("+")&&!t.startsWith("+++")?`<span class="diff-add">${(0,n.esc)(t)}</span>`:t.startsWith("-")&&!t.startsWith("---")?`<span class="diff-del">${(0,n.esc)(t)}</span>`:t.startsWith("@@")?`<span class="diff-hunk">${(0,n.esc)(t)}</span>`:/^(diff |index |---|[+]{3})/.test(t)?`<span class="diff-head">${(0,n.esc)(t)}</span>`:`<span>${(0,n.esc)(t)}</span>`).join(`
|
|
914
|
-
`)}function Sa(){(0,n.asyncEffect)(async e=>{let t=j.value,s=y.value,o=J.value||s?.path,a=(0,n.$)("git-area");if(!a||t!=="git")return;if(!o){a.innerHTML='<div class="p-4 text-sm text-base-content/50">Enter a project root above, or open a session with a working directory.</div>';return}let i=s?.id||"_";a.innerHTML='<div class="p-4 text-xs text-base-content/40">Loading\u2026</div>';try{let r=await h("GET",`/api/projects/${i}/git/status?root=${encodeURIComponent(o)}`,void 0,e);e.aborted||(a.innerHTML=er(r))}catch(r){!e.aborted&&r?.name!=="AbortError"&&(a.innerHTML=`<div class="p-4 text-xs text-error">${(0,n.esc)(r?.message??String(r))}</div>`)}}),(0,n.asyncEffect)(async e=>{let t=y.value,s=J.value||t?.path,o=$a.value,a=(0,n.$)("diff-content");if(!a||!s)return;let i=t?.id||"_";try{let r=await h("GET",`/api/projects/${i}/git/diff?root=${encodeURIComponent(s)}${o?"&staged=true":""}`,void 0,e);e.aborted||(a.innerHTML=r?.diff?tr(r.diff):'<span class="text-base-content/40">No changes</span>')}catch(r){!e.aborted&&r?.name!=="AbortError"&&(a.innerHTML=`<span class="text-error">${(0,n.esc)(r?.message??String(r))}</span>`)}}),n.delegate.on("click","#diff-unstaged, #diff-staged",(e,t)=>{let s=t.id==="diff-staged";$a.value=s,(0,n.$)("diff-unstaged").className=`cursor-pointer ${s?"text-base-content/50":"text-primary"}`,(0,n.$)("diff-staged").className=`cursor-pointer ${s?"text-primary":"text-base-content/50"}`}),n.delegate.on("click","#git-graph-toggle",()=>{let e=(0,n.$)("git-graph-panel"),t=(0,n.$)("git-graph-chevron"),s=e.classList.toggle("hidden");t.textContent=s?"\u25B6":"\u25BC",s||nr()})}function nr(){let e=y.peek(),t=J.peek()||e?.path,s=(0,n.$)("git-graph-content");if(!t||!s)return;let o=e?.id||"_";s.innerHTML='<span class="text-base-content/40">Loading\u2026</span>',h("GET",`/api/projects/${o}/git/log?root=${encodeURIComponent(t)}`).then(a=>{s.innerHTML=a.graph?Zi(a.graph):'<span class="text-base-content/40">No commits</span>'}).catch(a=>{s.innerHTML=`<span class="text-error">${(0,n.esc)(a.message)}</span>`})}C();I();var Y=null,T=null,Un=null,Zt=!1,Fn=null,Ma=null,Xt=0,Qt=null,As=!1;function Ta(){if(T)return;let e=(0,n.$)("terminal-container");if(e){if(typeof Terminal>"u"){setTimeout(Ta,100);return}T=new Terminal({cursorBlink:!0,fontFamily:"Fira Code, Courier New, monospace",fontSize:12,theme:{background:"#0d1117",foreground:"#c9d1d9",cursor:"#58a6ff",black:"#0d1117",red:"#ff7b72",green:"#3fb950",yellow:"#d29922",blue:"#58a6ff",magenta:"#bc8cff",cyan:"#39c5cf",white:"#b1bac4",brightBlack:"#484f58",brightRed:"#ffa198",brightGreen:"#56d364",brightYellow:"#e3b341",brightBlue:"#79c0ff",brightMagenta:"#d8b9ff",brightCyan:"#56d4dd",brightWhite:"#ffffff"}}),typeof FitAddon<"u"&&(Un=new FitAddon.FitAddon,T.loadAddon(Un)),T.open(e),js(),T.onData(t=>{Y&&Y.readyState===WebSocket.OPEN&&Y.send(JSON.stringify({type:"input",data:t}))}),window.addEventListener("resize",()=>{js(),Ds()})}}function js(){try{Un&&T&&Un.fit()}catch{}}function Ds(){Y&&Y.readyState===WebSocket.OPEN&&T&&Y.send(JSON.stringify({type:"resize",cols:T.cols,rows:T.rows}))}function Ea(){return f.machineId||M.peek()||null}function qn(e){Qt&&(clearTimeout(Qt),Qt=null),As=!0;let t=++Xt;if(Y){try{Y.close()}catch{}Y=null}Zt=!1;let s=location.protocol==="https:"?"wss":"ws",o=T?T.cols:80,a=T?T.rows:24,i=new URLSearchParams;f.token&&i.set("token",f.token),e&&i.set("cwd",e),i.set("cols",String(o)),i.set("rows",String(a));let r=Ea();k.peek()&&r&&i.set("machine",r),Fn=e||null,Ma=r,Y=new WebSocket(`${s}://${location.host}/ws/shell?${i}`),Y.addEventListener("open",()=>{t===Xt&&(Zt=!0,T?.write(`\x1B[32mConnected to interactive terminal\x1B[0m\r
|
|
915
|
-
`),Ds())}),Y.addEventListener("message",c=>{if(t!==Xt)return;let u;try{u=JSON.parse(c.data)}catch{return}u.type==="output"?T?.write(u.data):u.type==="exit"?T?.write(`\r
|
|
916
|
-
\x1B[33m[process exited: ${u.code}]\x1B[0m\r
|
|
917
|
-
`):u.type==="error"&&T?.write(`\r
|
|
918
|
-
\x1B[31m[error: ${u.data||u.message||""}]\x1B[0m\r
|
|
919
|
-
`)}),Y.addEventListener("close",()=>{t===Xt&&(Zt=!1,T?.write(`\r
|
|
920
|
-
\x1B[33m[disconnected]\x1B[0m\r
|
|
921
|
-
`),!(!As||j.peek()!=="shell")&&(T?.write(`\x1B[33m[reconnecting\u2026]\x1B[0m\r
|
|
922
|
-
`),Qt=setTimeout(()=>{Qt=null,qn(Fn)},1500)))}),Y.addEventListener("error",()=>{t===Xt&&T?.write(`\r
|
|
923
|
-
\x1B[31m[WebSocket error]\x1B[0m\r
|
|
924
|
-
`)})}function La(){let e=y.peek()?.path,t=(0,n.$)("term-cwd");t&&(t.textContent=e||"~"),T||Ta(),requestAnimationFrame(()=>{requestAnimationFrame(()=>{js();let s=Ea();!Zt||Fn!==(e||null)||Ma!==s?(T?.clear(),qn(e)):Ds()})})}function Ia(){document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&(j.peek()!=="shell"||!As||Zt||qn(Fn))}),(0,n.watch)(j,e=>{e==="shell"&&La()}),document.addEventListener("shell-tab-shown",()=>{j.peek()==="shell"&&La()}),document.getElementById("term-reconnect")?.addEventListener("click",()=>{T?.clear(),qn(y.peek()?.path)})}mt();gn();function _s(){let e=document.documentElement;e.classList.add("boot-ready"),e.classList.remove("boot-pending")}function Wn(){document.documentElement.classList.remove("boot-restore")}var Hs=rs(location.hash.slice(1)||"/"),Ca=Is()||!!(f.token&&Hs?.id);ya({restoreSession:Ca});yo();ka();Sa();To();Ia();go();Oo();ko(ke,He);(async()=>{let e=ct();if(f.token)try{let t=document.getElementById("auth-screen"),s=document.getElementById("app");if(t&&(t.style.display="none",t.classList.add("hidden")),s&&(s.style.display="flex",s.classList.remove("hidden")),Ca){document.documentElement.classList.add("boot-restore");let a=document.getElementById("welcome"),i=document.getElementById("project-view");a&&(a.classList.add("hidden"),a.style.display="none"),i&&(i.classList.remove("hidden"),i.style.cssText="display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden")}let o=await e;await Rn({hub:o,deepLink:Hs}),Hs?.id||($o(),(location.hash||"").startsWith("#/session/")||Wn()),requestAnimationFrame(()=>{requestAnimationFrame(_s)})}catch(t){console.error("[boot]",t),f.token=null,localStorage.removeItem("token"),Wn(),await Es(),_s()}else Wn(),await Es(),_s()})();document.addEventListener("router:home",()=>{Wn()});Qs();
|