import { attachTypeahead } from './components/HandleTypeahead.js';
import { bskyPostUrl } from './api.js';
import { resultsGrid, RESULTS_CSS } from './results.js';
import { FOOTER_HTML, FOOTER_CSS } from './footer.js';
import { esc } from './utils.js';
import { createElement, ArrowRight, Plus, User } from 'lucide';
// the shared results + footer components ship their own css (also used
// verbatim by the worker's public pages)
const resultsStyle = document.createElement('style');
resultsStyle.textContent = RESULTS_CSS + FOOTER_CSS;
document.head.appendChild(resultsStyle);
// footer lives inside main-screen so it only shows once signed in
document.getElementById('main-screen').insertAdjacentHTML('beforeend', FOOTER_HTML);
const ARROW_RIGHT_HTML = createElement(ArrowRight, { width: 16, height: 16, 'stroke-width': 2, class: 'inline align-text-bottom ml-1' }).outerHTML;
const PLUS_HTML = createElement(Plus, { width: 14, height: 14, 'stroke-width': 2, class: 'shrink-0' }).outerHTML;
const OPTION_INPUT_CLASS = 'option-input block w-full text-base p-1.5 bg-stone-100 dark:bg-stone-800 outline-none mt-2 resize-none overflow-hidden';
const MAX_OPTIONS = 12;
export function showLoading(msg) {
document.getElementById('login-screen').style.display = 'none';
document.getElementById('main-screen').style.display = 'none';
const ls = document.getElementById('loading-screen');
ls.style.display = 'block';
ls.textContent = msg || 'loading…';
}
export function showMain() {
document.getElementById('login-screen').style.display = 'none';
document.getElementById('loading-screen').style.display = 'none';
document.getElementById('main-screen').style.display = 'block';
}
export function setWhoami(profile) {
const el = document.getElementById('whoami');
if (!el) return;
el.title = `@${profile.handle}`;
el.className = 'flex items-center gap-2 min-w-0 text-gray-600 dark:text-gray-400';
const avatar = profile.avatar
? ``
: ``;
el.innerHTML =
avatar +
`${esc(profile.displayName || '@' + profile.handle)}`;
const slot = el.querySelector('.avatar-slot');
if (slot) slot.appendChild(createElement(User, { width: 16, height: 16, 'stroke-width': 2 }));
}
export function showLoginError(msg) {
document.getElementById('login-error').textContent = msg || '';
}
export function showError(msg) {
document.getElementById('error').textContent = msg || '';
}
export function setLoginLoading(isLoading) {
const btn = document.getElementById('login-btn');
btn.disabled = isLoading;
btn.innerHTML = isLoading ? 'redirecting…' : `sign in${ARROW_RIGHT_HTML}`;
if (isLoading) showLoginError('');
}
export function setPublishLoading(isLoading) {
const btn = document.getElementById('create-poll-btn');
btn.disabled = isLoading;
btn.textContent = isLoading ? 'publishing…' : 'publish poll';
}
// bluesky-composer-style budget donut: fills as you type, shows the
// remaining count when it gets close, goes red with the overage when over
export function updatePostBudget(graphemes, max) {
const el = document.getElementById('post-budget');
if (!el) return;
const remaining = max - graphemes;
const over = remaining < 0;
const r = 7;
const c = 2 * Math.PI * r;
const frac = Math.min(graphemes / max, 1);
el.className = `flex items-center gap-1.5 tabular-nums text-sm ${over ? 'text-red-500' : 'text-gray-600 dark:text-gray-400'}`;
el.innerHTML =
(over || remaining <= 20 ? `${remaining}` : '') +
``;
const btn = document.getElementById('create-poll-btn');
btn.disabled = over;
btn.classList.toggle('opacity-40', over);
}
export function setPollsDimmed(dimmed) {
const classList = document.getElementById('polls').classList;
if (dimmed) {
classList.add('opacity-40', 'pointer-events-none');
} else {
classList.remove('opacity-40', 'pointer-events-none');
}
}
export function readPollForm() {
const question = document.getElementById('poll-question').value.trim();
const options = [...document.querySelectorAll('.option-input')]
.map(i => i.value.trim())
.filter(Boolean);
return { question, options };
}
export function resetPollForm() {
const q = document.getElementById('poll-question');
q.value = '';
autoGrow(q);
const container = document.getElementById('option-inputs');
container.innerHTML = '';
addOptionInput();
addOptionInput();
}
function autoGrow(el) {
el.style.height = 'auto';
el.style.height = el.scrollHeight + 'px';
}
function addOptionInput() {
const container = document.getElementById('option-inputs');
if (container.children.length >= MAX_OPTIONS) return;
const input = document.createElement('textarea');
input.rows = 1;
input.className = OPTION_INPUT_CLASS;
input.placeholder = `option ${container.children.length + 1}`;
input.autocomplete = 'off';
// options are single-line (they wrap visually, but hold no newlines —
// each becomes one facet line in the post); the question allows breaks
input.addEventListener('keydown', e => { if (e.key === 'Enter') e.preventDefault(); });
input.addEventListener('input', () => {
if (input.value.includes('\n')) input.value = input.value.replace(/\s*\n\s*/g, ' ');
autoGrow(input);
});
container.appendChild(input);
document.getElementById('add-option-btn').style.display =
container.children.length >= MAX_OPTIONS ? 'none' : '';
}
// polls: [{ uri, question, createdAt, options: [{ uri, cid, text, count }] }]
export function renderPolls(polls, onDelete) {
const el = document.getElementById('polls');
el.innerHTML = '';
if (!polls.length) {
el.innerHTML = '';
return;
}
polls.forEach(poll => {
const total = poll.options.reduce((sum, o) => sum + o.count, 0);
const card = document.createElement('div');
card.className = 'mb-12 break-words';
card.innerHTML =
`