trieur.

card triage Β· online learning

Sort the pile once,
and stop re-deciding.

A pile of cards, zones around it, one gesture per card β€” by thumb, by mouse or by keyboard. And a model that learns, on every filing, where the next one probably goes.

Get started Watch the model learn

Suggestion

File a few cards: the model stays silent until it has learned something.

What it knows

filings0
accuracyβ€”
features0

From the keyboard

Sorting

File into a zone
a s d … or drag the card there
Accept the model's suggestion
Skip β€” back of the pile
Undo, and unlearn it
Fullscreen
the bar button; Esc leaves

Several zones at once

Stack while held
a s release to file them all
Latch the mode
a bare tap; again to leave or file
File the stack
Drop the stack
Esc

Touch

Accept the suggestion
double tap the card
Open the stack
hold the card, or hold anywhere to summon the pad
Stack, then file
sweep across zones, then let go

Three packages, no dependencies

@trieur/core

The stage, the zones, the gesture. Pointer Events β€” mouse, thumb and keyboard at the same level. The stage is carved into regions: you drop into what you see.

@trieur/learn

The models, the features, local storage and the wire protocol. Everything is online: no training phase, the model learns from the card you just filed.

@trieur/server

Bun + SQLite. It keeps the events β€” not just the model β€” so history can be replayed, and it runs what a browser cannot: embeddings.

The model ladder

Naive Bayes plateaus as soon as features interact: "github and rust" is not the sum of "github" and "rust". Every rung below runs on the same features, and they can be climbed one at a time.

RungWhat it buysWhat it costs
Bayes Learns from the 3rd example, explains itself, a hundred lines. Assumes features are independent.
crosses() A domainΓ—tag cross exposes the combination β€” without changing model. The vocabulary explodes; it has to be pruned.
Linear Learned weights instead of counts; copes with correlated features. A learning rate β€” tuned on its own by AdaGrad.
Knn "This link looks like those." The best cold start. The corpus has to be kept.
embeddings Brings two cards sharing no word closer together. A server and a network call.

None of these rungs is declared better than another: they vote, and their weight is their measured accuracy. On a real corpus of 3,412 links filed across 72 folders, the ensemble reaches 35.8% top-1 and 60.9% top-3, against 33.1 / 57.1 for its best member alone. The bench ships with the repo β€” and it runs on your corpus.

Light, then full

Light mode keeps everything in the browser: nothing leaves, nothing waits on the network. Full mode adds a server that receives the filings, trains what a tab cannot, and warm-starts a new machine. Going from one to the other is one line:

import { Deck } from '@trieur/core';
import { createRecommender } from '@trieur/learn';

const brain = createRecommender({
  key: 'links',
  server: { url: 'https://trieur.example.com', token },  // ← remove this = light mode
});

new Deck(document.querySelector('#sorter'), {
  items, zones,
  advisor: brain,
  multi: true,                                          // a card can go into several zones
  meta: (link) => ({ domain: link.host, tag: link.tags, title: link.title }),
  renderCard: (link, el) => (el.innerHTML = template(link)),
  onSortMany: (link, zones) => api.file(link.id, zones.map((z) => z.id)),
});