thcThought Control

under the hood

How it's built

One Rust binary. An append-only log as the truth, a database you can delete, and an editor engine with a test for every rule about where the caret goes. A longer write-up is coming. Here are the highlights.

The CLI, TUI and daemon share one core. Ops become events in a per-device log; replay builds a SQLite store; queries and views read it.thc (CLI)thc tui◆ agentsopsone writerper devicelog/<device>/*.jsonlappend-onlyreplay (pure)SQLite storea cachequeriesviews · @todayreads, and live updates from the daemonthe vault folder holds only the log (synced); the store is local and rebuildable

An append-only log

Every change is one JSON line in this device's own file. Sync tools never see two writers on one file, so there are no conflicted copies.

A pure replay

State is a function of the events: clocks, randomness and timezones live only in the writer. Delete the database and thc rebuild replays it.

Merges that converge

A hybrid logical clock orders events across devices. The newest field wins; concurrent text edits keep both versions for you to choose.

One primitive

A page, a day, a task and a tag are all nodes. "Task" is a status, "reminder" is an alert: fewer kinds, fewer edge cases.

caretline, the editor

The editor engine, now its own Rust crate (caretline.app, cargo add caretline): a plain-text block editor with documents and views, Markdown in and out, and one layout map for drawing, clicks and motion. Every caret position belongs to exactly one row. It comes with a small example: a whole Markdown editor in the terminal, in about 160 lines on top of the engine.

Rules that run

Caret motion is pure functions with invariants, checked over random documents, with golden examples written in a caret notation that reads like the docs.

a caret example
width 40
---
The quick brown fox jumps over the lazy dog and keeps running through the long ▮grass until dusk.

Short one.
---
↓   Short one.  at ▮Short

A golden test, as written: the caret at the start of a wrapped row, one ↓, and where it must land. caretline runs 103 of these (22 for motion, 81 for editing), plus property tests over random documents: a motion without ⇧ never leaves a selection, cut then paste changes nothing, and changing a line into a task never moves another line.

More soon: the full story of the data model. caretline has its own site, with docs and a playground. Meanwhile, the philosophy explains the why.