Signals' page copy says what happens, not that Jev does it; Auto spins while it picks (#263)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-16 19:28:52 +00:00
parent 15bdce486d
commit f34423735a
6 changed files with 20 additions and 14 deletions

View File

@@ -444,8 +444,8 @@ async function prefsModal(){
<div class="inline"><input type="password" id="stskey" autocomplete="off" spellcheck="false"
placeholder="${S.me?.typesafe_key_set?'Saved. Paste a new one to replace it':'Paste your key'}">
<button class="btn ico" id="stsforget" title="Forget the key" aria-label="Forget the key"${S.me?.typesafe_key_set?'':' hidden'}>${ICON.trash}</button></div>
<span class="hint">Signals pick what matters out of everything you subscribe to, using TypeSafe's
Jev on your own key, from <a href="https://typesafe.ai" target="_blank" rel="noopener">typesafe.ai</a>.
<span class="hint">Signals pick what matters out of everything you subscribe to, on your own
key from <a href="https://typesafe.ai" target="_blank" rel="noopener">typesafe.ai</a>.
The key is kept on this server and never shown again.</span></div>
<div class="field boxed"><label for="tokname">API tokens</label>
<div id="tokens"></div>

View File

@@ -134,7 +134,7 @@ function renderEntries(){
// Said only for a search, where the words typed are why; an empty feed or tab speaks for itself.
if(S.q) box.innerHTML='<p class="empty">Nothing matches that search.</p>';
// A new Signal is empty while Jev reads, which would otherwise look broken.
else if(sg&&S.filter==='all') box.innerHTML='<p class="empty">Nothing has come through yet. Jev reads new items as your feeds are checked.</p>';
else if(sg&&S.filter==='all') box.innerHTML='<p class="empty">Nothing has come through yet. New items are read as your feeds are checked.</p>';
return;
}
for(const e of S.entries) box.appendChild(epEl(e));

View File

@@ -139,7 +139,7 @@ function signalModal(sg?: Signal){
<input type="text" id="sgname" value="${esc(sg?.name||'')}" placeholder="Formula 1"></div>
<div id="sgpicker" hidden>
<div class="inline"><input type="search" id="sgiconq" placeholder="Search icons: robot, car, music…" autocomplete="off">
<button type="button" class="dlink" id="sgjev" title="Let Jev pick an icon">Auto</button>
<button type="button" class="dlink" id="sgjev" title="Pick an icon for me">Auto</button>
<button type="button" class="dlink" id="sgnoicon">Initials</button></div>
<div class="icongrid" id="sgicons" role="listbox" aria-label="Icons"></div>
</div></div>
@@ -147,10 +147,10 @@ function signalModal(sg?: Signal){
<div class="chips" id="sgkw">${chipsHTML(sg?.keywords||[],'sgkwin')}</div></div>
<div class="field"><label for="sgdesc">In your own words (optional)</label>
<input type="text" id="sgdesc" value="${esc(sg?.description||'')}" placeholder="AI tools for writing software: coding agents, vibe coding">
<span class="hint">TypeSafe's Jev reads every item in the feeds you subscribe to, the last 30
days of them and each new one, and pulls out the ones about this subject: its name, these
words and your sentence together. The rest stays noise, in its own feed. Each item is one
call on your TypeSafe key.${sg?' A change here reads everything again.':''}</span></div>
<span class="hint">Every item in the feeds you subscribe to is read, the last 30 days of them
and each new one, and the ones about this subject are pulled out: its name, these words and
your sentence together. The rest stays noise, in its own feed. Each item is one call on your
TypeSafe key.${sg?' A change here reads everything again.':''}</span></div>
${nokey?'<p class="hint" style="color:var(--warn)">Add your TypeSafe key in Settings first: without one, nothing is picked out.</p>':''}
<div class="cardacts"><button class="btn ico" onclick="closeModal()" title="Cancel" aria-label="Cancel">${ICON.close}</button>
<button class="btn ico primary" id="sgsave" title="${sg?'Save':'Make signal'}" aria-label="${sg?'Save':'Make signal'}">${sg?ICON.check:ICON.plus}</button></div>`);
@@ -179,14 +179,15 @@ function signalModal(sg?: Signal){
};
$<HTMLButtonElement>('#sgnoicon').onclick=()=>{ icon=null; drawPick(); $('#sgpicker').hidden=true; };
$<HTMLButtonElement>('#sgjev').onclick=async()=>{
const b=$<HTMLButtonElement>('#sgjev'); b.disabled=true; b.textContent='Asking Jev…';
// A spinner while it is picked: what picks it is nobody's concern.
const b=$<HTMLButtonElement>('#sgjev'); b.disabled=true; b.classList.add('busy'); b.setAttribute('aria-busy','true');
try{
const got=await jevIcon($<HTMLInputElement>('#sgname').value.trim(),chipWords($('#sgkw')).filter((w): w is string=>!!w),
$<HTMLInputElement>('#sgdesc').value.trim()||null);
if(got){ icon=got; drawPick(); $('#sgpicker').hidden=true; $('#sgicon').setAttribute('aria-expanded','false'); }
else toast('No icon matches those words');
}catch(e){ toast(why(e),true); }
finally{ b.disabled=false; b.textContent='Auto'; }
finally{ b.disabled=false; b.classList.remove('busy'); b.removeAttribute('aria-busy'); }
};
$<HTMLInputElement>('#sgname').focus();
$<HTMLButtonElement>('#sgsave').onclick=async()=>{
@@ -204,7 +205,7 @@ function signalModal(sg?: Signal){
if(!sg&&!icon) jevIcon(name,keywords,description)
.then(got=>{ if(got) return api(`/api/signals/${r.id}`,{method:'PATCH',body:JSON.stringify({icon:got})}).then(refreshSignals); })
.catch(()=>{});
closeModal(); toast(sg?'Saved. Jev reads everything again':'Listening. What Jev picks out shows up here');
closeModal(); toast(sg?'Saved. Everything is read again':'Listening. What comes through shows up here');
// Open, so the new one is there to see.
if(!expanded.has(SIGNALS)) toggleGroup(SIGNALS);
await loadFeeds(true); selectFeed(':signal:'+(sg?sg.id:r.id));