Merge branch 'pr-575' into visual-pr-playground

This commit is contained in:
pewdiepie-archdaemon
2026-06-02 06:26:31 +09:00
2 changed files with 60 additions and 38 deletions

View File

@@ -33,31 +33,53 @@ export function initSectionCollapse(Storage) {
Storage.setJSON('section-collapsed', state);
// Always clear any in-flight animation classes from a previous toggle
// so back-to-back clicks restart cleanly.
// so back-to-back clicks restart cleanly. Bump a generation token so
// any callback still pending from a superseded toggle becomes a no-op.
section.classList.remove('section-just-expanded', 'section-just-collapsing');
const gen = (section._collapseGen = (section._collapseGen || 0) + 1);
if (willCollapse) {
// Domino-out: play the fade/slide-down on .list-item children
// BEFORE actually adding .collapsed (which hides them via
// display:none). After the cascade finishes, lock in collapse.
// Force reflow so the keyframes restart.
// Domino-out: play the fade/slide-down on the row children BEFORE
// actually adding .collapsed (which hides them via display:none),
// then lock in collapse once the cascade finishes.
//
// We wait on the REAL animations (getAnimations) rather than a fixed
// timeout. Different sections animate different rows — .list-item in
// most, .models-row in #models-section — so any hard-coded duration
// either stalls with a dead pause (when the selector matches nothing,
// as it did for #models-section) or guesses the wrong length. Force a
// reflow first so the keyframes restart from the top.
// eslint-disable-next-line no-unused-expressions
section.offsetHeight;
section.classList.add('section-just-collapsing');
const itemCount = Math.min(12, section.querySelectorAll('.list-item').length);
const total = itemCount * 25 + 230; // matches CSS keyframes + stagger
setTimeout(() => {
const lockCollapsed = () => {
if (section._collapseGen !== gen) return; // superseded by a newer toggle
section.classList.remove('section-just-collapsing');
section.classList.add('collapsed');
}, total);
};
// Only the domino-out keyframes gate the collapse — ignore unrelated
// (and possibly infinite, e.g. spinners) animations in the subtree.
const dominoOut = section.getAnimations({ subtree: true })
.filter(a => a.animationName === 'section-domino-out');
if (dominoOut.length === 0) {
lockCollapsed(); // nothing to animate — collapse now, no dead pause
} else {
Promise.allSettled(dominoOut.map(a => a.finished)).then(lockCollapsed);
// Safety net: if an animation never settles (e.g. element removed),
// still lock in the collapse so the section can't get stuck open.
setTimeout(lockCollapsed, 600);
}
} else {
// Expand path — already had this: remove .collapsed and replay
// the inbound domino.
// Expand path — remove .collapsed and replay the inbound domino.
section.classList.remove('collapsed');
// eslint-disable-next-line no-unused-expressions
section.offsetHeight;
section.classList.add('section-just-expanded');
setTimeout(() => section.classList.remove('section-just-expanded'), 700);
setTimeout(() => {
if (section._collapseGen !== gen) return; // superseded by a newer toggle
section.classList.remove('section-just-expanded');
}, 700);
}
}

View File

