diff --git a/internal/server/web/static/ai.js b/internal/server/web/static/ai.js index f64b7f3..5f165de 100644 --- a/internal/server/web/static/ai.js +++ b/internal/server/web/static/ai.js @@ -502,13 +502,38 @@ c.classList.toggle('short', p.scrollWidth <= p.clientWidth + 1 && !/\n/.test(p.textContent)); })); } + // animateHeight: change() flips the card between folded and open; the + // text's box grows or shrinks to its new height instead of jumping. The + // text keeps its wrapped lines while it shrinks. + function animateHeight(pre, change) { + const h0 = pre.getBoundingClientRect().height; + change(); + const still = document.body.classList.contains('no-motion') || matchMedia('(prefers-reduced-motion: reduce)').matches; + const h1 = pre.getBoundingClientRect().height; + if (still || Math.abs(h1 - h0) < 1) return; + const st = pre.style; + st.boxSizing = 'border-box'; st.overflow = 'hidden'; st.whiteSpace = 'pre-wrap'; st.textOverflow = 'clip'; + st.height = h0 + 'px'; + pre.getBoundingClientRect(); // the start is drawn before the end is set + st.transition = 'height .24s cubic-bezier(.3,.7,.3,1)'; + st.height = h1 + 'px'; + let t = 0; + const done = () => { + clearTimeout(t); pre.removeEventListener('transitionend', done); + st.height = st.transition = st.overflow = st.whiteSpace = st.textOverflow = st.boxSizing = ''; + }; + pre.addEventListener('transitionend', done); + t = setTimeout(done, 450); + } const FOLD_CHEV = ''; list.addEventListener('click', ev => { const t = ev.target.closest('.foldable .ai-fold, .foldable pre'); if (!t) return; const card = t.closest('.foldable'); if (card.classList.contains('short') && !card.classList.contains('open')) return; - const msg = card.closest('.ai-msg'), on = card.classList.toggle('open'); + const msg = card.closest('.ai-msg'), pre = card.querySelector('pre'); + let on = false; + animateHeight(pre, () => { on = card.classList.toggle('open'); }); const i = Number(card.dataset.n); (msg._open = msg._open || new Set())[on ? 'add' : 'delete'](i); const b = card.querySelector('.ai-fold');