feat: Add Chrome AI support
This commit is contained in:
@@ -6,11 +6,17 @@ import "property-information"
|
||||
import React from "react"
|
||||
import { CodeBlock } from "./CodeBlock"
|
||||
|
||||
export default function Markdown({ message }: { message: string }) {
|
||||
export default function Markdown({
|
||||
message,
|
||||
className = "prose break-words dark:prose-invert prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark"
|
||||
}: {
|
||||
message: string
|
||||
className?: string
|
||||
}) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ReactMarkdown
|
||||
className="prose break-words dark:prose-invert prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark"
|
||||
className={className}
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
components={{
|
||||
code({ node, inline, className, children, ...props }) {
|
||||
|
||||
@@ -3,9 +3,9 @@ import { Dropdown, Tooltip } from "antd"
|
||||
import { LucideBrain } from "lucide-react"
|
||||
import React from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { OllamaIcon } from "../Icons/Ollama"
|
||||
import { fetchChatModels } from "@/services/ollama"
|
||||
import { useMessage } from "@/hooks/useMessage"
|
||||
import { ProviderIcons } from "./ProviderIcon"
|
||||
|
||||
export const ModelSelect: React.FC = () => {
|
||||
const { t } = useTranslation("common")
|
||||
@@ -29,7 +29,10 @@ export const ModelSelect: React.FC = () => {
|
||||
label: (
|
||||
<div className="w-52 gap-2 text-lg truncate inline-flex line-clamp-3 items-center dark:border-gray-700">
|
||||
<div>
|
||||
<OllamaIcon className="h-6 w-6 text-gray-400" />
|
||||
<ProviderIcons
|
||||
provider={d?.provider}
|
||||
className="h-6 w-6 text-gray-400"
|
||||
/>
|
||||
</div>
|
||||
{d.name}
|
||||
</div>
|
||||
@@ -53,7 +56,7 @@ export const ModelSelect: React.FC = () => {
|
||||
trigger={["click"]}>
|
||||
<Tooltip title={t("selectAModel")}>
|
||||
<button type="button" className="dark:text-gray-300">
|
||||
<LucideBrain className="h-5 w-5"/>
|
||||
<LucideBrain className="h-5 w-5" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</Dropdown>
|
||||
|
||||
@@ -64,7 +64,11 @@ export const PlaygroundMessage = (props: Props) => {
|
||||
</div>
|
||||
<div className="flex w-[calc(100%-50px)] flex-col gap-3 lg:w-[calc(100%-115px)]">
|
||||
<span className="text-xs font-bold text-gray-800 dark:text-white">
|
||||
{props.isBot ? props.name : "You"}
|
||||
{props.isBot
|
||||
? props.name === "chrome::gemini-nano::page-assist"
|
||||
? "Gemini Nano"
|
||||
: props.name
|
||||
: "You"}
|
||||
</span>
|
||||
|
||||
{props.isBot &&
|
||||
|
||||
17
src/components/Common/ProviderIcon.tsx
Normal file
17
src/components/Common/ProviderIcon.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { ChromeIcon } from "lucide-react"
|
||||
import { OllamaIcon } from "../Icons/Ollama"
|
||||
|
||||
export const ProviderIcons = ({
|
||||
provider,
|
||||
className
|
||||
}: {
|
||||
provider: string
|
||||
className?: string
|
||||
}) => {
|
||||
switch (provider) {
|
||||
case "chrome":
|
||||
return <ChromeIcon className={className} />
|
||||
default:
|
||||
return <OllamaIcon className={className} />
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import { useMessageOption } from "~/hooks/useMessageOption"
|
||||
import { Select, Tooltip } from "antd"
|
||||
import { getAllPrompts } from "@/db"
|
||||
import { ShareBtn } from "~/components/Common/ShareBtn"
|
||||
import { ProviderIcons } from "../Common/ProviderIcon"
|
||||
type Props = {
|
||||
setSidebarOpen: (open: boolean) => void
|
||||
setOpenModelSettings: (open: boolean) => void
|
||||
@@ -132,7 +133,10 @@ export const Header: React.FC<Props> = ({
|
||||
<span
|
||||
key={model.model}
|
||||
className="flex flex-row gap-3 items-center truncate">
|
||||
<OllamaIcon className="w-5 h-5" />
|
||||
<ProviderIcons
|
||||
provider={model?.provider}
|
||||
className="w-5 h-5"
|
||||
/>
|
||||
<span className="truncate">{model.name}</span>
|
||||
</span>
|
||||
),
|
||||
|
||||
@@ -6,11 +6,13 @@ import {
|
||||
BlocksIcon,
|
||||
InfoIcon,
|
||||
CombineIcon,
|
||||
ChromeIcon
|
||||
} from "lucide-react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { Link, useLocation } from "react-router-dom"
|
||||
import { OllamaIcon } from "../Icons/Ollama"
|
||||
import { Tag } from "antd"
|
||||
import { BetaTag } from "../Common/Beta"
|
||||
|
||||
function classNames(...classes: string[]) {
|
||||
return classes.filter(Boolean).join(" ")
|
||||
@@ -20,10 +22,12 @@ const LinkComponent = (item: {
|
||||
href: string
|
||||
name: string | JSX.Element
|
||||
icon: any
|
||||
current: string
|
||||
current: string,
|
||||
beta?: boolean
|
||||
}) => {
|
||||
return (
|
||||
<li>
|
||||
<li className="inline-flex items-center">
|
||||
|
||||
<Link
|
||||
to={item.href}
|
||||
className={classNames(
|
||||
@@ -43,6 +47,9 @@ const LinkComponent = (item: {
|
||||
/>
|
||||
{item.name}
|
||||
</Link>
|
||||
{
|
||||
item.beta && <BetaTag />
|
||||
}
|
||||
</li>
|
||||
)
|
||||
}
|
||||
@@ -65,7 +72,7 @@ export const SettingsLayout = ({ children }: { children: React.ReactNode }) => {
|
||||
icon={OrbitIcon}
|
||||
current={location.pathname}
|
||||
/>
|
||||
<LinkComponent
|
||||
<LinkComponent
|
||||
href="/settings/rag"
|
||||
name={t("rag.title")}
|
||||
icon={CombineIcon}
|
||||
@@ -77,6 +84,15 @@ export const SettingsLayout = ({ children }: { children: React.ReactNode }) => {
|
||||
icon={OllamaIcon}
|
||||
current={location.pathname}
|
||||
/>
|
||||
{import.meta.env.BROWSER === "chrome" && (
|
||||
<LinkComponent
|
||||
href="/settings/chrome"
|
||||
name={t("chromeAiSettings.title")}
|
||||
icon={ChromeIcon}
|
||||
current={location.pathname}
|
||||
beta
|
||||
/>
|
||||
)}
|
||||
<LinkComponent
|
||||
href="/settings/model"
|
||||
name={t("manageModels.title")}
|
||||
|
||||
73
src/components/Option/Settings/chrome.tsx
Normal file
73
src/components/Option/Settings/chrome.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import { useStorage } from "@plasmohq/storage/hook"
|
||||
import { useQuery } from "@tanstack/react-query"
|
||||
import { Alert, Skeleton, Switch } from "antd"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { getChromeAISupported } from "@/utils/chrome"
|
||||
import Markdown from "@/components/Common/Markdown"
|
||||
|
||||
export const ChromeApp = () => {
|
||||
const { t } = useTranslation("chrome")
|
||||
const [chromeAIStatus, setChromeAIStatus] = useStorage(
|
||||
"chromeAIStatus",
|
||||
false
|
||||
)
|
||||
const [selectedModel, setSelectedModel] = useStorage("selectedModel")
|
||||
|
||||
const { status, data } = useQuery({
|
||||
queryKey: ["fetchChromeAIInfo"],
|
||||
queryFn: async () => {
|
||||
const data = await getChromeAISupported()
|
||||
return data
|
||||
}
|
||||
})
|
||||
return (
|
||||
<div className="flex flex-col space-y-3">
|
||||
{status === "pending" && <Skeleton paragraph={{ rows: 4 }} active />}
|
||||
{status === "success" && (
|
||||
<div className="flex flex-col space-y-6">
|
||||
<div>
|
||||
<div>
|
||||
<h2 className="text-base font-semibold leading-7 text-gray-900 dark:text-white">
|
||||
{t("heading")}
|
||||
</h2>
|
||||
<div className="border border-b border-gray-200 dark:border-gray-600 mt-3 mb-6"></div>
|
||||
</div>
|
||||
|
||||
<div className="flex mb-3 flex-row justify-between">
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<span className="text-gray-700 text-sm dark:text-neutral-50">
|
||||
{t("status.label")}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Switch
|
||||
disabled={data !== "success"}
|
||||
checked={chromeAIStatus}
|
||||
onChange={(value) => {
|
||||
setChromeAIStatus(value)
|
||||
if (
|
||||
!value &&
|
||||
selectedModel === "chrome::gemini-nano::page-assist"
|
||||
) {
|
||||
setSelectedModel(null)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{data !== "success" && (
|
||||
<div className="space-y-3">
|
||||
<Alert message={t(`error.${data}`)} type="error" showIcon />
|
||||
<div className=" w-full">
|
||||
<Markdown
|
||||
className="text-sm text-gray-700 dark:text-neutral-50 leading-7 text-justify"
|
||||
message={t("errorDescription")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
exportPageAssistData,
|
||||
importPageAssistData
|
||||
} from "@/libs/export-import"
|
||||
import { BetaTag } from "@/components/Common/Beta"
|
||||
import { useStorage } from "@plasmohq/storage/hook"
|
||||
|
||||
export const GeneralSettings = () => {
|
||||
@@ -87,7 +86,6 @@ export const GeneralSettings = () => {
|
||||
</div>
|
||||
<div className="flex flex-row justify-between">
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<BetaTag />
|
||||
<span className="text-gray-700 dark:text-neutral-50">
|
||||
{t("generalSettings.settings.copilotResumeLastChat.label")}
|
||||
</span>
|
||||
@@ -99,7 +97,6 @@ export const GeneralSettings = () => {
|
||||
</div>
|
||||
<div className="flex flex-row justify-between">
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<BetaTag />
|
||||
<span className="text-gray-700 dark:text-neutral-50">
|
||||
{t("generalSettings.settings.hideCurrentChatModelSettings.label")}
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user