// Links tab: your own bookmarks, grouped. Saved with the other per-person settings (see app.js: store). // ---- Links: your own bookmarks, grouped, saved with your other settings (private to you, every device) const safeUrl = s => { let t = String(s || '').trim(); if (!t) return ''; if (!/^[a-z][a-z0-9+.-]*:/i.test(t)) t = 'https://' + t; try { const u = new URL(t); return /^https?:$/.test(u.protocol) ? u.href : ''; } catch { return ''; } }; const linkList = () => { const l = store.get('links', []); return Array.isArray(l) ? l.filter(x => x && safeUrl(x.url)) : []; }; const LINK_COLORS = ['#4f7cff', '#2f9e6e', '#e0742b', '#d9453a', '#8a5cf0', '#e5a00d', '#0f9d9a', '#c2417a']; const linkColor = l => LINK_COLORS[[...String(l.title || l.url)].reduce((n, c) => n + c.charCodeAt(0), 0) % LINK_COLORS.length]; /** Links that come with the hub rather than from you (Podcast Admin, for people who may use it). They cannot be edited or deleted. */ const builtinLinks = () => allowed('verse') && cfg.verse && safeUrl(cfg.verse) ? [{ id: 'builtin-verse', title: 'Podcast Admin', url: safeUrl(cfg.verse), group: 'Podcast', builtin: true }] : []; const allLinks = () => [...builtinLinks(), ...linkList()]; let linkQuery = ''; function viewLinks() { const all = allLinks(), q = linkQuery.trim().toLowerCase(); const shown = q ? all.filter(l => (l.title + ' ' + l.url + ' ' + (l.group || '')).toLowerCase().includes(q)) : all; const groups = [...new Set(shown.map(l => l.group || ''))].sort((a, b) => (a === '') - (b === '') || a.localeCompare(b)); $('#view').innerHTML = `
No links yet. Add the sites you use most; they are saved on your server and show up on every device.
'} ${all.length && !shown.length ? 'Nothing matches.
' : ''} ${groups.map(g => `${g ? `