diff --git a/apps/readest-app/src/app/reader/components/annotator/HighlightOptions.tsx b/apps/readest-app/src/app/reader/components/annotator/HighlightOptions.tsx index fd176b7f..b216359b 100644 --- a/apps/readest-app/src/app/reader/components/annotator/HighlightOptions.tsx +++ b/apps/readest-app/src/app/reader/components/annotator/HighlightOptions.tsx @@ -9,7 +9,15 @@ import { useResponsiveSize } from '@/hooks/useResponsiveSize'; import { saveSysSettings } from '@/helpers/settings'; const styles = ['highlight', 'underline', 'squiggly'] as HighlightStyle[]; -const colors = ['red', 'violet', 'blue', 'green', 'yellow'] as HighlightColor[]; +const defaultColors = ['red', 'violet', 'blue', 'green', 'yellow'] as HighlightColor[]; + +const getColorHex = ( + customColors: Record, + color: HighlightColor, +): string => { + if (color.startsWith('#')) return color; + return customColors[color as HighlightColor] ?? color; +}; interface HighlightOptionsProps { isVertical: boolean; @@ -41,6 +49,7 @@ const HighlightOptions: React.FC = ({ const einkBgColor = isDarkMode ? '#000000' : '#ffffff'; const einkFgColor = isDarkMode ? '#ffffff' : '#000000'; const customColors = globalReadSettings.customHighlightColors; + const userColors = globalReadSettings.userHighlightColors ?? []; const [selectedStyle, setSelectedStyle] = useState(_selectedStyle); const [selectedColor, setSelectedColor] = useState(_selectedColor); const size16 = useResponsiveSize(16); @@ -55,6 +64,7 @@ const HighlightOptions: React.FC = ({ setSelectedColor(globalReadSettings.highlightStyles[style]); onHandleHighlight(true); }; + const handleSelectColor = (color: HighlightColor) => { const newGlobalReadSettings = { ...globalReadSettings, @@ -65,10 +75,11 @@ const HighlightOptions: React.FC = ({ setSelectedColor(color); onHandleHighlight(true); }; + return (
= ({ height: size16, ...(style === 'highlight' && selectedStyle === 'highlight' && { - backgroundColor: isEink ? einkFgColor : customColors[selectedColor], + backgroundColor: isEink + ? einkFgColor + : getColorHex(customColors, selectedColor), color: isEink ? einkBgColor : '#d1d5db', paddingTop: '2px', }), @@ -123,7 +136,7 @@ const HighlightOptions: React.FC = ({ selectedStyle === style ? isEink ? einkFgColor - : customColors[selectedColor] + : getColorHex(customColors, selectedColor) : '#d1d5db', ...(style === 'squiggly' && { textDecorationStyle: 'wavy' }), }), @@ -138,31 +151,39 @@ const HighlightOptions: React.FC = ({
- {colors + {defaultColors + .concat(userColors) .filter((c) => (isEink ? selectedColor === c : true)) .map((color) => ( - +
+ +
))}
diff --git a/apps/readest-app/src/app/reader/utils/annotatorUtil.ts b/apps/readest-app/src/app/reader/utils/annotatorUtil.ts index 5df2d248..2468c3f7 100644 --- a/apps/readest-app/src/app/reader/utils/annotatorUtil.ts +++ b/apps/readest-app/src/app/reader/utils/annotatorUtil.ts @@ -2,9 +2,12 @@ import { HIGHLIGHT_COLOR_HEX } from '@/services/constants'; import { HighlightColor } from '@/types/book'; import { SystemSettings } from '@/types/settings'; -export const getHighlightColorHex = (settings: SystemSettings, color?: HighlightColor) => { - if (!color) return color; - +export const getHighlightColorHex = ( + settings: SystemSettings, + color?: HighlightColor, +): string | undefined => { + if (!color) return undefined; + if (color.startsWith('#')) return color; const customColors = settings.globalReadSettings.customHighlightColors; return customColors?.[color] ?? HIGHLIGHT_COLOR_HEX[color]; }; diff --git a/apps/readest-app/src/components/settings/ColorPanel.tsx b/apps/readest-app/src/components/settings/ColorPanel.tsx index cf99a4b9..072b90eb 100644 --- a/apps/readest-app/src/components/settings/ColorPanel.tsx +++ b/apps/readest-app/src/components/settings/ColorPanel.tsx @@ -61,6 +61,9 @@ const ColorPanel: React.FC = ({ bookKey, onRegisterReset const [customTtsHighlightColors, setCustomTtsHighlightColors] = useState( settings.globalReadSettings.customTtsHighlightColors || [], ); + const [userHighlightColors, setUserHighlightColors] = useState( + settings.globalReadSettings.userHighlightColors || [], + ); const { textures: customTextures, @@ -87,6 +90,7 @@ const ColorPanel: React.FC = ({ bookKey, onRegisterReset setBackgroundOpacity(0.6); setBackgroundSize('cover'); setCustomHighlightColors(HIGHLIGHT_COLOR_HEX); + setUserHighlightColors([]); }; useEffect(() => { @@ -253,6 +257,13 @@ const ColorPanel: React.FC = ({ bookKey, onRegisterReset saveSettings(envConfig, settings); }; + const handleUserHighlightColorsChange = (colors: string[]) => { + setUserHighlightColors(colors); + settings.globalReadSettings.userHighlightColors = colors; + setSettings(settings); + saveSettings(envConfig, settings); + }; + return (
{showCustomThemeEditor ? ( @@ -311,7 +322,9 @@ const ColorPanel: React.FC = ({ bookKey, onRegisterReset ; + userHighlightColors: string[]; onChange: (colors: Record) => void; + onUserColorsChange: (colors: string[]) => void; } const HighlightColorsEditor: React.FC = ({ customHighlightColors, + userHighlightColors, onChange, + onUserColorsChange, }) => { const _ = useTranslation(); + const [newColor, setNewColor] = useState('#808080'); const handleColorChange = (color: HighlightColor, value: string) => { const updated = { ...customHighlightColors, [color]: value }; onChange(updated); }; + const handleAddUserColor = () => { + if (userHighlightColors.length >= MAX_USER_HIGHLIGHT_COLORS) return; + if (!userHighlightColors.includes(newColor)) { + const updatedColors = [...userHighlightColors, newColor]; + onUserColorsChange(updatedColors); + } + }; + + const handleDeleteUserColor = (hex: string) => { + const updatedColors = userHighlightColors.filter((c) => c !== hex); + onUserColorsChange(updatedColors); + }; + + const handleUserColorChange = (oldHex: string, newHex: string) => { + const updatedColors = userHighlightColors.map((c) => (c === oldHex ? newHex : c)); + onUserColorsChange(updatedColors); + }; + return (

{_('Highlight Colors')}

-
- {(['red', 'violet', 'blue', 'green', 'yellow'] as HighlightColor[]).map( - (color, index, array) => { - const position = - index === 0 ? 'left' : index === array.length - 1 ? 'right' : 'center'; - return ( -
+
+
+ {(['red', 'violet', 'blue', 'green', 'yellow'] as HighlightColor[]).map( + (color, index, array) => { + const position = + index === 0 ? 'left' : index === array.length - 1 ? 'right' : 'center'; + return ( +
+
+ handleColorChange(color, value)} + /> +
+ ); + }, + )} +
+ + {(userHighlightColors.length > 0 || true) && ( +
+
+ + {_('Custom Colors')} ({userHighlightColors.length}/{MAX_USER_HIGHLIGHT_COLORS}) + +
handleColorChange(color, value)} + pickerPosition='right' + onChange={setNewColor} /> +
- ); - }, +
+ + {userHighlightColors.length > 0 && ( +
+ {userHighlightColors.map((hex, index) => ( +
+
+ handleUserColorChange(hex, value)} + /> + +
+ ))} +
+ )} +
)}
diff --git a/apps/readest-app/src/services/constants.ts b/apps/readest-app/src/services/constants.ts index 7a46cf47..9ab75a43 100644 --- a/apps/readest-app/src/services/constants.ts +++ b/apps/readest-app/src/services/constants.ts @@ -125,6 +125,7 @@ export const DEFAULT_READSETTINGS: ReadSettings = { squiggly: 'blue', }, customHighlightColors: HIGHLIGHT_COLOR_HEX, + userHighlightColors: [], customTtsHighlightColors: [], }; diff --git a/apps/readest-app/src/types/book.ts b/apps/readest-app/src/types/book.ts index 6f2ecd44..6aaac7c9 100644 --- a/apps/readest-app/src/types/book.ts +++ b/apps/readest-app/src/types/book.ts @@ -15,7 +15,8 @@ export type BookFormat = | 'MD'; export type BookNoteType = 'bookmark' | 'annotation' | 'excerpt'; export type HighlightStyle = 'highlight' | 'underline' | 'squiggly'; -export type HighlightColor = 'red' | 'yellow' | 'green' | 'blue' | 'violet'; +// Predefined highlight colors, can be extended with custom hex colors +export type HighlightColor = 'red' | 'yellow' | 'green' | 'blue' | 'violet' | string; export const FIXED_LAYOUT_FORMATS: Set = new Set(['PDF', 'CBZ']); diff --git a/apps/readest-app/src/types/settings.ts b/apps/readest-app/src/types/settings.ts index ca266f7f..0f845ab2 100644 --- a/apps/readest-app/src/types/settings.ts +++ b/apps/readest-app/src/types/settings.ts @@ -34,6 +34,7 @@ export interface ReadSettings { highlightStyle: HighlightStyle; highlightStyles: Record; customHighlightColors: Record; + userHighlightColors: string[]; customTtsHighlightColors: string[]; customThemes: CustomTheme[]; }