=== startWithSelection tail === 2009: async function startWithSelection() { 2010: hidePicker(); 2011: setCaption("WAKING HER UP…"); 2012: setMainButton("busy", "Loading…"); 2013: loading.classList.remove("done"); 2014: loading.classList.add("active"); 2015: loading.textContent = "Loading avatar..."; 2016: const newsPromise = (settings.avatar === "vuong.glb" || !settings.avatar) ? getHotNews().catch(() => []) : Promise.resolve([]); 2017: let ok = false; 2018: try { 2019: const initPromise = stage.init({ 2020: avatarUrl: settings.avatar ? `/avatars/${settings.avatar}` : undefined, 2021: onprogress: (ev) => { 2022: if (ev.lengthComputable) { 2023: loading.textContent = `Loading avatar ${Math.min(100, Math.round((ev.loaded / ev.total) * 100))}%`; 2024: } 2025: } 2026: }); 2027: const timeout = new Promise((_, rej) => setTimeout(() => rej(new Error("init timeout")), 25000)); 2028: await Promise.race([initPromise, timeout]); 2029: stage.resume(); // start render loop so the avatar is actually visible (not a single black frame) 2030: ok = true; 2031: } catch (err) { 2032: console.error("[startWithSelection] init failed/timeout:", err); 2033: } finally { 2034: try { preFetchedNews = await newsPromise; } catch { preFetchedNews = []; } 2035: newsSeq = preFetchedNews.slice(); 2036: newsSeqIdx = -1; 2037: newsTopicQuery = ""; 2038: loading.classList.remove("active"); 2039: if (ok) setCaption(CAPTIONS.idle); 2040: else setCaption("AVATAR KHÔNG HIỂN THỊ — THỬ CHỌN AVATAR KHÁC", "error"); 2041: setMainButton("start", "Start talking"); 2042: } 2043: } 2044: const pickerStartBtn = document.getElementById("picker-start"); 2045: if (pickerStartBtn) pickerStartBtn.addEventListener("click", () => void startWithSelection()); === index.html topic-tags line === H50: