feat(eink): optimize color and layout for e-ink mode (#2887)
This commit is contained in:
@@ -98,7 +98,9 @@ const AnnotationNotes: React.FC<AnnotationNotesProps> = ({
|
||||
role='none'
|
||||
key={note.id || index}
|
||||
onClick={() => handleShowAnnotation?.(note)}
|
||||
className={clsx('cursor-pointer rounded-lg bg-gray-600 shadow-lg transition-colors')}
|
||||
className={clsx(
|
||||
'popup-container cursor-pointer rounded-lg bg-gray-600 shadow-lg transition-colors',
|
||||
)}
|
||||
style={
|
||||
isVertical
|
||||
? {
|
||||
@@ -113,7 +115,8 @@ const AnnotationNotes: React.FC<AnnotationNotesProps> = ({
|
||||
<div
|
||||
dir='auto'
|
||||
className={clsx(
|
||||
'm-4 hyphens-auto text-justify font-sans text-sm text-white',
|
||||
'm-4 hyphens-auto text-justify font-sans text-sm',
|
||||
'not-eink:text-white eink:text-base-content',
|
||||
isVertical && 'writing-vertical-rl',
|
||||
)}
|
||||
style={
|
||||
@@ -127,7 +130,7 @@ const AnnotationNotes: React.FC<AnnotationNotesProps> = ({
|
||||
>
|
||||
<div className={clsx('flex flex-col justify-between gap-2')}>
|
||||
{note.note}
|
||||
<span className='text-sm text-white/50 sm:text-xs'>
|
||||
<span className='not-eink:text-white/50 eink:text-base-content/50 text-sm sm:text-xs'>
|
||||
{dayjs(note.createdAt).fromNow()}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { BookNote, HighlightColor } from '@/types/book';
|
||||
import { Point, TextSelection } from '@/utils/sel';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
|
||||
import { useAnnotationEditor } from '../../hooks/useAnnotationEditor';
|
||||
@@ -143,6 +144,9 @@ const AnnotationRangeEditor: React.FC<AnnotationRangeEditorProps> = ({
|
||||
onStartEdit,
|
||||
}) => {
|
||||
const { settings } = useSettingsStore();
|
||||
const { isDarkMode } = useThemeStore();
|
||||
const isEink = settings.globalViewSettings.isEink;
|
||||
const einkFgColor = isDarkMode ? '#ffffff' : '#000000';
|
||||
const { handlePositions, getHandlePositionsFromRange, handleAnnotationRangeChange } =
|
||||
useAnnotationEditor({ bookKey, annotation, getAnnotationText, setSelection });
|
||||
|
||||
@@ -223,7 +227,7 @@ const AnnotationRangeEditor: React.FC<AnnotationRangeEditorProps> = ({
|
||||
position={currentStart}
|
||||
isVertical={isVertical}
|
||||
type='start'
|
||||
color={handleColorHex}
|
||||
color={isEink ? einkFgColor : handleColorHex}
|
||||
onDragStart={handleStartDragStart}
|
||||
onDrag={handleStartDrag}
|
||||
onDragEnd={handleDragEnd}
|
||||
@@ -232,7 +236,7 @@ const AnnotationRangeEditor: React.FC<AnnotationRangeEditorProps> = ({
|
||||
position={currentEnd}
|
||||
isVertical={isVertical}
|
||||
type='end'
|
||||
color={handleColorHex}
|
||||
color={isEink ? einkFgColor : handleColorHex}
|
||||
onDragStart={handleEndDragStart}
|
||||
onDrag={handleEndDrag}
|
||||
onDragEnd={handleDragEnd}
|
||||
|
||||
@@ -30,7 +30,9 @@ const AnnotationToolButton: React.FC<AnnotationToolButtonProps> = ({
|
||||
onClick={handleClick}
|
||||
className={clsx(
|
||||
'flex h-8 min-h-8 w-8 items-center justify-center p-0',
|
||||
disabled ? 'cursor-not-allowed opacity-50' : 'rounded-md hover:bg-gray-500',
|
||||
disabled
|
||||
? 'cursor-not-allowed opacity-50'
|
||||
: 'not-eink:hover:bg-gray-500 eink:hover:border rounded-md',
|
||||
)}
|
||||
disabled={disabled}
|
||||
>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { BookNote, BooknoteGroup, HighlightColor, HighlightStyle } from '@/types
|
||||
import { NOTE_PREFIX } from '@/types/view';
|
||||
import { NativeTouchEventType } from '@/types/system';
|
||||
import { getLocale, getOSPlatform, uniqueId } from '@/utils/misc';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { useBookDataStore } from '@/store/bookDataStore';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
@@ -42,6 +43,7 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
const _ = useTranslation();
|
||||
const { envConfig, appService } = useEnv();
|
||||
const { settings } = useSettingsStore();
|
||||
const { isDarkMode } = useThemeStore();
|
||||
const { getConfig, saveConfig, getBookData, updateBooknotes } = useBookDataStore();
|
||||
const { getProgress, getView, getViewsById, getViewSettings } = useReaderStore();
|
||||
const { setNotebookVisible, setNotebookNewAnnotation } = useNotebookStore();
|
||||
@@ -293,10 +295,13 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
|
||||
const onDrawAnnotation = (event: Event) => {
|
||||
const viewSettings = getViewSettings(bookKey)!;
|
||||
const isEink = viewSettings.isEink;
|
||||
const detail = (event as CustomEvent).detail;
|
||||
const { draw, annotation, doc, range } = detail;
|
||||
const { style, color } = annotation as BookNote;
|
||||
const hexColor = getHighlightColorHex(settings, color);
|
||||
const einkBgColor = isDarkMode ? '#000000' : '#ffffff';
|
||||
const einkFgColor = isDarkMode ? '#ffffff' : '#000000';
|
||||
if (annotation.note) {
|
||||
const { defaultView } = doc;
|
||||
const node = range.startContainer;
|
||||
@@ -304,7 +309,7 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
const { writingMode } = defaultView.getComputedStyle(el);
|
||||
draw(Overlayer.bubble, { writingMode });
|
||||
} else if (style === 'highlight') {
|
||||
draw(Overlayer.highlight, { color: hexColor });
|
||||
draw(Overlayer.highlight, { color: isEink ? einkBgColor : hexColor });
|
||||
} else if (['underline', 'squiggly'].includes(style as string)) {
|
||||
const { defaultView } = doc;
|
||||
const node = range.startContainer;
|
||||
@@ -318,7 +323,11 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
const padding = viewSettings.vertical
|
||||
? (lineHeightValue - fontSizeValue) / 2 - strokeWidth + verticalCompensation
|
||||
: (lineHeightValue - fontSizeValue) / 2 - strokeWidth + horizontalCompensation;
|
||||
draw(Overlayer[style as keyof typeof Overlayer], { writingMode, color: hexColor, padding });
|
||||
draw(Overlayer[style as keyof typeof Overlayer], {
|
||||
writingMode,
|
||||
color: isEink ? einkFgColor : hexColor,
|
||||
padding,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import React, { useState } from 'react';
|
||||
import { FaCheckCircle } from 'react-icons/fa';
|
||||
import { HighlightColor, HighlightStyle } from '@/types/book';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
|
||||
import { saveSysSettings } from '@/helpers/settings';
|
||||
@@ -34,12 +35,15 @@ const HighlightOptions: React.FC<HighlightOptionsProps> = ({
|
||||
}) => {
|
||||
const { envConfig } = useEnv();
|
||||
const { settings } = useSettingsStore();
|
||||
const { isDarkMode } = useThemeStore();
|
||||
const globalReadSettings = settings.globalReadSettings;
|
||||
const isEink = settings.globalViewSettings.isEink;
|
||||
const einkBgColor = isDarkMode ? '#000000' : '#ffffff';
|
||||
const einkFgColor = isDarkMode ? '#ffffff' : '#000000';
|
||||
const customColors = globalReadSettings.customHighlightColors;
|
||||
const [selectedStyle, setSelectedStyle] = useState<HighlightStyle>(_selectedStyle);
|
||||
const [selectedColor, setSelectedColor] = useState<HighlightColor>(_selectedColor);
|
||||
const size16 = useResponsiveSize(16);
|
||||
const size18 = useResponsiveSize(18);
|
||||
const size28 = useResponsiveSize(28);
|
||||
const highlightOptionsHeightPx = useResponsiveSize(OPTIONS_HEIGHT_PIX);
|
||||
const highlightOptionsPaddingPx = useResponsiveSize(OPTIONS_PADDING_PIX);
|
||||
@@ -93,16 +97,17 @@ const HighlightOptions: React.FC<HighlightOptionsProps> = ({
|
||||
<button
|
||||
key={style}
|
||||
onClick={() => handleSelectStyle(style)}
|
||||
className='flex items-center justify-center rounded-full bg-gray-700 p-0'
|
||||
className='not-eink:bg-gray-700 eink-bordered flex items-center justify-center rounded-full p-0'
|
||||
style={{ width: size28, height: size28, minHeight: size28 }}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: size16,
|
||||
height: style === 'squiggly' ? size18 : size16,
|
||||
height: size16,
|
||||
...(style === 'highlight' &&
|
||||
selectedStyle === 'highlight' && {
|
||||
backgroundColor: customColors[selectedColor],
|
||||
backgroundColor: isEink ? einkFgColor : customColors[selectedColor],
|
||||
color: isEink ? einkBgColor : '#d1d5db',
|
||||
paddingTop: '2px',
|
||||
}),
|
||||
...(style === 'highlight' &&
|
||||
@@ -111,11 +116,15 @@ const HighlightOptions: React.FC<HighlightOptionsProps> = ({
|
||||
paddingTop: '2px',
|
||||
}),
|
||||
...((style === 'underline' || style === 'squiggly') && {
|
||||
color: '#d1d5db',
|
||||
color: isEink ? einkFgColor : '#d1d5db',
|
||||
textDecoration: 'underline',
|
||||
textDecorationThickness: '2px',
|
||||
textDecorationColor:
|
||||
selectedStyle === style ? customColors[selectedColor] : '#d1d5db',
|
||||
selectedStyle === style
|
||||
? isEink
|
||||
? einkFgColor
|
||||
: customColors[selectedColor]
|
||||
: '#d1d5db',
|
||||
...(style === 'squiggly' && { textDecorationStyle: 'wavy' }),
|
||||
}),
|
||||
}}
|
||||
@@ -129,27 +138,32 @@ const HighlightOptions: React.FC<HighlightOptionsProps> = ({
|
||||
|
||||
<div
|
||||
className={clsx(
|
||||
'flex items-center justify-center gap-2 rounded-3xl bg-gray-700',
|
||||
'not-eink:bg-gray-700 eink-bordered flex items-center justify-center gap-2 rounded-3xl',
|
||||
isVertical ? 'flex-col py-2' : 'flex-row px-2',
|
||||
)}
|
||||
style={isVertical ? { width: size28 } : { height: size28 }}
|
||||
>
|
||||
{colors.map((color) => (
|
||||
<button
|
||||
key={color}
|
||||
onClick={() => handleSelectColor(color)}
|
||||
style={{
|
||||
width: size16,
|
||||
height: size16,
|
||||
backgroundColor: selectedColor !== color ? customColors[color] : 'transparent',
|
||||
}}
|
||||
className='rounded-full p-0'
|
||||
>
|
||||
{selectedColor === color && (
|
||||
<FaCheckCircle size={size16} style={{ fill: customColors[color] }} />
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
{colors
|
||||
.filter((c) => (isEink ? selectedColor === c : true))
|
||||
.map((color) => (
|
||||
<button
|
||||
key={color}
|
||||
onClick={() => handleSelectColor(color)}
|
||||
style={{
|
||||
width: size16,
|
||||
height: size16,
|
||||
backgroundColor: selectedColor !== color ? customColors[color] : 'transparent',
|
||||
}}
|
||||
className='rounded-full p-0'
|
||||
>
|
||||
{selectedColor === color && (
|
||||
<FaCheckCircle
|
||||
size={size16}
|
||||
style={{ fill: isEink ? einkFgColor : customColors[color] }}
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -43,7 +43,7 @@ const QuickActionMenu: React.FC<QuickActionMenuProps> = ({
|
||||
<Menu
|
||||
className={clsx(
|
||||
'annotation-quick-action-menu dropdown-content z-20 mt-1 border',
|
||||
'bgcolor-base-200 border-base-200 shadow-2xl',
|
||||
'bgcolor-base-200 shadow-2xl',
|
||||
)}
|
||||
style={{
|
||||
maxWidth: `${window.innerWidth - 40}px`,
|
||||
|
||||
Reference in New Issue
Block a user