kotfu.net

Laptop Edit and Arrow Keys

image of 104 key keyboard with edit, arrow, number pad, and function keys each in different colors
Keyboard Layouts and Sizes
Image From Keychron

In the olden days, every computer keyboard had all the alphanumeric keys, a row of function keys, an inverted T arrangement of arrow keys, edit keys (page up, home, end, etc), and a number pad. Today there are many different keyboard sizes, with various combinations of all of these types of keys. Keyboard nerds know what a 60% keyboard is. The rest of us call it a laptop keyboard. I use the editing keys a lot when typing at my desk with a full size keyboard, and I really miss them when using my laptop. Inspired by Brett Terpstra’s Home Row Arrow Cluster, I decided to see if I could figure out a solution.

I use a Macbook Air, so my approach is only useful for Mac users. Like Brett, I decided to use Karabiner-Elements, an outstanding low-level keyboard customizer. In Brett’s implementation, you hold down the ; with your pinky, and then use I J K L for the arrow keys.

That feels uncomfortable to me, and I wanted to be able to use P and ; for Page Up and Page Down. I wanted something where I could enter an edit key mode, then have the keys work as arrows and edit keys, then leave the mode and they go back to normal. Kind of like how the Num Lock key works. I can’t use Num Lock, because Apple laptops and keyboards don’t have it. In it’s place is the Clear key, which nobody uses because nobody knows what it does.

While experimenting with several keystroke options I discovered I was already using them for something else. Funny how your fingers know to type a thing, but your brain can’t remember all the things you know how to type. I finally settled on using +Delete to activate and de-activate edit mode.

Once in edit mode, I had to decide which keys to use for arrow keys and edit keys. I also experimented with this a lot. On my full size keyboard, I move my right hand over to use the arrow keys, so I wanted to use keys on the right hand side of the keyboard. I use my left hand for the modifier keys, so when I want to select backwards by word I use my left hand to press and hold + and then use my right hand to repeatedly press Left Arrow. I want to be able to do a similar thing on my laptop.

I ended up with the following keys:

Key Function
I Up Arrow
J Left Arrow
K Down Arrow
L Right Arrow
U Home
O End
N Delete
Y Page Up
H Page Down

I configured I J K L for the arrow keys, in a similar configuration to the gaming W A S D but on the right hand. Because I exclusively used Unix and Linux before ever using Windows or MacOS, I have Home and End move to the beginning and end of a line. U and O are logically positioned for those actions. I originally had H for Delete, but changed it to N so I can have Y and N for Page Up and Page Down.

To implement this in Karabiner, you need to create a new complex rule, and paste in this JSON:

{
    "description": "Control-Delete toggles 60% edit mode (I/J/K/L → ↑/←/↓/→, U/O → Home/End, N → Delete, Y/H → PageUp/PageDown)",
    "manipulators": [
        {
            "conditions": [
                {
                    "name": "sixty_edit_mode",
                    "type": "variable_if",
                    "value": 0
                }
            ],
            "from": {
                "key_code": "delete_or_backspace",
                "modifiers": { "mandatory": ["control"] }
            },
            "to": [
                {
                    "set_variable": {
                        "name": "sixty_edit_mode",
                        "value": 1
                    }
                }
            ],
            "type": "basic"
        },
        {
            "conditions": [
                {
                    "name": "sixty_edit_mode",
                    "type": "variable_if",
                    "value": 1
                }
            ],
            "from": {
                "key_code": "delete_or_backspace",
                "modifiers": { "mandatory": ["control"] }
            },
            "to": [
                {
                    "set_variable": {
                        "name": "sixty_edit_mode",
                        "value": 0
                    }
                }
            ],
            "type": "basic"
        },
        {
            "conditions": [
                {
                    "name": "sixty_edit_mode",
                    "type": "variable_if",
                    "value": 1
                }
            ],
            "from": {
                "key_code": "i",
                "modifiers": { "optional": ["any"] }
            },
            "to": [{ "key_code": "up_arrow" }],
            "type": "basic"
        },
        {
            "conditions": [
                {
                    "name": "sixty_edit_mode",
                    "type": "variable_if",
                    "value": 1
                }
            ],
            "from": {
                "key_code": "k",
                "modifiers": { "optional": ["any"] }
            },
            "to": [{ "key_code": "down_arrow" }],
            "type": "basic"
        },
        {
            "conditions": [
                {
                    "name": "sixty_edit_mode",
                    "type": "variable_if",
                    "value": 1
                }
            ],
            "from": {
                "key_code": "j",
                "modifiers": { "optional": ["any"] }
            },
            "to": [{ "key_code": "left_arrow" }],
            "type": "basic"
        },
        {
            "conditions": [
                {
                    "name": "sixty_edit_mode",
                    "type": "variable_if",
                    "value": 1
                }
            ],
            "from": {
                "key_code": "l",
                "modifiers": { "optional": ["any"] }
            },
            "to": [{ "key_code": "right_arrow" }],
            "type": "basic"
        },
        {
            "conditions": [
                {
                    "name": "sixty_edit_mode",
                    "type": "variable_if",
                    "value": 1
                }
            ],
            "from": {
                "key_code": "u",
                "modifiers": { "optional": ["any"] }
            },
            "to": [{ "key_code": "home" }],
            "type": "basic"
        },
        {
            "conditions": [
                {
                    "name": "sixty_edit_mode",
                    "type": "variable_if",
                    "value": 1
                }
            ],
            "from": {
                "key_code": "o",
                "modifiers": { "optional": ["any"] }
            },
            "to": [{ "key_code": "end" }],
            "type": "basic"
        },
        {
            "conditions": [
                {
                    "name": "sixty_edit_mode",
                    "type": "variable_if",
                    "value": 1
                }
            ],
            "from": {
                "key_code": "n",
                "modifiers": { "optional": ["any"] }
            },
            "to": [{ "key_code": "delete_forward" }],
            "type": "basic"
        },
        {
            "conditions": [
                {
                    "name": "sixty_edit_mode",
                    "type": "variable_if",
                    "value": 1
                }
            ],
            "from": {
                "key_code": "y",
                "modifiers": { "optional": ["any"] }
            },
            "to": [{ "key_code": "page_up" }],
            "type": "basic"
        },
        {
            "conditions": [
                {
                    "name": "sixty_edit_mode",
                    "type": "variable_if",
                    "value": 1
                }
            ],
            "from": {
                "key_code": "h",
                "modifiers": { "optional": ["any"] }
            },
            "to": [{ "key_code": "page_down" }],
            "type": "basic"
        }
    ]
}

