itwillsync 1.5.1 → 1.6.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.
@@ -1 +1 @@
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}#session-count{font-size:13px;color:#a0a0b0}#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)}}
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}#session-count{font-size:13px;color:#a0a0b0}#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;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;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}
@@ -0,0 +1,2 @@
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 K(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 ue(e){const t=e.match(/^\/(?:Users|home)\/[^/]+/)?.[0];return t?"~"+e.slice(t.length):e}function pe(e){return e<1024?`${e} KB`:`${(e/1024).toFixed(1)} MB`}function fe(e,t,n){const o=document.createElement("div");o.className=`session-card${e.status==="attention"?" attention":""}`,o.dataset.sessionId=e.id;const s=K(Date.now()-e.connectedAt),a=ue(e.cwd),c=document.createElement("div");c.className="card-header";const i=document.createElement("div");i.className="card-agent";const f=document.createElement("div");f.className=`agent-dot ${e.status}`;const R=document.createElement("span");R.className="agent-name",R.textContent=e.name||e.agent,i.appendChild(f),i.appendChild(R);const q=document.createElement("span");q.className="card-uptime",q.textContent=s,c.appendChild(i),c.appendChild(q);const U=document.createElement("div");U.className="card-cwd",U.textContent=a;const L=document.createElement("div");L.className="card-preview";const D=document.createElement("pre");D.className="card-preview-text",D.textContent="Waiting for output...",L.appendChild(D),L.addEventListener("click",l=>{l.stopPropagation(),n.onOpen(e)});const N=document.createElement("div");N.className="card-status";const S=document.createElement("span");S.className=`status-badge ${e.status}`,S.textContent=e.status,e.status==="attention"&&(S.style.display="none");const I=document.createElement("span");I.className="attention-badge",I.textContent="Needs your attention",e.status!=="attention"&&(I.style.display="none"),N.appendChild(S),N.appendChild(I);const h=document.createElement("div");h.className="card-actions";const k=document.createElement("button");k.className="action-btn stop",k.textContent="Stop",k.addEventListener("click",l=>{l.stopPropagation(),he(o,e.id,n.onStop)});const B=document.createElement("button");B.className="action-btn rename",B.textContent="Rename",B.addEventListener("click",l=>{l.stopPropagation(),ye(o,e.id,n.onRename)});const x=document.createElement("button");x.className="action-btn info",x.textContent="Info",x.addEventListener("click",l=>{l.stopPropagation(),n.onInfo(e.id)});const P=document.createElement("button");P.className="action-btn open",P.textContent="Open",P.addEventListener("click",l=>{l.stopPropagation(),n.onOpen(e)}),h.appendChild(P),h.appendChild(B),h.appendChild(x),h.appendChild(k);const X=document.createElement("div");return X.className="card-metadata hidden",o.addEventListener("click",()=>{n.onOpen(e)}),o.appendChild(c),o.appendChild(U),o.appendChild(L),o.appendChild(N),o.appendChild(h),o.appendChild(X),o}function he(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 ye(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 ie(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=K(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 ge(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 Ee(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",pe(t.memoryKB)],["Uptime",K(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 f=document.createElement("span");f.className="meta-value",f.textContent=a,c.appendChild(i),c.appendChild(f),n.appendChild(c)}n.classList.toggle("hidden")}let r=null,Q=!1;function ve(){if(!Q){Q=!0;try{r=new AudioContext,r.state==="suspended"&&r.resume()}catch{}}}function Z(){if(!r)return;r.state==="suspended"&&r.resume();const e=r.currentTime,t=r.createOscillator(),n=r.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(r.destination),t.start(e),t.stop(e+.3);const o=r.createOscillator(),s=r.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(r.destination),o.start(e+.15),o.stop(e+.45)}const ee=120*1e3,p=new Map;function te(e){if(p.has(e))return;Z();const t=setTimeout(function n(){Z();const o=p.get(e);o&&(o.timerId=setTimeout(n,ee))},ee);p.set(e,{timerId:t})}function W(e){const t=p.get(e);t&&(clearTimeout(t.timerId),p.delete(e))}function ne(){for(const e of p.values())clearTimeout(e.timerId);p.clear()}const Ce=new URLSearchParams(window.location.search),O=Ce.get("token");if(!O)throw document.body.textContent="Missing authentication token.",new Error("No token in URL");const be=window.location.protocol==="https:"?"wss:":"ws:",we=`${be}//${window.location.host}?token=${O}`,re=window.location.hostname,E=new Map,u=new Map,Le=document.getElementById("session-list"),_=document.getElementById("empty-state"),Ne=document.getElementById("session-count"),oe=document.getElementById("status-dot");let V=null;function $(){ve(),document.removeEventListener("click",$),document.removeEventListener("touchstart",$)}document.addEventListener("click",$);document.addEventListener("touchstart",$);function C(e){d&&d.readyState===WebSocket.OPEN&&d.send(JSON.stringify(e))}const Se={onOpen(e){const t=E.get(e.id)||e;t.status==="attention"&&(C({type:"clear-attention",sessionId:t.id}),W(t.id));const n=window.location.href,o=`http://${re}:${t.port}?token=${t.token}&hub=${encodeURIComponent(n)}`;window.open(o,`session-${t.id}`)},onStop(e){C({type:"stop-session",sessionId:e})},onRename(e,t){C({type:"rename-session",sessionId:e,name:t})},onInfo(e){C({type:"get-metadata",sessionId:e})}};function de(){const e=E.size;Ne.textContent=`${e} session${e!==1?"s":""}`,e===0?_.style.display="flex":_.style.display="none"}function se(e){E.set(e.id,e);const t=fe(e,re,Se);u.set(e.id,t),Le.insertBefore(t,_),de()}function ae(e){E.delete(e);const t=u.get(e);t&&(t.remove(),u.delete(e)),de()}function Ie(e){E.set(e.id,e);const t=u.get(e.id);t&&ie(t,e)}function ke(){for(const[e,t]of E){const n=u.get(e);n&&ie(n,t)}}const Be=document.getElementById("fab-create"),b=document.getElementById("create-modal"),xe=document.getElementById("modal-close"),g=document.getElementById("tool-input"),ce=document.getElementById("tool-chips"),le=document.getElementById("dir-selected"),Pe=document.getElementById("btn-browse"),me=document.getElementById("btn-create-session"),M=document.getElementById("create-error"),v=document.getElementById("create-form-view"),T=document.getElementById("browse-view"),F=document.getElementById("browse-breadcrumb"),y=document.getElementById("browse-list"),$e=document.getElementById("browse-back"),Me=document.getElementById("browse-select"),G=document.getElementById("create-spinner");let w="~",j="~";function m(e,t,n){const o=document.createElement(e);return o.className=t,o.textContent=n,o}function Ae(){b.classList.remove("hidden"),v.classList.remove("hidden"),T.classList.add("hidden"),G.classList.add("hidden"),M.classList.add("hidden"),g.value="",w="~",le.textContent="~",Y(),Oe(),g.focus()}function J(){b.classList.add("hidden")}function Y(){me.disabled=!g.value.trim()}async function Oe(){try{const t=await(await fetch(`/api/tool-history?token=${O}`)).json();ce.replaceChildren();for(const n of t.tools.slice(0,6)){const o=m("span","chip",n);o.addEventListener("click",()=>{g.value=n,Y()}),ce.appendChild(o)}}catch{}}async function z(e){j=e,y.replaceChildren(m("div","browse-empty","Loading..."));try{const n=await(await fetch(`/api/browse?path=${encodeURIComponent(e)}&token=${O}`)).json();if(n.error){y.replaceChildren(m("div","browse-empty",n.error));return}const o=n.path||e;j=o,Te(o);const s=n.entries;if(s.length===0){y.replaceChildren(m("div","browse-empty","No subdirectories"));return}y.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",()=>z(`${o}/${a}`)),y.appendChild(c)}}catch{y.replaceChildren(m("div","browse-empty","Failed to load"))}}function Te(e){F.replaceChildren();const t=e.split("/").filter(Boolean);for(let n=0;n<t.length;n++){n>0&&F.appendChild(m("span","breadcrumb-sep"," / "));const o=m("span","breadcrumb-segment",t[n]),s=t.slice(0,n+1).join("/");o.addEventListener("click",()=>z(s)),F.appendChild(o)}}function Re(){const e=g.value.trim();e&&(M.classList.add("hidden"),v.classList.add("hidden"),G.classList.remove("hidden"),C({type:"create-session",tool:e,cwd:w}))}Be.addEventListener("click",Ae);xe.addEventListener("click",J);b.addEventListener("click",e=>{e.target===b&&J()});g.addEventListener("input",Y);me.addEventListener("click",Re);Pe.addEventListener("click",()=>{v.classList.add("hidden"),T.classList.remove("hidden"),z(w)});$e.addEventListener("click",()=>{T.classList.add("hidden"),v.classList.remove("hidden")});Me.addEventListener("click",()=>{w=j,le.textContent=w,T.classList.add("hidden"),v.classList.remove("hidden")});let d=null,A=0;const qe=1e4;function H(){d=new WebSocket(we),d.onopen=()=>{oe.className="connected",A=0,V&&clearInterval(V),V=setInterval(ke,1e4)},d.onmessage=e=>{try{const t=JSON.parse(e.data);switch(t.type){case"sessions":{ne();for(const n of u.keys())ae(n);for(const n of t.sessions)se(n),n.status==="attention"&&te(n.id);break}case"session-added":{se(t.session),b.classList.contains("hidden")||J();break}case"session-removed":{const n=t.sessionId;W(n),ae(n);break}case"session-updated":{const n=t.session;Ie(n),n.status==="attention"?te(n.id):W(n.id);break}case"preview":{const n=u.get(t.sessionId);n&&ge(n,t.lines);break}case"metadata":{const n=u.get(t.sessionId);n&&Ee(n,t.metadata);break}case"session-creating":break;case"session-create-error":{G.classList.add("hidden"),v.classList.remove("hidden"),M.textContent=t.error,M.classList.remove("hidden");break}case"operation-error":{console.warn(`Operation "${t.operation}" failed for session ${t.sessionId}: ${t.error}`);break}}}catch{}},d.onclose=()=>{oe.className="reconnecting",ne(),Ue()},d.onerror=()=>{d?.close()}}function Ue(){const e=Math.min(1e3*Math.pow(1.5,A),qe);A++,setTimeout(H,e)}document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&d?.readyState!==WebSocket.OPEN&&(A=0,H())});H();
@@ -7,8 +7,8 @@
7
7
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
8
8
  <meta name="theme-color" content="#1a1a2e" />
9
9
  <title>itwillsync Dashboard</title>
10
- <script type="module" crossorigin src="/assets/index-DdOxsvuU.js"></script>
11
- <link rel="stylesheet" crossorigin href="/assets/index-Erqx_a0N.css">
10
+ <script type="module" crossorigin src="/assets/index-DbIKRpLQ.js"></script>
11
+ <link rel="stylesheet" crossorigin href="/assets/index-D6z7Ixhf.css">
12
12
  </head>
13
13
  <body>
14
14
  <header id="header">
@@ -27,5 +27,50 @@
27
27
  </div>
28
28
  </main>
29
29
 
30
+ <!-- FAB: Create Session -->
31
+ <button id="fab-create" aria-label="New session">+</button>
32
+
33
+ <!-- Create Session Modal -->
34
+ <div id="create-modal" class="modal-overlay hidden">
35
+ <div class="modal-content">
36
+ <div class="modal-header">
37
+ <span class="modal-title">New Session</span>
38
+ <button class="modal-close" id="modal-close">&times;</button>
39
+ </div>
40
+
41
+ <!-- View: Create Form -->
42
+ <div id="create-form-view">
43
+ <label class="form-label">Tool</label>
44
+ <input type="text" id="tool-input" class="form-input" placeholder="e.g. claude, aider, bash" autocomplete="off" autocapitalize="off" />
45
+ <div id="tool-chips" class="chip-row"></div>
46
+
47
+ <label class="form-label">Working Directory</label>
48
+ <div id="dir-selected" class="dir-display">~</div>
49
+ <div class="dir-actions">
50
+ <button class="dir-action-btn" id="btn-browse">Browse...</button>
51
+ </div>
52
+
53
+ <div id="create-error" class="create-error hidden"></div>
54
+ <button id="btn-create-session" class="btn-create" disabled>Create Session</button>
55
+ </div>
56
+
57
+ <!-- View: Directory Browser -->
58
+ <div id="browse-view" class="hidden">
59
+ <div class="browse-header">
60
+ <button class="browse-back" id="browse-back">&larr; Back</button>
61
+ <button class="browse-select" id="browse-select">Select this folder</button>
62
+ </div>
63
+ <div id="browse-breadcrumb" class="browse-breadcrumb"></div>
64
+ <div id="browse-list" class="browse-list"></div>
65
+ </div>
66
+
67
+ <!-- Loading spinner shown during session creation -->
68
+ <div id="create-spinner" class="create-spinner hidden">
69
+ <div class="spinner"></div>
70
+ <span>Creating session...</span>
71
+ </div>
72
+ </div>
73
+ </div>
74
+
30
75
  </body>
31
76
  </html>
package/dist/index.js CHANGED
@@ -3835,17 +3835,40 @@ async function getTailscaleStatus() {
3835
3835
  }
3836
3836
 
3837
3837
  // src/network.ts
3838
+ var VIRTUAL_INTERFACE_PREFIXES = [
3839
+ "utun",
3840
+ "tun",
3841
+ "tap",
3842
+ "wg",
3843
+ // VPN/tunnel
3844
+ "tailscale",
3845
+ // Tailscale
3846
+ "docker",
3847
+ "br-",
3848
+ "veth",
3849
+ // Docker
3850
+ "virbr",
3851
+ "vboxnet",
3852
+ "vmnet"
3853
+ // VM
3854
+ ];
3855
+ function isVirtualInterface(name) {
3856
+ return VIRTUAL_INTERFACE_PREFIXES.some((prefix) => name.startsWith(prefix));
3857
+ }
3838
3858
  function getLocalIP() {
3839
3859
  const interfaces = networkInterfaces();
3840
- for (const addresses of Object.values(interfaces)) {
3860
+ let fallback = null;
3861
+ for (const [name, addresses] of Object.entries(interfaces)) {
3841
3862
  if (!addresses) continue;
3842
3863
  for (const addr of addresses) {
3843
- if (addr.family === "IPv4" && !addr.internal) {
3864
+ if (addr.family !== "IPv4" || addr.internal) continue;
3865
+ if (!isVirtualInterface(name)) {
3844
3866
  return addr.address;
3845
3867
  }
3868
+ fallback ??= addr.address;
3846
3869
  }
3847
3870
  }
3848
- return "127.0.0.1";
3871
+ return fallback ?? "127.0.0.1";
3849
3872
  }
3850
3873
  async function resolveSessionIP(mode, isLocalhost) {
3851
3874
  if (isLocalhost) return "127.0.0.1";
@@ -4617,7 +4640,8 @@ function parseArgs(argv) {
4617
4640
  local: false,
4618
4641
  hubInfo: false,
4619
4642
  hubStop: false,
4620
- hubStatus: false
4643
+ hubStatus: false,
4644
+ headless: false
4621
4645
  };
4622
4646
  const args = argv.slice(2);
4623
4647
  if (args.length > 0 && args[0] === "setup") {
@@ -4662,6 +4686,9 @@ function parseArgs(argv) {
4662
4686
  } else if (arg === "--hub-status") {
4663
4687
  options.hubStatus = true;
4664
4688
  i++;
4689
+ } else if (arg === "--headless") {
4690
+ options.headless = true;
4691
+ i++;
4665
4692
  } else if (arg === "--help" || arg === "-h") {
4666
4693
  printHelp();
4667
4694
  process.exit(0);
@@ -4846,7 +4873,7 @@ async function main() {
4846
4873
  console.error("Error: Cannot use both --tailscale and --local.\n");
4847
4874
  process.exit(1);
4848
4875
  }
4849
- if (!configExists() && !options.tailscale && !options.local && process.stdin.isTTY) {
4876
+ if (!options.headless && !configExists() && !options.tailscale && !options.local && process.stdin.isTTY) {
4850
4877
  await runSetupWizard();
4851
4878
  }
4852
4879
  if (options.command.length === 0) {
@@ -4854,6 +4881,7 @@ async function main() {
4854
4881
  printHelp();
4855
4882
  process.exit(1);
4856
4883
  }
4884
+ const headless = options.headless;
4857
4885
  const config = loadConfig();
4858
4886
  let networkingMode = "local";
4859
4887
  if (options.tailscale) {
@@ -4907,58 +4935,64 @@ async function main() {
4907
4935
  console.warn(` Warning: Failed to register with hub: ${err.message}`);
4908
4936
  }
4909
4937
  }
4910
- const dashboardUrl = hubConfig ? `http://${ip}:${HUB_EXTERNAL_PORT}?token=${hubConfig.masterToken}` : null;
4911
- if (dashboardUrl && !options.noQr) {
4912
- if (!isFirstSession) {
4938
+ let sleepGuard = null;
4939
+ if (!headless) {
4940
+ let handleResize2 = function() {
4941
+ if (process.stdout.columns && process.stdout.rows) {
4942
+ server.resizeFromLocal(process.stdout.columns, process.stdout.rows);
4943
+ }
4944
+ };
4945
+ var handleResize = handleResize2;
4946
+ const dashboardUrl = hubConfig ? `http://${ip}:${HUB_EXTERNAL_PORT}?token=${hubConfig.masterToken}` : null;
4947
+ if (dashboardUrl && !options.noQr) {
4948
+ if (!isFirstSession) {
4949
+ console.log(`
4950
+ Session "${cmd}" registered with hub.`);
4951
+ }
4952
+ displayQR(dashboardUrl);
4953
+ } else if (dashboardUrl) {
4913
4954
  console.log(`
4914
4955
  Session "${cmd}" registered with hub.`);
4956
+ console.log(` Dashboard: ${dashboardUrl}`);
4957
+ console.log("");
4958
+ } else if (!options.noQr) {
4959
+ const directUrl = `http://${ip}:${port}?token=${token}`;
4960
+ displayQR(directUrl);
4915
4961
  }
4916
- displayQR(dashboardUrl);
4917
- } else if (dashboardUrl) {
4918
- console.log(`
4919
- Session "${cmd}" registered with hub.`);
4920
- console.log(` Dashboard: ${dashboardUrl}`);
4962
+ sleepGuard = preventSleep();
4963
+ console.log(` Server listening on ${host}:${port}`);
4964
+ console.log(` Running: ${options.command.join(" ")}`);
4965
+ console.log(` PID: ${ptyManager.pid}`);
4966
+ console.log(` Sleep prevention: ${sleepGuard ? "active" : "unavailable"}`);
4921
4967
  console.log("");
4922
- } else if (!options.noQr) {
4923
- const directUrl = `http://${ip}:${port}?token=${token}`;
4924
- displayQR(directUrl);
4925
- }
4926
- const sleepGuard = preventSleep();
4927
- console.log(` Server listening on ${host}:${port}`);
4928
- console.log(` Running: ${options.command.join(" ")}`);
4929
- console.log(` PID: ${ptyManager.pid}`);
4930
- console.log(` Sleep prevention: ${sleepGuard ? "active" : "unavailable"}`);
4931
- console.log("");
4932
- if (process.stdin.isTTY) {
4933
- process.stdin.setRawMode(true);
4934
- }
4935
- process.stdin.resume();
4936
- process.stdin.setEncoding("utf-8");
4937
- process.stdin.on("data", (data) => {
4938
- if (process.stdout.columns && process.stdout.rows) {
4939
- if (process.stdout.columns !== ptyManager.cols || process.stdout.rows !== ptyManager.rows) {
4940
- server.resizeFromLocal(process.stdout.columns, process.stdout.rows);
4941
- }
4942
- }
4943
- ptyManager.write(data);
4944
- });
4945
- ptyManager.onData((data) => {
4946
- process.stdout.write(data);
4947
- });
4948
- function handleResize() {
4949
- if (process.stdout.columns && process.stdout.rows) {
4950
- server.resizeFromLocal(process.stdout.columns, process.stdout.rows);
4968
+ if (process.stdin.isTTY) {
4969
+ process.stdin.setRawMode(true);
4951
4970
  }
4971
+ process.stdin.resume();
4972
+ process.stdin.setEncoding("utf-8");
4973
+ process.stdin.on("data", (data) => {
4974
+ if (process.stdout.columns && process.stdout.rows) {
4975
+ if (process.stdout.columns !== ptyManager.cols || process.stdout.rows !== ptyManager.rows) {
4976
+ server.resizeFromLocal(process.stdout.columns, process.stdout.rows);
4977
+ }
4978
+ }
4979
+ ptyManager.write(data);
4980
+ });
4981
+ ptyManager.onData((data) => {
4982
+ process.stdout.write(data);
4983
+ });
4984
+ process.stdout.on("resize", handleResize2);
4985
+ handleResize2();
4952
4986
  }
4953
- process.stdout.on("resize", handleResize);
4954
- handleResize();
4955
4987
  async function cleanup() {
4956
- process.stdout.write(
4957
- "\x1B[>0u\x1B[?2004l\x1B[?1000l\x1B[?1002l\x1B[?1003l\x1B[?1006l\x1B[?25h\x1B[?1049l"
4958
- // Exit alternate screen buffer (if active)
4959
- );
4960
- if (process.stdin.isTTY) {
4961
- process.stdin.setRawMode(false);
4988
+ if (!headless) {
4989
+ process.stdout.write(
4990
+ "\x1B[>0u\x1B[?2004l\x1B[?1000l\x1B[?1002l\x1B[?1003l\x1B[?1006l\x1B[?25h\x1B[?1049l"
4991
+ // Exit alternate screen buffer (if active)
4992
+ );
4993
+ if (process.stdin.isTTY) {
4994
+ process.stdin.setRawMode(false);
4995
+ }
4962
4996
  }
4963
4997
  if (heartbeatInterval) {
4964
4998
  clearInterval(heartbeatInterval);