diff --git a/README.md b/README.md index 8cac2e34..b6189afc 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ | **Translate with DeepL** | From a single sentence to the entire book—translate instantly with DeepL. | ✅ | | **Text-to-Speech (TTS) Support** | Enjoy smooth, multilingual narration—even within a single book. | ✅ | | **Library Management** | Organize, sort, and manage your entire ebook library. | ✅ | +| **Code Syntax Highlighting** | Read software manuals with rich coloring of code examples. | ✅ | ## Planned Features @@ -94,7 +95,7 @@ Stay tuned for continuous improvements and updates! Contributions and suggestion The Readest app is available for download! 🥳 🚀 -- macOS / iOS / iPadOS : Search for "Readest" on the [App Store][link-appstore], also available on TestFlight for beta test (send your Apple ID to readestapp@gmail.com to request access). +- macOS / iOS / iPadOS : Search for "Readest" on the [App Store][link-appstore], also available on TestFlight for beta test (send your Apple ID to to request access). - Windows / Linux / Android: Visit [https://readest.com][link-website] or the [Releases on GitHub][link-gh-releases]. - Web: Visit [https://web.readest.com][link-web-readest]. diff --git a/apps/readest-app/package.json b/apps/readest-app/package.json index a38e73a5..34e1ef6d 100644 --- a/apps/readest-app/package.json +++ b/apps/readest-app/package.json @@ -64,6 +64,7 @@ "cors": "^2.8.5", "cssbeautify": "^0.3.1", "foliate-js": "workspace:*", + "highlight.js": "^11.11.1", "i18next": "^24.2.0", "i18next-browser-languagedetector": "^8.0.2", "i18next-http-backend": "^3.0.1", diff --git a/apps/readest-app/src/app/reader/components/FoliateViewer.tsx b/apps/readest-app/src/app/reader/components/FoliateViewer.tsx index 20a0b74d..419113d3 100644 --- a/apps/readest-app/src/app/reader/components/FoliateViewer.tsx +++ b/apps/readest-app/src/app/reader/components/FoliateViewer.tsx @@ -32,6 +32,7 @@ import { isCJKLang } from '@/utils/lang'; import { transformContent } from '@/services/transformService'; import { lockScreenOrientation } from '@/utils/bridge'; import { useTextTranslation } from '../hooks/useTextTranslation'; +import { manageSyntaxHighlighting } from '@/utils/highlightjs'; const FoliateViewer: React.FC<{ bookKey: string; @@ -116,6 +117,11 @@ const FoliateViewer: React.FC<{ mountAdditionalFonts(detail.doc, isCJKLang(bookData.book?.primaryLanguage)); + // only call on load if we have highlighting turned on. + if (viewSettings.codeHighlighting) { + manageSyntaxHighlighting(detail.doc, viewSettings); + } + if (!detail.doc.isEventListenersAdded) { // listened events in iframes are posted to the main window // and then used by useMouseEvent and useTouchEvent diff --git a/apps/readest-app/src/app/reader/components/settings/ColorPanel.tsx b/apps/readest-app/src/app/reader/components/settings/ColorPanel.tsx index c444696f..bb156c94 100644 --- a/apps/readest-app/src/app/reader/components/settings/ColorPanel.tsx +++ b/apps/readest-app/src/app/reader/components/settings/ColorPanel.tsx @@ -19,6 +19,8 @@ import { useTranslation } from '@/hooks/useTranslation'; import { useSettingsStore } from '@/store/settingsStore'; import { useResponsiveSize } from '@/hooks/useResponsiveSize'; import { saveViewSettings } from '../../utils/viewSettingsHelper'; +import { CODE_LANGUAGES, CodeLanguage, manageSyntaxHighlighting } from '@/utils/highlightjs'; +import Select from '@/components/Select'; import ThemeEditor from './ThemeEditor'; const ColorPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { @@ -27,7 +29,7 @@ const ColorPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { useThemeStore(); const { envConfig } = useEnv(); const { settings, setSettings } = useSettingsStore(); - const { getViewSettings } = useReaderStore(); + const { getView, getViewSettings } = useReaderStore(); const viewSettings = getViewSettings(bookKey)!; const [invertImgColorInDark, setInvertImgColorInDark] = useState( viewSettings.invertImgColorInDark, @@ -36,9 +38,11 @@ const ColorPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { const iconSize16 = useResponsiveSize(16); const iconSize24 = useResponsiveSize(24); const [editTheme, setEditTheme] = useState(null); - const [customThems, setCustomThemes] = useState([]); + const [customThemes, setCustomThemes] = useState([]); const [showCustomThemeEditor, setShowCustomThemeEditor] = useState(false); const [overrideColor, setOverrideColor] = useState(viewSettings.overrideColor!); + const [codeHighlighting, setcodeHighlighting] = useState(viewSettings.codeHighlighting!); + const [codeLanguage, setCodeLanguage] = useState(viewSettings.codeLanguage!); useEffect(() => { if (invertImgColorInDark === viewSettings.invertImgColorInDark) return; @@ -52,6 +56,24 @@ const ColorPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [overrideColor]); + useEffect(() => { + let update = false; // check if we need to update syntax highlighting + if (codeHighlighting !== viewSettings.codeHighlighting) { + saveViewSettings(envConfig, bookKey, 'codeHighlighting', codeHighlighting); + update = true; + } + if (codeLanguage !== viewSettings.codeLanguage) { + saveViewSettings(envConfig, bookKey, 'codeLanguage', codeLanguage); + update = true; + } + if (!update) return; + const view = getView(bookKey); + if (!view) return; + const docs = view.renderer.getContents(); + docs.forEach(({ doc }) => manageSyntaxHighlighting(doc, viewSettings)); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [codeHighlighting, codeLanguage]); + useEffect(() => { const customThemes = settings.globalReadSettings.customThemes ?? []; setCustomThemes( @@ -157,7 +179,7 @@ const ColorPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {

{_('Theme Color')}

- {themes.concat(customThems).map(({ name, label, colors, isCustomizale }) => ( + {themes.concat(customThemes).map(({ name, label, colors, isCustomizale }) => (
+ +
+

{_('Code Highlighting')}

+
+
+
+ {_('Enable Code Highlighting')} + setcodeHighlighting(!codeHighlighting)} + /> +
+ +
+ {_('Language Selection')} +