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/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

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

Remove the merge confetti and canvas-confetti

8032d15aa32facad8f21fad931187520ee81c02b · Keenan Nemetz · 6d ago · parent ac541b26

Remove the merge confetti and canvas-confetti

A landing no longer celebrates: the request page is the result, and the vendored library, its loader, and the ?merged=1 flag existed only for the burst. Dropping the flag means a landing redirects to the plain request page.

Remove the asset, its license, the NOTICE entry, the loader and its test, and the comments that described them.

server/web/static/app.js −60

@@ -503,66 +503,6 @@ const copyText = (value, done) => {
503 503 });
504 504 })();
505 505
506 // Landing a change request is the end of the review loop, so the page it lands on
507 // gets one burst of confetti. The library is a separate 25 KB asset and is fetched
508 // only on the redirect that carries ?merged=1: no other page pays for it, and a
509 // reload, bookmark or shared link is an ordinary visit because the flag is dropped
510 // from the address as soon as it has been read.
511 //
512 // The burst is drawn by an instance created with useWorker:false, not by the
513 // library's default export. The default export opts into a Web Worker built from
514 // a blob: URL, and this page's CSP (default-src 'self', no worker-src) blocks that
515 // worker — but the library hands its canvas to the worker anyway, transferring it
516 // with transferControlToOffscreen, and then draws nothing: the canvas is present
517 // and permanently empty, with no error to see. An instance that draws on the main
518 // thread costs a few hundred particles for a few seconds, which is nothing, and
519 // needs no CSP change.
520 //
521 // The volley is the library's "fireworks" preset, cut from fifteen seconds to just
522 // over one, because the page this lands on is one you are about to read. Every
523 // particle is thrown in from the left or right edge of the viewport: nothing opens
524 // in the middle of the page, so the celebration frames the content instead of
525 // covering it. The count steps down with the clock, which keeps it to roughly five
526 // hundred particles in total.
527 (() => {
528 const params = new URLSearchParams(window.location.search);
529 if (!params.has('merged')) return;
530 params.delete('merged');
531 const rest = params.toString();
532 window.history.replaceState(null, '',
533 window.location.pathname + (rest ? '?' + rest : '') + window.location.hash);
534
535 const script = document.createElement('script');
536 script.src = '/static/confetti.browser.js';
537 script.onload = () => {
538 if (typeof window.confetti !== 'function') return;
539 const burst = window.confetti.create(null, { resize: true, useWorker: false });
540 const look = {
541 startVelocity: 30,
542 spread: 180,
543 ticks: 400,
544 gravity: 0.5,
545 decay: 0.92,
546 scalar: 1.2,
547 disableForReducedMotion: true,
548 };
549
550 // The "fireworks" volley, from the two side edges only, tapering as the clock
551 // runs out.
552 const volleyMs = 1200;
553 const end = Date.now() + volleyMs;
554 (function frame() {
555 const left = end - Date.now();
556 if (left <= 0) return;
557 const particleCount = 60 * (left / volleyMs);
558 burst({ ...look, particleCount, angle: 60, spread: 55, origin: { x: 0 } });
559 burst({ ...look, particleCount, angle: 120, spread: 55, origin: { x: 1 } });
560 setTimeout(frame, 250);
561 })();
562 };
563 document.head.appendChild(script);
564 })();
565
566 506 // Live CI job output. The server serves the attempt's file from a byte offset and
567 507 // says when the log is final; the page asks again only while the job is running.
568 508 // Chunks are decoded with a streaming TextDecoder because a multi-byte character