From c51c95b883642ca06e71e07d2885628c22affc6d Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Mon, 2 Jun 2025 17:35:09 +0800 Subject: [PATCH] feat: support note taking with markdown, closes #1097 (#1315) --- README.md | 1 + apps/readest-app/package.json | 2 + .../reader/components/notebook/NoteEditor.tsx | 15 +++++-- .../components/sidebar/BooknoteItem.tsx | 21 ++++++--- .../src/app/reader/hooks/useTextSelector.ts | 11 ++++- apps/readest-app/tailwind.config.ts | 3 +- pnpm-lock.yaml | 45 +++++++++++++++++++ 7 files changed, 85 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 61e1c5ac..8cac2e34 100644 --- a/README.md +++ b/README.md @@ -253,6 +253,7 @@ The following libraries and frameworks are used in this software: - [fflate](https://github.com/101arrowz/fflate), which is MIT licensed. - [PDF.js](https://github.com/mozilla/pdf.js), which is licensed under Apache License 2.0. - [daisyUI](https://github.com/saadeghi/daisyui), which is MIT licensed. +- [marked](https://github.com/markedjs/marked), which is MIT licensed. - [next.js](https://github.com/vercel/next.js), which is MIT licensed. - [react-icons](https://github.com/react-icons/react-icons), which has various open-source licenses. - [react](https://github.com/facebook/react), which is MIT licensed. diff --git a/apps/readest-app/package.json b/apps/readest-app/package.json index 5bb2f92c..c595ea7d 100644 --- a/apps/readest-app/package.json +++ b/apps/readest-app/package.json @@ -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", diff --git a/apps/readest-app/src/app/reader/components/notebook/NoteEditor.tsx b/apps/readest-app/src/app/reader/components/notebook/NoteEditor.tsx index eeeedf23..5b85d591 100644 --- a/apps/readest-app/src/app/reader/components/notebook/NoteEditor.tsx +++ b/apps/readest-app/src/app/reader/components/notebook/NoteEditor.tsx @@ -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 = ({ onSave, onEdit }) => { } = useNotebookStore(); const editorRef = useRef(null); const [note, setNote] = React.useState(''); + const separatorWidth = useResponsiveSize(3); useEffect(() => { if (editorRef.current) { @@ -125,10 +127,15 @@ const NoteEditor: React.FC = ({ onSave, onEdit }) => { > -
-
-
- {getAnnotationText()} +
+
+
+ {getAnnotationText()}
diff --git a/apps/readest-app/src/app/reader/components/sidebar/BooknoteItem.tsx b/apps/readest-app/src/app/reader/components/sidebar/BooknoteItem.tsx index 2df1fa96..69ebec27 100644 --- a/apps/readest-app/src/app/reader/components/sidebar/BooknoteItem.tsx +++ b/apps/readest-app/src/app/reader/components/sidebar/BooknoteItem.tsx @@ -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 = ({ 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 = ({ bookKey, item }) => { } > {item.note && ( - - {item.note} - +
+
)}
{item.note && ( -
+
)} -
+
>, 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; } diff --git a/apps/readest-app/tailwind.config.ts b/apps/readest-app/tailwind.config.ts index 5f07f2ca..b5ec30b2 100644 --- a/apps/readest-app/tailwind.config.ts +++ b/apps/readest-app/tailwind.config.ts @@ -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 }) => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0532056d..c069a0ac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -131,6 +131,9 @@ importers: jwt-decode: specifier: ^4.0.0 version: 4.0.0 + marked: + specifier: ^15.0.12 + version: 15.0.12 next: specifier: 15.2.4 version: 15.2.4(@babel/core@7.26.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -168,6 +171,9 @@ importers: '@opennextjs/cloudflare': specifier: ^0.5.12 version: 0.5.12(wrangler@4.4.0) + '@tailwindcss/typography': + specifier: ^0.5.16 + version: 0.5.16(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2))) '@tauri-apps/cli': specifier: 2.5.0 version: 2.5.0 @@ -2489,6 +2495,11 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@tailwindcss/typography@0.5.16': + resolution: {integrity: sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' + '@tauri-apps/api@2.5.0': resolution: {integrity: sha512-Ldux4ip+HGAcPUmuLT8EIkk6yafl5vK0P0c0byzAKzxJh7vxelVtdPONjfgTm96PbN24yjZNESY8CKo8qniluA==} @@ -4380,9 +4391,15 @@ packages: lodash-es@4.17.21: resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + lodash.castarray@4.4.0: + resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} + lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -4412,6 +4429,11 @@ packages: make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + marked@15.0.12: + resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==} + engines: {node: '>= 18'} + hasBin: true + matchmediaquery@0.4.2: resolution: {integrity: sha512-wrZpoT50ehYOudhDjt/YvUJc6eUzcdFPdmbizfgvswCKNHD1/OBOHYJpHie+HXpu6bSkEGieFMYk6VuutaiRfA==} @@ -4858,6 +4880,10 @@ packages: peerDependencies: postcss: ^8.1.0 + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + postcss-selector-parser@6.1.2: resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} @@ -9282,6 +9308,14 @@ snapshots: dependencies: tslib: 2.8.1 + '@tailwindcss/typography@0.5.16(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2)))': + dependencies: + lodash.castarray: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + postcss-selector-parser: 6.0.10 + tailwindcss: 3.4.17(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2)) + '@tauri-apps/api@2.5.0': {} '@tauri-apps/cli-darwin-arm64@2.5.0': @@ -11409,8 +11443,12 @@ snapshots: lodash-es@4.17.21: {} + lodash.castarray@4.4.0: {} + lodash.debounce@4.0.8: {} + lodash.isplainobject@4.0.6: {} + lodash.merge@4.6.2: {} lodash.sortby@4.7.0: {} @@ -11436,6 +11474,8 @@ snapshots: make-error@1.3.6: optional: true + marked@15.0.12: {} + matchmediaquery@0.4.2: dependencies: css-mediaquery: 0.1.2 @@ -11835,6 +11875,11 @@ snapshots: postcss: 8.4.49 thenby: 1.3.4 + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + postcss-selector-parser@6.1.2: dependencies: cssesc: 3.0.0