Skip to content

core before UI - #68

Merged
rgdevment merged 7 commits into
mainfrom
rgdevment/core-before-ui
Sep 21, 2026
Merged

rgdevment merged 7 commits into
mainfrom
rgdevment/core-before-ui

Conversation

@rgdevment

Copy link
Copy Markdown
Owner

No description provided.

The mockup lists everything a card shows; the store only gave five of
those fields. `Listed` now carries app, label, colour, thumbnail, paste
count, last use, creation and breakage, and `list` returns a `Page` with
an opaque cursor keyed on the order so paging never repeats or skips
under ties. `facets` counts the classes within the current search and
`distinct_apps` the source applications. `Filter` grows exclusion of
classes and applications, application equality on the folded column,
`since` on `modified_at` (a recopy is a copy), broken hidden by default
and askable, a label-scoped query and three indexed orders. `parse`
turns `token @code ~1h` into a `Filter` through one table of keys and
symbols, treating a prefix as an operator only when its value is known.

Searching marks the fragment that matched, cut from the text as it was
copied: `search::excerpt` maps folded matches back to the original bytes
and says whether it hit the text, the label, the application or what
was read inside an image. Folding no longer allocates per character —
64 KB in 0.05 ms instead of 2.6 — which is what makes a snippet per row
affordable.

`sweep` applies a `Policy` in one pass — broken past their grace,
expired, beyond a count, beyond a byte quota, blob files nobody
references — and checkpoints once. Purging a broken item releases its
blobs; `update_text` edits in place, drops the renderings that no longer
match and checkpoints so the old text does not stay readable in the
database or its log; `mark_present` undoes `mark_broken`. The blob sweep
only touches digest-shaped names and reclaiming a blob refreshes its
mtime. Schema version 2 rebuilds the ordering indexes that gained an
`id` tiebreaker.

`paste_as` takes a neutral `Content` and answers `forms_for` and
`render`: JSON pretty, minified, keys and TSV table; colour as hex, rgb
and hsl with alpha; link as Markdown, domain or with its title; code as
one line, a fenced block or dedented; token as an Authorization header,
a curl or the decoded JWT claims; image as JPEG on white or as its OCR
text; files as paths; rich text as Markdown through a small HTML
converter. `Kind::Token` is the fifteenth class: a JWT or a known
prefix, no long-hex heuristic. `serde_json` joins the core.

`watch::insist` retries a too-slow source five times with 150 ms between
attempts and gives up as soon as the change count moves;
`cp_mac::capture::capture_insisting` wires it and probe C6 checks a
source that answers costs no pause. macOS builds the `Content` from its
formats and restores the synthetic ones the store writes, the way
Windows already did. Windows takes the rest in its own session.
The HTML-to-Markdown scanner sliced at byte one, so any rich copy whose
text after a tag began with an accented letter panicked; it now advances
by characters, gives up on a `<` with no `>` left in one pass instead of
rescanning per bracket, respects comments and quoted attributes, and
never slices before the start of an anchor whose surroundings were
trimmed inside `<pre>`. `dedent` strips the whitespace prefix every line
shares, character by character, instead of a byte count that could land
inside a non-breaking space. The curl form escapes single quotes: a JWT
signature may carry any character, and the rendered line is meant for a
shell. Percentages in rgb channels and in alpha are scaled. Quotes keep
every paragraph; fences outgrow the backticks inside; odd URLs go in
angle brackets.

`Form::AsIs` is gone — pasting the original item is the interface's job
— and every form `forms_for` offers now renders, which a test asserts
over ten contents.

`capture_insisting` no longer spawns a reader per attempt: `reading::begin`
hands back a `Pending` that can be waited on more than once, so a late
answer from Excel is collected instead of queued behind a fresh read,
and a hung source blocks one thread rather than five. Probe C6 allows a
legitimate attempt to take up to `PATIENCE`.
…rter

`cp_mac_sys::files::open` hands a file to its application and `reveal`
selects it in the Finder — the two card actions the mockup draws that
the core could do without a window. Probe M1 and M2 watch the Finder and
TextEdit come to the front through System Events, because
`NSWorkspace.frontmostApplication` in a command-line process reports a
dead application with pid -1 when the screen is locked; `frontmost()`
now returns nothing for such a pid instead of a destination nobody can
paste into.

Two copies from Vivaldi taught the HTML converter what Google writes:
everything is wrapped in a `<b style="font-weight:normal">` that is not
bold, real bold and italic live in `font-weight` and `font-style` on
spans, and a Sheets range is a `<table>`. Inline markers now come from
the tag and its style through one stack, so a stray close is ignored,
and a table renders as a Markdown table with a header rule, cells joined
on one line and pipes escaped.
…the branch

Safari offers an image as TIFF only, never PNG: `Content.image` now takes
any decodable bytes, so a picture copied from a web page can be pasted as
JPEG and read by OCR. Safari also attaches a 2.5 MB web archive to a
43 KB logo; the catalog gains `page_archives`, and `decide_in` notes them
as presence when the family is Image, the same shape as the Finder icon
rule. Windows declares no page archives. A Pages table and a Sheets range
in Safari confirmed the rest: iWork markers stay presence, the empty PNG
stays absent, the Markdown table keeps its header rule.

