fix(today): preserve unsaved entry during controls
This commit is contained in:
1 parent
8023f1d4b4
commit
667ab4b4b8
1 file changed
+1
-1
Vendored
+1
-1
@@ -17,7 +17,7 @@ function desktopIconClick(id,e){if(innerWidth<650){openApp(id);return}selectedIc
|
|||||||
function clock(){let d=new Date();document.querySelector('#clock').textContent=d.toLocaleTimeString([], {hour:'2-digit',minute:'2-digit'});document.querySelector('#date').textContent=d.toLocaleDateString('en-US',{month:'short',day:'numeric'}).toUpperCase()}function toggleLauncher(){launcher.classList.toggle('hidden');calendar.classList.add('hidden');launcher.innerHTML=`<div class="launchhead"><span>LIFE OS</span><span>GOOD ${new Date().getHours()<12?'MORNING':new Date().getHours()<18?'AFTERNOON':'EVENING'}</span></div><div class="launchgrid">${Object.entries(APPS).map(([id,a])=>`<button class="launchitem" onclick="openApp('${id}');toggleLauncher()">${a[0]}<br><br>${a[1]}</button>`).join('')}</div><div class="systemline">SYSTEM ONLINE · LIFE OS v0.1</div>`}function toggleCalendar(){calendar.classList.toggle('hidden');launcher.classList.add('hidden');calendar.innerHTML=`<div class="eyebrow">SYSTEM DATE</div><h2>${fmtDate(day())}</h2><p class="sub">${mems().length} memories · ${museum().length} preserved</p>`}function hideContext(){document.querySelector('#life-context')?.remove()}function showContext(e,id){e.preventDefault();e.stopPropagation();hideContext();let zh=lang()==='zh',labels=zh?{open:'打开',min:'最小化',close:'关闭',refresh:'刷新桌面',new:'新建今日记录',system:'系统设置'}:{open:'Open',min:'Minimize',close:'Close',refresh:'Refresh desktop',new:'New today entry',system:'System settings'},actions=id?`<button onclick="openApp('${id}');hideContext()">${labels.open}</button>${windows[id]?`<button onclick="minWin('${id}');hideContext()">${labels.min}</button><button onclick="closeWin('${id}');hideContext()">${labels.close}</button>`:''}`:`<button onclick="renderIcons();hideContext()">${labels.refresh}</button><button onclick="openApp('today');hideContext()">${labels.new}</button><hr><button onclick="openApp('system');hideContext()">${labels.system}</button>`;let m=document.createElement('div');m.id='life-context';m.style.cssText=`position:fixed;z-index:7000;left:${Math.min(e.clientX,innerWidth-190)}px;top:${Math.min(e.clientY,innerHeight-150)}px;width:180px;padding:6px;background:var(--panel);border:1px solid var(--line);border-radius:10px;box-shadow:var(--shadow)`;m.innerHTML=actions;m.querySelectorAll('button').forEach(b=>b.style.cssText='display:block;width:100%;border:0;background:transparent;color:var(--ink);text-align:left;border-radius:6px;padding:9px 10px;font:12px Manrope');m.querySelectorAll('button').forEach(b=>b.onmouseenter=()=>b.style.background='var(--panel2)');document.body.append(m)}document.addEventListener('contextmenu',e=>{if(e.target.closest('.desktop')&&!e.target.closest('.app-icon')&&!e.target.closest('.window'))showContext(e,null)});document.addEventListener('click',hideContext)
|
function clock(){let d=new Date();document.querySelector('#clock').textContent=d.toLocaleTimeString([], {hour:'2-digit',minute:'2-digit'});document.querySelector('#date').textContent=d.toLocaleDateString('en-US',{month:'short',day:'numeric'}).toUpperCase()}function toggleLauncher(){launcher.classList.toggle('hidden');calendar.classList.add('hidden');launcher.innerHTML=`<div class="launchhead"><span>LIFE OS</span><span>GOOD ${new Date().getHours()<12?'MORNING':new Date().getHours()<18?'AFTERNOON':'EVENING'}</span></div><div class="launchgrid">${Object.entries(APPS).map(([id,a])=>`<button class="launchitem" onclick="openApp('${id}');toggleLauncher()">${a[0]}<br><br>${a[1]}</button>`).join('')}</div><div class="systemline">SYSTEM ONLINE · LIFE OS v0.1</div>`}function toggleCalendar(){calendar.classList.toggle('hidden');launcher.classList.add('hidden');calendar.innerHTML=`<div class="eyebrow">SYSTEM DATE</div><h2>${fmtDate(day())}</h2><p class="sub">${mems().length} memories · ${museum().length} preserved</p>`}function hideContext(){document.querySelector('#life-context')?.remove()}function showContext(e,id){e.preventDefault();e.stopPropagation();hideContext();let zh=lang()==='zh',labels=zh?{open:'打开',min:'最小化',close:'关闭',refresh:'刷新桌面',new:'新建今日记录',system:'系统设置'}:{open:'Open',min:'Minimize',close:'Close',refresh:'Refresh desktop',new:'New today entry',system:'System settings'},actions=id?`<button onclick="openApp('${id}');hideContext()">${labels.open}</button>${windows[id]?`<button onclick="minWin('${id}');hideContext()">${labels.min}</button><button onclick="closeWin('${id}');hideContext()">${labels.close}</button>`:''}`:`<button onclick="renderIcons();hideContext()">${labels.refresh}</button><button onclick="openApp('today');hideContext()">${labels.new}</button><hr><button onclick="openApp('system');hideContext()">${labels.system}</button>`;let m=document.createElement('div');m.id='life-context';m.style.cssText=`position:fixed;z-index:7000;left:${Math.min(e.clientX,innerWidth-190)}px;top:${Math.min(e.clientY,innerHeight-150)}px;width:180px;padding:6px;background:var(--panel);border:1px solid var(--line);border-radius:10px;box-shadow:var(--shadow)`;m.innerHTML=actions;m.querySelectorAll('button').forEach(b=>b.style.cssText='display:block;width:100%;border:0;background:transparent;color:var(--ink);text-align:left;border-radius:6px;padding:9px 10px;font:12px Manrope');m.querySelectorAll('button').forEach(b=>b.onmouseenter=()=>b.style.background='var(--panel2)');document.body.append(m)}document.addEventListener('contextmenu',e=>{if(e.target.closest('.desktop')&&!e.target.closest('.app-icon')&&!e.target.closest('.window'))showContext(e,null)});document.addEventListener('click',hideContext)
|
||||||
function openApp(id){launcher.classList.add('hidden');let w=windows[id];if(w){w.classList.remove('min');focus(w);renderTaskbar();return}w=document.createElement('section');w.className='window';w.dataset.id=id;w.style.left=(innerWidth<650?0:90+Object.keys(windows).length*36)+'px';w.style.top=(innerWidth<650?0:72+Object.keys(windows).length*28)+'px';w.innerHTML=`<div class="titlebar"><span class="titleicon">${APPS[id][0]}</span><span class="wtitle">${APPS[id][1]}</span><div class="controls"><button data-window-action="min" aria-label="Minimize">—</button><button data-window-action="max" aria-label="Maximize">□</button><button data-window-action="close" aria-label="Close">×</button></div></div><div class="content" id="content-${id}"></div>`;windows[id]=w;document.querySelector('#windows').append(w);let controls=w.querySelector('.controls');controls.addEventListener('pointerdown',e=>{e.preventDefault();e.stopPropagation()});controls.addEventListener('click',e=>{let action=e.target.closest('button')?.dataset.windowAction;if(!action)return;e.preventDefault();e.stopPropagation();if(action==='min')minWin(id);if(action==='max')maxWin(id);if(action==='close')closeWin(id)});w.addEventListener('mousedown',()=>focus(w));drag(w);renderApp(id);focus(w);renderTaskbar()}function focus(w){w.style.zIndex=++z}function closeWin(id){windows[id]?.remove();delete windows[id];renderTaskbar()}function minWin(id){let w=windows[id];if(!w)return;w.classList.add('min');renderTaskbar();toast('MINIMIZED — TAP THE APP ICON TO RESTORE')}function maxWin(id){windows[id].classList.toggle('max');focus(windows[id])}function drag(w){let bar=w.querySelector('.titlebar'),x,y,l,t;bar.onpointerdown=e=>{if(e.target.closest('.controls')||innerWidth<650)return;x=e.clientX;y=e.clientY;l=w.offsetLeft;t=w.offsetTop;bar.setPointerCapture(e.pointerId);bar.onpointermove=e=>{w.style.left=Math.max(0,l+e.clientX-x)+'px';w.style.top=Math.max(0,t+e.clientY-y)+'px'};bar.onpointerup=()=>bar.onpointermove=null}}
|
function openApp(id){launcher.classList.add('hidden');let w=windows[id];if(w){w.classList.remove('min');focus(w);renderTaskbar();return}w=document.createElement('section');w.className='window';w.dataset.id=id;w.style.left=(innerWidth<650?0:90+Object.keys(windows).length*36)+'px';w.style.top=(innerWidth<650?0:72+Object.keys(windows).length*28)+'px';w.innerHTML=`<div class="titlebar"><span class="titleicon">${APPS[id][0]}</span><span class="wtitle">${APPS[id][1]}</span><div class="controls"><button data-window-action="min" aria-label="Minimize">—</button><button data-window-action="max" aria-label="Maximize">□</button><button data-window-action="close" aria-label="Close">×</button></div></div><div class="content" id="content-${id}"></div>`;windows[id]=w;document.querySelector('#windows').append(w);let controls=w.querySelector('.controls');controls.addEventListener('pointerdown',e=>{e.preventDefault();e.stopPropagation()});controls.addEventListener('click',e=>{let action=e.target.closest('button')?.dataset.windowAction;if(!action)return;e.preventDefault();e.stopPropagation();if(action==='min')minWin(id);if(action==='max')maxWin(id);if(action==='close')closeWin(id)});w.addEventListener('mousedown',()=>focus(w));drag(w);renderApp(id);focus(w);renderTaskbar()}function focus(w){w.style.zIndex=++z}function closeWin(id){windows[id]?.remove();delete windows[id];renderTaskbar()}function minWin(id){let w=windows[id];if(!w)return;w.classList.add('min');renderTaskbar();toast('MINIMIZED — TAP THE APP ICON TO RESTORE')}function maxWin(id){windows[id].classList.toggle('max');focus(windows[id])}function drag(w){let bar=w.querySelector('.titlebar'),x,y,l,t;bar.onpointerdown=e=>{if(e.target.closest('.controls')||innerWidth<650)return;x=e.clientX;y=e.clientY;l=w.offsetLeft;t=w.offsetTop;bar.setPointerCapture(e.pointerId);bar.onpointermove=e=>{w.style.left=Math.max(0,l+e.clientX-x)+'px';w.style.top=Math.max(0,t+e.clientY-y)+'px'};bar.onpointerup=()=>bar.onpointermove=null}}
|
||||||
function renderApp(id){let c=document.querySelector('#content-'+id);if(!c)return;if(id==='today')today(c);if(id==='timeline')timeline(c);if(id==='museum')museumApp(c);if(id==='insights')insights(c);if(id==='profile')profileApp(c);if(id==='system')systemApp(c);setTimeout(applyLanguage,0)}function renderAll(){Object.keys(windows).forEach(renderApp);renderTaskbar();setTimeout(applyLanguage,0)}
|
function renderApp(id){let c=document.querySelector('#content-'+id);if(!c)return;if(id==='today')today(c);if(id==='timeline')timeline(c);if(id==='museum')museumApp(c);if(id==='insights')insights(c);if(id==='profile')profileApp(c);if(id==='system')systemApp(c);setTimeout(applyLanguage,0)}function renderAll(){Object.keys(windows).forEach(renderApp);renderTaskbar();setTimeout(applyLanguage,0)}
|
||||||
function today(c){let m=mems().find(x=>x.date===day());selectedMood=m?.mood||'cloudy';selectedEnergy=m?.energy||3;let f=m||filmFor(selectedMood,selectedEnergy);c.innerHTML=`<div class="eyebrow">${new Date().toLocaleDateString('en-US',{weekday:'long'}).toUpperCase()}</div><h1>${fmtDate(day()).replace(/^[^,]+, /,'')}</h1><p class="sub">How was your day?</p><div class="today-grid"><div><textarea id="entry" class="entry" placeholder="Write something about today...">${esc(m?.text||'')}</textarea><div class="actions"><button class="primary" onclick="archiveToday()">${m?'UPDATE MEMORY':'ARCHIVE TODAY'}</button><span id="saveStatus" class="status"></span></div></div><aside><div class="eyebrow">TODAY'S WEATHER</div><div class="choices">${['clear','light','cloudy','rain','storm'].map(x=>`<button class="chip ${x===selectedMood?'selected':''}" onclick="setMood('${x}')">${x.toUpperCase()}</button>`).join('')}</div><div class="eyebrow" style="margin-top:22px">ENERGY LEVEL</div><div class="energy">${[1,2,3,4,5].map(x=>`<button class="${x===selectedEnergy?'selected':''}" onclick="setEnergy(${x})">${x}</button>`).join('')}</div><div class="film"><div class="eyebrow">TODAY'S FILM</div><div class="film-title">${f.filmTitle}</div><div class="sub">${f.filmSubtitle}</div><p class="film-copy">“${f.filmLine}”</p></div></aside></div>`}function filmFor(m,e){let n=Math.abs([...m].reduce((a,c)=>a+c.charCodeAt(0),e*7)+new Date().getDate())%films.length;return{filmTitle:films[n][0],filmSubtitle:films[n][1],filmLine:lines[(n*3+e)%lines.length]}}function setMood(x){selectedMood=x;today(document.querySelector('#content-today'))}function setEnergy(x){selectedEnergy=x;today(document.querySelector('#content-today'))}function archiveToday(){let text=document.querySelector('#entry').value.trim(),a=mems(),old=a.find(x=>x.date===day()),f=old||filmFor(selectedMood,selectedEnergy),now=new Date().toISOString(),m={id:old?.id||'memory_'+crypto.randomUUID(),date:day(),text,mood:selectedMood,energy:selectedEnergy,filmTitle:f.filmTitle,filmSubtitle:f.filmSubtitle,filmLine:f.filmLine,createdAt:old?.createdAt||now,updatedAt:now};let status=document.querySelector('#saveStatus');status.textContent='ARCHIVING MEMORY...';setTimeout(()=>{save('lifeos_memories',[...a.filter(x=>x.date!==day()),m].sort((x,y)=>y.date.localeCompare(x.date)));status.textContent='MEMORY ARCHIVED';renderAll()},360)}
|
let todayDraft=null;function today(c){let m=mems().find(x=>x.date===day());if(todayDraft===null){selectedMood=m?.mood||'cloudy';selectedEnergy=m?.energy||3}let f=m||filmFor(selectedMood,selectedEnergy),text=todayDraft===null?(m?.text||''):todayDraft;c.innerHTML=`<div class="eyebrow">${new Date().toLocaleDateString('en-US',{weekday:'long'}).toUpperCase()}</div><h1>${fmtDate(day()).replace(/^[^,]+, /,'')}</h1><p class="sub">How was your day?</p><div class="today-grid"><div><textarea id="entry" class="entry" placeholder="Write something about today..." oninput="todayDraft=this.value">${esc(text)}</textarea><div class="actions"><button class="primary" onclick="archiveToday()">${m?'UPDATE MEMORY':'ARCHIVE TODAY'}</button><span id="saveStatus" class="status"></span></div></div><aside><div class="eyebrow">TODAY'S WEATHER</div><div class="choices">${['clear','light','cloudy','rain','storm'].map(x=>`<button class="chip ${x===selectedMood?'selected':''}" onclick="setMood('${x}')">${x.toUpperCase()}</button>`).join('')}</div><div class="eyebrow" style="margin-top:22px">ENERGY LEVEL</div><div class="energy">${[1,2,3,4,5].map(x=>`<button class="${x===selectedEnergy?'selected':''}" onclick="setEnergy(${x})">${x}</button>`).join('')}</div><div class="film"><div class="eyebrow">TODAY'S FILM</div><div class="film-title">${f.filmTitle}</div><div class="sub">${f.filmSubtitle}</div><p class="film-copy">“${f.filmLine}”</p></div></aside></div>`}function filmFor(m,e){let n=Math.abs([...m].reduce((a,c)=>a+c.charCodeAt(0),e*7)+new Date().getDate())%films.length;return{filmTitle:films[n][0],filmSubtitle:films[n][1],filmLine:lines[(n*3+e)%lines.length]}}function rerenderToday(){let old=document.querySelector('#entry');todayDraft=old?old.value:todayDraft||'';today(document.querySelector('#content-today'));let entry=document.querySelector('#entry');entry?.focus();entry?.setSelectionRange(entry.value.length,entry.value.length)}function setMood(x){selectedMood=x;rerenderToday()}function setEnergy(x){selectedEnergy=x;rerenderToday()}function archiveToday(){let text=document.querySelector('#entry').value.trim(),a=mems(),old=a.find(x=>x.date===day()),f=old||filmFor(selectedMood,selectedEnergy),now=new Date().toISOString(),m={id:old?.id||'memory_'+crypto.randomUUID(),date:day(),text,mood:selectedMood,energy:selectedEnergy,filmTitle:f.filmTitle,filmSubtitle:f.filmSubtitle,filmLine:f.filmLine,createdAt:old?.createdAt||now,updatedAt:now};let status=document.querySelector('#saveStatus');status.textContent='ARCHIVING MEMORY...';setTimeout(()=>{save('lifeos_memories',[...a.filter(x=>x.date!==day()),m].sort((x,y)=>y.date.localeCompare(x.date)));todayDraft=null;status.textContent='MEMORY ARCHIVED';renderAll()},360)}
|
||||||
function timeline(c){let a=mems();c.innerHTML=`<div class="eyebrow">MEMORY ARCHIVE</div><h1>${a.length} ${a.length===1?'MEMORY':'MEMORIES'} ARCHIVED</h1>${a.length?`<div class="timeline">${a.map(m=>`<article class="memory"><button onclick="detail('${m.id}')"><div class="date">${shortDate(m.date)} · ${m.mood.toUpperCase()} · ENERGY ${m.energy}</div><div class="mtitle">${esc(m.filmTitle)}</div><div class="snippet">${esc(m.text||'No written note.')}</div></button></article>`).join('')}</div>`:'<div class="empty">No memories yet.<br>Open Today when you are ready to keep one.</div>'}`}function detail(id){let m=mems().find(x=>x.id===id);if(!m)return;modal.innerHTML=`<div class="modal"><div class="modalbox"><div class="eyebrow">MEMORY DETAIL</div><h1>${esc(m.filmTitle)}</h1><p class="sub">${fmtDate(m.date)} · ${m.mood.toUpperCase()} · ENERGY ${m.energy}</p><p style="line-height:1.65">${esc(m.text||'No written note.').replace(/\n/g,'<br>')}</p><p class="film-copy">${m.filmSubtitle}</p><div class="actions"><button class="primary" onclick="editMemory('${m.id}')">EDIT</button><button class="secondary" onclick="preserve('${m.id}')">${museum().some(x=>x.memoryId===m.id)?'PRESERVED':'ADD TO MUSEUM'}</button><button class="danger" onclick="confirmDelete('${m.id}')">DELETE</button></div></div></div>`}function editMemory(id){let m=mems().find(x=>x.id===id);modal.innerHTML='';openApp('today');selectedMood=m.mood;selectedEnergy=m.energy;let cur=mems(),ix=cur.findIndex(x=>x.id===id);cur[ix].date=day();save('lifeos_memories',cur);today(document.querySelector('#content-today'));document.querySelector('#entry').value=m.text;toast('EDITING AS TODAY — SAVE TO UPDATE')}function preserve(id){let m=mems().find(x=>x.id===id),a=museum();if(!a.some(x=>x.memoryId===id)){save('lifeos_museum',[...a,{id:'artifact_'+crypto.randomUUID(),memoryId:id,preservedAt:new Date().toISOString()}]);toast('MEMORY PRESERVED')}modal.innerHTML='';renderAll()}function confirmDelete(id){modal.innerHTML=`<div class="modal"><div class="modalbox"><div class="eyebrow">CONFIRM DELETE</div><h2>Remove this memory?</h2><p class="sub">This cannot be undone.</p><div class="actions"><button class="danger" onclick="deleteMemory('${id}')">DELETE MEMORY</button><button class="secondary" onclick="modal.innerHTML=''">CANCEL</button></div></div></div>`}function deleteMemory(id){save('lifeos_memories',mems().filter(x=>x.id!==id));save('lifeos_museum',museum().filter(x=>x.memoryId!==id));modal.innerHTML='';renderAll();toast('MEMORY REMOVED')}
|
function timeline(c){let a=mems();c.innerHTML=`<div class="eyebrow">MEMORY ARCHIVE</div><h1>${a.length} ${a.length===1?'MEMORY':'MEMORIES'} ARCHIVED</h1>${a.length?`<div class="timeline">${a.map(m=>`<article class="memory"><button onclick="detail('${m.id}')"><div class="date">${shortDate(m.date)} · ${m.mood.toUpperCase()} · ENERGY ${m.energy}</div><div class="mtitle">${esc(m.filmTitle)}</div><div class="snippet">${esc(m.text||'No written note.')}</div></button></article>`).join('')}</div>`:'<div class="empty">No memories yet.<br>Open Today when you are ready to keep one.</div>'}`}function detail(id){let m=mems().find(x=>x.id===id);if(!m)return;modal.innerHTML=`<div class="modal"><div class="modalbox"><div class="eyebrow">MEMORY DETAIL</div><h1>${esc(m.filmTitle)}</h1><p class="sub">${fmtDate(m.date)} · ${m.mood.toUpperCase()} · ENERGY ${m.energy}</p><p style="line-height:1.65">${esc(m.text||'No written note.').replace(/\n/g,'<br>')}</p><p class="film-copy">${m.filmSubtitle}</p><div class="actions"><button class="primary" onclick="editMemory('${m.id}')">EDIT</button><button class="secondary" onclick="preserve('${m.id}')">${museum().some(x=>x.memoryId===m.id)?'PRESERVED':'ADD TO MUSEUM'}</button><button class="danger" onclick="confirmDelete('${m.id}')">DELETE</button></div></div></div>`}function editMemory(id){let m=mems().find(x=>x.id===id);modal.innerHTML='';openApp('today');selectedMood=m.mood;selectedEnergy=m.energy;let cur=mems(),ix=cur.findIndex(x=>x.id===id);cur[ix].date=day();save('lifeos_memories',cur);today(document.querySelector('#content-today'));document.querySelector('#entry').value=m.text;toast('EDITING AS TODAY — SAVE TO UPDATE')}function preserve(id){let m=mems().find(x=>x.id===id),a=museum();if(!a.some(x=>x.memoryId===id)){save('lifeos_museum',[...a,{id:'artifact_'+crypto.randomUUID(),memoryId:id,preservedAt:new Date().toISOString()}]);toast('MEMORY PRESERVED')}modal.innerHTML='';renderAll()}function confirmDelete(id){modal.innerHTML=`<div class="modal"><div class="modalbox"><div class="eyebrow">CONFIRM DELETE</div><h2>Remove this memory?</h2><p class="sub">This cannot be undone.</p><div class="actions"><button class="danger" onclick="deleteMemory('${id}')">DELETE MEMORY</button><button class="secondary" onclick="modal.innerHTML=''">CANCEL</button></div></div></div>`}function deleteMemory(id){save('lifeos_memories',mems().filter(x=>x.id!==id));save('lifeos_museum',museum().filter(x=>x.memoryId!==id));modal.innerHTML='';renderAll();toast('MEMORY REMOVED')}
|
||||||
function museumApp(c){let a=museum(),ms=mems();c.innerHTML=`<div class="eyebrow">MUSEUM</div><h1>Preserved moments</h1><p class="sub">Worth keeping for the long view.</p>${a.length?`<div class="museum">${a.map(x=>{let m=ms.find(q=>q.id===x.memoryId);return m?`<button class="artifact" onclick="detail('${m.id}')"><span>COLLECTION #${x.id.slice(-4).toUpperCase()}</span><strong>${esc(m.filmTitle)}</strong><span>${fmtDate(m.date)}<br><br>STATUS: PRESERVED</span></button>`:''}).join('')}</div>`:'<div class="empty">A museum begins with one preserved moment.</div>'}`}
|
function museumApp(c){let a=museum(),ms=mems();c.innerHTML=`<div class="eyebrow">MUSEUM</div><h1>Preserved moments</h1><p class="sub">Worth keeping for the long view.</p>${a.length?`<div class="museum">${a.map(x=>{let m=ms.find(q=>q.id===x.memoryId);return m?`<button class="artifact" onclick="detail('${m.id}')"><span>COLLECTION #${x.id.slice(-4).toUpperCase()}</span><strong>${esc(m.filmTitle)}</strong><span>${fmtDate(m.date)}<br><br>STATUS: PRESERVED</span></button>`:''}).join('')}</div>`:'<div class="empty">A museum begins with one preserved moment.</div>'}`}
|
||||||
function streak(a){let s=0,d=new Date();while(a.some(m=>m.date===d.toISOString().slice(0,10))){s++;d.setDate(d.getDate()-1)}return s}function longest(a){let ds=a.map(x=>x.date).sort(),best=0,run=0,last='';ds.forEach(d=>{run=last&&((new Date(d)-new Date(last))/864e5===1)?run+1:1;best=Math.max(best,run);last=d});return best}function insights(c){let a=mems(),last=a.filter(m=>(Date.now()-new Date(m.date+'T12:00:00'))<2592e6),cnt={clear:0,light:0,cloudy:0,rain:0,storm:0};last.forEach(x=>cnt[x.mood]++);let avg=a.length?(a.reduce((s,x)=>s+x.energy,0)/a.length).toFixed(1):'—',days={};a.forEach(x=>{let d=new Date(x.date+'T12:00:00').toLocaleDateString('en-US',{weekday:'long'});days[d]=(days[d]||0)+1});let active=Object.entries(days).sort((x,y)=>y[1]-x[1])[0]?.[0]||'—';c.innerHTML=`<div class="eyebrow">LAST 30 DAYS</div><h1>Insights, quietly.</h1><div class="insights"><section class="metric"><div class="eyebrow">WEATHER</div>${Object.entries(cnt).map(([k,v])=>`<div class="sub">${cap(k)} <b style="float:right;color:var(--ink)">${v} DAYS</b></div>`).join('')}</section><section class="metric"><div class="eyebrow">AVERAGE ENERGY</div><div class="big">${avg}<span style="font-size:16px"> / 5</span></div><div class="bars">${a.slice(0,10).reverse().map(x=>`<i class="bar" style="height:${x.energy*18}%"></i>`).join('')||'<span class="sub">No entries yet</span>'}</div></section><section class="metric"><div class="eyebrow">MOST ACTIVE DAY</div><div class="big" style="font-size:26px">${active}</div></section><section class="metric"><div class="eyebrow">CURRENT STREAK</div><div class="big">${streak(a)}<span style="font-size:16px"> DAYS</span></div><div class="sub">Longest streak: ${longest(a)} days</div></section></div>`}
|
function streak(a){let s=0,d=new Date();while(a.some(m=>m.date===d.toISOString().slice(0,10))){s++;d.setDate(d.getDate()-1)}return s}function longest(a){let ds=a.map(x=>x.date).sort(),best=0,run=0,last='';ds.forEach(d=>{run=last&&((new Date(d)-new Date(last))/864e5===1)?run+1:1;best=Math.max(best,run);last=d});return best}function insights(c){let a=mems(),last=a.filter(m=>(Date.now()-new Date(m.date+'T12:00:00'))<2592e6),cnt={clear:0,light:0,cloudy:0,rain:0,storm:0};last.forEach(x=>cnt[x.mood]++);let avg=a.length?(a.reduce((s,x)=>s+x.energy,0)/a.length).toFixed(1):'—',days={};a.forEach(x=>{let d=new Date(x.date+'T12:00:00').toLocaleDateString('en-US',{weekday:'long'});days[d]=(days[d]||0)+1});let active=Object.entries(days).sort((x,y)=>y[1]-x[1])[0]?.[0]||'—';c.innerHTML=`<div class="eyebrow">LAST 30 DAYS</div><h1>Insights, quietly.</h1><div class="insights"><section class="metric"><div class="eyebrow">WEATHER</div>${Object.entries(cnt).map(([k,v])=>`<div class="sub">${cap(k)} <b style="float:right;color:var(--ink)">${v} DAYS</b></div>`).join('')}</section><section class="metric"><div class="eyebrow">AVERAGE ENERGY</div><div class="big">${avg}<span style="font-size:16px"> / 5</span></div><div class="bars">${a.slice(0,10).reverse().map(x=>`<i class="bar" style="height:${x.energy*18}%"></i>`).join('')||'<span class="sub">No entries yet</span>'}</div></section><section class="metric"><div class="eyebrow">MOST ACTIVE DAY</div><div class="big" style="font-size:26px">${active}</div></section><section class="metric"><div class="eyebrow">CURRENT STREAK</div><div class="big">${streak(a)}<span style="font-size:16px"> DAYS</span></div><div class="sub">Longest streak: ${longest(a)} days</div></section></div>`}
|
||||||
|
|||||||
Reference in new issue
Block a user