April 28th 2026 - Things I Worked On

Made blog tables prettier

Added table styling to match the rest of the site:

table {
    border-collapse: collapse;
    background-color: #2b2b2b;
    color: #a9b7c6;
}

/* Header styling */
th {
    background-color: #313335;
    color: #b68ef0;
    font-weight: bold;
    text-align: left;
    padding: 12px 15px;
    border-bottom: 2px solid #4b4b4b;
}

/* Cell styling */
td {
    padding: 10px 15px;
    border-bottom: 1px solid #323232;
}

/* Zebra striping for rows */
tr:nth-child(even) {
    background-color: #2e2e2e;
}

/* Hover effect for interactivity */
tr:hover {
    background-color: #353739;
}

/* Styling for links within the table */
td a {
    color: #ffb66c;
    text-decoration: none;
}

Generated a blog index date

I wanted a faster way of inserting the date for my blog entries in my blog index page so I came up with this.

(defun my/blog-create-index-date-heading ()
  "Create the index.org date heading for the given date"
  (interactive)
  (let* ((date (read-string "" (format-time-string "%Y-%m-%d")))
         (heading (concat "** " date)))
    (insert heading)))

It prompts for a date the current date as the default value and then inserts it as a second level heading.

Of course, I can remove the given date and put anything I want, including something that's not a date.

Published poker-equity web calculator to github

Added preventing user from inputting wrong rank or suit in poker equity calculator

Added strict input validation for hands and board cards to my poker equity calculator. It prevents the user from entering more than 2 cards per hand and 5 cards for the board. It also auto-capitalizes rank and suit cards like Ac2h.

This should reduce the number of input errors.

attachPokerInputValidation(inputField, maxChars) {
    const isRank = char => /[2-9AKQJT]/i.test(char);
    const isSuit = char => /[CHSD]/i.test(char);

    inputField.addEventListener('input', (event) => {
        const cursorPosition = inputField.selectionStart;
        const currentInputValue = inputField.value;

        let formattedValue = '';
        let expectRank = true;

        for (const char of currentInputValue) {
            if (formattedValue.length === maxChars) break;

            if (expectRank && isRank(char)) {
                formattedValue += char.toUpperCase();
                expectRank = false;
            } else if (!expectRank && isSuit(char)) {
                formattedValue += char.toLowerCase();
                expectRank = true;
            }
        }

        if (currentInputValue !== formattedValue) {
            const lengthDifference = currentInputValue.length
                  - formattedValue.length;

            const adjustedPosition = Math.max(0, cursorPosition
                                              - lengthDifference);

            inputField.value = formattedValue;
            inputField.setSelectionRange(
                adjustedPosition,
                adjustedPosition);
        }
    });
}

initializeBoardInput() {
    const boardInput = document.getElementById("board-input");
    if (boardInput) {
        this.attachPokerInputValidation(boardInput, 10);
    }
}

initializeHandInputs() {
    for (let i = 1; i <= MAX_PLAYERS; i++) {
        const playerInput = document.getElementById(`hand-input-${i}`);
        if (playerInput) {
            this.attachPokerInputValidation(playerInput, 4);
        }
    }
}