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