itwillsync 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,8 +8,8 @@
8
8
  <meta name="theme-color" content="#1a1a2e">
9
9
  <title>itwillsync</title>
10
10
  <link rel="manifest" href="./manifest.json">
11
- <script type="module" crossorigin src="/assets/index-Bg1a3YQa.js"></script>
12
- <link rel="stylesheet" crossorigin href="/assets/index-CmAz03xC.css">
11
+ <script type="module" crossorigin src="/assets/index-C2_Zd4WS.js"></script>
12
+ <link rel="stylesheet" crossorigin href="/assets/index-7O6Lccue.css">
13
13
  </head>
14
14
  <body>
15
15
  <div id="status-bar">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "itwillsync",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "Sync any terminal-based coding agent to your phone over local network",
5
5
  "type": "module",
6
6
  "bin": {
@@ -44,14 +44,16 @@
44
44
  "dependencies": {
45
45
  "@clack/prompts": "^1.1.0",
46
46
  "node-pty": "^1.0.0",
47
+ "@itwillsync/shared": "workspace:*",
47
48
  "qrcode-terminal": "^0.12.0",
48
49
  "ws": "^8.18.0"
49
50
  },
50
51
  "devDependencies": {
51
- "@types/node": "^25.3.5",
52
+ "@types/node": "^25.5.0",
53
+ "tweetnacl": "^1.0.3",
52
54
  "@types/ws": "^8.5.0",
53
55
  "tsup": "^8.0.0",
54
56
  "typescript": "^5.7.0",
55
- "vitest": "^4.0.18"
57
+ "vitest": "^4.1.0"
56
58
  }
57
59
  }
