import { createCard } from './components/Card.js'; import { renderProgressMap as renderProgressMapEl } from './components/ProgressMap.js'; import { attachTypeahead } from './components/HandleTypeahead.js'; import { createElement, RefreshCw, UserPlus, UserMinus, Sprout, ArrowRight } from 'lucide'; const ARROW_RIGHT_HTML = createElement(ArrowRight, { width: 16, height: 16, 'stroke-width': 2, class: 'inline align-text-bottom ml-1' }).outerHTML; const ICON_MAP = { follow: UserPlus, unfollow: UserMinus, seed: Sprout, refresh: RefreshCw }; export function iconHtml(type, size = 14) { const node = ICON_MAP[type]; if (!node) return ''; return createElement(node, { width: size, height: size, 'stroke-width': 2, class: 'inline align-text-bottom' }).outerHTML; } export const KEYS = ['a', 's', 'd', 'f']; export const secretBtns = `
[r] refresh / [b] back / [n] next`; export function showLoading(msg) { document.getElementById('login-screen').style.display = 'none'; document.getElementById('discover-screen').style.display = 'none'; const ls = document.getElementById('loading-screen'); ls.style.display = 'block'; ls.textContent = msg || 'loading…'; } export function showDiscover() { document.getElementById('login-screen').style.display = 'none'; document.getElementById('loading-screen').style.display = 'none'; document.getElementById('discover-screen').style.display = 'block'; } export function setStatus(msg) { document.getElementById('status').innerHTML = msg; } export function updateFollowCounter(count) { const el = document.getElementById('follow-counter'); if (el) el.textContent = `${count.toLocaleString()} following`; } export function setUndoVisible(visible) { const hint = document.getElementById('z-hint'); if (hint) hint.style.display = visible ? 'block' : 'none'; } export function setSmallOnlyChecked(checked) { const el = document.getElementById('small-only-toggle'); if (el) el.checked = checked; } export function setBackVisible(visible) { const btn = document.getElementById('back-btn'); if (btn) btn.style.display = visible ? '' : 'none'; } export function setForwardVisible(visible) { const btn = document.getElementById('forward-btn'); if (btn) btn.style.display = visible ? '' : 'none'; } export function showLoginError(msg) { document.getElementById('login-error').textContent = msg || ''; } export function showError(msg) { document.getElementById('error').textContent = msg || ''; } export function setRefreshSpinnerVisible(visible) { const el = document.getElementById('refresh-spinner'); if (el) el.style.display = visible ? 'flex' : 'none'; } export function setGridDimmed(dimmed) { const classList = document.getElementById('grid').classList; if (dimmed) { classList.add('opacity-40', 'pointer-events-none'); } else { classList.remove('opacity-40', 'pointer-events-none'); } } export function highlightCard(index) { const card = document.getElementById('grid').children[index]; if (!card) return () => {}; card.classList.add('outline', 'outline-4', 'outline-offset-4', 'outline-black', 'dark:outline-gray-600'); return () => card.classList.remove('outline', 'outline-4', 'outline-offset-4', 'outline-black', 'dark:outline-gray-600'); } export function renderGrid(candidates, onSelect, followingSet) { const grid = document.getElementById('grid'); grid.innerHTML = ''; candidates.forEach((p, i) => { const followed = followingSet?.has(p.did) ?? false; const card = createCard(p, KEYS[i], followed ? null : () => onSelect(i), followed); grid.appendChild(card); }); } export function renderProgressMap(timelineEvents, myAvatar, cursor) { renderProgressMapEl(document.getElementById('progress-map'), timelineEvents, myAvatar, cursor); } export function setLoginLoading(isLoading) { const btn = document.getElementById('login-btn'); btn.disabled = isLoading; btn.innerHTML = isLoading ? 'redirecting…' : `enter tunnel${ARROW_RIGHT_HTML}`; if (isLoading) showLoginError(''); } export function bindEvents({ onLogin, onLogout, onKeySelect, onRefresh, onBack, onForward, onUndo, onSmallOnlyToggle }) { const smallToggle = document.getElementById('small-only-toggle'); if (smallToggle && onSmallOnlyToggle) { smallToggle.addEventListener('change', () => { onSmallOnlyToggle(smallToggle.checked); smallToggle.blur(); }); } setLoginLoading(false); const handleInput = document.getElementById('handle'); document.getElementById('login-btn').addEventListener('click', () => { onLogin(handleInput.value.trim()); }); attachTypeahead(handleInput, document.getElementById('handle-typeahead'), { onSubmit: v => onLogin(v), }); document.getElementById('logout-btn').addEventListener('click', onLogout); document.addEventListener('click', e => { if (e.target.id === 'refresh-btn' && onRefresh) onRefresh(); if (e.target.id === 'back-btn' && onBack) onBack(); if (e.target.id === 'forward-btn' && onForward) onForward(); if (e.target.id === 'new-seed-btn' && onForward) onForward(); if (e.target.id === 'undo-btn' && onUndo) onUndo(); if (e.target.id === 'undo-btn-bottom' && onUndo) onUndo(); }); document.addEventListener('keydown', e => { if (e.target.tagName === 'INPUT') return; if (e.key === 'r') { if (onRefresh) onRefresh(); return; } if (e.key === 'b') { if (onBack) onBack(); return; } if (e.key === 'n') { if (onForward) onForward(); return; } if (e.key === 'z') { if (onUndo) onUndo(); return; } const i = KEYS.indexOf(e.key); if (i !== -1) { onKeySelect(i); } }); }