feat: all apps in sidebar sorted by activity; inactive apps grouped below divider

This commit is contained in:
posimai 2026-03-22 23:31:58 +09:00
parent f85ad8bd8d
commit 6944c83c0b
2 changed files with 40 additions and 5 deletions

View File

@ -908,7 +908,8 @@ function hasNext(tasks) { return tasks.some(t => t.status === 'next'); }
// ── Sidebar nav ────────────────────────────────────────────────────────────────
function renderAppNav() {
const q = appSearch.toLowerCase();
const allApps = [...data.apps, { id: 'global', tasks: data.global || [] }];
const globalEntry = { id: 'global', tasks: data.global || [] };
const allApps = [globalEntry, ...data.apps];
const filtered = q ? allApps.filter(a => a.id.toLowerCase().includes(q)) : allApps;
// Update total open count
@ -922,23 +923,40 @@ function renderAppNav() {
const upd = document.getElementById('sidebarUpdatedLabel');
if (upd && data.updated) upd.textContent = `更新: ${data.updated}`;
let html = '';
for (const app of filtered) {
// Sort: global first, then active apps (next > any open > done-only), then inactive apps (alpha)
const globalList = filtered.filter(a => a.id === 'global');
const active = filtered.filter(a => a.id !== 'global' && openCount(a.tasks) > 0)
.sort((a, b) => (hasNext(b.tasks) ? 1 : 0) - (hasNext(a.tasks) ? 1 : 0));
const inactive = filtered.filter(a => a.id !== 'global' && openCount(a.tasks) === 0)
.sort((a, b) => a.id.localeCompare(b.id));
function navItem(app) {
const open = openCount(app.tasks);
const hn = hasNext(app.tasks);
const isActive = currentView === 'app' && currentApp === app.id;
const countHtml = open > 0
? `<span class="nav-count${hn ? ' has-next' : ''}">${open}</span>`
: '';
html += `<a class="nav-item${isActive ? ' active' : ''}" data-nav="app" data-app-id="${app.id}" href="#" role="listitem">
return `<a class="nav-item${isActive ? ' active' : ''}" data-nav="app" data-app-id="${app.id}" href="#" role="listitem">
<i data-lucide="${app.id === 'global' ? 'globe' : 'package'}" style="width:14px;height:14px;stroke-width:1.75"></i>
<span class="nav-item-label">${shortName(app.id)}</span>
${countHtml}
<i data-lucide="chevron-right" style="width:11px;height:11px;stroke-width:2.5" class="nav-chevron"></i>
</a>`;
}
let html = '';
if (!filtered.length) {
html = '<div style="padding:8px 10px;font-size:12px;color:var(--text3);">該当なし</div>';
} else {
globalList.forEach(a => { html += navItem(a); });
active.forEach(a => { html += navItem(a); });
if (inactive.length) {
if (active.length || globalList.length) {
html += `<div style="font-size:10px;font-weight:600;color:var(--text3);text-transform:uppercase;letter-spacing:0.1em;padding:10px 8px 4px;opacity:0.7;">全アプリ</div>`;
}
inactive.forEach(a => { html += navItem(a); });
}
}
document.getElementById('appNavList').innerHTML = html;

View File

@ -207,7 +207,24 @@
"done_at": null
}
]
}
},
{ "id": "posimai-dashboard", "tasks": [] },
{ "id": "posimai-brain", "tasks": [] },
{ "id": "posimai-daily", "tasks": [] },
{ "id": "posimai-reader", "tasks": [] },
{ "id": "posimai-journal", "tasks": [] },
{ "id": "posimai-site", "tasks": [] },
{ "id": "posimai-events", "tasks": [] },
{ "id": "posimai-maps", "tasks": [] },
{ "id": "posimai-ambient", "tasks": [] },
{ "id": "posimai-timer", "tasks": [] },
{ "id": "posimai-pulse", "tasks": [] },
{ "id": "posimai-lens", "tasks": [] },
{ "id": "posimai-diff", "tasks": [] },
{ "id": "posimai-clean", "tasks": [] },
{ "id": "posimai-digest", "tasks": [] },
{ "id": "posimai-think", "tasks": [] },
{ "id": "posimai-tech-events","tasks": [] }
],
"global": [
{