feat(sync): cross-device background texture sync (#4079)

Plug the texture replica adapter into the kind-agnostic primitives
shipped in #4077. Textures imported on one device download and
become available on every signed-in device, with the same shape as
the font sync stack (single-file binary, contentId from
partialMD5+size+filename, bundleDir layout, replica-publish on
import, full activation on auto-download).

Includes legacy flat-path migration so pre-existing textures sync
without re-import, ColorPanel import flow now publishes the row
and queues the binary upload, and createCustomTexture preserves
contentId/bundleDir/byteSize through addTexture (mirrors the
font-import fix). Server allowlist gains 'texture' with a
single-image Zod schema; useBackgroundTexture passes replica
metadata through addTexture so the boot-time "ensure selected
texture is in store" path doesn't silently un-publish a remote
record.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Huang Xin
2026-05-07 15:55:32 +08:00
committed by GitHub
parent ca674bb5e7
commit de6529523f
15 changed files with 671 additions and 34 deletions
@@ -74,12 +74,12 @@ const Reader: React.FC<{ ids?: string }> = ({ ids }) => {
useTheme({ systemUIVisible: settings.alwaysShowStatusBar, appThemeColor: 'base-100' });
useScreenWakeLock(settings.screenWakeLock);
useTransferQueue(libraryLoaded, 5000);
// Reader needs dictionaries for word-lookup and fonts for rendering.
// Mounted here (not in the app-router page wrapper) so the web pages-
// router entry at `pages/reader/[ids].tsx` also gets the pull.
// Module-scoped dedup means navigating between library and reader
// doesn't re-pull.
useReplicaPull({ kinds: ['dictionary', 'font'] });
// Reader needs dictionaries for word-lookup, fonts for rendering, and
// textures for the page background. Mounted here (not in the app-
// router page wrapper) so the web pages-router entry at
// `pages/reader/[ids].tsx` also gets the pull. Module-scoped dedup
// means navigating between library and reader doesn't re-pull.
useReplicaPull({ kinds: ['dictionary', 'font', 'texture'] });
useEffect(() => {
mountAdditionalFonts(document);