@@ -1 +0,0 @@
1
- *{margin:0;padding:0;box-sizing:border-box}html,body{height:100%;width:100%;background:#1a1a2e;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;color:#e0e0e0;overflow-x:hidden;-webkit-overflow-scrolling:touch}#header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:#16213e;border-bottom:1px solid #0f3460;position:sticky;top:0;z-index:10}.header-left{display:flex;align-items:center;gap:10px}.logo{font-size:16px;font-weight:700;color:#e94560;letter-spacing:-.5px}#status-dot{width:8px;height:8px;border-radius:50%;background:#e74c3c;transition:background .3s ease}#status-dot.connected{background:#2ecc71}#status-dot.reconnecting{background:#f39c12;animation:pulse 1s infinite}.header-right{display:flex;align-items:center;gap:12px}#session-count{font-size:13px;color:#a0a0b0}.header-icon-btn{background:none;border:1px solid #2a2a44;border-radius:8px;color:#a0a0b0;width:32px;height:32px;display:flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:color .15s,border-color .15s}.header-icon-btn:active{color:#e94560;border-color:#e94560}#session-list{padding:12px 12px 80px;display:flex;flex-direction:column;gap:12px}#empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:60vh;color:#606080;text-align:center;gap:8px}#empty-state .empty-icon{font-size:48px;margin-bottom:8px;opacity:.5}#empty-state p{font-size:16px}#empty-state .empty-hint{font-size:13px;color:#505070}#empty-state code{background:#252540;padding:2px 8px;border-radius:4px;font-family:Cascadia Code,Fira Code,JetBrains Mono,monospace;font-size:12px;color:#e94560}.session-card{background:#16213e;border:1px solid #0f3460;border-radius:12px;padding:14px 16px;cursor:pointer;transition:transform .15s ease,border-color .2s ease;-webkit-tap-highlight-color:transparent}.session-card:active{transform:scale(.98)}.session-card:hover{border-color:#e94560}.session-card.attention{border-color:#e94560;animation:attention-glow 2s ease-in-out infinite}@keyframes attention-glow{0%,to{box-shadow:0 0 #e9456000}50%{box-shadow:0 0 12px 2px #e945604d}}.card-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.card-agent{display:flex;align-items:center;gap:8px}.agent-dot{width:10px;height:10px;border-radius:50%;flex-shrink:0}.agent-dot.active{background:#2ecc71}.agent-dot.idle{background:#f39c12}.agent-dot.attention{background:#e94560;animation:pulse 1s infinite}.agent-name{font-size:15px;font-weight:600;color:#f0f0f0}.card-uptime{font-size:12px;color:#707090;font-variant-numeric:tabular-nums}.card-cwd{font-size:12px;color:#808098;margin-bottom:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-family:Cascadia Code,Fira Code,JetBrains Mono,monospace}.card-preview{background:#112;border-radius:6px;padding:8px 10px;margin-bottom:10px;overflow:hidden;min-height:24px;max-height:90px}.card-preview-text{font-family:Cascadia Code,Fira Code,JetBrains Mono,monospace;font-size:11px;line-height:1.4;color:#a0a0b0;white-space:pre;overflow:hidden;text-overflow:ellipsis;margin:0}.card-preview-text.empty{color:#505070;font-style:italic}.card-status{display:flex;align-items:center;gap:6px;font-size:12px;color:#a0a0b0}.status-badge{padding:2px 8px;border-radius:10px;font-size:11px;font-weight:500;text-transform:uppercase;letter-spacing:.5px}.status-badge.active{background:#2ecc7126;color:#2ecc71}.status-badge.idle{background:#f39c1226;color:#f39c12}.status-badge.attention{background:#e9456026;color:#e94560}.attention-badge{padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600;background:#e9456033;color:#e94560;animation:attention-pulse 2s ease-in-out infinite}@keyframes attention-pulse{0%,to{opacity:1}50%{opacity:.6}}.card-actions{display:flex;gap:6px;margin-top:10px;padding-top:10px;border-top:1px solid #0f3460}.action-btn{flex:1;padding:6px 0;border:1px solid #2a2a44;border-radius:6px;background:#1e1e36;color:#a0a0b0;font-size:12px;font-weight:500;font-family:inherit;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s,color .15s}.action-btn:active{background:#2a2a44}.action-btn.open{color:#2ecc71;border-color:#2ecc714d}.action-btn.stop{color:#e94560;border-color:#e945604d}.action-btn.stop:active{background:#e9456026}.confirm-overlay{display:flex;align-items:center;justify-content:center;gap:10px;padding:10px;margin-top:8px;background:#2d1810;border:1px solid #e94560;border-radius:8px;animation:fadeIn .15s ease}.confirm-msg{font-size:13px;color:#f0f0f0;flex:1}.confirm-btn{padding:5px 14px;border:none;border-radius:5px;font-size:12px;font-weight:600;font-family:inherit;cursor:pointer}.confirm-btn.yes{background:#e94560;color:#fff}.confirm-btn.no{background:#2a2a44;color:#a0a0b0}.rename-input{background:#112;border:1px solid #e94560;border-radius:4px;color:#f0f0f0;font-size:15px;font-weight:600;font-family:inherit;padding:2px 6px;outline:none;width:120px}.card-metadata{margin-top:8px;padding:8px 10px;background:#112;border-radius:6px;animation:fadeIn .2s ease}.card-metadata.hidden{display:none}.meta-row{display:flex;justify-content:space-between;padding:3px 0;font-size:11px}.meta-label{color:#707090;font-weight:500}.meta-value{color:#c0c0d0;font-family:Cascadia Code,Fira Code,JetBrains Mono,monospace;font-size:11px;text-align:right;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@media(min-width:768px){#session-list{max-width:600px;margin:0 auto}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.4}}@keyframes fadeIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.session-card{animation:fadeIn .3s ease}#reconnect-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:#1a1a2ef2;display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:100;color:#e0e0e0;font-size:16px;gap:12px}#reconnect-overlay .spinner{width:32px;height:32px;border:3px solid #0f3460;border-top-color:#e94560;border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}#fab-create{position:fixed;bottom:24px;right:24px;width:56px;height:56px;border-radius:50%;background:#e94560;color:#fff;font-size:28px;font-weight:300;border:none;cursor:pointer;box-shadow:0 4px 16px #e9456066;z-index:20;-webkit-tap-highlight-color:transparent;transition:transform .15s,box-shadow .15s;display:flex;align-items:center;justify-content:center;line-height:1}#fab-create:active{transform:scale(.92)}.modal-overlay{position:fixed;inset:0;background:#0a0a14d9;z-index:50;display:flex;align-items:flex-end;justify-content:center;animation:fadeIn .15s ease}.modal-overlay.hidden{display:none}.modal-content{background:#16213e;border-radius:16px 16px 0 0;width:100%;max-width:480px;max-height:85vh;max-height:85dvh;overflow-y:auto;padding:20px;animation:slideUp .2s ease}@keyframes slideUp{0%{transform:translateY(100%)}to{transform:translateY(0)}}.modal-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:20px}.modal-title{font-size:18px;font-weight:600;color:#f0f0f0}.modal-close{background:none;border:none;color:#707090;font-size:24px;cursor:pointer;padding:4px 8px;line-height:1}.form-label{display:block;font-size:13px;font-weight:500;color:#a0a0b0;margin-bottom:6px;margin-top:16px}.form-label:first-of-type{margin-top:0}.form-input{width:100%;padding:10px 12px;background:#112;border:1px solid #2a2a44;border-radius:8px;color:#f0f0f0;font-size:15px;font-family:Cascadia Code,Fira Code,JetBrains Mono,monospace;outline:none;transition:border-color .15s}.form-input:focus{border-color:#e94560}.form-input.hidden{display:none}.chip-row{display:flex;gap:6px;margin-top:8px;flex-wrap:wrap}.chip{padding:5px 12px;background:#1e1e36;border:1px solid #2a2a44;border-radius:16px;font-size:12px;color:#c0c0d0;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s,border-color .15s;white-space:nowrap;font-family:Cascadia Code,Fira Code,JetBrains Mono,monospace}.chip:active,.chip.selected{background:#e9456026;border-color:#e94560;color:#e94560}.dir-display{padding:10px 12px;background:#112;border:1px solid #2a2a44;border-radius:8px;font-size:13px;font-family:Cascadia Code,Fira Code,JetBrains Mono,monospace;color:#c0c0d0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dir-actions{display:flex;gap:8px;margin-top:10px}.dir-action-btn{flex:1;padding:8px 0;background:#1e1e36;border:1px solid #2a2a44;border-radius:8px;color:#a0a0b0;font-size:13px;font-family:inherit;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s}.dir-action-btn:active{background:#2a2a44}.btn-create{width:100%;margin-top:20px;padding:14px;background:#e94560;color:#fff;border:none;border-radius:10px;font-size:16px;font-weight:600;font-family:inherit;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:opacity .15s}.btn-create:disabled{opacity:.4;cursor:default}.btn-create:not(:disabled):active{opacity:.8}.create-error{margin-top:12px;padding:8px 12px;background:#e9456026;border-radius:6px;font-size:13px;color:#e94560}.create-error.hidden{display:none}.create-spinner{display:flex;flex-direction:column;align-items:center;gap:12px;padding:32px 0;color:#a0a0b0;font-size:14px}.create-spinner.hidden{display:none}.browse-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}.browse-back{background:none;border:none;color:#a0a0b0;font-size:14px;font-family:inherit;cursor:pointer;padding:4px 0}.browse-select{padding:6px 14px;background:#e94560;color:#fff;border:none;border-radius:6px;font-size:13px;font-weight:500;font-family:inherit;cursor:pointer}.browse-breadcrumb{display:flex;gap:4px;align-items:center;margin-bottom:12px;font-size:13px;font-family:Cascadia Code,Fira Code,JetBrains Mono,monospace;color:#808098;flex-wrap:wrap}.breadcrumb-segment{cursor:pointer;color:#a0a0b0;transition:color .15s}.breadcrumb-segment:hover,.breadcrumb-segment:active{color:#e94560}.breadcrumb-sep{color:#505070}.browse-list{display:flex;flex-direction:column;gap:2px;max-height:50vh;max-height:50dvh;overflow-y:auto}.browse-item{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;background:#112;border-radius:6px;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .1s}.browse-item:active{background:#1e1e36}.browse-item-name{font-size:14px;color:#d0d0e0;font-family:Cascadia Code,Fira Code,JetBrains Mono,monospace}.browse-item-arrow{color:#505070;font-size:14px}.browse-empty{text-align:center;padding:24px;color:#505070;font-size:13px}.hidden{display:none!important}.setting-row{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;background:#112;border-radius:10px}.setting-info{display:flex;align-items:center;gap:12px;flex:1;min-width:0}.setting-icon{font-size:22px;flex-shrink:0}.setting-label{font-size:15px;font-weight:600;color:#f0f0f0}.setting-sublabel{font-size:11px;color:#707090;margin-top:2px}.toggle-btn{width:48px;height:28px;border-radius:14px;background:#2a2a44;border:none;cursor:pointer;position:relative;flex-shrink:0;transition:background .25s ease;-webkit-tap-highlight-color:transparent}.toggle-btn[aria-checked=true]{background:#2ecc71}.toggle-btn:disabled{opacity:.4;cursor:default}.toggle-knob{display:block;width:22px;height:22px;border-radius:50%;background:#707090;position:absolute;top:3px;left:3px;transition:transform .25s ease,background .25s ease}.toggle-btn[aria-checked=true] .toggle-knob{transform:translate(20px);background:#fff}.setting-warning{margin-top:12px;padding:10px 14px;background:#f39c121a;border:1px solid rgba(243,156,18,.25);border-radius:8px;font-size:12px;color:#f39c12;line-height:1.5}.setting-note{margin-top:8px;font-size:11px;color:#505070;text-align:center}.setting-tip{margin-top:10px;padding:8px 12px;background:#0f346066;border-radius:6px;font-size:12px;color:#808098;line-height:1.5}#sleep-password-section{margin-top:16px;padding-top:16px;border-top:1px solid #0f3460}#sleep-password-section .form-label{margin-top:0}.sleep-actions{display:flex;gap:10px;margin-top:14px}.sleep-actions .action-btn{flex:1;padding:12px;font-size:14px}.sleep-actions .btn-create{flex:1;margin-top:0;padding:12px;font-size:14px}
@@ -1,2 +0,0 @@
1
- (function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))o(s);new MutationObserver(s=>{for(const a of s)if(a.type==="childList")for(const c of a.addedNodes)c.tagName==="LINK"&&c.rel==="modulepreload"&&o(c)}).observe(document,{childList:!0,subtree:!0});function n(s){const a={};return s.integrity&&(a.integrity=s.integrity),s.referrerPolicy&&(a.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?a.credentials="include":s.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function o(s){if(s.ep)return;s.ep=!0;const a=n(s);fetch(s.href,a)}})();function Z(e){const t=Math.floor(e/1e3);if(t<60)return`${t}s`;const n=Math.floor(t/60);if(n<60)return`${n}m`;const o=Math.floor(n/60),s=n%60;return`${o}h ${s}m`}function Be(e){const t=e.match(/^\/(?:Users|home)\/[^/]+/)?.[0];return t?"~"+e.slice(t.length):e}function Se(e){return e<1024?`${e} KB`:`${(e/1024).toFixed(1)} MB`}function Ne(e,t,n){const o=document.createElement("div");o.className=`session-card${e.status==="attention"?" attention":""}`,o.dataset.sessionId=e.id;const s=Z(Date.now()-e.connectedAt),a=Be(e.cwd),c=document.createElement("div");c.className="card-header";const i=document.createElement("div");i.className="card-agent";const y=document.createElement("div");y.className=`agent-dot ${e.status}`;const W=document.createElement("span");W.className="agent-name",W.textContent=e.name||e.agent,i.appendChild(y),i.appendChild(W);const _=document.createElement("span");_.className="card-uptime",_.textContent=s,c.appendChild(i),c.appendChild(_);const j=document.createElement("div");j.className="card-cwd",j.textContent=a;const N=document.createElement("div");N.className="card-preview";const K=document.createElement("pre");K.className="card-preview-text",K.textContent="Waiting for output...",N.appendChild(K),N.addEventListener("click",l=>{l.stopPropagation(),n.onOpen(e)});const x=document.createElement("div");x.className="card-status";const P=document.createElement("span");P.className=`status-badge ${e.status}`,P.textContent=e.status,e.status==="attention"&&(P.style.display="none");const $=document.createElement("span");$.className="attention-badge",$.textContent="Needs your attention",e.status!=="attention"&&($.style.display="none"),x.appendChild(P),x.appendChild($);const g=document.createElement("div");g.className="card-actions";const M=document.createElement("button");M.className="action-btn stop",M.textContent="Stop",M.addEventListener("click",l=>{l.stopPropagation(),xe(o,e.id,n.onStop)});const A=document.createElement("button");A.className="action-btn rename",A.textContent="Rename",A.addEventListener("click",l=>{l.stopPropagation(),Pe(o,e.id,n.onRename)});const O=document.createElement("button");O.className="action-btn info",O.textContent="Info",O.addEventListener("click",l=>{l.stopPropagation(),n.onInfo(e.id)});const T=document.createElement("button");T.className="action-btn open",T.textContent="Open",T.addEventListener("click",l=>{l.stopPropagation(),n.onOpen(e)}),g.appendChild(T),g.appendChild(A),g.appendChild(O),g.appendChild(M);const ie=document.createElement("div");return ie.className="card-metadata hidden",o.addEventListener("click",()=>{n.onOpen(e)}),o.appendChild(c),o.appendChild(j),o.appendChild(N),o.appendChild(x),o.appendChild(g),o.appendChild(ie),o}function xe(e,t,n){e.querySelector(".confirm-overlay")?.remove();const o=document.createElement("div");o.className="confirm-overlay";const s=document.createElement("span");s.className="confirm-msg",s.textContent="Stop this session?";const a=document.createElement("button");a.className="confirm-btn yes",a.textContent="Yes",a.addEventListener("click",i=>{i.stopPropagation(),n(t),o.remove()});const c=document.createElement("button");c.className="confirm-btn no",c.textContent="No",c.addEventListener("click",i=>{i.stopPropagation(),o.remove()}),o.addEventListener("click",i=>i.stopPropagation()),o.appendChild(s),o.appendChild(a),o.appendChild(c),e.appendChild(o)}function Pe(e,t,n){const o=e.querySelector(".agent-name");if(!o)return;const s=o.textContent||"",a=document.createElement("input");a.className="rename-input",a.type="text",a.value=s;const c=()=>{const i=a.value.trim();i&&i!==s&&n(t,i),o.textContent=i||s,o.style.display="",a.remove()};a.addEventListener("keydown",i=>{i.stopPropagation(),i.key==="Enter"?c():i.key==="Escape"&&(o.textContent=s,o.style.display="",a.remove())}),a.addEventListener("blur",c),a.addEventListener("click",i=>i.stopPropagation()),o.style.display="none",o.parentElement?.insertBefore(a,o.nextSibling),a.focus(),a.select()}function be(e,t){const n=e.querySelector(".agent-dot"),o=e.querySelector(".status-badge"),s=e.querySelector(".card-uptime"),a=e.querySelector(".agent-name");n&&(n.className=`agent-dot ${t.status}`),o&&(o.className=`status-badge ${t.status}`,o.textContent=t.status),s&&(s.textContent=Z(Date.now()-t.connectedAt)),a&&!a.style.display&&(a.textContent=t.name||t.agent);const c=t.status==="attention";o&&(o.style.display=c?"none":"");const i=e.querySelector(".attention-badge");i&&(i.style.display=c?"":"none"),t.status==="attention"?e.classList.add("attention"):e.classList.remove("attention")}function $e(e,t){const n=e.querySelector(".card-preview-text");n&&(t.length>0?(n.textContent=t.join(`
2
- `),n.classList.remove("empty")):(n.textContent="Waiting for output...",n.classList.add("empty")))}function Me(e,t){const n=e.querySelector(".card-metadata");if(!n)return;for(;n.firstChild;)n.removeChild(n.firstChild);const o=[["PID",String(t.pid)],["Agent",t.agent],["Port",String(t.port)],["Directory",t.cwd],["Memory",Se(t.memoryKB)],["Uptime",Z(t.uptimeMs)]];for(const[s,a]of o){const c=document.createElement("div");c.className="meta-row";const i=document.createElement("span");i.className="meta-label",i.textContent=s;const y=document.createElement("span");y.className="meta-value",y.textContent=a,c.appendChild(i),c.appendChild(y),n.appendChild(c)}n.classList.toggle("hidden")}let d=null,de=!1;function Ae(){if(!de){de=!0;try{d=new AudioContext,d.state==="suspended"&&d.resume()}catch{}}}function re(){if(!d)return;d.state==="suspended"&&d.resume();const e=d.currentTime,t=d.createOscillator(),n=d.createGain();t.frequency.value=587.33,t.type="sine",n.gain.setValueAtTime(.3,e),n.gain.exponentialRampToValueAtTime(.01,e+.3),t.connect(n),n.connect(d.destination),t.start(e),t.stop(e+.3);const o=d.createOscillator(),s=d.createGain();o.frequency.value=880,o.type="sine",s.gain.setValueAtTime(.3,e+.15),s.gain.exponentialRampToValueAtTime(.01,e+.45),o.connect(s),s.connect(d.destination),o.start(e+.15),o.stop(e+.45)}const le=120*1e3,h=new Map;function me(e){if(h.has(e))return;re();const t=setTimeout(function n(){re();const o=h.get(e);o&&(o.timerId=setTimeout(n,le))},le);h.set(e,{timerId:t})}function Y(e){const t=h.get(e);t&&(clearTimeout(t.timerId),h.delete(e))}function ue(){for(const e of h.values())clearTimeout(e.timerId);h.clear()}let z=null,k,u,I,f,b,D,H,pe;function Oe(e){z=e;const t=document.getElementById("btn-settings");k=document.getElementById("settings-modal");const n=document.getElementById("settings-modal-close");u=document.getElementById("sleep-toggle"),I=document.getElementById("sleep-password-section"),f=document.getElementById("sleep-password"),b=document.getElementById("sleep-error"),D=document.getElementById("sleep-spinner"),H=document.getElementById("sleep-unsupported"),pe=document.getElementById("sleep-enable");const o=document.getElementById("sleep-cancel");t.addEventListener("click",()=>{k.classList.remove("hidden")}),n.addEventListener("click",he),k.addEventListener("click",s=>{s.target===k&&he()}),u.addEventListener("click",()=>{if(u.disabled)return;u.getAttribute("aria-checked")==="true"?z?.({type:"disable-sleep-prevention"}):(I.classList.remove("hidden"),b.classList.add("hidden"),f.value="",f.focus())}),pe.addEventListener("click",fe),f.addEventListener("keydown",s=>{s.key==="Enter"&&(s.preventDefault(),fe())}),o.addEventListener("click",ee)}function ee(){I.classList.add("hidden"),f.value="",b.classList.add("hidden")}function fe(){const e=f.value;if(!e){Ce("Password is required");return}f.value="",b.classList.add("hidden"),I.classList.add("hidden"),D.classList.remove("hidden"),z?.({type:"enable-sleep-prevention",password:e})}function he(){k.classList.add("hidden"),ee()}function Ce(e){b.textContent=e,b.classList.remove("hidden")}function Te(e){if(D.classList.add("hidden"),!e.supported){u.disabled=!0,u.setAttribute("aria-checked","false"),H.classList.remove("hidden");return}H.classList.add("hidden"),u.disabled=!1,u.setAttribute("aria-checked",e.enabled?"true":"false"),ee()}function Re(e){D.classList.add("hidden"),I.classList.remove("hidden"),Ce(e),u.setAttribute("aria-checked","false")}const Ue=new URLSearchParams(window.location.search),V=Ue.get("token");if(!V)throw document.body.textContent="Missing authentication token.",new Error("No token in URL");const qe=window.location.protocol==="https:"?"wss:":"ws:",De=`${qe}//${window.location.host}?token=${V}`,we=window.location.hostname,w=new Map,p=new Map,Ve=document.getElementById("session-list"),X=document.getElementById("empty-state"),Fe=document.getElementById("session-count"),ye=document.getElementById("status-dot");let G=null;function R(){Ae(),document.removeEventListener("click",R),document.removeEventListener("touchstart",R)}document.addEventListener("click",R);document.addEventListener("touchstart",R);function v(e){r&&r.readyState===WebSocket.OPEN&&r.send(JSON.stringify(e))}Oe(v);const We={onOpen(e){const t=w.get(e.id)||e;t.status==="attention"&&(v({type:"clear-attention",sessionId:t.id}),Y(t.id));const n=window.location.href,o=`http://${we}:${t.port}?token=${t.token}&hub=${encodeURIComponent(n)}`;window.location.href=o},onStop(e){v({type:"stop-session",sessionId:e})},onRename(e,t){v({type:"rename-session",sessionId:e,name:t})},onInfo(e){v({type:"get-metadata",sessionId:e})}};function Le(){const e=w.size;Fe.textContent=`${e} session${e!==1?"s":""}`,e===0?X.style.display="flex":X.style.display="none"}function ge(e){w.set(e.id,e);const t=Ne(e,we,We);p.set(e.id,t),Ve.insertBefore(t,X),Le()}function Ee(e){w.delete(e);const t=p.get(e);t&&(t.remove(),p.delete(e)),Le()}function _e(e){w.set(e.id,e);const t=p.get(e.id);t&&be(t,e)}function je(){for(const[e,t]of w){const n=p.get(e);n&&be(n,t)}}const te=document.getElementById("fab-create"),B=document.getElementById("create-modal"),Ke=document.getElementById("modal-close"),C=document.getElementById("tool-input"),ve=document.getElementById("tool-chips"),ke=document.getElementById("dir-selected"),Ge=document.getElementById("btn-browse"),Ie=document.getElementById("btn-create-session"),U=document.getElementById("create-error"),L=document.getElementById("create-form-view"),F=document.getElementById("browse-view"),J=document.getElementById("browse-breadcrumb"),E=document.getElementById("browse-list"),Je=document.getElementById("browse-back"),Ye=document.getElementById("browse-select"),ne=document.getElementById("create-spinner");let S="~",Q="~";function m(e,t,n){const o=document.createElement(e);return o.className=t,o.textContent=n,o}function ze(){B.classList.remove("hidden"),L.classList.remove("hidden"),F.classList.add("hidden"),ne.classList.add("hidden"),U.classList.add("hidden"),C.value="",S="~",ke.textContent="~",se(),He(),C.focus()}function oe(){B.classList.add("hidden")}function se(){Ie.disabled=!C.value.trim()}async function He(){try{const t=await(await fetch(`/api/tool-history?token=${V}`)).json();ve.replaceChildren();for(const n of t.tools.slice(0,6)){const o=m("span","chip",n);o.addEventListener("click",()=>{C.value=n,se()}),ve.appendChild(o)}}catch{}}async function ae(e){Q=e,E.replaceChildren(m("div","browse-empty","Loading..."));try{const n=await(await fetch(`/api/browse?path=${encodeURIComponent(e)}&token=${V}`)).json();if(n.error){E.replaceChildren(m("div","browse-empty",n.error));return}const o=n.path||e;Q=o,Xe(o);const s=n.entries;if(s.length===0){E.replaceChildren(m("div","browse-empty","No subdirectories"));return}E.replaceChildren();for(const a of s){const c=document.createElement("div");c.className="browse-item",c.appendChild(m("span","browse-item-name",a)),c.appendChild(m("span","browse-item-arrow","›")),c.addEventListener("click",()=>ae(`${o}/${a}`)),E.appendChild(c)}}catch{E.replaceChildren(m("div","browse-empty","Failed to load"))}}function Xe(e){J.replaceChildren();const t=e.split("/").filter(Boolean);for(let n=0;n<t.length;n++){n>0&&J.appendChild(m("span","breadcrumb-sep"," / "));const o=m("span","breadcrumb-segment",t[n]),s=t.slice(0,n+1).join("/");o.addEventListener("click",()=>ae(s)),J.appendChild(o)}}function Qe(){const e=C.value.trim();e&&(U.classList.add("hidden"),L.classList.add("hidden"),ne.classList.remove("hidden"),v({type:"create-session",tool:e,cwd:S}))}te.addEventListener("click",ze);Ke.addEventListener("click",oe);B.addEventListener("click",e=>{e.target===B&&oe()});C.addEventListener("input",se);Ie.addEventListener("click",Qe);Ge.addEventListener("click",()=>{L.classList.add("hidden"),F.classList.remove("hidden"),ae(S)});Je.addEventListener("click",()=>{F.classList.add("hidden"),L.classList.remove("hidden")});Ye.addEventListener("click",()=>{S=Q,ke.textContent=S,F.classList.add("hidden"),L.classList.remove("hidden")});let r=null,q=0;const Ze=1e4;function ce(){r=new WebSocket(De),r.onopen=()=>{ye.className="connected",q=0,G&&clearInterval(G),G=setInterval(je,1e4)},r.onmessage=e=>{try{const t=JSON.parse(e.data);switch(t.type){case"sessions":{ue();for(const n of p.keys())Ee(n);for(const n of t.sessions)ge(n),n.status==="attention"&&me(n.id);break}case"session-added":{ge(t.session),B.classList.contains("hidden")||oe();break}case"session-removed":{const n=t.sessionId;Y(n),Ee(n);break}case"session-updated":{const n=t.session;_e(n),n.status==="attention"?me(n.id):Y(n.id);break}case"preview":{const n=p.get(t.sessionId);n&&$e(n,t.lines);break}case"metadata":{const n=p.get(t.sessionId);n&&Me(n,t.metadata);break}case"session-creating":break;case"session-create-error":{ne.classList.add("hidden"),L.classList.remove("hidden"),U.textContent=t.error,U.classList.remove("hidden");break}case"sleep-state":{Te(t.state);break}case"sleep-error":{Re(t.error);break}case"operation-error":{console.warn(`Operation "${t.operation}" failed for session ${t.sessionId}: ${t.error}`);break}}}catch{}},r.onclose=()=>{ye.className="reconnecting",ue(),et()},r.onerror=()=>{r?.close()}}function et(){const e=Math.min(1e3*Math.pow(1.5,q),Ze);q++,setTimeout(ce,e)}document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&r?.readyState!==WebSocket.OPEN&&(q=0,ce())});ce();document.addEventListener("focusin",e=>{const t=e.target;t.matches("input, textarea")&&(te.style.display="none",setTimeout(()=>{t.scrollIntoView({block:"center",behavior:"smooth"})},300))});document.addEventListener("focusout",()=>{te.style.display=""});