From 7f4202afd73105b850498b55ad66922b3505f0e3 Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Sun, 14 Dec 2025 16:39:25 -0800 Subject: feat: Add unified reader settings with local overrides (#2230) * Add initial impl * fix some format inconsistencies, add indicator in user settings when local is out of sync * Fix sliders in user settings, unify constants and formatting * address CodeRabbit suggestions * add mobile implementation * address coderabbit nitpicks * fix responsiveness of the reader settings popover * Move more of the web UI strings to i18n * update translations for more coverage * remove duplicate logic/definitions * fix android font family * add shared reading setting hook between web and mobile * unify reader settings context for both web and mobile * remove unused export * address coderabbit suggestions * fix tests --- packages/shared/types/readers.ts | 59 ++++++++++++++++++++++++++++++++++++++++ packages/shared/types/users.ts | 10 +++++++ 2 files changed, 69 insertions(+) create mode 100644 packages/shared/types/readers.ts (limited to 'packages/shared') diff --git a/packages/shared/types/readers.ts b/packages/shared/types/readers.ts new file mode 100644 index 00000000..117dd51b --- /dev/null +++ b/packages/shared/types/readers.ts @@ -0,0 +1,59 @@ +import { z } from "zod"; + +import { ZReaderFontFamily, zReaderFontFamilySchema } from "./users"; + +export const READER_DEFAULTS = { + fontSize: 18, + lineHeight: 1.6, + fontFamily: "serif" as const, +} as const; + +export const READER_FONT_FAMILIES: Record = { + serif: "ui-serif, Georgia, Cambria, serif", + sans: "ui-sans-serif, system-ui, sans-serif", + mono: "ui-monospace, Menlo, Monaco, monospace", +} as const; + +// Setting constraints for UI controls +export const READER_SETTING_CONSTRAINTS = { + fontSize: { min: 12, max: 24, step: 1 }, + lineHeight: { min: 1.2, max: 2.5, step: 0.1 }, +} as const; + +// Formatting functions for display +export function formatFontSize(value: number): string { + return `${value}px`; +} + +export function formatLineHeight(value: number): string { + return value.toFixed(1); +} + +export function formatFontFamily( + value: ZReaderFontFamily, + t?: (key: string) => string, +): string { + if (t) { + return t(`settings.info.reader_settings.${value}`); + } + // Fallback labels when no translation function provided + switch (value) { + case "serif": + return "Serif"; + case "sans": + return "Sans Serif"; + case "mono": + return "Monospace"; + } +} + +export const zReaderSettings = z.object({ + fontSize: z.number().int().min(12).max(24), + lineHeight: z.number().min(1.2).max(2.5), + fontFamily: zReaderFontFamilySchema, +}); + +export type ReaderSettings = z.infer; + +export const zReaderSettingsPartial = zReaderSettings.partial(); +export type ReaderSettingsPartial = z.infer; diff --git a/packages/shared/types/users.ts b/packages/shared/types/users.ts index 9f020d52..73b99885 100644 --- a/packages/shared/types/users.ts +++ b/packages/shared/types/users.ts @@ -102,6 +102,9 @@ export const zUserStatsResponseSchema = z.object({ ), }); +export const zReaderFontFamilySchema = z.enum(["serif", "sans", "mono"]); +export type ZReaderFontFamily = z.infer; + export const zUserSettingsSchema = z.object({ bookmarkClickAction: z.enum([ "open_original_link", @@ -112,6 +115,10 @@ export const zUserSettingsSchema = z.object({ backupsEnabled: z.boolean(), backupsFrequency: z.enum(["daily", "weekly"]), backupsRetentionDays: z.number().int().min(1).max(365), + // Reader settings (nullable = opt-in, null means use client default) + readerFontSize: z.number().int().min(12).max(24).nullable(), + readerLineHeight: z.number().min(1.2).max(2.5).nullable(), + readerFontFamily: zReaderFontFamilySchema.nullable(), }); export type ZUserSettings = z.infer; @@ -123,6 +130,9 @@ export const zUpdateUserSettingsSchema = zUserSettingsSchema.partial().pick({ backupsEnabled: true, backupsFrequency: true, backupsRetentionDays: true, + readerFontSize: true, + readerLineHeight: true, + readerFontFamily: true, }); export const zUpdateBackupSettingsSchema = zUpdateUserSettingsSchema.pick({ -- cgit v1.2.3-70-g09d2