If these keys don’t work for you, and you are handy with JSON, you can probably figure out how to edit this.

If you aren’t handy with JSON or aren’t a Karabiner expert, paste the code above into your favorite chatbot, tell it this is a Karabiner-Elements complex rule, and use plain english to describe what you want to change. It will do a pretty good job of making the changes for you.

Authors v. AI

Anthropic bought paper copies of books, scanned them, and used the resulting data to train their large langugage models. Authors sued, claiming this was not fair use under copyright law, and got their case certified as a class action.

The case was assigned to District Judge William Alsup, a seasoned US district court judge with deep experience in technology law, including two jury trials in the Google v Oracle saga over the Java APIs in Android. Last month Judge Alsup ruled that it’s fair use for legally obtained books to be used to train large language models. That’s a big win for Anthropic and the other frontier model companies.

However, Anthropic is also accused of pirating digital copies of more than 7 million books, and these illegally acquired works are not part of the fair use ruling. Judge Alsup also ruled that Anthropic must face a trial over the author’s piracy complaints.

US Copyright law includes statutory damages of up to $150,000 for willful copyright infringement of a single work. With 7 million works in question, Anthropic faces potential damages of up to a trillion dollars.

In their first major filing since Judge Alsup’s ruling, Anthropic is appealing the certification of the class. It’s their best tactic to reduce their financial exposure. Ashley Belanger summarizes Anthropic’s appeal for Ars Technica:

Confronted with such extreme potential damages, Anthropic may lose its rights to raise valid defenses of its AI training, deciding it would be more prudent to settle, the company argued. And that could set an alarming precedent, considering all the other lawsuits generative AI (GenAI) companies face over training on copyrighted materials, Anthropic argued.

No, Anthropic won’t lose any rights if they decide to settle. They made their choice when they willfully decided to pirate copyrighted works and hoped nobody would notice. Now that everyone knows, they may choose to settle and avoid a trial because if they lose at trial it’s a company ending event. Yes, the precendent would be ground-breaking, but it’s only alarming if you are infringing author’s rights. If other frontier model companies have done the same thing, they should have to face lawsuits as well.

Copyright class action lawsuits are incredibly complex, the case against Anthropic will take a long time, and the outcome is far from certain. Authors Guild v. Google took 10 years for the appeals court to finally rule that Google’s Books project of scanning copyrighted works and publishing them on the internet “provides a public service without violating intellectual property law”. It was appealed to the Supreme Court, which declined to hear the case.

I’ll be following Authors Guild v. Anthropic with great interest, even if the certification of the class is overturned. If the individual authors win against Anthropic, the floodgates will be open for claimants to sue every frontier model company. We already know that Meta pirated 7.5 books and 81 million research papers. If there is a precedent set in Authors Guild v. Anthropic, I’ll bet it will only take one phone call to find a lawyer who will help you sue Meta, OpenAI, and Google on the same grounds. A trillion dollars of damages from Anthropic, another trillion from Meta, and pretty soon you are talking real money.

