diff --git a/internal/handlers/koreader.go b/internal/handlers/koreader.go index cb16c72..9771a66 100644 --- a/internal/handlers/koreader.go +++ b/internal/handlers/koreader.go @@ -993,10 +993,12 @@ func (h *KOReaderHandler) GetMetadata(c *echo.Context) error { Text: ann.SelectionText, Pos0: pos0, Pos1: pos1, - // No color served: devices render their own default and - // cannot round-trip web colors — the web color only changes - // when the highlight is edited on the device (push carries - // the device color, ingested with the name→hex map). + // Web colors flow to the device, mapped to KOReader's named + // palette. Round-trip safety: the device suppresses the color + // when echoing un-edited applied entries (a pink→purple + // palette mismatch must not rewrite the stored hex), and an + // actual device edit pushes its color, which wins. + Color: mapColorToKOReader(ann.Color.String), Datetime: ann.CreatedAt.Time.Format(time.RFC3339), DedupKey: ann.DedupKey.String, } @@ -1196,6 +1198,25 @@ func mapColorFromKOReader(name string) string { return "#ffd54f" } +var koreaderColorFromHex = map[string]string{ + "#ffd54f": "yellow", + "#a5d6a7": "green", + "#90caf9": "blue", + "#ce93d8": "purple", + "#f48fb1": "purple", +} + +// mapColorToKOReader normalizes a web hex swatch to the nearest KOReader +// color name (default yellow) when serving to devices. Pink maps to purple +// (the palette's closest); round-trip drift is prevented on the device by +// suppressing echo colors for un-edited applied entries. +func mapColorToKOReader(hex string) string { + if name, ok := koreaderColorFromHex[strings.ToLower(strings.TrimSpace(hex))]; ok { + return name + } + return "yellow" +} + // 1. A device-native CRE xpointer ("/body/...") in startPosition wins — // round-trip identical for KOReader-pushed annotations (converting the // stored CFI instead could drift and duplicate on the device).