From 75b498b4132356e0317bb765cf285a80329d4cad Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Wed, 9 Sep 2026 12:38:42 -0400 Subject: [PATCH] fix(sync): adopt keyed echoes of device-native annotations by pos0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every served annotation carries a dedup key (the server stamps computed keys at creation), so findLocal's pos0 fallback was unreachable for keyed serves: when the key had no local holder it returned 'real new entry' immediately. A device-native highlight had no local key until the first pull stamped one — so the first serve-back of our own creation applied as a SECOND local annotation. Duplicated on the device, while the server stayed single-row (both copies resolve to the same computed key on later pushes). When the key has no local holder, now fall back to pos0 matching restricted to keyless local entries of the same kind (v2: drawer set vs page bookmark; v1: notes field holding selection text). That is our own echo: stamp the key and update in place instead of duplicating. Keyed local entries are never pos0-matched, so distinct annotations sharing a position still cannot cross-match. --- main.lua | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/main.lua b/main.lua index 7d88fbf..cdc401c 100644 --- a/main.lua +++ b/main.lua @@ -1545,7 +1545,7 @@ function Bookhoard:applyServerAnnotations(annotations) end local has_pages = self.ui.document.info.has_pages - local function findLocal(server_entry) + local function findLocal(server_entry, has_text) -- Identity match by dedup key: survives pos0 drift (improved -- server conversion) and, crucially, never cross-matches a -- DIFFERENT annotation that merely shares the position. @@ -1556,7 +1556,38 @@ function Bookhoard:applyServerAnnotations(annotations) return i end end - return nil -- keyed but not present: a real new entry + -- No key holder, but a keyed serve may still be THIS device's + -- own creation echoed back: the server stamped a computed key + -- on our push before we ever pulled one. Adopt a keyless local + -- annotation of the same kind at the identical pos0 + -- (addOrUpdate stamps + updates it) instead of applying a + -- duplicate beside it. Keyed local entries are never + -- pos0-matched, so distinct annotations sharing a position + -- cannot cross-match. + local pos0 = server_entry.pos0 or "" + if pos0 ~= "" then + for i, bm in ipairs(entries) do + if not bm.bookhoard_dedup_key then + local is_highlight + if model == "v2" then + is_highlight = bm.drawer ~= nil + else + is_highlight = bm.notes ~= nil and bm.notes ~= "" + end + if is_highlight ~= has_text then + -- Wrong kind: a page bookmark at the same spot + -- is a different annotation, never an echo. + else + local p = bm.pos0 + if type(p) == "table" then p = tostring(p.page or "") end + if p == pos0 then + return i + end + end + end + end + end + return nil -- genuinely absent: a real new entry end -- Legacy serve (no key): fall back to pos0 matching. local pos0 = server_entry.pos0 or "" @@ -1631,7 +1662,7 @@ function Bookhoard:applyServerAnnotations(annotations) -- locators return above and are never tracked — absence of an -- annotation this device never held is not a deletion. local kind = has_text and "highlight" or "bookmark" - local idx = findLocal(server_entry) + local idx = findLocal(server_entry, has_text) if idx then local bm = entries[idx] -- Legacy pos0 match on a device-native entry: stamp the key so