anthropair 2026.2.1
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 +4 -0
- package/bin/cli.js +3 -0
- package/dist/apple-touch-icon.png +0 -0
- package/dist/assets/index-BRVcy13M.js +59 -0
- package/dist/assets/index-Dw22H9VD.css +1 -0
- package/dist/assets/livekit-client.esm-BK0XEXgq.js +38 -0
- package/dist/favicon-96x96.png +0 -0
- package/dist/favicon.ico +0 -0
- package/dist/favicon.svg +3 -0
- package/dist/index.html +81 -0
- package/dist/site.webmanifest +21 -0
- package/dist/web-app-manifest-192x192.png +0 -0
- package/dist/web-app-manifest-512x512.png +0 -0
- package/package.json +33 -0
- package/server/index.js +91 -0
- package/server/routes/agent.js +20 -0
- package/server/routes/files.js +29 -0
- package/server/routes/livekit.js +35 -0
- package/server/routes/settings.js +162 -0
- package/server/services/agent-session.js +142 -0
- package/server/services/file-tree.js +71 -0
- package/server/ws/handler.js +96 -0
package/README.md
ADDED
package/bin/cli.js
ADDED
|
Binary file
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))o(r);new MutationObserver(r=>{for(const s of r)if(s.type==="childList")for(const i of s.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&o(i)}).observe(document,{childList:!0,subtree:!0});function n(r){const s={};return r.integrity&&(s.integrity=r.integrity),r.referrerPolicy&&(s.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?s.credentials="include":r.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function o(r){if(r.ep)return;r.ep=!0;const s=n(r);fetch(r.href,s)}})();const x={chatMessages:[],tasks:[],fileTree:[],agentStatus:"idle",livekitRoom:null,pendingPermission:null,settings:K(),wsConnected:!1},T=new Map;function U(e){return e?x[e]:{...x}}function l(e,t){x[e]=t,_(e,t)}function V(e,t){return T.has(e)||T.set(e,new Set),T.get(e).add(t),()=>T.get(e).delete(t)}function _(e,t){const n=T.get(e);n&&n.forEach(o=>o(t))}function R(e,t){const n=[...x[e]||[],t];l(e,n)}function W(e,t,n){const o=(x[e]||[]).map(r=>r.id===t?{...r,...n}:r);l(e,o)}function K(){try{return JSON.parse(localStorage.getItem("claude-collab-settings")||"{}")}catch{return{}}}let g=null,N=null;const b=new Map;function D(){if(g&&g.readyState<=1)return;const t=`${location.protocol==="https:"?"wss:":"ws:"}//${location.host}/ws`;l("wsConnected",!1),I("connecting"),g=new WebSocket(t),g.onopen=()=>{console.log("[ws] connected"),l("wsConnected",!0),I("connected"),clearTimeout(N)},g.onmessage=n=>{let o;try{o=JSON.parse(n.data)}catch{console.warn("[ws] invalid message:",n.data);return}G(o)},g.onclose=()=>{console.log("[ws] disconnected"),l("wsConnected",!1),I("disconnected"),Q()},g.onerror=n=>{console.error("[ws] error:",n)}}function Q(){clearTimeout(N),N=setTimeout(()=>D(),2e3)}function I(e){const t=document.getElementById("connection-status");t&&(t.className=`status-dot ${e}`,t.title=e.charAt(0).toUpperCase()+e.slice(1))}function E(e){g&&g.readyState===WebSocket.OPEN?g.send(JSON.stringify(e)):console.warn("[ws] not connected, message dropped:",e)}function y(e,t){return b.has(e)||b.set(e,new Set),b.get(e).add(t),()=>b.get(e).delete(t)}function G(e){const t=b.get(e.type);t&&t.forEach(o=>o(e));const n=b.get("*");n&&n.forEach(o=>o(e))}function z(e,t){const n=document.createElement("div"),o=document.createElement("div");if(o.className=`tree-item ${e.type}`,o.innerHTML=`<span class="icon"></span><span class="name">${X(e.name)}</span>`,n.appendChild(o),e.type==="directory"){let r=!1,s=!1;const i=document.createElement("div");i.className="tree-children",i.style.display="none",n.appendChild(i),o.addEventListener("click",async()=>{if(s=!s,o.classList.toggle("expanded",s),i.style.display=s?"":"none",!r){r=!0,i.innerHTML='<div style="padding:4px 8px;color:var(--text-muted);font-size:12px;">Loading...</div>';try{const a=await t(e.path);i.innerHTML="";for(const d of a)i.appendChild(z(d,t))}catch{i.innerHTML='<div style="color:var(--danger);padding:4px 8px;font-size:12px;">Failed to load</div>'}}})}return n}function X(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}const Y=()=>document.getElementById("file-tree");function Z(){var e;C("."),(e=document.getElementById("refresh-files-btn"))==null||e.addEventListener("click",()=>{C(".")}),y("files:changed",()=>C("."))}async function C(e){try{const n=await(await fetch(`/api/files?path=${encodeURIComponent(e)}`)).json();return e==="."&&(l("fileTree",n),ee(n)),n}catch(t){return console.error("Failed to load file tree:",t),[]}}function ee(e){const t=Y();if(t){t.innerHTML="";for(const n of e)t.appendChild(z(n,C))}}function te(e,t){const n=document.createElement("div");n.className=`chat-msg ${e}`;const o=document.createElement("div");return o.className="msg-content",o.textContent=t,n.appendChild(o),n}const H=()=>document.getElementById("chat-messages"),j=()=>document.getElementById("chat-input"),ne=()=>document.getElementById("send-btn"),S=()=>document.getElementById("interrupt-btn");let u=null,h="";function oe(){var e,t,n;(e=ne())==null||e.addEventListener("click",M),(t=j())==null||t.addEventListener("keydown",o=>{o.key==="Enter"&&!o.shiftKey&&(o.preventDefault(),M())}),(n=S())==null||n.addEventListener("click",()=>{E({type:"agent:interrupt"})}),y("agent:text",se),y("agent:tool_call",re),y("agent:done",ie),y("agent:error",ae),y("agent:status",ce),y("agent:interrupted",le),y("agent:permission",de)}function M(){var n;const e=j(),t=(n=e==null?void 0:e.value)==null?void 0:n.trim();t&&($("user",t),R("chatMessages",{role:"user",content:t,ts:Date.now()}),E({type:"agent:send",message:t}),e.value="",e.focus(),l("agentStatus","thinking"),S().style.display="")}function O(e){const t=j();t&&(t.value=e,M())}function se(e){l("agentStatus","streaming"),u||(u=$("assistant",""),h=""),h+=e.text,u.querySelector(".msg-content").textContent=h,B()}function re(e){u||(u=$("assistant",""),h="");const t=document.createElement("div");t.className="tool-call",t.innerHTML=`
|
|
2
|
+
<div class="tool-call-header">⚙ ${L(e.tool)}</div>
|
|
3
|
+
<div class="tool-call-body"><pre>${L(JSON.stringify(e.input,null,2))}</pre></div>
|
|
4
|
+
`,t.addEventListener("click",()=>t.classList.toggle("expanded")),u.appendChild(t),B()}function ie(e){var t;if(l("agentStatus","idle"),S().style.display="none",u&&e.cost){const n=document.createElement("div");n.className="msg-meta",n.style.cssText="font-size:11px;color:var(--text-muted);margin-top:6px;",n.textContent=`Cost: $${((t=e.cost)==null?void 0:t.toFixed(4))||"?"} | ${e.duration?(e.duration/1e3).toFixed(1)+"s":""}`,u.appendChild(n)}R("chatMessages",{role:"assistant",content:h,ts:Date.now()}),u=null,h=""}function ae(e){l("agentStatus","idle"),S().style.display="none",$("assistant",`Error: ${e.error}`).style.color="var(--danger)",u=null,h=""}function ce(e){l("agentStatus",e.status),e.status==="thinking"&&(S().style.display="")}function le(){if(l("agentStatus","idle"),S().style.display="none",u){const e=document.createElement("div");e.style.cssText="color:var(--warning);font-size:12px;margin-top:4px;",e.textContent="[interrupted]",u.appendChild(e)}u=null,h=""}function de(e){l("pendingPermission",e);const t=H(),n=document.createElement("div");n.className="permission-request",n.innerHTML=`
|
|
5
|
+
<div class="perm-title">Permission Required: ${L(e.tool||"Unknown tool")}</div>
|
|
6
|
+
<div class="perm-detail" style="font-size:12px;color:var(--text-muted);margin-bottom:8px;">
|
|
7
|
+
${L(JSON.stringify(e.input||{},null,2))}
|
|
8
|
+
</div>
|
|
9
|
+
<div class="permission-buttons">
|
|
10
|
+
<button class="btn btn-sm btn-success perm-approve">Approve</button>
|
|
11
|
+
<button class="btn btn-sm btn-danger perm-deny">Deny</button>
|
|
12
|
+
</div>
|
|
13
|
+
`,n.querySelector(".perm-approve").addEventListener("click",()=>{E({type:"permission:respond",approved:!0}),n.remove(),l("pendingPermission",null)}),n.querySelector(".perm-deny").addEventListener("click",()=>{E({type:"permission:respond",approved:!1}),n.remove(),l("pendingPermission",null)}),t.appendChild(n),B()}function $(e,t){const n=te(e,t);return H().appendChild(n),B(),n}function B(){const e=H();e&&(e.scrollTop=e.scrollHeight)}function L(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}function ue(e,{onApprove:t,onReject:n}){const o=document.createElement("div");if(o.className="task-card",o.dataset.id=e.id,o.innerHTML=`
|
|
14
|
+
<div class="task-card-header">
|
|
15
|
+
<span class="badge ${e.status}">${e.status}</span>
|
|
16
|
+
<span style="font-size:11px;color:var(--text-muted);">${e.id}</span>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="prompt-preview">${pe(e.prompt||"")}</div>
|
|
19
|
+
`,e.status==="review"){const r=document.createElement("div");r.className="task-card-actions";const s=document.createElement("button");s.className="btn btn-sm btn-success",s.textContent="Approve",s.addEventListener("click",()=>t(e.id));const i=document.createElement("button");i.className="btn btn-sm btn-danger",i.textContent="Reject",i.addEventListener("click",()=>n(e.id)),r.appendChild(s),r.appendChild(i),o.appendChild(r)}return o}function pe(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}const me=()=>document.getElementById("task-queue"),fe=()=>document.getElementById("task-count");function ge(){y("task:update",ye),V("tasks",ve)}function ye(e){U("tasks").find(o=>o.id===e.task.id)?W("tasks",e.task.id,e.task):R("tasks",e.task)}function ve(e){const t=me();if(!t)return;t.innerHTML="";for(const o of e)t.appendChild(ue(o,{onApprove:r=>E({type:"task:approve",taskId:r}),onReject:r=>E({type:"task:reject",taskId:r})}));const n=fe();if(n){const o=e.filter(r=>r.status!=="done"&&r.status!=="rejected").length;n.textContent=o}}const he="modulepreload",be=function(e){return"/"+e},A={},Ee=function(t,n,o){let r=Promise.resolve();if(n&&n.length>0){let i=function(c){return Promise.all(c.map(f=>Promise.resolve(f).then(p=>({status:"fulfilled",value:p}),p=>({status:"rejected",reason:p}))))};document.getElementsByTagName("link");const a=document.querySelector("meta[property=csp-nonce]"),d=(a==null?void 0:a.nonce)||(a==null?void 0:a.getAttribute("nonce"));r=i(n.map(c=>{if(c=be(c),c in A)return;A[c]=!0;const f=c.endsWith(".css"),p=f?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${c}"]${p}`))return;const m=document.createElement("link");if(m.rel=f?"stylesheet":he,f||(m.as="script"),m.crossOrigin="",m.href=c,d&&m.setAttribute("nonce",d),document.head.appendChild(m),f)return new Promise((w,k)=>{m.addEventListener("load",w),m.addEventListener("error",()=>k(new Error(`Unable to preload CSS for ${c}`)))})}))}function s(i){const a=new Event("vite:preloadError",{cancelable:!0});if(a.payload=i,window.dispatchEvent(a),!a.defaultPrevented)throw i}return r.then(i=>{for(const a of i||[])a.status==="rejected"&&s(a.reason);return t().catch(s)})};let v=null;function Se(){var e,t;(e=document.getElementById("join-room-btn"))==null||e.addEventListener("click",we),(t=document.getElementById("share-screen-btn"))==null||t.addEventListener("click",ke)}async function we(){var n,o;const e=(o=(n=document.getElementById("room-input"))==null?void 0:n.value)==null?void 0:o.trim();if(!e)return;const t=`user-${Date.now().toString(36)}`;try{const s=await(await fetch("/api/livekit/token",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({room:e,identity:t})})).json();if(s.error){console.error("LiveKit token error:",s.error),alert(s.error);return}const{Room:i,RoomEvent:a}=await Ee(async()=>{const{Room:d,RoomEvent:c}=await import("./livekit-client.esm-BK0XEXgq.js");return{Room:d,RoomEvent:c}},[]);v=new i,v.on(a.TrackSubscribed,(d,c,f)=>{if(d.kind==="video"){const p=d.attach();d.source==="screen_share"?(document.getElementById("remote-video-container").innerHTML="",document.getElementById("remote-video-container").appendChild(p),document.getElementById("screen-share-placeholder").style.display="none"):(document.getElementById("webcam-pip").innerHTML="",document.getElementById("webcam-pip").appendChild(p),document.getElementById("webcam-pip").style.display="block")}}),v.on(a.TrackUnsubscribed,d=>{d.detach().forEach(c=>c.remove())}),v.on(a.Disconnected,()=>{l("livekitRoom",null),document.getElementById("screen-share-placeholder").style.display="",document.getElementById("remote-video-container").innerHTML="",document.getElementById("webcam-pip").innerHTML="",document.getElementById("webcam-pip").style.display="none"}),await v.connect(s.wsUrl,s.token),l("livekitRoom",e),document.getElementById("screen-share-placeholder").textContent=`Connected to "${e}"`,console.log("[livekit] connected to room:",e)}catch(r){console.error("[livekit] failed to connect:",r),alert("Failed to connect to room: "+r.message)}}async function ke(){if(!v){alert("Join a room first");return}try{const e=v.localParticipant.isScreenShareEnabled;await v.localParticipant.setScreenShareEnabled(!e),document.getElementById("share-screen-btn").textContent=e?"Share Screen":"Stop Sharing"}catch(e){console.error("[livekit] screen share error:",e)}}const Te=()=>document.getElementById("task-buttons");let P=[];async function xe(){try{const t=await(await fetch("/api/files/content?path=client/config/buttons.json")).json();P=JSON.parse(t.content)}catch{P=Ce()}Le()}function Ce(){return[{id:"explain",label:"Explain",promptTemplate:"Explain the current code in {file}",defaultVisible:!0},{id:"fix",label:"Fix Bug",promptTemplate:"Find and fix bugs in this project",defaultVisible:!0},{id:"refactor",label:"Refactor",promptTemplate:"Suggest refactoring improvements",defaultVisible:!0},{id:"test",label:"Write Tests",promptTemplate:"Write tests for the current module",defaultVisible:!0},{id:"review",label:"Code Review",promptTemplate:"Review the recent changes and provide feedback",defaultVisible:!0},{id:"docs",label:"Add Docs",promptTemplate:"Add documentation comments to the code",defaultVisible:!1},{id:"optimize",label:"Optimize",promptTemplate:"Suggest performance optimizations",defaultVisible:!1}]}function Le(){const e=Te();if(!e)return;const n=U("settings").hiddenButtons||[];e.innerHTML="";for(const o of P){if(!o.defaultVisible&&n.includes(o.id)||n.includes(o.id))continue;const r=document.createElement("button");r.className="task-btn",r.textContent=o.label,r.title=o.promptTemplate,r.addEventListener("click",()=>{if(o.requiresInput){const s=prompt(`Input for "${o.label}":`);if(!s)return;O(o.promptTemplate.replace("{input}",s))}else O(o.promptTemplate)}),e.appendChild(r)}}function $e(){const e=document.getElementById("settings-btn");e&&e.addEventListener("click",Be)}async function Be(){if(document.querySelector(".modal-overlay"))return;const e=await fetch("/api/settings"),{settings:t}=await e.json(),n=document.createElement("div");n.className="modal-overlay",n.innerHTML=`
|
|
20
|
+
<div class="modal">
|
|
21
|
+
<div class="modal-header">
|
|
22
|
+
<h2>Settings</h2>
|
|
23
|
+
</div>
|
|
24
|
+
<form class="settings-form">
|
|
25
|
+
${t.map(s=>`
|
|
26
|
+
<div class="form-group">
|
|
27
|
+
<label class="form-label" for="setting-${s.key}">${s.label}</label>
|
|
28
|
+
${s.type==="select"?`
|
|
29
|
+
<select
|
|
30
|
+
class="form-input form-select"
|
|
31
|
+
id="setting-${s.key}"
|
|
32
|
+
data-key="${s.key}"
|
|
33
|
+
data-type="${s.type}"
|
|
34
|
+
>
|
|
35
|
+
${(s.options||[]).map(i=>`
|
|
36
|
+
<option value="${i.value}" ${i.value===s.value?"selected":""}>${i.label}</option>
|
|
37
|
+
`).join("")}
|
|
38
|
+
</select>
|
|
39
|
+
`:`
|
|
40
|
+
<input
|
|
41
|
+
class="form-input"
|
|
42
|
+
id="setting-${s.key}"
|
|
43
|
+
data-key="${s.key}"
|
|
44
|
+
data-type="${s.type}"
|
|
45
|
+
type="${s.type==="secret"?"password":"text"}"
|
|
46
|
+
${s.type==="secret"?`placeholder="${s.value}"`:`value="${s.value}"`}
|
|
47
|
+
autocomplete="off"
|
|
48
|
+
/>
|
|
49
|
+
`}
|
|
50
|
+
${s.restart?'<span class="form-hint">Requires restart</span>':""}
|
|
51
|
+
</div>
|
|
52
|
+
`).join("")}
|
|
53
|
+
<div class="modal-footer">
|
|
54
|
+
<button type="button" class="btn btn-cancel">Cancel</button>
|
|
55
|
+
<button type="submit" class="btn btn-primary">Save</button>
|
|
56
|
+
</div>
|
|
57
|
+
</form>
|
|
58
|
+
</div>
|
|
59
|
+
`,document.body.appendChild(n);const o=()=>n.remove();n.querySelector(".btn-cancel").addEventListener("click",o),n.addEventListener("click",s=>{s.target===n&&o()});const r=s=>{s.key==="Escape"&&(o(),document.removeEventListener("keydown",r))};document.addEventListener("keydown",r),n.querySelector(".settings-form").addEventListener("submit",async s=>{var f;s.preventDefault();const i=n.querySelectorAll(".form-input"),a={};for(const p of i){const m=p.dataset.key,w=p.dataset.type,k=p.value.trim();if(!(w==="secret"&&!k)){if(w==="text"||w==="select"){const F=((f=t.find(J=>J.key===m))==null?void 0:f.value)||"";if(k===F)continue}a[m]=k}}if(Object.keys(a).length===0){o();return}const c=await(await fetch("/api/settings",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)})).json();o(),c.restartNeeded&&alert("Settings saved. Some changes require a server restart to take effect.")})}D();Z();oe();ge();xe();Se();$e();const Ie=new URLSearchParams(location.search),q=Ie.get("room");if(q){const e=document.getElementById("room-input");e&&(e.value=q)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}:root{--bg: #12101a;--surface: #1a1724;--surface-2: #221e2d;--border: #38314a;--text: #ece6f0;--text-muted: #9e92ad;--primary: #c88ea6;--primary-hover: #dba4bc;--danger: #f85149;--success: #3fb950;--warning: #d29922;--radius: 6px;--font: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;--mono: "SF Mono", "Fira Code", "Fira Mono", Menlo, Consolas, monospace}html,body{height:100%;font-family:var(--font);background:var(--bg);color:var(--text);font-size:14px;overflow:hidden}#app{display:grid;grid-template-columns:250px 1fr 350px;grid-template-rows:auto 1fr 200px;grid-template-areas:"toolbar toolbar toolbar" "filetree chat screenshare" "taskqueue taskqueue taskqueue";height:100vh;gap:1px;background:var(--border)}#toolbar{grid-area:toolbar;display:flex;align-items:center;justify-content:space-between;padding:8px 16px;background:var(--surface);border-bottom:1px solid var(--border)}.toolbar-left{display:flex;align-items:center;gap:12px}.toolbar-center,.toolbar-right{display:flex;align-items:center;gap:8px}.logo{font-size:16px;font-weight:600;color:var(--primary)}.logo-icon{width:24px;height:24px}.status-dot{width:8px;height:8px;border-radius:50%;display:inline-block}.status-dot.connected{background:var(--success)}.status-dot.disconnected{background:var(--danger)}.status-dot.connecting{background:var(--warning)}#room-input{background:var(--surface-2);border:1px solid var(--border);border-radius:var(--radius);color:var(--text);padding:4px 10px;font-size:13px;width:160px}.panel{background:var(--surface);display:flex;flex-direction:column;overflow:hidden}.panel-header{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--text-muted);border-bottom:1px solid var(--border);flex-shrink:0}.panel-body{flex:1;overflow-y:auto;padding:8px}#file-tree-panel{grid-area:filetree}#chat-panel{grid-area:chat}#screen-share-panel{grid-area:screenshare}#task-queue-panel{grid-area:taskqueue}.tree-item{display:flex;align-items:center;gap:6px;padding:3px 8px;cursor:pointer;border-radius:var(--radius);font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tree-item:hover{background:var(--surface-2)}.tree-item .icon{font-size:12px;width:16px;text-align:center;flex-shrink:0}.tree-children{padding-left:16px}.tree-item.directory .icon:before{content:"▸"}.tree-item.directory.expanded .icon:before{content:"▾"}.tree-item.file .icon:before{content:"📄";font-size:11px}#chat-messages{display:flex;flex-direction:column;gap:12px}.chat-msg{padding:8px 12px;border-radius:var(--radius);max-width:100%;font-size:13px;line-height:1.5;white-space:pre-wrap;word-break:break-word}.chat-msg.user{background:#2d1f3a;align-self:flex-end;border-bottom-right-radius:2px}.chat-msg.assistant{background:var(--surface-2);align-self:flex-start;border-bottom-left-radius:2px}.chat-msg .tool-call{background:#1a1721;border:1px solid var(--border);border-radius:var(--radius);padding:6px 10px;margin:6px 0;font-family:var(--mono);font-size:12px;cursor:pointer}.tool-call-header{display:flex;align-items:center;gap:6px;color:var(--warning);font-weight:600}.tool-call-body{display:none;margin-top:6px;color:var(--text-muted);overflow-x:auto}.tool-call.expanded .tool-call-body{display:block}.chat-input-row{display:flex;gap:8px;padding:8px 12px;border-top:1px solid var(--border);flex-shrink:0}#chat-input{flex:1;background:var(--surface-2);border:1px solid var(--border);border-radius:var(--radius);color:var(--text);padding:8px 12px;font-family:var(--font);font-size:13px;resize:none}#chat-input:focus{outline:none;border-color:var(--primary)}.permission-request{background:#2d1b00;border:1px solid var(--warning);border-radius:var(--radius);padding:10px;margin:6px 0}.permission-request .perm-title{color:var(--warning);font-weight:600;margin-bottom:6px}.permission-buttons{display:flex;gap:8px;margin-top:8px}.task-buttons{display:flex;flex-wrap:wrap;gap:4px;padding:6px 12px;border-top:1px solid var(--border);flex-shrink:0}.task-btn{background:var(--surface-2);border:1px solid var(--border);border-radius:16px;color:var(--text-muted);padding:4px 12px;font-size:12px;cursor:pointer;transition:all .15s}.task-btn:hover{background:var(--border);color:var(--text)}.task-queue-body{display:flex;gap:8px;padding:8px;overflow-x:auto;flex-direction:row}.task-card{background:var(--surface-2);border:1px solid var(--border);border-radius:var(--radius);padding:10px;min-width:220px;max-width:300px;flex-shrink:0}.task-card-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:6px}.badge{background:var(--border);color:var(--text-muted);padding:2px 8px;border-radius:10px;font-size:11px}.badge.queued{background:var(--border)}.badge.running{background:#1f3a1f;color:var(--success)}.badge.review{background:#3d2800;color:var(--warning)}.badge.done{background:#1a2e1a;color:var(--success)}.badge.error{background:#3d1010;color:var(--danger)}.task-card .prompt-preview{font-size:12px;color:var(--text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.task-card-actions{display:flex;gap:6px;margin-top:8px}#screen-share-container{position:relative;background:#000;padding:0}#remote-video-container{width:100%;height:100%}#remote-video-container video{width:100%;height:100%;object-fit:contain}#webcam-pip{position:absolute;bottom:8px;right:8px;width:120px;height:90px;border-radius:var(--radius);overflow:hidden;border:2px solid var(--border);display:none}#webcam-pip video{width:100%;height:100%;object-fit:cover}.placeholder-text{display:flex;align-items:center;justify-content:center;height:100%;color:var(--text-muted);font-size:13px}.btn{background:var(--surface-2);border:1px solid var(--border);border-radius:var(--radius);color:var(--text);padding:6px 14px;font-size:13px;cursor:pointer;transition:all .15s}.btn:hover{background:var(--border)}.btn-sm{padding:4px 10px;font-size:12px}.btn-xs{padding:2px 6px;font-size:11px}.btn-primary{background:var(--primary);border-color:var(--primary);color:#000;font-weight:600}.btn-primary:hover{background:var(--primary-hover)}.btn-danger{background:transparent;border-color:var(--danger);color:var(--danger)}.btn-danger:hover{background:#3d1010}.btn-success{background:transparent;border-color:var(--success);color:var(--success)}.btn-success:hover{background:#1a2e1a}.btn-icon{background:transparent;border:none;color:var(--text-muted);font-size:16px;padding:4px;cursor:pointer}.btn-icon:hover{color:var(--text)}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border);border-radius:4px}::-webkit-scrollbar-thumb:hover{background:var(--text-muted)}.modal-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:#0009;display:flex;align-items:center;justify-content:center;z-index:100}.modal{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:20px;min-width:300px;max-width:500px}.modal h2{font-size:16px;margin-bottom:0}.modal-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;padding-bottom:12px;border-bottom:1px solid var(--border)}.settings-form{display:flex;flex-direction:column;gap:12px}.form-group{display:flex;flex-direction:column;gap:4px}.form-label{font-size:12px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.3px}.form-input{background:var(--surface-2);border:1px solid var(--border);border-radius:var(--radius);color:var(--text);padding:8px 12px;font-family:var(--mono);font-size:13px}.form-input:focus{outline:none;border-color:var(--primary)}.form-select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%239e92ad' viewBox='0 0 16 16'%3E%3Cpath d='M8 11L3 6h10z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center;padding-right:30px;cursor:pointer}.form-input::placeholder{color:var(--text-muted)}.form-hint{font-size:11px;color:var(--warning)}.modal-footer{display:flex;justify-content:flex-end;gap:8px;margin-top:8px;padding-top:12px;border-top:1px solid var(--border)}.btn-cancel{background:var(--surface-2);border:1px solid var(--border);color:var(--text-muted)}.btn-cancel:hover{color:var(--text);background:var(--border)}
|