1(function () { 2 'use strict'; 3 4 const NS = (window.HAIPHEN = window.HAIPHEN || {}); 5 const STORAGE_KEY = 'haiphen.welcome_seen'; 6 7 // Capture session early (before async template fetch) 8 let _sessionUser = undefined; // undefined = not yet received 9 window.addEventListener('haiphen:session:ready', (e) => { 10 _sessionUser = e.detail?.user || null; 11 }, { once: true }); 12 13 function injectCssOnce(href) { 14 if (document.querySelector(`link[rel="stylesheet"][href="${href}"]`)) return; 15 const link = document.createElement('link'); 16 link.rel = 'stylesheet'; 17 link.href = href; 18 document.head.appendChild(link); 19 } 20 21 async function fetchText(url) { 22 const r = await fetch(url, { cache: 'no-store' }); 23 if (!r.ok) throw new Error(`HTTP ${r.status}`); 24 return r.text(); 25 } 26 27 function alreadySeen() { 28 try { return !!localStorage.getItem(STORAGE_KEY); } catch { return false; } 29 } 30 31 function markSeen() { 32 try { localStorage.setItem(STORAGE_KEY, new Date().toISOString()); } catch { /* noop */ } 33 } 34 35 NS.WelcomeOverlay = { 36 async init(mountSelector) { 37 if (alreadySeen()) return; 38 39 const mount = document.querySelector(mountSelector); 40 if (!mount) return; 41 42 injectCssOnce('components/welcome-overlay/welcome-overlay.css'); 43 44 try { 45 const html = await fetchText('components/welcome-overlay/welcome-overlay.html'); 46 mount.innerHTML = html; 47 } catch (e) { 48 console.warn('[welcome-overlay] failed to load template', e); 49 return; 50 } 51 52 const backdrop = mount.querySelector('[data-welcome-overlay]'); 53 if (!backdrop) return; 54 55 function show() { 56 setTimeout(() => { 57 backdrop.classList.add('is-visible'); 58 }, 600); 59 } 60 61 // Session may have already fired before template loaded 62 if (_sessionUser !== undefined) { 63 if (_sessionUser) show(); 64 } else { 65 // Still waiting â listen again 66 window.addEventListener('haiphen:session:ready', (e) => { 67 if (e.detail?.user) show(); 68 }, { once: true }); 69 } 70 71 // Also check if session pill already exists (logged in) 72 if (_sessionUser === undefined && document.querySelector('.session-pill')) { 73 show(); 74 } 75 76 // Wire buttons 77 backdrop.addEventListener('click', (e) => { 78 const action = e.target.closest('[data-welcome-action]'); 79 if (!action) { 80 if (e.target === backdrop) dismiss(); 81 return; 82 } 83 84 const act = action.getAttribute('data-welcome-action'); 85 if (act === 'explore') { 86 markSeen(); 87 hide(); 88 if (typeof window.navigateTo === 'function') { 89 window.navigateTo('GettingStarted'); 90 } else if (typeof window.showSection === 'function') { 91 window.showSection('GettingStarted'); 92 window.location.hash = '#getting-started'; 93 } 94 } else if (act === 'dismiss') { 95 dismiss(); 96 } 97 }); 98 99 function dismiss() { 100 markSeen(); 101 hide(); 102 } 103 104 function hide() { 105 backdrop.classList.remove('is-visible'); 106 setTimeout(() => { mount.innerHTML = ''; }, 400); 107 } 108 109 const onKey = (e) => { 110 if (e.key === 'Escape') { 111 dismiss(); 112 document.removeEventListener('keydown', onKey); 113 } 114 }; 115 document.addEventListener('keydown', onKey); 116 } 117 }; 118})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.