6ed0fa0ea0
- Custom branded screen/window picker (picker.html) — useSystemPicker was silently no-op on Win11 and auto-shared the primary display with no choice. - Use checkForUpdates (not ...AndNotify): drops electron-updater's own native "Update ready" toast that duplicated our in-app banner (two restart prompts). - Call notification DP: wait up to 2.5s for the caller photo on persistent (call) toasts instead of 600ms so the DP actually shows. - Re-surface Join/Decline invite when opening a chat with an active incoming call, so a call-notification click always lands on a joinable call. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
84 lines
4.7 KiB
HTML
84 lines
4.7 KiB
HTML
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src data:; style-src 'unsafe-inline'; script-src 'unsafe-inline'">
|
|
<title>Choose what to share</title>
|
|
<style>
|
|
:root{ --brand:#1F3B73; --bg:#f4f6fb; --card:#fff; --line:#e3e8f2; --muted:#64748b; }
|
|
*{ box-sizing:border-box; }
|
|
html,body{ margin:0; height:100%; font-family:'Segoe UI',system-ui,sans-serif; background:var(--bg); color:#0f172a; }
|
|
.wrap{ display:flex; flex-direction:column; height:100%; }
|
|
header{ padding:16px 20px 10px; }
|
|
header h1{ margin:0; font-size:17px; font-weight:700; color:var(--brand); }
|
|
header p{ margin:3px 0 0; font-size:12.5px; color:var(--muted); }
|
|
.tabs{ display:flex; gap:6px; padding:8px 20px 0; }
|
|
.tab{ border:0; background:transparent; font:inherit; font-size:13px; font-weight:600; color:var(--muted); padding:7px 12px; border-radius:8px 8px 0 0; cursor:pointer; }
|
|
.tab.on{ color:var(--brand); background:var(--card); box-shadow:inset 0 -2px 0 var(--brand); }
|
|
.grid{ flex:1; overflow:auto; display:grid; grid-template-columns:repeat(auto-fill,minmax(190px,1fr)); gap:12px; padding:14px 20px; align-content:start; }
|
|
.src{ background:var(--card); border:1.5px solid var(--line); border-radius:12px; padding:8px; cursor:pointer; text-align:left; font:inherit; transition:border-color .12s, transform .08s; overflow:hidden; }
|
|
.src:hover{ border-color:var(--brand); transform:translateY(-1px); }
|
|
.src.sel{ border-color:var(--brand); box-shadow:0 0 0 2px rgba(31,59,115,.18); }
|
|
.thumb{ width:100%; height:112px; border-radius:8px; background:#0b1220; object-fit:contain; display:block; }
|
|
.meta{ display:flex; align-items:center; gap:7px; padding:8px 4px 2px; }
|
|
.appic{ width:18px; height:18px; border-radius:4px; flex:0 0 auto; }
|
|
.nm{ font-size:12.5px; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
|
.empty{ grid-column:1/-1; text-align:center; color:var(--muted); font-size:13px; padding:40px 0; }
|
|
footer{ display:flex; justify-content:flex-end; gap:10px; padding:12px 20px; border-top:1px solid var(--line); background:var(--card); }
|
|
button.act{ font:inherit; font-size:13.5px; font-weight:600; padding:9px 18px; border-radius:9px; cursor:pointer; border:1.5px solid var(--line); background:#fff; color:#334155; }
|
|
button.act.primary{ background:var(--brand); border-color:var(--brand); color:#fff; }
|
|
button.act.primary:disabled{ opacity:.45; cursor:default; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="wrap">
|
|
<header>
|
|
<h1>Share your screen</h1>
|
|
<p>Choose a screen or a window to share with the call.</p>
|
|
</header>
|
|
<div class="tabs">
|
|
<button class="tab on" data-t="screen">Entire screen</button>
|
|
<button class="tab" data-t="window">Application window</button>
|
|
</div>
|
|
<div class="grid" id="grid"></div>
|
|
<footer>
|
|
<button class="act" id="cancel">Cancel</button>
|
|
<button class="act primary" id="share" disabled>Share</button>
|
|
</footer>
|
|
</div>
|
|
<script>
|
|
const { ipcRenderer } = require('electron');
|
|
let SOURCES = { screen:[], window:[] };
|
|
let tab='screen', selected=null;
|
|
|
|
ipcRenderer.on('picker-sources', (_e, data)=>{ SOURCES=data||{screen:[],window:[]}; render(); });
|
|
|
|
document.querySelectorAll('.tab').forEach(b=>b.onclick=()=>{
|
|
tab=b.dataset.t; selected=null; document.getElementById('share').disabled=true;
|
|
document.querySelectorAll('.tab').forEach(x=>x.classList.toggle('on', x===b));
|
|
render();
|
|
});
|
|
document.getElementById('cancel').onclick=()=>ipcRenderer.send('picker-choose', null);
|
|
document.getElementById('share').onclick=()=>{ if(selected) ipcRenderer.send('picker-choose', selected); };
|
|
window.addEventListener('keydown', e=>{ if(e.key==='Escape') ipcRenderer.send('picker-choose', null); });
|
|
|
|
function render(){
|
|
const grid=document.getElementById('grid'); grid.innerHTML='';
|
|
const list=SOURCES[tab]||[];
|
|
if(!list.length){ grid.innerHTML='<div class="empty">Nothing available to share here.</div>'; return; }
|
|
list.forEach(s=>{
|
|
const card=document.createElement('button'); card.className='src'; card.dataset.id=s.id;
|
|
const ap = s.appIcon ? '<img class="appic" src="'+s.appIcon+'">' : '';
|
|
card.innerHTML='<img class="thumb" src="'+s.thumb+'">'
|
|
+ '<div class="meta">'+ap+'<span class="nm">'+esc(s.name||'Untitled')+'</span></div>';
|
|
card.onclick=()=>{ selected=s.id; document.getElementById('share').disabled=false;
|
|
document.querySelectorAll('.src').forEach(x=>x.classList.toggle('sel', x===card)); };
|
|
card.ondblclick=()=>{ selected=s.id; ipcRenderer.send('picker-choose', selected); };
|
|
grid.appendChild(card);
|
|
});
|
|
}
|
|
function esc(s){ return String(s).replace(/[&<>"]/g,c=>({'&':'&','<':'<','>':'>','"':'"'}[c])); }
|
|
</script>
|
|
</body>
|
|
</html>
|