Update localization messages for Chinese, English, and Japanese languages
This commit is contained in:
		
							parent
							
								
									36c1cae5fb
								
							
						
					
					
						commit
						9eaa0c9d66
					
				| @ -1,5 +1,7 @@ | |||||||
| { | { | ||||||
|     "tooltip": { |     "tooltip": { | ||||||
|         "embed": "It may take a few minutes to embed the page. Please wait..." |         "embed": "It may take a few minutes to embed the page. Please wait...", | ||||||
|  |         "clear": "Erase chat history", | ||||||
|  |         "history": "Chat history" | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -1,14 +1,12 @@ | |||||||
| import { SaveButton } from "@/components/Common/SaveButton" | import { SaveButton } from "@/components/Common/SaveButton" | ||||||
| import { getSearchSettings, setSearchSettings } from "@/services/search" |  | ||||||
| import { getTTSSettings, setTTSSettings } from "@/services/tts" | import { getTTSSettings, setTTSSettings } from "@/services/tts" | ||||||
| import { useWebUI } from "@/store/webui" | import { useWebUI } from "@/store/webui" | ||||||
| import { SUPPORTED_SERACH_PROVIDERS } from "@/utils/search-provider" |  | ||||||
| import { useForm } from "@mantine/form" | import { useForm } from "@mantine/form" | ||||||
| import { useQuery, useQueryClient } from "@tanstack/react-query" | import { useQuery } from "@tanstack/react-query" | ||||||
| import { Select, Skeleton, Switch, InputNumber } from "antd" | import { Select, Skeleton, Switch } from "antd" | ||||||
| import { useTranslation } from "react-i18next" | import { useTranslation } from "react-i18next" | ||||||
| 
 | 
 | ||||||
| export const TTSModeSettings = ({ hideTitle }: { hideTitle?: boolean }) => { | export const TTSModeSettings = ({ hideBorder }: { hideBorder?: boolean }) => { | ||||||
|   const { t } = useTranslation("settings") |   const { t } = useTranslation("settings") | ||||||
|   const { setTTSEnabled } = useWebUI() |   const { setTTSEnabled } = useWebUI() | ||||||
| 
 | 
 | ||||||
| @ -36,14 +34,14 @@ export const TTSModeSettings = ({ hideTitle }: { hideTitle?: boolean }) => { | |||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|     <div> |     <div> | ||||||
|       {!hideTitle && ( |       <div className="mb-5"> | ||||||
|         <div className="mb-5"> |         <h2 className="text-base font-semibold leading-7 text-gray-900 dark:text-white"> | ||||||
|           <h2 className="text-base font-semibold leading-7 text-gray-900 dark:text-white"> |           {t("generalSettings.tts.heading")} | ||||||
|             {t("generalSettings.tts.heading")} |         </h2> | ||||||
|           </h2> |         {!hideBorder && ( | ||||||
|           <div className="border border-b border-gray-200 dark:border-gray-600 mt-3"></div> |           <div className="border border-b border-gray-200 dark:border-gray-600 mt-3"></div> | ||||||
|         </div> |         )} | ||||||
|       )} |       </div> | ||||||
|       <form |       <form | ||||||
|         onSubmit={form.onSubmit(async (values) => { |         onSubmit={form.onSubmit(async (values) => { | ||||||
|           await setTTSSettings(values) |           await setTTSSettings(values) | ||||||
|  | |||||||
| @ -2,10 +2,12 @@ import React from "react" | |||||||
| import { PlaygroundMessage } from "~/components/Common/Playground/Message" | import { PlaygroundMessage } from "~/components/Common/Playground/Message" | ||||||
| import { useMessage } from "~/hooks/useMessage" | import { useMessage } from "~/hooks/useMessage" | ||||||
| import { EmptySidePanel } from "../Chat/empty" | import { EmptySidePanel } from "../Chat/empty" | ||||||
|  | import { useWebUI } from "@/store/webui" | ||||||
| 
 | 
 | ||||||
| export const SidePanelBody = () => { | export const SidePanelBody = () => { | ||||||
|   const { messages, streaming } = useMessage() |   const { messages, streaming } = useMessage() | ||||||
|   const divRef = React.useRef<HTMLDivElement>(null) |   const divRef = React.useRef<HTMLDivElement>(null) | ||||||
|  |   const {ttsEnabled} = useWebUI() | ||||||
|   React.useEffect(() => { |   React.useEffect(() => { | ||||||
|     if (divRef.current) { |     if (divRef.current) { | ||||||
|       divRef.current.scrollIntoView({ behavior: "smooth" }) |       divRef.current.scrollIntoView({ behavior: "smooth" }) | ||||||
| @ -27,6 +29,7 @@ export const SidePanelBody = () => { | |||||||
|           onRengerate={() => {}} |           onRengerate={() => {}} | ||||||
|           isProcessing={streaming} |           isProcessing={streaming} | ||||||
|           hideEditAndRegenerate |           hideEditAndRegenerate | ||||||
|  |           isTTSEnabled={ttsEnabled} | ||||||
|         /> |         /> | ||||||
|       ))} |       ))} | ||||||
|       <div className="w-full h-32 md:h-48 flex-shrink-0"></div> |       <div className="w-full h-32 md:h-48 flex-shrink-0"></div> | ||||||
|  | |||||||
| @ -8,7 +8,8 @@ import { | |||||||
|   getAllModels, |   getAllModels, | ||||||
|   getOllamaURL, |   getOllamaURL, | ||||||
|   isOllamaRunning, |   isOllamaRunning, | ||||||
|   setOllamaURL as saveOllamaURL |   setOllamaURL as saveOllamaURL, | ||||||
|  |   fetchChatModels | ||||||
| } from "~/services/ollama" | } from "~/services/ollama" | ||||||
| 
 | 
 | ||||||
| export const EmptySidePanel = () => { | export const EmptySidePanel = () => { | ||||||
| @ -24,7 +25,7 @@ export const EmptySidePanel = () => { | |||||||
|     queryFn: async () => { |     queryFn: async () => { | ||||||
|       const ollamaURL = await getOllamaURL() |       const ollamaURL = await getOllamaURL() | ||||||
|       const isOk = await isOllamaRunning() |       const isOk = await isOllamaRunning() | ||||||
|       const models = await getAllModels({ returnEmpty: false }) |       const models = await fetchChatModels({ returnEmpty: false }) | ||||||
| 
 | 
 | ||||||
|       return { |       return { | ||||||
|         isOk, |         isOk, | ||||||
|  | |||||||
| @ -2,10 +2,10 @@ import logoImage from "~/assets/icon.png" | |||||||
| import { useMessage } from "~/hooks/useMessage" | import { useMessage } from "~/hooks/useMessage" | ||||||
| import { Link } from "react-router-dom" | import { Link } from "react-router-dom" | ||||||
| import { Tooltip } from "antd" | import { Tooltip } from "antd" | ||||||
| import { BoxesIcon, CogIcon, RefreshCcw } from "lucide-react" | import { BoxesIcon, CogIcon, EraserIcon, HistoryIcon } from "lucide-react" | ||||||
| import { useTranslation } from "react-i18next" | import { useTranslation } from "react-i18next" | ||||||
| export const SidepanelHeader = () => { | export const SidepanelHeader = () => { | ||||||
|   const { clearChat, isEmbedding } = useMessage() |   const { clearChat, isEmbedding, messages } = useMessage() | ||||||
|   const { t } = useTranslation(["sidepanel", "common"]) |   const { t } = useTranslation(["sidepanel", "common"]) | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
| @ -25,13 +25,22 @@ export const SidepanelHeader = () => { | |||||||
|             <BoxesIcon className="h-5 w-5 text-gray-500 dark:text-gray-400 animate-bounce animate-infinite" /> |             <BoxesIcon className="h-5 w-5 text-gray-500 dark:text-gray-400 animate-bounce animate-infinite" /> | ||||||
|           </Tooltip> |           </Tooltip> | ||||||
|         ) : null} |         ) : null} | ||||||
|         <button |         {messages.length > 0 && ( | ||||||
|           onClick={() => { |           <Tooltip title={t("tooltip.clear")}> | ||||||
|             clearChat() |             <button | ||||||
|           }} |               onClick={() => { | ||||||
|           className="flex items-center space-x-1 focus:outline-none focus-visible:ring-2 focus-visible:ring-pink-700"> |                 clearChat() | ||||||
|           <RefreshCcw className="h-5 w-5 text-gray-500 dark:text-gray-400" /> |               }} | ||||||
|         </button> |               className="flex items-center space-x-1 focus:outline-none focus-visible:ring-2 focus-visible:ring-pink-700"> | ||||||
|  |               <EraserIcon className="h-5 w-5 text-gray-500 dark:text-gray-400" /> | ||||||
|  |             </button> | ||||||
|  |           </Tooltip> | ||||||
|  |         )} | ||||||
|  |         {/* <Tooltip title={t("tooltip.history")}> | ||||||
|  |           <Link to="/history"> | ||||||
|  |             <HistoryIcon className="h-5 w-5 text-gray-500 dark:text-gray-400" /> | ||||||
|  |           </Link> | ||||||
|  |         </Tooltip> */} | ||||||
|         <Link to="/settings"> |         <Link to="/settings"> | ||||||
|           <CogIcon className="h-5 w-5 text-gray-500 dark:text-gray-400" /> |           <CogIcon className="h-5 w-5 text-gray-500 dark:text-gray-400" /> | ||||||
|         </Link> |         </Link> | ||||||
|  | |||||||
| @ -22,6 +22,7 @@ import { useMessage } from "~/hooks/useMessage" | |||||||
| import { MoonIcon, SunIcon } from "lucide-react" | import { MoonIcon, SunIcon } from "lucide-react" | ||||||
| import { useTranslation } from "react-i18next" | import { useTranslation } from "react-i18next" | ||||||
| import { useI18n } from "@/hooks/useI18n" | import { useI18n } from "@/hooks/useI18n" | ||||||
|  | import { TTSModeSettings } from "@/components/Option/Settings/tts-mode" | ||||||
| 
 | 
 | ||||||
| export const SettingsBody = () => { | export const SettingsBody = () => { | ||||||
|   const { t } = useTranslation("settings") |   const { t } = useTranslation("settings") | ||||||
| @ -285,6 +286,9 @@ export const SettingsBody = () => { | |||||||
|           </div> |           </div> | ||||||
|         </Form> |         </Form> | ||||||
|       </div> |       </div> | ||||||
|  |       <div className="border border-gray-300 dark:border-gray-700 rounded p-4 bg-white dark:bg-[#171717]"> | ||||||
|  |         <TTSModeSettings hideBorder /> | ||||||
|  |       </div> | ||||||
|       <div className="border border-gray-300 dark:border-gray-700 rounded p-4 bg-white dark:bg-[#171717]"> |       <div className="border border-gray-300 dark:border-gray-700 rounded p-4 bg-white dark:bg-[#171717]"> | ||||||
|         <h2 className="text-md mb-4 font-semibold dark:text-white"> |         <h2 className="text-md mb-4 font-semibold dark:text-white"> | ||||||
|           {t("generalSettings.settings.language.label")}{" "} |           {t("generalSettings.settings.language.label")}{" "} | ||||||
|  | |||||||
| @ -1,4 +1,6 @@ | |||||||
| import { getOllamaURL, isOllamaRunning } from "../services/ollama" | import { getOllamaURL, isOllamaRunning } from "../services/ollama" | ||||||
|  | import { Storage } from "@plasmohq/storage" | ||||||
|  | 
 | ||||||
| const progressHuman = (completed: number, total: number) => { | const progressHuman = (completed: number, total: number) => { | ||||||
|   return ((completed / total) * 100).toFixed(0) + "%" |   return ((completed / total) * 100).toFixed(0) + "%" | ||||||
| } | } | ||||||
| @ -75,6 +77,8 @@ const streamDownload = async (url: string, model: string) => { | |||||||
| } | } | ||||||
| export default defineBackground({ | export default defineBackground({ | ||||||
|   main() { |   main() { | ||||||
|  |     const storage = new Storage() | ||||||
|  | 
 | ||||||
|     chrome.runtime.onMessage.addListener(async (message) => { |     chrome.runtime.onMessage.addListener(async (message) => { | ||||||
|       if (message.type === "sidepanel") { |       if (message.type === "sidepanel") { | ||||||
|         chrome.tabs.query( |         chrome.tabs.query( | ||||||
| @ -139,8 +143,8 @@ export default defineBackground({ | |||||||
|           { active: true, currentWindow: true }, |           { active: true, currentWindow: true }, | ||||||
|           async (tabs) => { |           async (tabs) => { | ||||||
|             const tab = tabs[0] |             const tab = tabs[0] | ||||||
|             await chrome.sidePanel.open({ |             chrome.sidePanel.open({ | ||||||
|               windowId: tab.windowId! |               tabId: tab.id! | ||||||
|             }) |             }) | ||||||
|           } |           } | ||||||
|         ) |         ) | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ import { Document } from "@langchain/core/documents" | |||||||
| import { compile } from "html-to-text" | import { compile } from "html-to-text" | ||||||
| import { chromeRunTime } from "~/libs/runtime" | import { chromeRunTime } from "~/libs/runtime" | ||||||
| import { YtTranscript } from "yt-transcript" | import { YtTranscript } from "yt-transcript" | ||||||
|  | import { isWikipedia, parseWikipedia } from "@/parser/wiki" | ||||||
| 
 | 
 | ||||||
| const YT_REGEX = | const YT_REGEX = | ||||||
|   /(?:https?:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?v=)?([a-zA-Z0-9_-]+)/ |   /(?:https?:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?v=)?([a-zA-Z0-9_-]+)/ | ||||||
| @ -16,7 +17,6 @@ const getTranscript = async (url: string) => { | |||||||
|   return await ytTranscript.getTranscript() |   return await ytTranscript.getTranscript() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| export interface WebLoaderParams { | export interface WebLoaderParams { | ||||||
|   html: string |   html: string | ||||||
|   url: string |   url: string | ||||||
| @ -24,7 +24,8 @@ export interface WebLoaderParams { | |||||||
| 
 | 
 | ||||||
| export class PageAssistHtmlLoader | export class PageAssistHtmlLoader | ||||||
|   extends BaseDocumentLoader |   extends BaseDocumentLoader | ||||||
|   implements WebLoaderParams { |   implements WebLoaderParams | ||||||
|  | { | ||||||
|   html: string |   html: string | ||||||
|   url: string |   url: string | ||||||
| 
 | 
 | ||||||
| @ -47,7 +48,6 @@ export class PageAssistHtmlLoader | |||||||
|         text += item.text + " " |         text += item.text + " " | ||||||
|       }) |       }) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|       return [ |       return [ | ||||||
|         { |         { | ||||||
|           metadata: { |           metadata: { | ||||||
| @ -58,10 +58,23 @@ export class PageAssistHtmlLoader | |||||||
|         } |         } | ||||||
|       ] |       ] | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     let html = this.html | ||||||
|  | 
 | ||||||
|  |     if (isWikipedia(this.url)) { | ||||||
|  |       console.log("Wikipedia URL detected") | ||||||
|  |       html = parseWikipedia(html) | ||||||
|  |     }  | ||||||
|  |      | ||||||
|  |     // else if (isTwitter(this.url)) {
 | ||||||
|  |     //   console.log("Twitter URL detected")
 | ||||||
|  |     //   html = parseTweet(html, this.url)
 | ||||||
|  |     // }
 | ||||||
|  | 
 | ||||||
|     const htmlCompiler = compile({ |     const htmlCompiler = compile({ | ||||||
|       wordwrap: false |       wordwrap: false | ||||||
|     }) |     }) | ||||||
|     const text = htmlCompiler(this.html) |     const text = htmlCompiler(html) | ||||||
|     const metadata = { source: this.url } |     const metadata = { source: this.url } | ||||||
|     return [new Document({ pageContent: text, metadata })] |     return [new Document({ pageContent: text, metadata })] | ||||||
|   } |   } | ||||||
| @ -79,7 +92,6 @@ export class PageAssistHtmlLoader | |||||||
|         text += item.text + " " |         text += item.text + " " | ||||||
|       }) |       }) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|       return [ |       return [ | ||||||
|         { |         { | ||||||
|           metadata: { |           metadata: { | ||||||
| @ -92,7 +104,18 @@ export class PageAssistHtmlLoader | |||||||
|     } |     } | ||||||
|     await chromeRunTime(this.url) |     await chromeRunTime(this.url) | ||||||
|     const fetchHTML = await fetch(this.url) |     const fetchHTML = await fetch(this.url) | ||||||
|     const html = await fetchHTML.text() |     let html = await fetchHTML.text() | ||||||
|  | 
 | ||||||
|  |     if (isWikipedia(this.url)) { | ||||||
|  |       console.log("Wikipedia URL detected") | ||||||
|  |       html = parseWikipedia(await fetchHTML.text()) | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // else if (isTwitter(this.url)) {
 | ||||||
|  |     //   console.log("Twitter URL detected")
 | ||||||
|  |     //   html = parseTweet(await fetchHTML.text(), this.url)
 | ||||||
|  |     // }
 | ||||||
|  | 
 | ||||||
|     const htmlCompiler = compile({ |     const htmlCompiler = compile({ | ||||||
|       wordwrap: false, |       wordwrap: false, | ||||||
|       selectors: [ |       selectors: [ | ||||||
|  | |||||||
							
								
								
									
										90
									
								
								src/parser/twitter.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								src/parser/twitter.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,90 @@ | |||||||
|  | import * as cheerio from "cheerio" | ||||||
|  | 
 | ||||||
|  | export const isTweet = (url: string) => { | ||||||
|  |   const TWEET_REGEX = /twitter\.com\/[a-zA-Z0-9_]+\/status\/[0-9]+/g | ||||||
|  |   return TWEET_REGEX.test(url) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const isTwitterProfile = (url: string) => { | ||||||
|  |   const PROFILE_REGEX = /twitter\.com\/[a-zA-Z0-9_]+/g | ||||||
|  |   return PROFILE_REGEX.test(url) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const isTwitterTimeline = (url: string) => { | ||||||
|  |   const TIMELINE_REGEX = /twitter\.com\/home/g | ||||||
|  |   return TIMELINE_REGEX.test(url) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const isTwitter = (url: string) => { | ||||||
|  |   return isTweet(url) || isTwitterProfile(url) || isTwitterTimeline(url) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const isTwitterNotification = (url: string) => { | ||||||
|  |   const NOTIFICATION_REGEX = /twitter\.com\/notifications/g | ||||||
|  |   return NOTIFICATION_REGEX.test(url) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const parseTweet = (html: string, url: string) => { | ||||||
|  |   if (!html) { | ||||||
|  |     return "" | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const $ = cheerio.load(html) | ||||||
|  | 
 | ||||||
|  |   if (isTweet(url)) { | ||||||
|  |     console.log("tweet") | ||||||
|  |     const tweet = $("div[data-testid='tweet']") | ||||||
|  |     const tweetContent = tweet.find("div[lang]") | ||||||
|  |     const tweetMedia = tweet.find("div[role='group']") | ||||||
|  |     const author = tweet.find("a[role='link']").text() | ||||||
|  |     const date = tweet.find("time").text() | ||||||
|  |     return `<div>${author} ${tweetContent.text()} ${tweetMedia.html()} ${date}</div>` | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (isTwitterTimeline(url)) { | ||||||
|  |     console.log("timeline") | ||||||
|  |     const timeline = $("div[data-testid='primaryColumn']") | ||||||
|  |     const timelineContent = timeline.find("div[data-testid='tweet']") | ||||||
|  |     console.log(timelineContent.html()) | ||||||
|  |     const tweet = timelineContent | ||||||
|  |       .map((i, el) => { | ||||||
|  |         const author = $(el).find("a[role='link']").text() | ||||||
|  |         const content = $(el).find("div[lang]").text() | ||||||
|  |         const media = $(el).find("div[role='group']").html() | ||||||
|  |         const date = $(el).find("time").text() | ||||||
|  |         return `<div>${author} ${content} ${media} ${date}</div>` | ||||||
|  |       }) | ||||||
|  |       .get() | ||||||
|  |       .join("") | ||||||
|  |     console.log(tweet) | ||||||
|  |     return `<div>${tweet}</div>` | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (isTwitterNotification(url)) { | ||||||
|  |     console.log("notification") | ||||||
|  |     const notification = $("div[data-testid='primaryColumn']") | ||||||
|  |     const notificationContent = notification.find("div[data-testid='tweet']") | ||||||
|  |     return `<div>${notificationContent.html()}</div>` | ||||||
|  |   } | ||||||
|  |   if (isTwitterProfile(url)) { | ||||||
|  |     console.log("profile") | ||||||
|  |     const profile = $("div[data-testid='primaryColumn']") | ||||||
|  |     const profileContent = profile.find( | ||||||
|  |       "div[data-testid='UserProfileHeader_Items']" | ||||||
|  |     ) | ||||||
|  |     const profileTweets = profile.find("div[data-testid='tweet']") | ||||||
|  |     return `<div>${profileContent.html()}</div><div>${profileTweets.html()}</div>` | ||||||
|  |   } | ||||||
|  |   console.log("no match") | ||||||
|  |   const timeline = $("div[data-testid='primaryColumn']") | ||||||
|  |   const timelineContent = timeline.find("div[data-testid='tweet']") | ||||||
|  |   const tweet = timelineContent.map((i, el) => { | ||||||
|  |     const author = $(el).find("a[role='link']").text() | ||||||
|  |     const content = $(el).find("div[lang]").text() | ||||||
|  |     const media = $(el).find("div[role='group']").html() | ||||||
|  |     const date = $(el).find("time").text() | ||||||
|  |     return `<div>${author} ${content} ${media} ${date}</div>` | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   return `<div>${tweet}</div>` | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								src/parser/wiki.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/parser/wiki.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | import * as cheerio from "cheerio" | ||||||
|  | 
 | ||||||
|  | export const isWikipedia = (url: string) => { | ||||||
|  |   const WIKI_REGEX = /wikipedia\.org\/wiki\//g | ||||||
|  |   return WIKI_REGEX.test(url) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const parseWikipedia = (html: string) => { | ||||||
|  |   if (!html) { | ||||||
|  |     return "" | ||||||
|  |   } | ||||||
|  |   const $ = cheerio.load(html) | ||||||
|  |   const title = $("h1#firstHeading") | ||||||
|  |   const content = $("#mw-content-text") | ||||||
|  |   content?.find("sup.reference")?.remove() | ||||||
|  |   content?.find("div.thumb")?.remove() | ||||||
|  |   content?.find("div.reflist")?.remove() | ||||||
|  |   content?.find("div.navbox")?.remove() | ||||||
|  |   content?.find("table.infobox")?.remove() | ||||||
|  |   content?.find("div.sister-wikipedia")?.remove() | ||||||
|  |   content?.find("div.sister-projects")?.remove() | ||||||
|  |   content?.find("div.metadata")?.remove() | ||||||
|  |   content?.find("div.vertical-navbox")?.remove() | ||||||
|  |   content?.find("div.toc")?.remove() | ||||||
|  |   const newHtml = content?.html() | ||||||
|  | 
 | ||||||
|  |   return `<div>TITLE: ${title?.text()}</div><div>${newHtml}</div>` | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user