Write Only Log for October 2025 - osric.uk

Simple text tools

The two I keep looking for online are:

  • Base 64 encode/decode
  • JSON format (validity check)

Browsers can do both locally, so MVP is a text area with buttons for each command.

Put the result in a new copy of the text area, with its own buttons, so chaining operations is easier.

Post MVP:

  • Copy to clipboard

  • Hook into fetcher (for logged in users at least)

  • JPath/jq stuff

    ("Download this url, extract these parts of the doc, print the output, or use it as the body for another request")

  • Date maths ("today plus two weeks")

Feature: Base64

Scenario Outline: Decode valid Base64 Given the input is set to When the "Decode Base64" command is triggered Then the result is

Examples: |encoded|plain|

... and then add some base64 encoded strings, including an empty string, and something with funky Unicode.

Belt can carry 30 items/sec.

Green circuit uses 3 cables in 0.5 sec, so 6 cables/sec.

Therefore max green factory per belt is 30/6 = 5.

Cable factory makes 2 cables in 0.5 sec, so 4 cables/sec.

Therefore need 30/4 = 7.5 cable factories to keep 5 circuit factories busy.

Green circuits also uses 1 iron in 0.5 sec, or 2 iron/sec, or 10 iron/sec for 5 factories.

30 iron/sec can feed 15 circuit factories.

Cable needs 2 copper/sec for 4 cables/sec, so 30 copper/sec can also feed 15 circuit factories.

Conclusion: Two red belts, one each of copper and iron can make 30 cables/sec from 15 circuit factories and 22.5 cable factories.

Now I've got a html parser, I should be able to put together a template engine thing, either using processing instructions or <on-server> tags.

All I want is to be able to create new web pages through my website! Is that too much to ask! I miss the site I had in the early 00s that did exactly that with mod_perl; there was an editor page that copied itself into a textarea and saved changes over itself. (Yes, I bricked it quite a few times!)

I should be able to do something like that with ASP, except ASP is compiled at build time. (There is a live recompile package but it's very much intended not to be run in production) (Also, I'm a bit more paranoid than I used to be, so it works be nice to run pages in a very small sandbox).

Really, something like html with embedded lox should work. Parse the html, run the scripts, paste the output back into the html and ship it to the client. Give lox an API for storage (along the lines of local storage for browsers), and maybe the ability to make HTTPS requests from the server, and I can stay writing really simple tools without being too do that whole build/deploy dance.

Blah, blah, blah, security, but my current with system is good enough (and that's why I'm using a different language).

Keep pages in a database, and use the same db as a backing store for the storage API.

Maybe.

On the one hand I want to be able to play with the editor in the editor. On the other, I want to be able to roll back changes when I break the editor!

If the editor is an enhanced textarea then fall back is easy enough, add a query param to say either don't include JS, or load a specific version, but I'm not sure that textarea can give me enough control.

What do I want from an editor? Auto indent! Maybe vim controls. Indent looks fairly easy ("on a enter keystroke check how many spaces are at the start of the current line, and insert those"). Vim (modal) controls is probably going to be a grind more than anything else - each motion will be differently complicated, and same for verbs, but not that bad so long as I keep the two nice and sperate.

I wonder how hard it will be to get the cursor position in terms of character row/column.

Alright. First job is to get cursor movement working, then mode switch to insert, then delete.

TextArea has selectionStart and selectionEnd.

In normal mode movement cancels the current selection (sets end = start). Visual mode changes it, needs thought. Insert mode has fewer movements, but otherwise works like normal mode.

Maybe a hash table per mode mapping keys to commands? Need to match chords (e.g. ctrl-x).

Or swap out key handlers?

Probably need to wait for command before doing movement, so add in at least delete (and then can compose e.g. change is delete + insert mode).

Don't forget clipboard, yank and paste should be fairly simple, and then can extend to named buffers if I can think through parsing arguments.

Lets have a state type to keep track of current mode, named buffer (mostly null except between a " and a yank/paste command), and a proxy through to the textarea to handle cursor position.

D'oh! Command comes first anyway!

Commands can call a movement function that returns a (start, end) pair that identifies the character(s) indicated by the movement.

Problem is that I've been thinking in terms of "wait for next char from stdin", but what I've actually got is "wait for next key event".

Should prototype this, but could turn events into promises something like:

const nextKeyEvent = await new Promise((resolve, reject) => {
    const handler = e => {
        target.removeEventListener(handler);
        resolve(e);
    }
    target.addEventListener("keydown", handler);
});

But I don't think that would work with held down keys. Also, I don't like repeatedly adding and removing the event handler.

Maybe generators?

async function* nextKey(target) {
    const handler = e => {
        yield e;
    }

    target.addEventListener("keydown", handler);
}

That feels better. I'm going to try it.

Nope. The yield call doesn't count as being in the body of the generator (because it's in the body of handler, I guess).

nextKey needs to set the keypress handler for target. The handler needs to pass a value back to nextkey, so next key can yield that value to it's caller.

Handler can pass a value to nextKey with a promise. nextKey creates a promise in a variable shared with handler, and then awaits it. Handler gets a key and resolves the promise. nextKey yields the value from the promise, and them loops to reinitialize the promise.


async function* nextKey(target) {
  let sharedPromise;

  function handler(e) {
    sharedPromise.resolve(e);
  }

  target.addEventListener("keypress", handler);

  while (true) {
    sharedPromise = new Promise();    
    yield await sharedPromise;
  }
}

After testing, the code now looks like:

    async function* nextKey(target) {
        let q = Promise.withResolvers();

        function handler(e) {
            q.resolve(e);
        }

        target.addEventListener("keypress", handler);

        while (true) {
            yield await q.promise;
            q = Promise.withResolvers();
        }
    }

Promise.withResolvers returns an object with promise, reject, and resolve properties.

Next problem: keypress events don't capture tabs. ::SadFace::

Important tweak to the above code - use keydown, not keypress!

I'm thinking about how to match commands. If I could guarantee that commands were all single characters with no modifier keys then I'd been happy enough using a hash lookup.

But life is not that simple. "Redo" is Ctrl-r for example. I could write a KeyboardEvent toString method that includes modifier keys in a specific order, and use that as the hash key.

I'm starting to prefer that to the other idea I've had, which is to hand the KeyboardEvent to each command to see who wants it, with each command doing any needed "isCtrlPressed" checks.

But the more I think, the more I like the toString idea. It does most of the computation up front, other than a cheap hash lookup. It's very clear what keystroke is being mapped. And it makes reading in config files much cleaner ("cmd-redo = Ctrl-r").

Calling that decided, commands now have a name ("redo", "delete", "insert-mode"), a keystroke (and a mode for that keystroke?) ("Ctrl-R"/normal, "d"/normal "i"/normal), and an implementation (that can call a getMovement function, and can modify the text buffer).

I think I also want a way to 'script' commands, since "o" is a "d" command followed by an "i" command (although for MVP, I can call the "d" and "i" implementations explicitly.)

The important point of the KeyboardEvent toString method is to get the modifiers in the same order every time (probably alpha - Alt, Ctrl, Shift - is the easiest to get right manually).

Turns out that commands don't know their own keystrokes. Do they still need to get their own movement, or can we pass that as an argument?

To remember your current position in the blog, this page must store some data in this browser.

Are you OK with that?