Skip to content

Instantly share code, notes, and snippets.

View johnloy's full-sized avatar

John Loy johnloy

View GitHub Profile
@johnloy
johnloy / gist:297fbe06069eb9655474e0167037ce3c
Created October 15, 2026 15:13
Git workflow to freeze release candidates while supporting CI into the main branch.

The model that fits this is trunk-based development with short-lived release branches. main keeps moving, and each release gets its own branch cut from the RC commit. Fixes reach the release branch only by explicit cherry-pick, so nothing merged to main after the cut can leak into v1.6.0.

The shape

main:        A──B──C──D──E──F──G──H──I ──►        (PRs keep merging)
                   │           ▲
                   │           └ fix X (merged to main first)
                   │
release/1.6:       C ─────────── X' 
@johnloy
johnloy / toggleSelectionKeepOne.ts
Created September 23, 2026 16:03
Ensure at least one option in a multi-select set stays selected
/**
* Toggles an option in a multi-select list while guaranteeing at least one
* option stays selected.
*
* Checking adds the option. Unchecking removes it unless it is the last
* remaining selection; in that case the next option in `options` is selected
* instead (wrapping to the start). With two options, unchecking A selects B.
* With one option the same item stays on.
*/
export const toggleSelectionKeepOne = <T>(
@johnloy
johnloy / avif-player.html
Created July 9, 2026 18:05
AVIF player using ImageDecoder API
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>AVIF Frame Player</title>
<style>
:root {
--bg: #0b0d10;
--panel: #14171c;
@johnloy
johnloy / minimal.css
Last active October 10, 2025 13:12
The Least Amount of CSS for a decent looking site
/* @see: https://thecascade.dev/article/least-amount-of-css/ */
html {
color-scheme: light dark;
}
body {
font-family: system-ui;
font-size: 1.25rem;
line-height: 1.5;
}
@johnloy
johnloy / README.md
Last active September 26, 2025 19:35
npm-release-notes bash function

Installation

Copy the two bash functions in this gist into .bashrc (or wherever appropriate for your startup files).

Usage

bash-5.1$ npm-release-notes -h
Usage: npm-release-notes [OPTIONS] PACKAGE [VERSION]
@johnloy
johnloy / reorganize-imports.js
Created August 28, 2025 16:29
Script to reorganize JS/TS imports
#!/usr/bin/env node
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
function isNodeModulesImport(importPath) {
@johnloy
johnloy / image-download-progress.html
Created June 10, 2024 18:58
Image download progress with XHR
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<progress id='imageProgress' value='0' max='100'></progress>
@johnloy
johnloy / scrollparent.js
Created June 10, 2024 17:57 — forked from gre/scrollparent.js
get first parent scrollable container of a dom element
// more minimal version of https://github.com/olahol/scrollparent.js/blob/master/scrollparent.js
const regex = /(auto|scroll)/;
const style = (node, prop) =>
getComputedStyle(node, null).getPropertyValue(prop);
const scroll = (node) =>
regex.test(
style(node, "overflow") +
style(node, "overflow-y") +
@johnloy
johnloy / typescript-custom-events.ts
Last active May 31, 2024 19:00
Strongly-typed EventTarget custom events with TypeScript
// Credits: https://medium.com/@saeed.asghari.241/create-custom-event-in-typescript-8219054cee5d
class EventBus extends EventTarget {
constructor() {
super()
}
addCustomEventListener<T extends Partial<Record<string, any>>>(
type: string,
listener: (event: CustomEvent<T>) => void,
@johnloy
johnloy / executable-standalone-module.md
Created March 4, 2024 20:55 — forked from WebReflection/executable-standalone-module.md
NodeJS Executable Standalone Module

Update

If you're OK in having a node-esm executable, please consider this solution.

#!/usr/bin/env sh
# the /usr/local/bin/node-esm executable
input_file=$1
shift
exec node --input-type=module - $@ &lt;$input_file