Update localization messages for Chinese, English, and Japanese languages

This commit is contained in:
n4ze3m 2024-04-14 18:16:47 +05:30
parent 36c1cae5fb
commit 9eaa0c9d66
10 changed files with 194 additions and 32 deletions

View File

@ -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"
}
}

View File

@ -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>
<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)

View File

@ -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>

View File

@ -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,

View File

@ -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}
<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" />
</button>
{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">
<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>

View File

@ -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")}{" "}

View File

@ -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!
})
}
)

View File

@ -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
View 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
View 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>`
}