.bud-duplicate-flash: auto-ease-out 3s after FYI + palette swap — note.js's Brief.showDuplicateBanner FYI handler now setTimeout(() => target.classList.remove('bud-duplicate-flash'), 3000) after the .add(); the existing transition: color 600ms ease, text-shadow 600ms ease rule on the class already covered the ease-in (default → flash), so the same rule now also covers the ease-out (flash → default) when the class drops — net behaviour: tap FYI → flash peaks → flash visibly fades back to the default text styling over ~600ms after a 3s hold, instead of persisting til page refresh; palette keys swapped per user steer — color: var(--terUser); text-shadow: var(--ninUser)color: var(--ninUser); text-shadow: var(--terUser), so the highlight reads as a lighter handle w. a gold glow rather than a gold handle w. a light glow, matching the duplicate-guard spec the user re-aligned on; affects all three flash targets uniformly (.bud-entry .bud-name on /billboard/my-buds/, .post-recipient on post.html share-flow, .gate-slot.filled on the gatekeeper invite-flow) since they all flow through the same _bud.scss .bud-duplicate-flash selector + the same Brief.showDuplicateBanner JS handler; new Jasmine spec D7b in NoteSpec.js uses jasmine.clock().install() + clock().tick(3001) to fast-forward past the dismiss window + assert the class is gone (existing D7 still pins the immediate-after-FYI peak state); existing FTs (test_bill_my_buds.test_re_add_existing_bud_shows_already_present_brief… + test_core_bud_btn duplicate-guard FTs) still green because they assert immediately after the FYI click (well inside the 3s hold) — TDD
Some checks failed
ci/woodpecker/push/pyswiss Pipeline was successful
ci/woodpecker/push/main Pipeline failed

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-05-13 00:43:03 -04:00
parent e2040fda8f
commit f7fa250804
4 changed files with 54 additions and 5 deletions

View File

@@ -92,7 +92,17 @@ const Brief = (() => {
banner.querySelector('.note-banner__fyi').addEventListener('click', function () { banner.querySelector('.note-banner__fyi').addEventListener('click', function () {
if (opts.target_selector) { if (opts.target_selector) {
var target = document.querySelector(opts.target_selector); var target = document.querySelector(opts.target_selector);
if (target) target.classList.add('bud-duplicate-flash'); if (target) {
target.classList.add('bud-duplicate-flash');
// Auto-ease-out — the SCSS rule has a 600ms transition
// on color + text-shadow, so removing the class 3s after
// FYI lets the peak state breathe before fading back to
// the default text styling. Without this the flash
// persisted til page refresh.
setTimeout(function () {
target.classList.remove('bud-duplicate-flash');
}, 3000);
}
} }
banner.remove(); banner.remove();
}); });

View File

@@ -230,6 +230,25 @@ describe('Brief.showDuplicateBanner', () => {
expect(name.classList.contains('bud-duplicate-flash')).toBeTrue(); expect(name.classList.contains('bud-duplicate-flash')).toBeTrue();
}); });
it('D7b: .bud-duplicate-flash auto-eases out — class is removed ~3s after FYI', () => {
jasmine.clock().install();
try {
Brief.showDuplicateBanner({
display_name: 'alice',
target_selector: '.bud-entry[data-bud-id="42"] .bud-name',
});
document.querySelector('.note-banner__fyi').click();
const name = fixture.querySelector('.bud-name');
// Immediately after FYI: flash is on (the visible peak state).
expect(name.classList.contains('bud-duplicate-flash')).toBeTrue();
// After the auto-dismiss window: flash is gone.
jasmine.clock().tick(3001);
expect(name.classList.contains('bud-duplicate-flash')).toBeFalse();
} finally {
jasmine.clock().uninstall();
}
});
it('D8: FYI dismisses cleanly when target_selector is missing', () => { it('D8: FYI dismisses cleanly when target_selector is missing', () => {
Brief.showDuplicateBanner({ display_name: 'alice' }); Brief.showDuplicateBanner({ display_name: 'alice' });
document.querySelector('.note-banner__fyi').click(); document.querySelector('.note-banner__fyi').click();

View File

@@ -177,11 +177,12 @@ html:has(#id_kit_bag_dialog[open]) #id_bud_btn {
// Eased-in flash applied by Brief.showDuplicateBanner's FYI button to a // Eased-in flash applied by Brief.showDuplicateBanner's FYI button to a
// caller-supplied target element — one of .bud-entry .bud-name (My Buds), // caller-supplied target element — one of .bud-entry .bud-name (My Buds),
// .post-recipient (post share), or .gate-slot.filled (gatekeeper invite). // .post-recipient (post share), or .gate-slot.filled (gatekeeper invite).
// Persists until page refresh; --terUser color + --ninUser text-shadow // Class is auto-removed by note.js 3s after FYI; SCSS transition handles
// per the duplicate-guard spec. // both the ease-in (on add) AND the ease-out (on remove). Palette swap
// vs. the original spec: color = --ninUser, text-shadow = --terUser.
.bud-duplicate-flash { .bud-duplicate-flash {
color: rgba(var(--terUser), 1); color: rgba(var(--ninUser), 1);
text-shadow: 0 0 0.5em rgba(var(--ninUser), 1); text-shadow: 0 0 0.5em rgba(var(--terUser), 1);
transition: color 600ms ease, text-shadow 600ms ease; transition: color 600ms ease, text-shadow 600ms ease;
} }

View File

@@ -230,6 +230,25 @@ describe('Brief.showDuplicateBanner', () => {
expect(name.classList.contains('bud-duplicate-flash')).toBeTrue(); expect(name.classList.contains('bud-duplicate-flash')).toBeTrue();
}); });
it('D7b: .bud-duplicate-flash auto-eases out — class is removed ~3s after FYI', () => {
jasmine.clock().install();
try {
Brief.showDuplicateBanner({
display_name: 'alice',
target_selector: '.bud-entry[data-bud-id="42"] .bud-name',
});
document.querySelector('.note-banner__fyi').click();
const name = fixture.querySelector('.bud-name');
// Immediately after FYI: flash is on (the visible peak state).
expect(name.classList.contains('bud-duplicate-flash')).toBeTrue();
// After the auto-dismiss window: flash is gone.
jasmine.clock().tick(3001);
expect(name.classList.contains('bud-duplicate-flash')).toBeFalse();
} finally {
jasmine.clock().uninstall();
}
});
it('D8: FYI dismisses cleanly when target_selector is missing', () => { it('D8: FYI dismisses cleanly when target_selector is missing', () => {
Brief.showDuplicateBanner({ display_name: 'alice' }); Brief.showDuplicateBanner({ display_name: 'alice' });
document.querySelector('.note-banner__fyi').click(); document.querySelector('.note-banner__fyi').click();