diff --git a/Clipboard.qml b/Clipboard.qml index 23c7c09..1c8ea06 100644 --- a/Clipboard.qml +++ b/Clipboard.qml @@ -10,7 +10,7 @@ import "Classify.js" as Classify // Clipboard history picker — Raycast-style: fuzzy search bar, result list, // and a per-type preview pane. Clone of omarchy.clipboard with richer -// capture metadata (app, size, dims, pins, usage) and full-text fuzzy search. +// capture metadata (app, size, dims, pins, usage) and full-text fuzzy filtering. Item { id: root diff --git a/Fuzzy.js b/Fuzzy.js index 76dedd4..6672063 100644 --- a/Fuzzy.js +++ b/Fuzzy.js @@ -153,7 +153,7 @@ function fuzzyMatch(needle, haystack) { score += 8 } - // Gap penalty (light — we rank mostly by bonuses and recency). + // Gap penalty (light). if (k > 0) { var gap = pos - positions[k - 1] - 1 if (gap > 0) score -= Math.min(6, gap) @@ -227,7 +227,7 @@ function recencyBonus(ts, now) { } // rows: [{ entry, content, app, type, ts, pinned, uses, bytes }] -// Returns up to `limit` rows sorted by relevance: [{ row, score, positions, type }] +// Returns up to `limit` matching rows, newest first: [{ row, score, positions, type }] function searchRows(rows, queryStr, now, limit) { var parsed = parseQuery(queryStr) var results = [] @@ -255,11 +255,9 @@ function searchRows(rows, queryStr, now, limit) { if (!matched) continue } - var score = matched ? matched.score : 0 - score += recencyBonus(row.ts, now) - if (row.entry.pinned) score += 500 - if (row.uses > 0) score += 4 * Math.min(10, row.uses) - + // Search only filters. The list is always ordered newest-first; no + // relevance, pin, or usage ranking. + var score = 0 results.push({ row: row, score: score, @@ -268,7 +266,6 @@ function searchRows(rows, queryStr, now, limit) { } results.sort(function(a, b) { - if (b.score !== a.score) return b.score - a.score return (b.row.ts || 0) - (a.row.ts || 0) }) diff --git a/README.md b/README.md index dbd99a7..4d69556 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ full theme integration. ## Highlights -- **Fuzzy search over everything** — fzf-style scoring across content, source - app, and type, boosted by recency, pins, and usage. Query tokens: +- **Fuzzy search over everything** — fzf-style matching across content, source + app, and type. The list is always newest-first; search only filters it. Query tokens: - `type:image|link|text|files|code|json|color|email|html|number` (prefix match) - `app:firefox` — fuzzy match on the source app - `is:pinned`, `today`, `yesterday`, `week`, `<2h`, `>30s`, `<3d` diff --git a/tests/test-fuzzy.mjs b/tests/test-fuzzy.mjs index cb0a5b4..f844188 100644 --- a/tests/test-fuzzy.mjs +++ b/tests/test-fuzzy.mjs @@ -79,6 +79,21 @@ test("searchRows empty query sorts by recency", () => { assert.equal(res[0].row.content, "new") }) +test("searchRows: always newest first, pins and pastes do not reorder", () => { + const now = 1000000 + const rows = [ + { entry: {}, content: "pasted twice, 5 min ago", app: "", type: "text", ts: now - 300, pinned: false, uses: 2, bytes: 3 }, + { entry: {}, content: "just copied", app: "", type: "text", ts: now - 5, pinned: false, uses: 0, bytes: 3 }, + { entry: { pinned: true }, content: "pinned, old", app: "", type: "text", ts: now - 86400 * 3, pinned: true, uses: 0, bytes: 3 } + ] + const res = Fuzzy.searchRows(rows, "", now, 10) + assert.deepEqual(Array.from(res, r => r.row.content), ["just copied", "pasted twice, 5 min ago", "pinned, old"]) + const typed = Fuzzy.searchRows(rows, "type:text <1h", now, 10) + assert.deepEqual(Array.from(typed, r => r.row.content), ["just copied", "pasted twice, 5 min ago"]) + const searched = Fuzzy.searchRows(rows, "past", now, 10) + assert.deepEqual(Array.from(searched, r => r.row.content), ["pasted twice, 5 min ago"]) +}) + test("searchRows fuzzy ranks match above recency when strong", () => { const now = 1000000 const rows = [ @@ -122,14 +137,16 @@ test("searchRows matches app field", () => { assert.equal(res[0].row.app, "firefox") }) -test("searchRows pinned boost wins", () => { +test("searchRows is:pinned filters without reordering", () => { const now = 1000000 const rows = [ - { entry: { pinned: true }, content: "zzz pinned weak", app: "", type: "text", ts: now - 86400 * 10, pinned: true, uses: 0, bytes: 3 }, + { entry: { pinned: true }, content: "zzz pinned old", app: "", type: "text", ts: now - 86400 * 10, pinned: true, uses: 0, bytes: 3 }, { entry: {}, content: "fresh", app: "", type: "text", ts: now, pinned: false, uses: 0, bytes: 3 } ] - const res = Fuzzy.searchRows(rows, "", now, 10) - assert.equal(res[0].row.content, "zzz pinned weak") + assert.equal(Fuzzy.searchRows(rows, "", now, 10)[0].row.content, "fresh") + const pinned = Fuzzy.searchRows(rows, "is:pinned", now, 10) + assert.equal(pinned.length, 1) + assert.equal(pinned[0].row.content, "zzz pinned old") }) test("searchRows respects limit", () => {