Update localization messages for Chinese, English, and Japanese languages
This commit is contained in:
		
							parent
							
								
									36c1cae5fb
								
							
						
					
					
						commit
						9eaa0c9d66
					
				| @ -1,5 +1,7 @@ | ||||
| { | ||||
|     "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 { getSearchSettings, setSearchSettings } from "@/services/search" | ||||
| import { getTTSSettings, setTTSSettings } from "@/services/tts" | ||||
| import { useWebUI } from "@/store/webui" | ||||
| import { SUPPORTED_SERACH_PROVIDERS } from "@/utils/search-provider" | ||||
| import { useForm } from "@mantine/form" | ||||
| import { useQuery, useQueryClient } from "@tanstack/react-query" | ||||
| import { Select, Skeleton, Switch, InputNumber } from "antd" | ||||
| import { useQuery } from "@tanstack/react-query" | ||||
| import { Select, Skeleton, Switch } from "antd" | ||||
| import { useTranslation } from "react-i18next" | ||||
| 
 | ||||
| export const TTSModeSettings = ({ hideTitle }: { hideTitle?: boolean }) => { | ||||
| export const TTSModeSettings = ({ hideBorder }: { hideBorder?: boolean }) => { | ||||
|   const { t } = useTranslation("settings") | ||||
|   const { setTTSEnabled } = useWebUI() | ||||
| 
 | ||||
| @ -36,14 +34,14 @@ export const TTSModeSettings = ({ hideTitle }: { hideTitle?: boolean }) => { | ||||
| 
 | ||||
|   return ( | ||||
|     <div> | ||||
|       {!hideTitle && ( | ||||
|       <div className="mb-5"> | ||||
|         <h2 className="text-base font-semibold leading-7 text-gray-900 dark:text-white"> | ||||
|           {t("generalSettings.tts.heading")} | ||||
|         </h2> | ||||
|         {!hideBorder && ( | ||||
|           <div className="border border-b border-gray-200 dark:border-gray-600 mt-3"></div> | ||||
|         </div> | ||||
|         )} | ||||
|       </div> | ||||
|       <form | ||||
|         onSubmit={form.onSubmit(async (values) => { | ||||
|           await setTTSSettings(values) | ||||
|  | ||||
| @ -2,10 +2,12 @@ import React from "react" | ||||
| import { PlaygroundMessage } from "~/components/Common/Playground/Message" | ||||
| import { useMessage } from "~/hooks/useMessage" | ||||
| import { EmptySidePanel } from "../Chat/empty" | ||||
| import { useWebUI } from "@/store/webui" | ||||
| 
 | ||||
| export const SidePanelBody = () => { | ||||
|   const { messages, streaming } = useMessage() | ||||
|   const divRef = React.useRef<HTMLDivElement>(null) | ||||
|   const {ttsEnabled} = useWebUI() | ||||
|   React.useEffect(() => { | ||||
|     if (divRef.current) { | ||||
|       divRef.current.scrollIntoView({ behavior: "smooth" }) | ||||
| @ -27,6 +29,7 @@ export const SidePanelBody = () => { | ||||
|           onRengerate={() => {}} | ||||
|           isProcessing={streaming} | ||||
|           hideEditAndRegenerate | ||||
|           isTTSEnabled={ttsEnabled} | ||||
|         /> | ||||
|       ))} | ||||
|       <div className="w-full h-32 md:h-48 flex-shrink-0"></div> | ||||
|  | ||||
| @ -8,7 +8,8 @@ import { | ||||
|   getAllModels, | ||||
|   getOllamaURL, | ||||
|   isOllamaRunning, | ||||
|   setOllamaURL as saveOllamaURL | ||||
|   setOllamaURL as saveOllamaURL, | ||||
|   fetchChatModels | ||||
| } from "~/services/ollama" | ||||
| 
 | ||||
| export const EmptySidePanel = () => { | ||||
| @ -24,7 +25,7 @@ export const EmptySidePanel = () => { | ||||
|     queryFn: async () => { | ||||
|       const ollamaURL = await getOllamaURL() | ||||
|       const isOk = await isOllamaRunning() | ||||
|       const models = await getAllModels({ returnEmpty: false }) | ||||
|       const models = await fetchChatModels({ returnEmpty: false }) | ||||
| 
 | ||||
|       return { | ||||
|         isOk, | ||||
|  | ||||
| @ -2,10 +2,10 @@ import logoImage from "~/assets/icon.png" | ||||
| import { useMessage } from "~/hooks/useMessage" | ||||
| import { Link } from "react-router-dom" | ||||
| import { Tooltip } from "antd" | ||||
| import { BoxesIcon, CogIcon, RefreshCcw } from "lucide-react" | ||||
| import { BoxesIcon, CogIcon, EraserIcon, HistoryIcon } from "lucide-react" | ||||
| import { useTranslation } from "react-i18next" | ||||
| export const SidepanelHeader = () => { | ||||
|   const { clearChat, isEmbedding } = useMessage() | ||||
|   const { clearChat, isEmbedding, messages } = useMessage() | ||||
|   const { t } = useTranslation(["sidepanel", "common"]) | ||||
| 
 | ||||
|   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" /> | ||||
|           </Tooltip> | ||||
|         ) : null} | ||||
|         {messages.length > 0 && ( | ||||
|           <Tooltip title={t("tooltip.clear")}> | ||||
|             <button | ||||
|               onClick={() => { | ||||
|                 clearChat() | ||||
|               }} | ||||
|               className="flex items-center space-x-1 focus:outline-none focus-visible:ring-2 focus-visible:ring-pink-700"> | ||||
|           <RefreshCcw className="h-5 w-5 text-gray-500 dark:text-gray-400" /> | ||||
|               <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"> | ||||
|           <CogIcon className="h-5 w-5 text-gray-500 dark:text-gray-400" /> | ||||
|         </Link> | ||||
|  | ||||
| @ -22,6 +22,7 @@ import { useMessage } from "~/hooks/useMessage" | ||||
| import { MoonIcon, SunIcon } from "lucide-react" | ||||
| import { useTranslation } from "react-i18next" | ||||
| import { useI18n } from "@/hooks/useI18n" | ||||
| import { TTSModeSettings } from "@/components/Option/Settings/tts-mode" | ||||
| 
 | ||||
| export const SettingsBody = () => { | ||||
|   const { t } = useTranslation("settings") | ||||
| @ -285,6 +286,9 @@ export const SettingsBody = () => { | ||||
|           </div> | ||||
|         </Form> | ||||
|       </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]"> | ||||
|         <h2 className="text-md mb-4 font-semibold dark:text-white"> | ||||
|           {t("generalSettings.settings.language.label")}{" "} | ||||
|  | ||||
| @ -1,4 +1,6 @@ | ||||
| import { getOllamaURL, isOllamaRunning } from "../services/ollama" | ||||
| import { Storage } from "@plasmohq/storage" | ||||
| 
 | ||||
| const progressHuman = (completed: number, total: number) => { | ||||
|   return ((completed / total) * 100).toFixed(0) + "%" | ||||
| } | ||||
| @ -75,6 +77,8 @@ const streamDownload = async (url: string, model: string) => { | ||||
| } | ||||
| export default defineBackground({ | ||||
|   main() { | ||||
|     const storage = new Storage() | ||||
| 
 | ||||
|     chrome.runtime.onMessage.addListener(async (message) => { | ||||
|       if (message.type === "sidepanel") { | ||||
|         chrome.tabs.query( | ||||
| @ -139,8 +143,8 @@ export default defineBackground({ | ||||
|           { active: true, currentWindow: true }, | ||||
|           async (tabs) => { | ||||
|             const tab = tabs[0] | ||||
|             await chrome.sidePanel.open({ | ||||
|               windowId: tab.windowId! | ||||
|             chrome.sidePanel.open({ | ||||
|               tabId: tab.id! | ||||
|             }) | ||||
|           } | ||||
|         ) | ||||
|  | ||||
| @ -3,6 +3,7 @@ import { Document } from "@langchain/core/documents" | ||||
| import { compile } from "html-to-text" | ||||
| import { chromeRunTime } from "~/libs/runtime" | ||||
| import { YtTranscript } from "yt-transcript" | ||||
| import { isWikipedia, parseWikipedia } from "@/parser/wiki" | ||||
| 
 | ||||
| const YT_REGEX = | ||||
|   /(?: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() | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| export interface WebLoaderParams { | ||||
|   html: string | ||||
|   url: string | ||||
| @ -24,7 +24,8 @@ export interface WebLoaderParams { | ||||
| 
 | ||||
| export class PageAssistHtmlLoader | ||||
|   extends BaseDocumentLoader | ||||
|   implements WebLoaderParams { | ||||
|   implements WebLoaderParams | ||||
| { | ||||
|   html: string | ||||
|   url: string | ||||
| 
 | ||||
| @ -47,7 +48,6 @@ export class PageAssistHtmlLoader | ||||
|         text += item.text + " " | ||||
|       }) | ||||
| 
 | ||||
| 
 | ||||
|       return [ | ||||
|         { | ||||
|           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({ | ||||
|       wordwrap: false | ||||
|     }) | ||||
|     const text = htmlCompiler(this.html) | ||||
|     const text = htmlCompiler(html) | ||||
|     const metadata = { source: this.url } | ||||
|     return [new Document({ pageContent: text, metadata })] | ||||
|   } | ||||
| @ -79,7 +92,6 @@ export class PageAssistHtmlLoader | ||||
|         text += item.text + " " | ||||
|       }) | ||||
| 
 | ||||
| 
 | ||||
|       return [ | ||||
|         { | ||||
|           metadata: { | ||||
| @ -92,7 +104,18 @@ export class PageAssistHtmlLoader | ||||
|     } | ||||
|     await chromeRunTime(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({ | ||||
|       wordwrap: false, | ||||
|       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