The whole branch then went through five lenses at once, and the sum
showed what the pairs had not. FTS5 with external content keeps the
terms of a deleted row in its segments until a merge: a password copied
and erased stayed readable in `history.db` with the WAL at zero. The
index now runs with `secure-delete`, schema version 3 optimizes and
checkpoints existing databases, and the residue tests look for every
token instead of a hyphenated phrase the tokenizer splits. The Markdown
writer kept byte offsets for the open link, cell and quote that a later
truncation left inside an `é` or past the end; `truncate_to` clamps all
three, quotes nest, italics parse `font-style: italic`, attributes match
only after whitespace, adjacent bold runs merge, 84 Latin-1 entities
decode, and code fences carry no inline markers.

`list` with a query sorted whole rows, preview text included; it now
sorts `(key, id)` in a subquery and joins the wide columns for the page
that survives: 143 → 45 ms on the worst query over 50 000 items, and no
temporary B-tree in any order, which the plan test now checks against
the real SQL. `blob_is_shared` scanned `item_formats` per released blob,
so a byte-quota sweep took thirty seconds; `formats_by_blob` and one
transaction per eviction bring it to three. Partial covering indexes
make `facets` and `distinct_apps` cost milliseconds; `count` is a
difference of two indexes and no longer counts broken rows;
`count_matching` and `distinct_apps` take the filter. `checkpoint`
reports whether it truncated, interactive deletes wait fifty
milliseconds instead of five seconds, the capture connection runs
without autocheckpoint and `checkpoint_passive` is for whoever cleans
up. Previews are capped at 64 KiB on write and 2 000 characters on
read; `insert_text` classifies and refuses what `placement` refuses.
`update_text` and `insert_item` are transactions, and a released blob
respects the same sixty-second grace as the sweep so a capture writing
the same digest on another connection cannot lose it. Partial files are
private per writer, `remove_at` zeroes in place before unlinking, and a
symlink named like a digest is unlinked, never followed. `migrate` runs
in one transaction and rebuilds the ordering indexes whenever they are
stale, not only from version one.

For the interface: `Content` borrows through `Cow`, so Windows can hand
over UTF-16 text; `Rendered::into_item` gives every form a paste path
with `SYNTHETIC_JPEG` mapped to `public.jpeg`; `Store::item` rebuilds
an item; `Cursor` carries its order, is refused under another one, and
encodes to text; a query made only of operator symbols is no query;
`set_pinned`, `set_thumb`, `label_query`, `Form::ALL`, `FoundIn`. The
old `search` family is gone and `budget` measures `list`. `files::open`
reveals an executable instead of launching it; `path_of` lives in
`files` and `resolved_file_url` says what it returns. Excerpts keep a
combining mark or a skin tone with its base. The probe's M1 and M2 skip
instead of failing when an app will not come to the front, watch
osascript with a three-second guard, and clean their scratch on drop.
The OCR is kept as read: `ocr_text` sits next to the folded `search_ocr`
in schema version four, added by `ALTER TABLE` when a database lacks it,
so the excerpt found inside an image and the "paste as text read" form
show `Pedido AB-4417` and not `pedido ab-4417`. Deleting and editing
clear both columns; an OCR that arrives after the delete revives
nothing; a version-three database keeps showing its folded text until
the image is read again.

`Form::ColorName` pastes the CSS name of a colour: exact when there is
one, the nearest of 139 when it is within a redmean distance of 30, and
not offered at all when no name is honestly that colour or when the
colour carries alpha. Where two names share a value the common spelling
wins. Integer arithmetic, no floats, and a test per scenario.

`cp_core::capture::Captured` is the one enum for both platforms, with
`Kept`, `Refused`, `Nothing`, `TooSlow` and `Superseded`, and the
`insisting` adapter beside it. On macOS `capture` now says why it did
not keep: the refusal and its marker, an empty pasteboard, or a change
count that moved mid-read. Windows drops its own enum for the shared
one; `clippy -D warnings` passes for the Windows target from here.

Pasting as plain text is one path like every other form: `render`,
`into_item`, `to_pasteboard`. The stored item keeps its rich formats,
which probe I5 checks by restoring it afterwards.

Copying the same thing twice was two items from four of six sources,
measured with the new `twice` example: Docs writes a fresh
`docs-internal-guid` in its HTML on every copy, Word rotates the `\rsid`
of its RTF and the `applewebdata://` UUID of its web archive, Pages and
Numbers stamp a date inside the RTFD, Excel rotates the web archive
UUID; only Sheets was stable on its own. The identity now drops the
variable part of that GUID from markup, and renderings — RTF, RTFD, web
archive, PDF — carry no identity when a primary format is present; a
copy that is only a rendering keeps one of its own. Rich and plain stay
two items: the identity is still what would be pasted. All six sources
now give one fingerprint for two copies.

The symlink test builds on Windows through `symlink_file`, and the
probe bodies are formatted the way the CI step checks them.
The Windows mutants job mutated cp-core, cp-store and cp-win in one
sixty-minute slot and was cancelled at the hour twice on this branch,
while macOS finished in fifty-two. It now runs as two shards of the
same set, and every workflow cancels the run a newer push superseded
instead of spending the hour on a commit nobody will merge.
@rgdevment
rgdevment force-pushed the rgdevment/core-before-ui branch from 8d0e3e5 to 6ab971e Compare September 21, 2026 12:53
@rgdevment rgdevment changed the title Rgdevment/core before UI core before UI Sep 21, 2026
@rgdevment
rgdevment merged commit c5f8d9d into main Sep 21, 2026
13 of 15 checks passed
@rgdevment
rgdevment deleted the rgdevment/core-before-ui branch September 21, 2026 13:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant