feat(chat): soften the committed-block reveal into a materialize cascade
The 140ms fade read as a pop next to the scroll glide. The reveal now runs 320ms on an ease-out-quint curve with a 10px rise and a touch of blur resolving — paint-only properties throughout, so row measurement is untouched — and blocks committed in the same tick cascade with a 55ms stagger instead of appearing together. Reduced motion still disables the whole thing.
This commit is contained in:
@@ -843,6 +843,7 @@ const useMorphdomMarkdown = ({
|
||||
// Reconcile per block: only re-morph blocks whose content changed, leaving
|
||||
// stable leading blocks untouched. Keeps per-stream-step DOM work bounded
|
||||
// to the trailing (growing) block instead of the whole message.
|
||||
let enteredThisPass = 0;
|
||||
blocks.forEach((block, index) => {
|
||||
let el = existing[index];
|
||||
let isNewBlock = false;
|
||||
@@ -863,9 +864,16 @@ const useMorphdomMarkdown = ({
|
||||
// goes on the block's children — the wrapper is display:contents
|
||||
// and cannot animate — and the transform never changes layout, so
|
||||
// row measurement stays exact. Skipped for the first block so a
|
||||
// full initial render does not shimmer.
|
||||
// full initial render does not shimmer. Several blocks committed
|
||||
// in one tick cascade with a small stagger instead of popping in
|
||||
// together.
|
||||
const delayMs = Math.min(enteredThisPass, 4) * 55;
|
||||
enteredThisPass += 1;
|
||||
for (const child of Array.from(temp.children)) {
|
||||
child.classList.add('oc-md-block-enter');
|
||||
if (delayMs > 0 && child instanceof HTMLElement) {
|
||||
child.style.setProperty('--oc-md-enter-delay', `${delayMs}ms`);
|
||||
}
|
||||
}
|
||||
}
|
||||
const hadMermaidBlock = shouldRefreshMermaidViewers(el);
|
||||
|
||||
@@ -1378,21 +1378,29 @@ html:not(.dark) .chat-scroll {
|
||||
}
|
||||
}
|
||||
|
||||
/* A block committed mid-stream enters with a short fade-and-rise; the
|
||||
transform is paint-only, so virtualized row measurement is unaffected. */
|
||||
/* A block committed mid-stream materializes: fade, a gentle rise, and a
|
||||
touch of blur resolving. Transform and filter are paint-only, so
|
||||
virtualized row measurement is unaffected. Blocks committed in the same
|
||||
tick cascade via --oc-md-enter-delay set inline by the renderer. */
|
||||
@keyframes oc-md-block-enter {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(4px);
|
||||
transform: translateY(10px);
|
||||
filter: blur(2px);
|
||||
}
|
||||
60% {
|
||||
filter: blur(0);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
filter: none;
|
||||
}
|
||||
}
|
||||
|
||||
.oc-md-block-enter {
|
||||
animation: oc-md-block-enter 140ms ease-out both;
|
||||
animation: oc-md-block-enter 320ms cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
animation-delay: var(--oc-md-enter-delay, 0ms);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
|
||||
Reference in New Issue
Block a user