feat: support note taking with markdown, closes #1097 (#1315)

This commit is contained in:
Huang Xin
2025-06-02 17:35:09 +08:00
committed by GitHub
parent 62081bebfd
commit c51c95b883
7 changed files with 85 additions and 13 deletions
+2
View File
@@ -67,6 +67,7 @@
"i18next-http-backend": "^3.0.1",
"js-md5": "^0.8.3",
"jwt-decode": "^4.0.0",
"marked": "^15.0.12",
"next": "15.2.4",
"posthog-js": "^1.246.0",
"react": "19.0.0",
@@ -81,6 +82,7 @@
},
"devDependencies": {
"@opennextjs/cloudflare": "^0.5.12",
"@tailwindcss/typography": "^0.5.16",
"@tauri-apps/cli": "2.5.0",
"@types/cors": "^2.8.17",
"@types/cssbeautify": "^0.3.5",
@@ -2,6 +2,7 @@ import clsx from 'clsx';
import React, { useEffect, useRef } from 'react';
import { useNotebookStore } from '@/store/notebookStore';
import { useTranslation } from '@/hooks/useTranslation';
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
import { TextSelection } from '@/utils/sel';
import { md5Fingerprint } from '@/utils/md5';
import { BookNote } from '@/types/book';
@@ -24,6 +25,7 @@ const NoteEditor: React.FC<NoteEditorProps> = ({ onSave, onEdit }) => {
} = useNotebookStore();
const editorRef = useRef<HTMLTextAreaElement>(null);
const [note, setNote] = React.useState('');
const separatorWidth = useResponsiveSize(3);
useEffect(() => {
if (editorRef.current) {
@@ -125,10 +127,15 @@ const NoteEditor: React.FC<NoteEditorProps> = ({ onSave, onEdit }) => {
></textarea>
</div>
</div>
<div className='flex items-start pt-2'>
<div className='mr-2 min-h-full self-stretch border-l-2 border-gray-300'></div>
<div className='content font-size-sm line-clamp-3 py-2'>
<span className='content font-size-xs inline text-gray-500'>{getAnnotationText()}</span>
<div className='flex items-center pt-2'>
<div
className='me-2 mt-0.5 min-h-full self-stretch rounded-xl bg-gray-300'
style={{
minWidth: `${separatorWidth}px`,
}}
></div>
<div className='content font-size-sm line-clamp-3'>
<span className='content font-size-xs text-gray-500'>{getAnnotationText()}</span>
</div>
</div>
<div className='flex justify-end p-2' dir='ltr'>
@@ -1,6 +1,7 @@
import clsx from 'clsx';
import React from 'react';
import { marked } from 'marked';
import { useEnv } from '@/context/EnvContext';
import { BookNote } from '@/types/book';
import { useSettingsStore } from '@/store/settingsStore';
@@ -8,8 +9,9 @@ import { useReaderStore } from '@/store/readerStore';
import { useNotebookStore } from '@/store/notebookStore';
import { useBookDataStore } from '@/store/bookDataStore';
import { useTranslation } from '@/hooks/useTranslation';
import useScrollToItem from '../../hooks/useScrollToItem';
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
import { eventDispatcher } from '@/utils/event';
import useScrollToItem from '../../hooks/useScrollToItem';
interface BooknoteItemProps {
bookKey: string;
@@ -23,6 +25,7 @@ const BooknoteItem: React.FC<BooknoteItemProps> = ({ bookKey, item }) => {
const { getConfig, saveConfig, updateBooknotes } = useBookDataStore();
const { getProgress, getView, getViewsById } = useReaderStore();
const { setNotebookEditAnnotation, setNotebookVisible } = useNotebookStore();
const separatorWidth = useResponsiveSize(3);
const { text, cfi, note } = item;
const progress = getProgress(bookKey);
@@ -82,15 +85,21 @@ const BooknoteItem: React.FC<BooknoteItemProps> = ({ bookKey, item }) => {
}
>
{item.note && (
<span className='content font-size-sm font-normal' dir='auto'>
{item.note}
</span>
<div className='content prose prose-sm'
dir='auto'
dangerouslySetInnerHTML={{ __html: marked.parse(item.note) }}>
</div>
)}
<div className='flex items-start'>
{item.note && (
<div className='my-1 me-2 min-h-full self-stretch border-l-2 border-gray-300'></div>
<div
className='me-2 mt-2.5 min-h-full self-stretch rounded-xl bg-gray-300'
style={{
minWidth: `${separatorWidth}px`,
}}
></div>
)}
<div className={clsx('content font-size-sm line-clamp-3', item.note && 'my-2')}>
<div className={clsx('content font-size-sm line-clamp-3', item.note && 'mt-2')}>
<span
className={clsx(
'inline',
@@ -1,4 +1,5 @@
import { useEffect, useRef, useState } from 'react';
import { useEnv } from '@/context/EnvContext';
import { useReaderStore } from '@/store/readerStore';
import { useBookDataStore } from '@/store/bookDataStore';
import { getOSPlatform } from '@/utils/misc';
@@ -11,6 +12,7 @@ export const useTextSelector = (
setSelection: React.Dispatch<React.SetStateAction<TextSelection | null>>,
handleDismissPopup: () => void,
) => {
const { appService } = useEnv();
const { getBookData } = useBookDataStore();
const { getView, getViewSettings } = useReaderStore();
const view = getView(bookKey);
@@ -109,10 +111,15 @@ export const useTextSelector = (
};
const handleScroll = () => {
// Prevent the container from scrolling when text is selected in paginated mode
// This is a workaround for the issue #873
// FIXME: this is a workaround for issue #873
// TODO: support text selection across pages
const viewSettings = getViewSettings(bookKey);
if (!viewSettings?.scrolled && view?.renderer?.containerPosition && selectionPosition.current) {
if (
appService?.isAndroidApp &&
!viewSettings?.scrolled &&
view?.renderer?.containerPosition &&
selectionPosition.current
) {
console.warn('Keep container position', selectionPosition.current);
view.renderer.containerPosition = selectionPosition.current;
}
+2 -1
View File
@@ -1,6 +1,7 @@
import type { Config } from 'tailwindcss';
import { themes } from './src/styles/themes';
import daisyui from 'daisyui';
import typography from '@tailwindcss/typography';
const config: Config = {
content: [
@@ -23,7 +24,7 @@ const config: Config = {
},
},
},
plugins: [daisyui],
plugins: [daisyui, typography],
daisyui: {
themes: themes.reduce(
(acc, { name, colors }) => {