Home

dev / tinycast

publicthedavidweng/tinycast· sync paused
Overview Code History Branches Pull requestsIssuesInsights
main
HomeOverview Code PRsIssues

Order clipboard pins by pin time, and harden the pin paths (#68)

2 months ago

023fdc0
Authored
Abue Ammar7/28/2026, 4:16:46 PM
* Harden clipboard pin ordering, load and pruning

Three follow-ups from the review of #63:

- A pinned entry could fall outside the FTS statement's LIMIT 200 and
  vanish from a filtered search. Every pinned row is resident in `items`
  by construction, so the pinned block is now matched in memory and the
  FTS result contributes only unpinned rows.
- `load`'s `pinned = 1 OR rowid >= ?` could not be driven from an index
  while holding row order, so it scanned the whole table on every launch
  (~12ms at 200k rows, on the main actor). Split into two indexed
  branches over a partial index on the pin flag: ~1ms for the same data.
- `prune`'s cheap guard tested the oldest row, which a retention-exempt
  pin at the tail made permanently true, re-scanning the window on every
  capture. It now tests the oldest unpinned row.

* Order the Pinned section by pin time, Raycast-style

Pins were a boolean, so the Pinned section stayed in recency order: pinning
two entries interleaved them by when they were copied rather than keeping
them in the order they were pinned, and unpinning dropped a row back into
whatever date bucket it came from, jumping the list under the selection.

`pinned` becomes `pinned_at`, a stamp:

- The Pinned section is ordered newest pin first, so pinning a row never
  reshuffles the pins already there.
- Unpinning rejoins the history as its newest entry (the same delete +
  re-insert `promote` uses), so the row stays where the eye already is.
- `promote` skips pinned rows: a pasted pin holds its place instead of
  rewriting the row and its FTS entry for no visible change.

The boolean column never shipped, so nothing migrates it.

Adds `Tools/clipboard-test.swift` (23 checks: pin order, unpin recency,
paste, retention exemption, the FTS limit, persistence, and migrating a
shipped pre-pin database), which is why the store gained an injectable
directory and dropped its unused AppKit import.

* Order pins oldest first, so a new pin joins the end of the section

Parentec136f1

5 files changed
  • AGENTS.md+3−1
  • Tinycast/Core/ClipboardStore.swift+140−80
  • Tools/clipboard-test.swift+291−0
  • docs/clipboard.md+30−12
  • docs/development.md+7−1