Tear Down This Wall

Apple created an iconic commercial for the introduction of the Macintosh in 1984. The punchline: “You’ll see why 1984 won’t be like 1984”. 40 years later, Apple has become Oceania.

A few weeks ago, Judge Yvonne Gonzalez Rogers made a landmark ruling in the Epic v. Apple case started in August 2020. After a trial, Judge Rogers issued the first ruling in September 2021. Apple won 9 of the 10 counts, but the court found Apple’s prohibition on linking to web for “outside the app” payments was anticompetitive and issued an injunction. Apple appealed all the way to the Supreme Court, who declined to take up the case. Meanwhile Apple continued with what many have labeled malicious compliance. There is an extensive record in this case: the April 30 order is the 1,508th item in the docket. This latest ruling is not a rushed one, the Judge has had enough. These are the first two sentences or the order:

For the reasons set forth herein, the Court FINDS Apple in willful violation of this Court’s 2021 Injunction which issued to restrain and prohibit Apple’s anticompetitive conduct and anticompetitive pricing. Apple’s continued attempts to interfere with competition will not be tolerated.

It gets worse:

In stark contrast to Apple’s initial in-court testimony, contemporaneous business documents reveal that Apple knew exactly what it was doing and at every turn chose the most anticompetitive option. To hide the truth, Vice-President of Finance, Alex Roman, outright lied under oath. Internally, Phillip Schiller had advocated that Apple comply with the Injunction, but Tim Cook ignored Schiller and instead allowed Chief Financial Officer Luca Maestri and his finance team to convince him otherwise. Cook chose poorly. The real evidence, detailed herein, more than meets the clear and convincing standard to find a violation. The Court refers the matter to the United States Attorney for the Northern District of California to investigate whether criminal contempt proceedings are appropriate.

This is an injunction, not a negotiation. There are no do-overs once a party willfully disregards a court order. Time is of the essence. The Court will not tolerate further delays. As previously ordered, Apple will not impede competition. The Court enjoins Apple from implementing its new anticompetitive acts to avoid compliance with the Injunction. Effective immediately Apple will no longer impede developers’ ability to communicate with users nor will they levy or impose a new commission on off-app purchases.

And here’s her closer:

Apple willfully chose not to comply with this Court’s Injunction. It did so with the express intent to create new anticompetitive barriers which would, by design and in effect, maintain a valued revenue stream; a revenue stream previously found to be anticompetitive. That it thought this Court would tolerate such insubordination was a gross miscalculation. As always, the cover- up made it worse. For this Court, there is no second bite at the apple.

It has taken years, but the European Digital Markets Act combined with this ruling in the United States has finally opened the gate to more meaningful competition in Apple’s walled garden. The costs have been staggering, as you might expect when going up against one of the most valueable companies in the world, who also has an enormous cash reserve. In the May 6 episode of his Channels podcast, Peter Kafka interviewed Tim Sweeney, CEO of Epic Games. Tim says they spent more than $100 million in legal feeds on Epic vs Apple.

Naturally Apple is appealing this recent order, but I think it’s unlikely to be successful. Meanwhile, they are finally complying with the injuction issued way back in 2021. Amazon added a “Get Book” to the Kindle app. Spotify now shows links in their app to buy various subscription plans on the web. Patreon updated their app to allow fans to subscribe on the web, bypassing Apple’s 30% fee.

The gate is open, let’s tear down this wall.

Shot Heard Round The World

Old North Bridge near Concord, MA
Old North Bridge
National Park Service Photo Archives

250 years ago to the day, the simmering anger between the American colonists and the British crown spilled into a shooting war. We don’t know whether the first shots that day were fired on the Lexington green or at the Old North Bridge. Wherever it was, that day marked a key moment on the path to independence.

As a child, Ralph Waldo Emerson’s father watched the events at the Old North Bridge from his nearby home. In that same home, Emerson would later write:

By the rude bridge that arched the flood,
Their flag to April’s breeze unfurled,
Here once the embattled farmers stood,
And fired the shot heard round the world.

− Ralph Waldo Emerson, “Concord Hymn”

My eighth great-grandfather Peter Crapo was one of those American farmers, living in nearby Freetown, MA. He was too far away to have been at the Old North Bridge, but records from that day document that he marched with Captain Levi Rounsevell’s Company on “the alarm of April 19, 1775”.

The Commonwealth of Massachusetts commemorates these events as Patriots’ Day. I mark the day with gratitude for the sacrifices of my ancestors to secure the blessings of life, liberty, and the pursuit of happiness. I likewise honor all who have served, and who serve today for the same noble cause.

OpenBSD Satellite Time Sensor

I just added a new Guide describing how to add a GPS Receiver to your OpenBSD NTP Server which increases resiliance and ensures accurate time when internet connectivity is not available.