Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Celebrate a landed change request, once

c8fa5759b293126f1b247abc746bc339791568cb · Keenan Nemetz · 13d ago · parent 53ff967b

Celebrate a landed change request, once

A successful landing now redirects with ?merged=1, and app.js answers that
parameter by fetching static/confetti.browser.js (canvas-confetti 1.9.3, ISC)
and firing one burst. The flag is removed from the address as soon as it is
read, so a reload, a bookmark or a shared link is an ordinary visit, and no
page without the parameter ever requests the bundle. The library honours
prefers-reduced-motion through disableForReducedMotion, and reset() before
firing means a page restored from the back/forward cache cannot stack a
second canvas.

The asset is vendored the way htmx and Lucide are: the file is unmodified
from the published npm tarball, with the license beside it, so script-src
'self' stays as it is. It is deliberately not in the page shell, because
Static() serves assets uncompressed and a shell script would make every page
pay for a celebration only one page uses.

Refs #01M5XZ7T4R88BA9KV1KTSV7ZXP

server/web/static/app.js +30

@@ -337,3 +337,33 @@ const copyText = (value, done) => {
337 337 }
338 338 });
339 339 })();
340
341 // Landing a change request is the end of the review loop, so the page it lands on
342 // gets one burst of confetti. The library is a separate 25 KB asset and is fetched
343 // only on the redirect that carries ?merged=1: no other page pays for it, and a
344 // reload, bookmark or shared link is an ordinary visit because the flag is dropped
345 // from the address as soon as it has been read. The library honours
346 // prefers-reduced-motion itself (disableForReducedMotion), and reset() first means a
347 // page restored from the back/forward cache cannot stack a second canvas.
348 (() => {
349 const params = new URLSearchParams(window.location.search);
350 if (!params.has('merged')) return;
351 params.delete('merged');
352 const rest = params.toString();
353 window.history.replaceState(null, '',
354 window.location.pathname + (rest ? '?' + rest : '') + window.location.hash);
355
356 const script = document.createElement('script');
357 script.src = '/static/confetti.browser.js';
358 script.onload = () => {
359 if (typeof window.confetti !== 'function') return;
360 window.confetti.reset();
361 window.confetti({
362 particleCount: 120,
363 spread: 70,
364 origin: { x: 0.5, y: 0.6 },
365 disableForReducedMotion: true,
366 });
367 };
368 document.head.appendChild(script);
369 })();