feat: the text of a picture's or a search's card folds and unfolds smoothly

This commit is contained in:
redtea committed 2026-10-18 12:13:29 +03:00
1 parent dd1244a169
commit dde85031aa
1 file changed
+26 -1
+26 -1
View File
@@ -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 = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="m9 6 6 6-6 6"/></svg>';
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');