Default to All, move OPML into settings, dress the feed actions
Opening a feed whose episodes are all read showed an empty list, so All leads the tabs and is the default. OPML import/export moves under Settings -- an occasional job, not a daily control -- freeing the sidebar. Feed actions become icon pills, with Unsubscribe pushed to the far end and quietened; it sat beside Settings looking identical. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AdXho5tTkjFLeUXKbEjKBh
This commit is contained in:
11
PROGRESS.md
11
PROGRESS.md
@@ -56,6 +56,17 @@ and until now nothing set them.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-09-10 — Defaults and chrome
|
||||||
|
|
||||||
|
* **All** is the default episode filter and the first tab; Unread, Downloaded and Flagged follow.
|
||||||
|
Opening a feed and seeing nothing because everything in it was read is a poor first impression.
|
||||||
|
* **OPML import/export moved into Settings**, under a Subscriptions heading, and the OPML button is
|
||||||
|
gone from the sidebar -- it is a thing you do once in a while, not a daily control.
|
||||||
|
* **The feed actions are pills with icons**, and Unsubscribe is pushed to the far end in a quiet
|
||||||
|
style: it sat next to Settings looking exactly like it, one slip away from losing a feed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 2026-09-10 — Phone layout
|
## 2026-09-10 — Phone layout
|
||||||
|
|
||||||
The UI was unusable on a phone, starting with the worst of it: the ☰ button lived inside the player
|
The UI was unusable on a phone, starting with the worst of it: the ☰ button lived inside the player
|
||||||
|
|||||||
@@ -160,12 +160,21 @@ input:focus,select:focus{outline:0;border-color:var(--accent)}
|
|||||||
.fhead .sub{color:var(--dim);font-size:13px;margin-bottom:8px}
|
.fhead .sub{color:var(--dim);font-size:13px;margin-bottom:8px}
|
||||||
.fhead .sub a{color:var(--dim);text-decoration:none}
|
.fhead .sub a{color:var(--dim);text-decoration:none}
|
||||||
.fhead .sub a:hover{color:var(--accent)}
|
.fhead .sub a:hover{color:var(--accent)}
|
||||||
.acts{display:flex;gap:7px;flex-wrap:wrap;margin-top:auto}
|
.acts{display:flex;gap:7px;flex-wrap:wrap;align-items:center;margin-top:auto}
|
||||||
|
.acts .btn{display:inline-flex;align-items:center;gap:6px;padding:7px 13px;border-radius:999px}
|
||||||
|
.acts .btn i{font-style:normal;font-size:13px;line-height:1;opacity:.7}
|
||||||
|
.acts .btn.primary i{opacity:.9}
|
||||||
|
.acts .btn:hover{background:var(--raise)}
|
||||||
|
.acts .btn.primary:hover{background:var(--accent2);border-color:var(--accent2)}
|
||||||
|
/* Leaving a feed is not one of the everyday actions: keep it off on its own. */
|
||||||
|
.acts .btn.last{margin-left:auto;border-color:transparent;background:none;color:var(--faint)}
|
||||||
|
.acts .btn.last:hover{background:none;border-color:var(--bad);color:var(--bad)}
|
||||||
.btn{
|
.btn{
|
||||||
background:var(--panel2);border:1px solid var(--line);border-radius:8px;
|
background:var(--panel2);border:1px solid var(--line);border-radius:8px;
|
||||||
padding:6px 12px;font-size:13px;
|
padding:6px 12px;font-size:13px;
|
||||||
}
|
}
|
||||||
.btn:hover{border-color:var(--accent)}
|
.btn:hover{border-color:var(--accent)}
|
||||||
|
a.btn{text-decoration:none;color:inherit}
|
||||||
.btn.primary{background:var(--accent);border-color:var(--accent);color:var(--ink);font-weight:600}
|
.btn.primary{background:var(--accent);border-color:var(--accent);color:var(--ink);font-weight:600}
|
||||||
.btn.primary:hover{filter:brightness(1.08)}
|
.btn.primary:hover{filter:brightness(1.08)}
|
||||||
.btn.danger:hover{border-color:var(--bad);color:var(--bad)}
|
.btn.danger:hover{border-color:var(--bad);color:var(--bad)}
|
||||||
@@ -352,7 +361,6 @@ input[type=range]::-moz-range-thumb{width:12px;height:12px;border:0;border-radiu
|
|||||||
<div class="sidetools">
|
<div class="sidetools">
|
||||||
<button id="addFeed">+ Feed</button>
|
<button id="addFeed">+ Feed</button>
|
||||||
<button id="scanAll">Scan all</button>
|
<button id="scanAll">Scan all</button>
|
||||||
<button id="opml" title="Import / export OPML">OPML</button>
|
|
||||||
<button id="logs" title="Daemon and web log">Log</button>
|
<button id="logs" title="Daemon and web log">Log</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="searchwrap"><input type="search" id="feedFilter" placeholder="Filter feeds…"></div>
|
<div class="searchwrap"><input type="search" id="feedFilter" placeholder="Filter feeds…"></div>
|
||||||
@@ -474,7 +482,7 @@ function nav(on){ $('#sidebar').classList.toggle('open',on); $('#scrim').hidden=
|
|||||||
/* ---------------- state ---------------- */
|
/* ---------------- state ---------------- */
|
||||||
const S = {
|
const S = {
|
||||||
feeds:[], feed:null, entries:[], total:0, offset:0, limit:50,
|
feeds:[], feed:null, entries:[], total:0, offset:0, limit:50,
|
||||||
filter:'unread', q:'', sel:null, busy:new Set(),
|
filter:'all', q:'', sel:null, busy:new Set(),
|
||||||
};
|
};
|
||||||
const LIMIT = 50;
|
const LIMIT = 50;
|
||||||
|
|
||||||
@@ -551,17 +559,17 @@ function renderFeed(){
|
|||||||
OPML subscription. It was kept rather than removed because it has downloaded episodes.</div>`:''}
|
OPML subscription. It was kept rather than removed because it has downloaded episodes.</div>`:''}
|
||||||
${f.group?`<div class="sub">From the OPML subscription <b>${esc(f.group)}</b></div>`:''}
|
${f.group?`<div class="sub">From the OPML subscription <b>${esc(f.group)}</b></div>`:''}
|
||||||
<div class="acts">
|
<div class="acts">
|
||||||
<button class="btn primary" data-a="scan">Scan now</button>
|
<button class="btn primary" data-a="scan"><i>⟳</i>Scan now</button>
|
||||||
<button class="btn" data-a="dl">Download latest…</button>
|
<button class="btn" data-a="dl"><i>↓</i>Download latest…</button>
|
||||||
<button class="btn" data-a="read">Mark all read</button>
|
<button class="btn" data-a="read"><i>✓</i>Mark all read</button>
|
||||||
<button class="btn" data-a="settings">Settings</button>
|
<button class="btn" data-a="settings"><i>⚙</i>Settings</button>
|
||||||
<button class="btn danger" data-a="rm">Unsubscribe</button>
|
<button class="btn danger last" data-a="rm"><i>✕</i>Unsubscribe</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
${['unread','all','downloaded','flagged'].map(t=>
|
${['all','unread','downloaded','flagged'].map(t=>
|
||||||
`<button data-f="${t}" class="${S.filter===t?'on':''}">${t[0].toUpperCase()+t.slice(1)}</button>`).join('')}
|
`<button data-f="${t}" class="${S.filter===t?'on':''}">${t[0].toUpperCase()+t.slice(1)}</button>`).join('')}
|
||||||
</div>
|
</div>
|
||||||
<input type="search" class="grow" id="epSearch" placeholder="Search episodes…" value="${esc(S.q)}">
|
<input type="search" class="grow" id="epSearch" placeholder="Search episodes…" value="${esc(S.q)}">
|
||||||
@@ -600,10 +608,10 @@ function renderGroup(f,kids){
|
|||||||
${gone?`<div class="sub" style="color:var(--warn)">${gone} feed${gone===1?' is':'s are'} no longer
|
${gone?`<div class="sub" style="color:var(--warn)">${gone} feed${gone===1?' is':'s are'} no longer
|
||||||
listed in this OPML but kept because ${gone===1?'it has':'they have'} downloads.</div>`:''}
|
listed in this OPML but kept because ${gone===1?'it has':'they have'} downloads.</div>`:''}
|
||||||
<div class="acts">
|
<div class="acts">
|
||||||
<button class="btn primary" data-a="scan">Re-read OPML</button>
|
<button class="btn primary" data-a="scan"><i>⟳</i>Re-read OPML</button>
|
||||||
<button class="btn" data-a="settings">Settings</button>
|
<button class="btn" data-a="read"><i>✓</i>Mark all read</button>
|
||||||
<button class="btn" data-a="read">Mark all read</button>
|
<button class="btn" data-a="settings"><i>⚙</i>Settings</button>
|
||||||
<button class="btn danger" data-a="rm">Unsubscribe</button>
|
<button class="btn danger last" data-a="rm"><i>✕</i>Unsubscribe</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1151,8 +1159,16 @@ async function prefsModal(){
|
|||||||
<input type="number" id="gage" min="0" value="${g.max_age_days}"></div>
|
<input type="number" id="gage" min="0" value="${g.max_age_days}"></div>
|
||||||
<div class="field"><label>Download folder</label>
|
<div class="field"><label>Download folder</label>
|
||||||
<span class="hint" style="overflow-wrap:anywhere">${esc(g.download_dir)}</span></div>
|
<span class="hint" style="overflow-wrap:anywhere">${esc(g.download_dir)}</span></div>
|
||||||
|
<div class="field"><label>Subscriptions</label>
|
||||||
|
<div class="inline">
|
||||||
|
<a class="btn" href="/api/opml" download="ipx-subscriptions.opml">Export OPML</a>
|
||||||
|
<button class="btn" id="gopml">Import OPML…</button>
|
||||||
|
</div>
|
||||||
|
<span class="hint">Export hands every subscription to another podcast app. Import adds the
|
||||||
|
feeds listed in an OPML you paste in.</span></div>
|
||||||
<div class="cardacts"><button class="btn" onclick="closeModal()">Cancel</button>
|
<div class="cardacts"><button class="btn" onclick="closeModal()">Cancel</button>
|
||||||
<button class="btn primary" id="gsave">Save</button></div>`);
|
<button class="btn primary" id="gsave">Save</button></div>`);
|
||||||
|
$('#gopml').onclick=opmlModal;
|
||||||
$('#gsave').onclick=async()=>{
|
$('#gsave').onclick=async()=>{
|
||||||
try{
|
try{
|
||||||
await api('/api/settings',{method:'PATCH',body:JSON.stringify({
|
await api('/api/settings',{method:'PATCH',body:JSON.stringify({
|
||||||
@@ -1253,7 +1269,7 @@ function removeFeed(f){
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#opml').onclick=()=>{
|
function opmlModal(){
|
||||||
openModal(`<h3>OPML</h3>
|
openModal(`<h3>OPML</h3>
|
||||||
<p style="color:var(--dim);font-size:13.5px">Move subscriptions between podcast apps.</p>
|
<p style="color:var(--dim);font-size:13.5px">Move subscriptions between podcast apps.</p>
|
||||||
<div class="cardacts" style="justify-content:flex-start">
|
<div class="cardacts" style="justify-content:flex-start">
|
||||||
@@ -1269,7 +1285,7 @@ $('#opml').onclick=()=>{
|
|||||||
closeModal(); toast(`Imported ${r.added} feed(s)`); loadFeeds(true);
|
closeModal(); toast(`Imported ${r.added} feed(s)`); loadFeeds(true);
|
||||||
}catch(e){ toast(e.message,true); }
|
}catch(e){ toast(e.message,true); }
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
function on(sel,ev,fn){
|
function on(sel,ev,fn){
|
||||||
const el=$(sel);
|
const el=$(sel);
|
||||||
|
|||||||
Reference in New Issue
Block a user