@@ -1251,21 +1251,21 @@ body.bg-pattern-sparkles {
for ~700ms), the .list-item children cascade in one after another,
same feel as the chat input's tools menu. Each row springs in
from a tiny offset below + scaled-down, staggered by nth-child. */
.section.section-just-expanded .list-item {
.section.section-just-expanded :is(.list-item, .models-row) {
animation: section-domino-in 0.36s cubic-bezier(0.22, 1.61, 0.36, 1) backwards;
}
.section.section-just-expanded .list-item:nth-child(1) { animation-delay: 0.04s; }
.section.section-just-expanded .list-item:nth-child(2) { animation-delay: 0.08s; }
.section.section-just-expanded .list-item:nth-child(3) { animation-delay: 0.12s; }
.section.section-just-expanded .list-item:nth-child(4) { animation-delay: 0.16s; }
.section.section-just-expanded .list-item:nth-child(5) { animation-delay: 0.20s; }
.section.section-just-expanded .list-item:nth-child(6) { animation-delay: 0.24s; }
.section.section-just-expanded .list-item:nth-child(7) { animation-delay: 0.28s; }
.section.section-just-expanded .list-item:nth-child(8) { animation-delay: 0.32s; }
.section.section-just-expanded .list-item:nth-child(9) { animation-delay: 0.36s; }
.section.section-just-expanded .list-item:nth-child(10) { animation-delay: 0.40s; }
.section.section-just-expanded .list-item:nth-child(11) { animation-delay: 0.44s; }
.section.section-just-expanded .list-item:nth-child(12) { animation-delay: 0.48s; }
.section.section-just-expanded :is(.list-item, .models-row):nth-child(1) { animation-delay: 0.04s; }
.section.section-just-expanded :is(.list-item, .models-row):nth-child(2) { animation-delay: 0.08s; }
.section.section-just-expanded :is(.list-item, .models-row):nth-child(3) { animation-delay: 0.12s; }
.section.section-just-expanded :is(.list-item, .models-row):nth-child(4) { animation-delay: 0.16s; }
.section.section-just-expanded :is(.list-item, .models-row):nth-child(5) { animation-delay: 0.20s; }
.section.section-just-expanded :is(.list-item, .models-row):nth-child(6) { animation-delay: 0.24s; }
.section.section-just-expanded :is(.list-item, .models-row):nth-child(7) { animation-delay: 0.28s; }
.section.section-just-expanded :is(.list-item, .models-row):nth-child(8) { animation-delay: 0.32s; }
.section.section-just-expanded :is(.list-item, .models-row):nth-child(9) { animation-delay: 0.36s; }
.section.section-just-expanded :is(.list-item, .models-row):nth-child(10) { animation-delay: 0.40s; }
.section.section-just-expanded :is(.list-item, .models-row):nth-child(11) { animation-delay: 0.44s; }
.section.section-just-expanded :is(.list-item, .models-row):nth-child(12) { animation-delay: 0.48s; }
@keyframes section-domino-in {
0% { opacity: 0; transform: translateY(8px) translateX(-4px) scale(0.92); }
60% { opacity: 1; }
@@ -1279,21 +1279,21 @@ body.bg-pattern-sparkles {
nth-last-child so the BOTTOM item leaves first and the cascade
rolls upward mirrors the "stacked deck" feeling of the open
animation reversed. */
.section.section-just-collapsing .list-item {
.section.section-just-collapsing :is(.list-item, .models-row) {
animation: section-domino-out 0.22s ease-in forwards;
}
.section.section-just-collapsing .list-item:nth-last-child(1) { animation-delay: 0.00s; }
.section.section-just-collapsing .list-item:nth-last-child(2) { animation-delay: 0.025s; }
.section.section-just-collapsing .list-item:nth-last-child(3) { animation-delay: 0.05s; }
.section.section-just-collapsing .list-item:nth-last-child(4) { animation-delay: 0.075s; }
.section.section-just-collapsing .list-item:nth-last-child(5) { animation-delay: 0.10s; }
.section.section-just-collapsing .list-item:nth-last-child(6) { animation-delay: 0.125s; }
.section.section-just-collapsing .list-item:nth-last-child(7) { animation-delay: 0.15s; }
.section.section-just-collapsing .list-item:nth-last-child(8) { animation-delay: 0.175s; }
.section.section-just-collapsing .list-item:nth-last-child(9) { animation-delay: 0.20s; }
.section.section-just-collapsing .list-item:nth-last-child(10) { animation-delay: 0.225s; }
.section.section-just-collapsing .list-item:nth-last-child(11) { animation-delay: 0.25s; }
.section.section-just-collapsing .list-item:nth-last-child(12) { animation-delay: 0.275s; }
.section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(1) { animation-delay: 0.00s; }
.section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(2) { animation-delay: 0.025s; }
.section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(3) { animation-delay: 0.05s; }
.section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(4) { animation-delay: 0.075s; }
.section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(5) { animation-delay: 0.10s; }
.section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(6) { animation-delay: 0.125s; }
.section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(7) { animation-delay: 0.15s; }
.section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(8) { animation-delay: 0.175s; }
.section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(9) { animation-delay: 0.20s; }
.section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(10) { animation-delay: 0.225s; }
.section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(11) { animation-delay: 0.25s; }
.section.section-just-collapsing :is(.list-item, .models-row):nth-last-child(12) { animation-delay: 0.275s; }
@keyframes section-domino-out {
0% { opacity: 1; transform: translateY(0) translateX(0) scale(1); }
100% { opacity: 0; transform: translateY(6px) translateX(-3px) scale(0.94); }