1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -40,3 +40,4 @@ keys.json | ||||
| 
 | ||||
| # typescript | ||||
| .tsbuildinfo | ||||
| .wxt | ||||
							
								
								
									
										43
									
								
								package.json
									
									
									
									
									
								
							
							
						
						| @ -5,9 +5,14 @@ | ||||
|   "description": "Use your locally running AI models to assist you in your web browsing.", | ||||
|   "author": "n4ze3m", | ||||
|   "scripts": { | ||||
|     "dev": "plasmo dev", | ||||
|     "build": "plasmo build", | ||||
|     "package": "plasmo package" | ||||
|     "dev": "wxt", | ||||
|     "dev:firefox": "wxt -b firefox", | ||||
|     "build": "wxt build", | ||||
|     "build:firefox": "wxt build -b firefox", | ||||
|     "zip": "wxt zip", | ||||
|     "zip:firefox": "wxt zip -b firefox", | ||||
|     "compile": "tsc --noEmit", | ||||
|     "postinstall": "wxt prepare" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@ant-design/cssinjs": "^1.18.4", | ||||
| @ -21,6 +26,7 @@ | ||||
|     "@tailwindcss/forms": "^0.5.7", | ||||
|     "@tailwindcss/typography": "^0.5.10", | ||||
|     "@tanstack/react-query": "^5.17.19", | ||||
|     "@vitejs/plugin-react": "^4.2.1", | ||||
|     "antd": "^5.13.3", | ||||
|     "axios": "^1.6.7", | ||||
|     "dayjs": "^1.11.10", | ||||
| @ -53,34 +59,7 @@ | ||||
|     "postcss": "^8.4.33", | ||||
|     "prettier": "3.2.4", | ||||
|     "tailwindcss": "^3.4.1", | ||||
|     "typescript": "5.3.3" | ||||
|   }, | ||||
|   "manifest": { | ||||
|     "host_permissions": [ | ||||
|       "http://*/*", | ||||
|       "https://*/*" | ||||
|     ], | ||||
|     "commands": { | ||||
|       "_execute_action": { | ||||
|         "suggested_key": { | ||||
|           "default": "Ctrl+Shift+L" | ||||
|         } | ||||
|       }, | ||||
|       "execute_side_panel": { | ||||
|         "description": "Open the side panel", | ||||
|         "suggested_key": { | ||||
|           "default": "Ctrl+Shift+P" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "permissions": [ | ||||
|       "storage", | ||||
|       "activeTab", | ||||
|       "scripting", | ||||
|       "declarativeNetRequest", | ||||
|       "action", | ||||
|       "unlimitedStorage", | ||||
|       "contextMenus" | ||||
|     ] | ||||
|     "typescript": "5.3.3", | ||||
|     "wxt": "^0.17.7" | ||||
|   } | ||||
| } | ||||
|  | ||||
| Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB | 
| @ -1,138 +0,0 @@ | ||||
| import { getOllamaURL, isOllamaRunning } from "~services/ollama" | ||||
| 
 | ||||
| export {} | ||||
| 
 | ||||
| const progressHuman = (completed: number, total: number) => { | ||||
|   return ((completed / total) * 100).toFixed(0) + "%" | ||||
| } | ||||
| 
 | ||||
| const clearBadge = () => { | ||||
|   chrome.action.setBadgeText({ text: "" }) | ||||
|   chrome.action.setTitle({ title: "" }) | ||||
| } | ||||
| 
 | ||||
| const streamDownload = async (url: string, model: string) => { | ||||
|   url += "/api/pull" | ||||
|   const response = await fetch(url, { | ||||
|     method: "POST", | ||||
|     headers: { | ||||
|       "Content-Type": "application/json" | ||||
|     }, | ||||
|     body: JSON.stringify({ model, stream: true }) | ||||
|   }) | ||||
| 
 | ||||
|   const reader = response.body?.getReader() | ||||
| 
 | ||||
|   const decoder = new TextDecoder() | ||||
| 
 | ||||
|   let isSuccess = true | ||||
|   while (true) { | ||||
|     const { done, value } = await reader.read() | ||||
| 
 | ||||
|     if (done) { | ||||
|       break | ||||
|     } | ||||
| 
 | ||||
|     const text = decoder.decode(value) | ||||
|     try { | ||||
|       const json = JSON.parse(text.trim()) as { | ||||
|         status: string | ||||
|         total?: number | ||||
|         completed?: number | ||||
|       } | ||||
|       if (json.total && json.completed) { | ||||
|         chrome.action.setBadgeText({ | ||||
|           text: progressHuman(json.completed, json.total) | ||||
|         }) | ||||
|         chrome.action.setBadgeBackgroundColor({ color: "#0000FF" }) | ||||
|       } else { | ||||
|         chrome.action.setBadgeText({ text: "🏋️♂️" }) | ||||
|         chrome.action.setBadgeBackgroundColor({ color: "#FFFFFF" }) | ||||
|       } | ||||
| 
 | ||||
|       chrome.action.setTitle({ title: json.status }) | ||||
| 
 | ||||
|       if (json.status === "success") { | ||||
|         isSuccess = true | ||||
|       } | ||||
|     } catch (e) { | ||||
|       console.error(e) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (isSuccess) { | ||||
|     chrome.action.setBadgeText({ text: "✅" }) | ||||
|     chrome.action.setBadgeBackgroundColor({ color: "#00FF00" }) | ||||
|     chrome.action.setTitle({ title: "Model pulled successfully" }) | ||||
|   } else { | ||||
|     chrome.action.setBadgeText({ text: "❌" }) | ||||
|     chrome.action.setBadgeBackgroundColor({ color: "#FF0000" }) | ||||
|     chrome.action.setTitle({ title: "Model pull failed" }) | ||||
|   } | ||||
| 
 | ||||
|   setTimeout(() => { | ||||
|     clearBadge() | ||||
|   }, 5000) | ||||
| } | ||||
| 
 | ||||
| chrome.runtime.onMessage.addListener(async (message) => { | ||||
|   if (message.type === "sidepanel") { | ||||
|     chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => { | ||||
|       const tab = tabs[0] | ||||
|       await chrome.sidePanel.open({ | ||||
|         tabId: tab.id | ||||
|       }) | ||||
|     }) | ||||
|   } else if (message.type === "pull_model") { | ||||
|     const ollamaURL = await getOllamaURL() | ||||
| 
 | ||||
|     const isRunning = await isOllamaRunning() | ||||
| 
 | ||||
|     if (!isRunning) { | ||||
|       chrome.action.setBadgeText({ text: "E" }) | ||||
|       chrome.action.setBadgeBackgroundColor({ color: "#FF0000" }) | ||||
|       chrome.action.setTitle({ title: "Ollama is not running" }) | ||||
|       setTimeout(() => { | ||||
|         clearBadge() | ||||
|       }, 5000) | ||||
|     } | ||||
| 
 | ||||
|     await streamDownload(ollamaURL, message.modelName) | ||||
|   } | ||||
| }) | ||||
| 
 | ||||
| chrome.action.onClicked.addListener((tab) => { | ||||
|   chrome.tabs.create({ url: chrome.runtime.getURL("options.html") }) | ||||
| }) | ||||
| 
 | ||||
| chrome.commands.onCommand.addListener((command) => { | ||||
|   switch (command) { | ||||
|     case "execute_side_panel": | ||||
|       chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => { | ||||
|         const tab = tabs[0] | ||||
|         await chrome.sidePanel.open({ | ||||
|           tabId: tab.id | ||||
|         }) | ||||
|       }) | ||||
|       break | ||||
|     default: | ||||
|       break | ||||
|   } | ||||
| }) | ||||
| 
 | ||||
| chrome.contextMenus.create({ | ||||
|   id: "open-side-panel-pa", | ||||
|   title: "Open Side Panel to Chat", | ||||
|   contexts: ["all"] | ||||
| }) | ||||
| 
 | ||||
| chrome.contextMenus.onClicked.addListener((info, tab) => { | ||||
|   if (info.menuItemId === "open-side-panel-pa") { | ||||
|     chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => { | ||||
|       const tab = tabs[0] | ||||
|       await chrome.sidePanel.open({ | ||||
|         tabId: tab.id | ||||
|       }) | ||||
|     }) | ||||
|   } | ||||
| }) | ||||
| @ -14,7 +14,7 @@ import { | ||||
|   RunnableMap, | ||||
|   RunnableSequence, | ||||
| } from "langchain/schema/runnable"; | ||||
| import type { ChatHistory } from "~store"; | ||||
| import type { ChatHistory } from "~/store"; | ||||
| type RetrievalChainInput = { | ||||
|   chat_history: string; | ||||
|   question: string; | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| import { useForm } from "@mantine/form" | ||||
| import React from "react" | ||||
| import useDynamicTextareaSize from "~hooks/useDynamicTextareaSize" | ||||
| import useDynamicTextareaSize from "~/hooks/useDynamicTextareaSize" | ||||
| 
 | ||||
| type Props = { | ||||
|   value: string | ||||
|  | ||||
| @ -1,13 +1,13 @@ | ||||
| import { Form, Image, Input, Modal, Tooltip, message } from "antd" | ||||
| import { Share } from "lucide-react" | ||||
| import { useState } from "react" | ||||
| import type { Message } from "~store/option" | ||||
| import type { Message } from "~/store/option" | ||||
| import Markdown from "./Markdown" | ||||
| import React from "react" | ||||
| import { useMutation } from "@tanstack/react-query" | ||||
| import { getPageShareUrl } from "~services/ollama" | ||||
| import { cleanUrl } from "~libs/clean-url" | ||||
| import { getUserId, saveWebshare } from "~libs/db" | ||||
| import { getPageShareUrl } from "~/services/ollama" | ||||
| import { cleanUrl } from "~/libs/clean-url" | ||||
| import { getUserId, saveWebshare } from "~/libs/db" | ||||
| 
 | ||||
| type Props = { | ||||
|   messages: Message[] | ||||
|  | ||||
| @ -4,8 +4,8 @@ import { useLocation, NavLink } from "react-router-dom" | ||||
| import { Sidebar } from "../Option/Sidebar" | ||||
| import { Drawer, Select, Tooltip } from "antd" | ||||
| import { useQuery } from "@tanstack/react-query" | ||||
| import { getAllModels } from "~services/ollama" | ||||
| import { useMessageOption } from "~hooks/useMessageOption" | ||||
| import { getAllModels } from "~/services/ollama" | ||||
| import { useMessageOption } from "~/hooks/useMessageOption" | ||||
| import { | ||||
|   ChevronLeft, | ||||
|   CogIcon, | ||||
| @ -15,8 +15,8 @@ import { | ||||
|   SquarePen, | ||||
|   ZapIcon | ||||
| } from "lucide-react" | ||||
| import { getAllPrompts } from "~libs/db" | ||||
| import { ShareBtn } from "~components/Common/ShareBtn" | ||||
| import { getAllPrompts } from "~/libs/db" | ||||
| import { ShareBtn } from "~/components/Common/ShareBtn" | ||||
| 
 | ||||
| export default function OptionLayout({ | ||||
|   children | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" | ||||
| import { Skeleton, Table, Tag, Tooltip, notification, Modal, Input } from "antd" | ||||
| import { bytePerSecondFormatter } from "~libs/byte-formater" | ||||
| import { deleteModel, getAllModels } from "~services/ollama" | ||||
| import { bytePerSecondFormatter } from "~/libs/byte-formater" | ||||
| import { deleteModel, getAllModels } from "~/services/ollama" | ||||
| import dayjs from "dayjs" | ||||
| import relativeTime from "dayjs/plugin/relativeTime" | ||||
| import { useState } from "react" | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| import React from "react" | ||||
| import { useMessageOption } from "~hooks/useMessageOption" | ||||
| import { useMessageOption } from "~/hooks/useMessageOption" | ||||
| import { PlaygroundEmpty } from "./PlaygroundEmpty" | ||||
| import { PlaygroundMessage } from "~components/Common/Playground/Message" | ||||
| import { PlaygroundMessage } from "~/components/Common/Playground/Message" | ||||
| 
 | ||||
| export const PlaygroundChat = () => { | ||||
|   const { | ||||
|  | ||||
| @ -5,7 +5,7 @@ import { | ||||
|   getOllamaURL, | ||||
|   isOllamaRunning, | ||||
|   setOllamaURL as saveOllamaURL | ||||
| } from "~services/ollama" | ||||
| } from "~/services/ollama" | ||||
| 
 | ||||
| export const PlaygroundEmpty = () => { | ||||
|   const [ollamaURL, setOllamaURL] = useState<string>("") | ||||
|  | ||||
| @ -1,16 +1,16 @@ | ||||
| import { useForm } from "@mantine/form" | ||||
| import { useMutation, useQueryClient } from "@tanstack/react-query" | ||||
| import React from "react" | ||||
| import useDynamicTextareaSize from "~hooks/useDynamicTextareaSize" | ||||
| import { toBase64 } from "~libs/to-base64" | ||||
| import { useMessageOption } from "~hooks/useMessageOption" | ||||
| import useDynamicTextareaSize from "~/hooks/useDynamicTextareaSize" | ||||
| import { toBase64 } from "~/libs/to-base64" | ||||
| import { useMessageOption } from "~/hooks/useMessageOption" | ||||
| import { Checkbox, Dropdown, Switch, Tooltip } from "antd" | ||||
| import { Image } from "antd" | ||||
| import { useSpeechRecognition } from "~hooks/useSpeechRecognition" | ||||
| import { useWebUI } from "~store/webui" | ||||
| import { defaultEmbeddingModelForRag } from "~services/ollama" | ||||
| import { useSpeechRecognition } from "~/hooks/useSpeechRecognition" | ||||
| import { useWebUI } from "~/store/webui" | ||||
| import { defaultEmbeddingModelForRag } from "~/services/ollama" | ||||
| import { ImageIcon, MicIcon, StopCircleIcon, X } from "lucide-react" | ||||
| import { getVariable } from "~utils/select-varaible" | ||||
| import { getVariable } from "~/utils/select-varaible" | ||||
| 
 | ||||
| type Props = { | ||||
|   dropedFile: File | undefined | ||||
|  | ||||
| @ -16,7 +16,7 @@ import { | ||||
|   getAllPrompts, | ||||
|   savePrompt, | ||||
|   updatePrompt | ||||
| } from "~libs/db" | ||||
| } from "~/libs/db" | ||||
| 
 | ||||
| export const PromptBody = () => { | ||||
|   const queryClient = useQueryClient() | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| import { useMutation, useQuery } from "@tanstack/react-query" | ||||
| import { Form, InputNumber, Select, Skeleton } from "antd" | ||||
| import { useState } from "react" | ||||
| import { SaveButton } from "~components/Common/SaveButton" | ||||
| import { SaveButton } from "~/components/Common/SaveButton" | ||||
| import { | ||||
|   defaultEmbeddingChunkOverlap, | ||||
|   defaultEmbeddingChunkSize, | ||||
| @ -10,7 +10,7 @@ import { | ||||
|   getOllamaURL, | ||||
|   saveForRag, | ||||
|   setOllamaURL as saveOllamaURL | ||||
| } from "~services/ollama" | ||||
| } from "~/services/ollama" | ||||
| import { SettingPrompt } from "./prompt" | ||||
| 
 | ||||
| export const SettingsOllama = () => { | ||||
|  | ||||
| @ -1,9 +1,9 @@ | ||||
| import { useQueryClient } from "@tanstack/react-query" | ||||
| import { useDarkMode } from "~hooks/useDarkmode" | ||||
| import { useMessageOption } from "~hooks/useMessageOption" | ||||
| import { PageAssitDatabase } from "~libs/db" | ||||
| import { useDarkMode } from "~/hooks/useDarkmode" | ||||
| import { useMessageOption } from "~/hooks/useMessageOption" | ||||
| import { PageAssitDatabase } from "~/libs/db" | ||||
| import { Select } from "antd" | ||||
| import { SUPPORTED_LANGUAGES } from "~utils/supporetd-languages" | ||||
| import { SUPPORTED_LANGUAGES } from "~/utils/supporetd-languages" | ||||
| import { MoonIcon, SunIcon } from "lucide-react" | ||||
| import { SearchModeSettings } from "./search-mode" | ||||
| 
 | ||||
|  | ||||
| @ -1,14 +1,14 @@ | ||||
| import { useQuery, useQueryClient } from "@tanstack/react-query" | ||||
| import { Skeleton, Radio, Form, Alert } from "antd" | ||||
| import React from "react" | ||||
| import { SaveButton } from "~components/Common/SaveButton" | ||||
| import { SaveButton } from "~/components/Common/SaveButton" | ||||
| import { | ||||
|   getWebSearchPrompt, | ||||
|   setSystemPromptForNonRagOption, | ||||
|   systemPromptForNonRagOption, | ||||
|   geWebSearchFollowUpPrompt, | ||||
|   setWebPrompts | ||||
| } from "~services/ollama" | ||||
| } from "~/services/ollama" | ||||
| 
 | ||||
| export const SettingPrompt = () => { | ||||
|   const [selectedValue, setSelectedValue] = React.useState<"normal" | "web">( | ||||
|  | ||||
| @ -3,7 +3,7 @@ import { Skeleton, Switch } from "antd" | ||||
| import { | ||||
|   getIsSimpleInternetSearch, | ||||
|   setIsSimpleInternetSearch | ||||
| } from "~services/ollama" | ||||
| } from "~/services/ollama" | ||||
| 
 | ||||
| export const SearchModeSettings = () => { | ||||
|   const { data, status } = useQuery({ | ||||
|  | ||||
| @ -1,10 +1,10 @@ | ||||
| import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" | ||||
| import { Form, Input, Skeleton, Table, Tooltip, message } from "antd" | ||||
| import { Trash2 } from "lucide-react" | ||||
| import { SaveButton } from "~components/Common/SaveButton" | ||||
| import { deleteWebshare, getAllWebshares, getUserId } from "~libs/db" | ||||
| import { getPageShareUrl, setPageShareUrl } from "~services/ollama" | ||||
| import { verifyPageShareURL } from "~utils/verify-page-share" | ||||
| import { SaveButton } from "~/components/Common/SaveButton" | ||||
| import { deleteWebshare, getAllWebshares, getUserId } from "~/libs/db" | ||||
| import { getPageShareUrl, setPageShareUrl } from "~/services/ollama" | ||||
| import { verifyPageShareURL } from "~/utils/verify-page-share" | ||||
| 
 | ||||
| export const OptionShareBody = () => { | ||||
|   const queryClient = useQueryClient() | ||||
|  | ||||
| @ -5,9 +5,9 @@ import { | ||||
|   formatToMessage, | ||||
|   deleteByHistoryId, | ||||
|   updateHistory | ||||
| } from "~libs/db" | ||||
| } from "~/libs/db" | ||||
| import { Empty, Skeleton } from "antd" | ||||
| import { useMessageOption } from "~hooks/useMessageOption" | ||||
| import { useMessageOption } from "~/hooks/useMessageOption" | ||||
| import { useState } from "react" | ||||
| import { PencilIcon, Trash2 } from "lucide-react" | ||||
| import { useNavigate } from "react-router-dom" | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| import React from "react" | ||||
| import { PlaygroundMessage } from "~components/Common/Playground/Message" | ||||
| import { useMessage } from "~hooks/useMessage" | ||||
| import { PlaygroundMessage } from "~/components/Common/Playground/Message" | ||||
| import { useMessage } from "~/hooks/useMessage" | ||||
| import { EmptySidePanel } from "../Chat/empty" | ||||
| 
 | ||||
| export const SidePanelBody = () => { | ||||
|  | ||||
| @ -2,13 +2,13 @@ import { useQuery } from "@tanstack/react-query" | ||||
| import { Select } from "antd" | ||||
| import { RotateCcw } from "lucide-react" | ||||
| import { useEffect, useState } from "react" | ||||
| import { useMessage } from "~hooks/useMessage" | ||||
| import { useMessage } from "~/hooks/useMessage" | ||||
| import { | ||||
|   getAllModels, | ||||
|   getOllamaURL, | ||||
|   isOllamaRunning, | ||||
|   setOllamaURL as saveOllamaURL | ||||
| } from "~services/ollama" | ||||
| } from "~/services/ollama" | ||||
| 
 | ||||
| export const EmptySidePanel = () => { | ||||
|   const [ollamaURL, setOllamaURL] = useState<string>("") | ||||
|  | ||||
| @ -1,13 +1,13 @@ | ||||
| import { useForm } from "@mantine/form" | ||||
| import { useMutation } from "@tanstack/react-query" | ||||
| import React from "react" | ||||
| import useDynamicTextareaSize from "~hooks/useDynamicTextareaSize" | ||||
| import { useMessage } from "~hooks/useMessage" | ||||
| import { toBase64 } from "~libs/to-base64" | ||||
| import useDynamicTextareaSize from "~/hooks/useDynamicTextareaSize" | ||||
| import { useMessage } from "~/hooks/useMessage" | ||||
| import { toBase64 } from "~/libs/to-base64" | ||||
| import { Checkbox, Dropdown, Image, Tooltip } from "antd" | ||||
| import { useSpeechRecognition } from "~hooks/useSpeechRecognition" | ||||
| import { useWebUI } from "~store/webui" | ||||
| import { defaultEmbeddingModelForRag } from "~services/ollama" | ||||
| import { useSpeechRecognition } from "~/hooks/useSpeechRecognition" | ||||
| import { useWebUI } from "~/store/webui" | ||||
| import { defaultEmbeddingModelForRag } from "~/services/ollama" | ||||
| import { ImageIcon, MicIcon, X } from "lucide-react" | ||||
| 
 | ||||
| type Props = { | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import logoImage from "data-base64:~assets/icon.png" | ||||
| import { useMessage } from "~hooks/useMessage" | ||||
| 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" | ||||
|  | ||||
| @ -12,13 +12,13 @@ import { | ||||
|   defaultEmbeddingChunkSize, | ||||
|   defaultEmbeddingModelForRag, | ||||
|   saveForRag | ||||
| } from "~services/ollama" | ||||
| } from "~/services/ollama" | ||||
| 
 | ||||
| import { Skeleton, Radio, Select, Form, InputNumber } from "antd" | ||||
| import { useDarkMode } from "~hooks/useDarkmode" | ||||
| import { SaveButton } from "~components/Common/SaveButton" | ||||
| import { SUPPORTED_LANGUAGES } from "~utils/supporetd-languages" | ||||
| import { useMessage } from "~hooks/useMessage" | ||||
| import { useDarkMode } from "~/hooks/useDarkmode" | ||||
| import { SaveButton } from "~/components/Common/SaveButton" | ||||
| import { SUPPORTED_LANGUAGES } from "~/utils/supporetd-languages" | ||||
| import { useMessage } from "~/hooks/useMessage" | ||||
| import { MoonIcon, SunIcon } from "lucide-react" | ||||
| 
 | ||||
| export const SettingsBody = () => { | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| import logoImage from "data-base64:~assets/icon.png" | ||||
| import { ChevronLeft } from "lucide-react" | ||||
| import { Link } from "react-router-dom" | ||||
| import logoImage from "~/assets/icon.png" | ||||
| 
 | ||||
| export const SidepanelSettingsHeader = () => { | ||||
|   return ( | ||||
|     <div className="flex px-3 justify-start gap-3 bg-white dark:bg-[#171717] border-b border-gray-300 dark:border-gray-700  py-4 items-center"> | ||||
|  | ||||
| @ -1,54 +0,0 @@ | ||||
| import type { PlasmoCSConfig } from "plasmo" | ||||
| 
 | ||||
| export const config: PlasmoCSConfig = { | ||||
|   matches: ["*://ollama.com/library/*"], | ||||
|   all_frames: true | ||||
| } | ||||
| 
 | ||||
| const downloadModel = async (modelName: string) => { | ||||
|   const ok = confirm( | ||||
|     `[Page Assist Extension] Do you want to pull ${modelName} model? This has nothing to do with Ollama.com website. The model will be pulled locally once you confirm.` | ||||
|   ) | ||||
|   if (ok) { | ||||
|     alert( | ||||
|       `[Page Assist Extension] Pulling ${modelName} model. For more details, check the extension icon.` | ||||
|     ) | ||||
| 
 | ||||
|     await chrome.runtime.sendMessage({ | ||||
|       type: "pull_model", | ||||
|       modelName | ||||
|     }) | ||||
|     return true | ||||
|   } | ||||
|   return false | ||||
| } | ||||
| 
 | ||||
| const downloadSVG = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="h-5 w-5 pageasssist-icon">
 | ||||
| <path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3" /> | ||||
| </svg> | ||||
| ` | ||||
| const codeDiv = document.querySelectorAll("div.language-none") | ||||
| 
 | ||||
| for (let i = 0; i < codeDiv.length; i++) { | ||||
|   const button = codeDiv[i].querySelector("button") | ||||
|   const command = codeDiv[i].querySelector("input") | ||||
|   if (button && command) { | ||||
|     const newButton = document.createElement("button") | ||||
|     newButton.innerHTML = downloadSVG | ||||
|     newButton.className = `border-l ${button.className}` | ||||
|     newButton.id = `download-${i}-pageassist` | ||||
|     const modelName = command?.value | ||||
|       .replace("ollama run", "") | ||||
|       .replace("ollama pull", "") | ||||
|       .trim() | ||||
|     newButton.addEventListener("click", () => { | ||||
|       downloadModel(modelName) | ||||
|     }) | ||||
| 
 | ||||
|     const span = document.createElement("span") | ||||
|     span.title = "Download model via Page Assist" | ||||
|     span.appendChild(newButton) | ||||
| 
 | ||||
|     button.parentNode.appendChild(span) | ||||
|   } | ||||
| } | ||||
							
								
								
									
										142
									
								
								src/entries/background.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,142 @@ | ||||
| 
 | ||||
| import { getOllamaURL, isOllamaRunning } from "../services/ollama" | ||||
| const progressHuman = (completed: number, total: number) => { | ||||
|   return ((completed / total) * 100).toFixed(0) + "%" | ||||
| } | ||||
| 
 | ||||
| const clearBadge = () => { | ||||
|   chrome.action.setBadgeText({ text: "" }) | ||||
|   chrome.action.setTitle({ title: "" }) | ||||
| } | ||||
| const streamDownload = async (url: string, model: string) => { | ||||
|   url += "/api/pull" | ||||
|   const response = await fetch(url, { | ||||
|     method: "POST", | ||||
|     headers: { | ||||
|       "Content-Type": "application/json" | ||||
|     }, | ||||
|     body: JSON.stringify({ model, stream: true }) | ||||
|   }) | ||||
| 
 | ||||
|   const reader = response.body?.getReader() | ||||
| 
 | ||||
|   const decoder = new TextDecoder() | ||||
| 
 | ||||
|   let isSuccess = true | ||||
|   while (true) { | ||||
|     if (!reader) { | ||||
|       break | ||||
|     } | ||||
|     const { done, value } = await reader.read() | ||||
| 
 | ||||
|     if (done) { | ||||
|       break | ||||
|     } | ||||
| 
 | ||||
|     const text = decoder.decode(value) | ||||
|     try { | ||||
|       const json = JSON.parse(text.trim()) as { | ||||
|         status: string | ||||
|         total?: number | ||||
|         completed?: number | ||||
|       } | ||||
|       if (json.total && json.completed) { | ||||
|         chrome.action.setBadgeText({ | ||||
|           text: progressHuman(json.completed, json.total) | ||||
|         }) | ||||
|         chrome.action.setBadgeBackgroundColor({ color: "#0000FF" }) | ||||
|       } else { | ||||
|         chrome.action.setBadgeText({ text: "🏋️♂️" }) | ||||
|         chrome.action.setBadgeBackgroundColor({ color: "#FFFFFF" }) | ||||
|       } | ||||
| 
 | ||||
|       chrome.action.setTitle({ title: json.status }) | ||||
| 
 | ||||
|       if (json.status === "success") { | ||||
|         isSuccess = true | ||||
|       } | ||||
|     } catch (e) { | ||||
|       console.error(e) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (isSuccess) { | ||||
|     chrome.action.setBadgeText({ text: "✅" }) | ||||
|     chrome.action.setBadgeBackgroundColor({ color: "#00FF00" }) | ||||
|     chrome.action.setTitle({ title: "Model pulled successfully" }) | ||||
|   } else { | ||||
|     chrome.action.setBadgeText({ text: "❌" }) | ||||
|     chrome.action.setBadgeBackgroundColor({ color: "#FF0000" }) | ||||
|     chrome.action.setTitle({ title: "Model pull failed" }) | ||||
|   } | ||||
| 
 | ||||
|   setTimeout(() => { | ||||
|     clearBadge() | ||||
|   }, 5000) | ||||
| } | ||||
| export default defineBackground({ | ||||
|   main() { | ||||
|     chrome.runtime.onMessage.addListener(async (message) => { | ||||
|       if (message.type === "sidepanel") { | ||||
|         chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => { | ||||
|           const tab = tabs[0] | ||||
|           chrome.sidePanel.open({ | ||||
|             tabId: tab.id! | ||||
|           }) | ||||
|         }) | ||||
|       } else if (message.type === "pull_model") { | ||||
|         const ollamaURL = await getOllamaURL() | ||||
|    | ||||
|         const isRunning = await isOllamaRunning() | ||||
|    | ||||
|         if (!isRunning) { | ||||
|           chrome.action.setBadgeText({ text: "E" }) | ||||
|           chrome.action.setBadgeBackgroundColor({ color: "#FF0000" }) | ||||
|           chrome.action.setTitle({ title: "Ollama is not running" }) | ||||
|           setTimeout(() => { | ||||
|             clearBadge() | ||||
|           }, 5000) | ||||
|         } | ||||
|    | ||||
|         await streamDownload(ollamaURL, message.modelName) | ||||
|       } | ||||
|     }) | ||||
|    | ||||
|     chrome.action.onClicked.addListener((tab) => { | ||||
|       chrome.tabs.create({ url: chrome.runtime.getURL("options.html") }) | ||||
|     }) | ||||
|    | ||||
|     chrome.commands.onCommand.addListener((command) => { | ||||
|       switch (command) { | ||||
|         case "execute_side_panel": | ||||
|           chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => { | ||||
|             const tab = tabs[0] | ||||
|             chrome.sidePanel.open({ | ||||
|               tabId: tab.id! | ||||
|             }) | ||||
|           }) | ||||
|           break | ||||
|         default: | ||||
|           break | ||||
|       } | ||||
|     }) | ||||
|    | ||||
|     chrome.contextMenus.create({ | ||||
|       id: "open-side-panel-pa", | ||||
|       title: "Open Side Panel to Chat", | ||||
|       contexts: ["all"] | ||||
|     }) | ||||
|    | ||||
|     chrome.contextMenus.onClicked.addListener((info, tab) => { | ||||
|       if (info.menuItemId === "open-side-panel-pa") { | ||||
|         chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => { | ||||
|           const tab = tabs[0] | ||||
|           await chrome.sidePanel.open({ | ||||
|             tabId: tab.id! | ||||
|           }) | ||||
|         }) | ||||
|       } | ||||
|     }) | ||||
|   }, | ||||
|   persistent: true | ||||
| }) | ||||
							
								
								
									
										56
									
								
								src/entries/ollama-pull.content.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,56 @@ | ||||
| export default defineContentScript({ | ||||
|   main(ctx) { | ||||
|     const downloadModel = async (modelName: string) => { | ||||
|       const ok = confirm( | ||||
|         `[Page Assist Extension] Do you want to pull ${modelName} model? This has nothing to do with Ollama.com website. The model will be pulled locally once you confirm.` | ||||
|       ) | ||||
|       if (ok) { | ||||
|         alert( | ||||
|           `[Page Assist Extension] Pulling ${modelName} model. For more details, check the extension icon.` | ||||
|         ) | ||||
| 
 | ||||
|         await chrome.runtime.sendMessage({ | ||||
|           type: "pull_model", | ||||
|           modelName | ||||
|         }) | ||||
|         return true | ||||
|       } | ||||
|       return false | ||||
|     } | ||||
| 
 | ||||
|     const downloadSVG = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="h-5 w-5 pageasssist-icon">
 | ||||
|     <path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3" /> | ||||
|     </svg> | ||||
|     ` | ||||
|     const codeDiv = document.querySelectorAll("div.language-none") | ||||
| 
 | ||||
|     for (let i = 0; i < codeDiv.length; i++) { | ||||
|       const button = codeDiv[i].querySelector("button") | ||||
|       const command = codeDiv[i].querySelector("input") | ||||
|       if (button && command) { | ||||
|         const newButton = document.createElement("button") | ||||
|         newButton.innerHTML = downloadSVG | ||||
|         newButton.className = `border-l ${button.className}` | ||||
|         newButton.id = `download-${i}-pageassist` | ||||
|         const modelName = command?.value | ||||
|           .replace("ollama run", "") | ||||
|           .replace("ollama pull", "") | ||||
|           .trim() | ||||
|         newButton.addEventListener("click", () => { | ||||
|           downloadModel(modelName) | ||||
|         }) | ||||
| 
 | ||||
|         const span = document.createElement("span") | ||||
|         span.title = "Download model via Page Assist" | ||||
|         span.appendChild(newButton) | ||||
| 
 | ||||
|         if (button.parentNode) { | ||||
|           button.parentNode.appendChild(span) | ||||
|         } | ||||
| 
 | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   allFrames: true, | ||||
|   matches: ["*://ollama.com/library/*"] | ||||
| }) | ||||
| @ -3,11 +3,10 @@ import { MemoryRouter } from "react-router-dom" | ||||
| import { ToastContainer } from "react-toastify" | ||||
| import "react-toastify/dist/ReactToastify.css" | ||||
| const queryClient = new QueryClient() | ||||
| import "./css/tailwind.css" | ||||
| import { ConfigProvider, theme } from "antd" | ||||
| import { StyleProvider } from "@ant-design/cssinjs" | ||||
| import { useDarkMode } from "~hooks/useDarkmode" | ||||
| import { OptionRouting } from "~routes" | ||||
| import { useDarkMode } from "~/hooks/useDarkmode" | ||||
| import { OptionRouting } from "~/routes" | ||||
| function IndexOption() { | ||||
|   const { mode } = useDarkMode() | ||||
|   return ( | ||||
							
								
								
									
										14
									
								
								src/entries/options/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,14 @@ | ||||
| <!doctype html> | ||||
| <html> | ||||
|   <head> | ||||
|     <title>Page Assist - Web UI</title> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||
|     <meta name="manifest.type" content="browser_action" /> | ||||
|     <link href="~/assets/tailwind.css" rel="stylesheet" /> | ||||
|     <meta charset="utf-8" /> | ||||
|   </head> | ||||
|   <body class="bg-white dark:bg-[#171717]"> | ||||
|     <div id="root"></div> | ||||
|     <script type="module" src="./main.tsx"></script> | ||||
|   </body> | ||||
| </html> | ||||
							
								
								
									
										10
									
								
								src/entries/options/main.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,10 @@ | ||||
| import React from 'react'; | ||||
| import ReactDOM from 'react-dom/client'; | ||||
| import IndexOption from './App'; | ||||
| 
 | ||||
| 
 | ||||
| ReactDOM.createRoot(document.getElementById('root')!).render( | ||||
|   <React.StrictMode> | ||||
|     <IndexOption /> | ||||
|   </React.StrictMode>, | ||||
| ); | ||||
| @ -1,13 +1,12 @@ | ||||
| import { QueryClient, QueryClientProvider } from "@tanstack/react-query" | ||||
| import { MemoryRouter } from "react-router-dom" | ||||
| import { SidepanelRouting } from "~routes" | ||||
| import { SidepanelRouting } from "~/routes" | ||||
| import { ToastContainer } from "react-toastify" | ||||
| import "react-toastify/dist/ReactToastify.css" | ||||
| const queryClient = new QueryClient() | ||||
| import "./css/tailwind.css" | ||||
| import { ConfigProvider, theme } from "antd" | ||||
| import { StyleProvider } from "@ant-design/cssinjs" | ||||
| import { useDarkMode } from "~hooks/useDarkmode" | ||||
| import { useDarkMode } from "~/hooks/useDarkmode" | ||||
| function IndexSidepanel() { | ||||
|   const { mode } = useDarkMode() | ||||
| 
 | ||||
							
								
								
									
										14
									
								
								src/entries/sidepanel/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,14 @@ | ||||
| <!doctype html> | ||||
| <html> | ||||
|   <head> | ||||
|     <title>Page Assist - Web UI</title> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||
|     <meta name="manifest.type" content="browser_action" /> | ||||
|     <link href="~/assets/tailwind.css" rel="stylesheet" /> | ||||
|     <meta charset="utf-8" /> | ||||
|   </head> | ||||
|   <body class="bg-white dark:bg-[#171717]"> | ||||
|     <div id="root"></div> | ||||
|     <script type="module" src="./main.tsx"></script> | ||||
|   </body> | ||||
| </html> | ||||
							
								
								
									
										9
									
								
								src/entries/sidepanel/main.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,9 @@ | ||||
| import React from "react" | ||||
| import ReactDOM from "react-dom/client" | ||||
| import IndexSidepanel from "./App" | ||||
| 
 | ||||
| ReactDOM.createRoot(document.getElementById("root")!).render( | ||||
|   <React.StrictMode> | ||||
|     <IndexSidepanel /> | ||||
|   </React.StrictMode> | ||||
| ) | ||||
| @ -1,5 +1,5 @@ | ||||
| import React from "react" | ||||
| import { cleanUrl } from "~libs/clean-url" | ||||
| import { cleanUrl } from "~/libs/clean-url" | ||||
| import { | ||||
|   defaultEmbeddingChunkOverlap, | ||||
|   defaultEmbeddingChunkSize, | ||||
| @ -7,8 +7,8 @@ import { | ||||
|   getOllamaURL, | ||||
|   promptForRag, | ||||
|   systemPromptForNonRag | ||||
| } from "~services/ollama" | ||||
| import { useStoreMessage, type ChatHistory, type Message } from "~store" | ||||
| } from "~/services/ollama" | ||||
| import { useStoreMessage, type ChatHistory, type Message } from "~/store" | ||||
| import { ChatOllama } from "@langchain/community/chat_models/ollama" | ||||
| import { | ||||
|   HumanMessage, | ||||
| @ -16,16 +16,16 @@ import { | ||||
|   type MessageContent, | ||||
|   SystemMessage | ||||
| } from "@langchain/core/messages" | ||||
| import { getHtmlOfCurrentTab } from "~libs/get-html" | ||||
| import { PageAssistHtmlLoader } from "~loader/html" | ||||
| import { getHtmlOfCurrentTab } from "~/libs/get-html" | ||||
| import { PageAssistHtmlLoader } from "~/loader/html" | ||||
| import { RecursiveCharacterTextSplitter } from "langchain/text_splitter" | ||||
| import { OllamaEmbeddings } from "@langchain/community/embeddings/ollama" | ||||
| import { | ||||
|   createChatWithWebsiteChain, | ||||
|   groupMessagesByConversation | ||||
| } from "~chain/chat-with-website" | ||||
| } from "~/chain/chat-with-website" | ||||
| import { MemoryVectorStore } from "langchain/vectorstores/memory" | ||||
| import { chromeRunTime } from "~libs/runtime" | ||||
| import { chromeRunTime } from "~/libs/runtime" | ||||
| export type BotResponse = { | ||||
|   bot: { | ||||
|     text: string | ||||
|  | ||||
| @ -1,11 +1,11 @@ | ||||
| import React from "react" | ||||
| import { cleanUrl } from "~libs/clean-url" | ||||
| import { cleanUrl } from "~/libs/clean-url" | ||||
| import { | ||||
|   geWebSearchFollowUpPrompt, | ||||
|   getOllamaURL, | ||||
|   systemPromptForNonRagOption | ||||
| } from "~services/ollama" | ||||
| import { type ChatHistory, type Message } from "~store/option" | ||||
| } from "~/services/ollama" | ||||
| import { type ChatHistory, type Message } from "~/store/option" | ||||
| import { ChatOllama } from "@langchain/community/chat_models/ollama" | ||||
| import { | ||||
|   HumanMessage, | ||||
| @ -13,7 +13,7 @@ import { | ||||
|   type MessageContent, | ||||
|   SystemMessage | ||||
| } from "@langchain/core/messages" | ||||
| import { useStoreMessageOption } from "~store/option" | ||||
| import { useStoreMessageOption } from "~/store/option" | ||||
| import { | ||||
|   deleteChatForEdit, | ||||
|   getPromptById, | ||||
| @ -21,10 +21,10 @@ import { | ||||
|   saveHistory, | ||||
|   saveMessage, | ||||
|   updateMessageByIndex | ||||
| } from "~libs/db" | ||||
| } from "~/libs/db" | ||||
| import { useNavigate } from "react-router-dom" | ||||
| import { notification } from "antd" | ||||
| import { getSystemPromptForWeb } from "~web/web" | ||||
| import { getSystemPromptForWeb } from "~/web/web" | ||||
| 
 | ||||
| export type BotResponse = { | ||||
|   bot: { | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| import { | ||||
|   type ChatHistory as ChatHistoryType, | ||||
|   type Message as MessageType | ||||
| } from "~store/option" | ||||
| } from "~/store/option" | ||||
| 
 | ||||
| type HistoryInfo = { | ||||
|   id: string | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| import { BaseDocumentLoader } from "langchain/document_loaders/base" | ||||
| import { Document } from "@langchain/core/documents" | ||||
| import { compile } from "html-to-text" | ||||
| import { chromeRunTime } from "~libs/runtime" | ||||
| import { chromeRunTime } from "~/libs/runtime" | ||||
| import { YtTranscript } from "yt-transcript" | ||||
| 
 | ||||
| const YT_REGEX = | ||||
|  | ||||
| @ -1,8 +0,0 @@ | ||||
| <!doctype html> | ||||
| <html> | ||||
|   <head> | ||||
|     <title>__plasmo_static_index_title__</title> | ||||
|     <meta charset="utf-8" /> | ||||
|   </head> | ||||
|   <body class="bg-white dark:bg-[#171717]"></body> | ||||
| </html> | ||||
							
								
								
									
										
											BIN
										
									
								
								src/public/icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/public/icon/128.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/public/icon/16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 345 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/icon/32.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 535 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/icon/48.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 951 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/icon/64.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 KiB | 
| @ -1,6 +1,6 @@ | ||||
| import { Route, Routes } from "react-router-dom" | ||||
| import { SidepanelChat } from "./sidepanel-chat" | ||||
| import { useDarkMode } from "~hooks/useDarkmode" | ||||
| import { useDarkMode } from "~/hooks/useDarkmode" | ||||
| import { SidepanelSettings } from "./sidepanel-settings" | ||||
| import { OptionIndex } from "./option-index" | ||||
| import { OptionModal } from "./option-settings-model" | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import OptionLayout from "~components/Layouts/Layout" | ||||
| import { Playground } from "~components/Option/Playground/Playground" | ||||
| import OptionLayout from "~/components/Layouts/Layout" | ||||
| import { Playground } from "~/components/Option/Playground/Playground" | ||||
| 
 | ||||
| export const OptionIndex = () => { | ||||
|   return ( | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| import { SettingsLayout } from "~components/Layouts/SettingsOptionLayout" | ||||
| import OptionLayout from "~components/Layouts/Layout" | ||||
| import { ModelsBody } from "~components/Option/Models" | ||||
| import { SettingsLayout } from "~/components/Layouts/SettingsOptionLayout" | ||||
| import OptionLayout from "~/components/Layouts/Layout" | ||||
| import { ModelsBody } from "~/components/Option/Models" | ||||
| 
 | ||||
| export const OptionModal = () => { | ||||
|   return ( | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| import { SettingsLayout } from "~components/Layouts/SettingsOptionLayout" | ||||
| import OptionLayout from "~components/Layouts/Layout" | ||||
| import { PromptBody } from "~components/Option/Prompt" | ||||
| import { SettingsLayout } from "~/components/Layouts/SettingsOptionLayout" | ||||
| import OptionLayout from "~/components/Layouts/Layout" | ||||
| import { PromptBody } from "~/components/Option/Prompt" | ||||
| 
 | ||||
| export const OptionPrompt = () => { | ||||
|   return ( | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| import { SettingsLayout } from "~components/Layouts/SettingsOptionLayout" | ||||
| import OptionLayout from "~components/Layouts/Layout" | ||||
| import { OptionShareBody } from "~components/Option/Share" | ||||
| import { SettingsLayout } from "~/components/Layouts/SettingsOptionLayout" | ||||
| import OptionLayout from "~/components/Layouts/Layout" | ||||
| import { OptionShareBody } from "~/components/Option/Share" | ||||
| 
 | ||||
| export const OptionShare = () => { | ||||
|   return ( | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| import { SettingsLayout } from "~components/Layouts/SettingsOptionLayout" | ||||
| import OptionLayout from "~components/Layouts/Layout" | ||||
| import { SettingOther } from "~components/Option/Settings/other" | ||||
| import { SettingsLayout } from "~/components/Layouts/SettingsOptionLayout" | ||||
| import OptionLayout from "~/components/Layouts/Layout" | ||||
| import { SettingOther } from "~/components/Option/Settings/other" | ||||
| 
 | ||||
| export const OptionSettings = () => { | ||||
|   return ( | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| import { SettingsLayout } from "~components/Layouts/SettingsOptionLayout" | ||||
| import OptionLayout from "~components/Layouts/Layout" | ||||
| import { SettingsOllama } from "~components/Option/Settings/ollama" | ||||
| import { SettingsLayout } from "~/components/Layouts/SettingsOptionLayout" | ||||
| import OptionLayout from "~/components/Layouts/Layout" | ||||
| import { SettingsOllama } from "~/components/Option/Settings/ollama" | ||||
| 
 | ||||
| export const OptionOllamaSettings = () => { | ||||
|   return ( | ||||
|  | ||||
| @ -1,8 +1,8 @@ | ||||
| import React from "react" | ||||
| import { SidePanelBody } from "~components/Sidepanel/Chat/body" | ||||
| import { SidepanelForm } from "~components/Sidepanel/Chat/form" | ||||
| import { SidepanelHeader } from "~components/Sidepanel/Chat/header" | ||||
| import { useMessage } from "~hooks/useMessage" | ||||
| import { SidePanelBody } from "~/components/Sidepanel/Chat/body" | ||||
| import { SidepanelForm } from "~/components/Sidepanel/Chat/form" | ||||
| import { SidepanelHeader } from "~/components/Sidepanel/Chat/header" | ||||
| import { useMessage } from "~/hooks/useMessage" | ||||
| 
 | ||||
| export const SidepanelChat = () => { | ||||
|   const drop = React.useRef<HTMLDivElement>(null) | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import { SettingsBody } from "~components/Sidepanel/Settings/body" | ||||
| import { SidepanelSettingsHeader } from "~components/Sidepanel/Settings/header" | ||||
| import { SettingsBody } from "~/components/Sidepanel/Settings/body" | ||||
| import { SidepanelSettingsHeader } from "~/components/Sidepanel/Settings/header" | ||||
| 
 | ||||
| export const SidepanelSettings = () => { | ||||
|   return ( | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| import { Storage } from "@plasmohq/storage" | ||||
| import { cleanUrl } from "~libs/clean-url" | ||||
| import { chromeRunTime } from "~libs/runtime" | ||||
| import { cleanUrl } from "../libs/clean-url" | ||||
| import { chromeRunTime } from "../libs/runtime" | ||||
| 
 | ||||
| const storage = new Storage() | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import { cleanUrl } from "~libs/clean-url" | ||||
| import { cleanUrl } from "~/libs/clean-url" | ||||
| 
 | ||||
| export const verifyPageShareURL = async (url: string) => { | ||||
|     const res = await fetch(`${cleanUrl(url)}/api/v1/ping`) | ||||
|  | ||||
| @ -2,10 +2,10 @@ import { OllamaEmbeddings } from "@langchain/community/embeddings/ollama" | ||||
| import type { Document } from "@langchain/core/documents" | ||||
| import { RecursiveCharacterTextSplitter } from "langchain/text_splitter" | ||||
| import { MemoryVectorStore } from "langchain/vectorstores/memory" | ||||
| import { cleanUrl } from "~libs/clean-url" | ||||
| import { chromeRunTime } from "~libs/runtime" | ||||
| import { PageAssistHtmlLoader } from "~loader/html" | ||||
| import { defaultEmbeddingChunkOverlap, defaultEmbeddingChunkSize, defaultEmbeddingModelForRag, getIsSimpleInternetSearch, getOllamaURL } from "~services/ollama" | ||||
| import { cleanUrl } from "~/libs/clean-url" | ||||
| import { chromeRunTime } from "~/libs/runtime" | ||||
| import { PageAssistHtmlLoader } from "~/loader/html" | ||||
| import { defaultEmbeddingChunkOverlap, defaultEmbeddingChunkSize, defaultEmbeddingModelForRag, getIsSimpleInternetSearch, getOllamaURL } from "~/services/ollama" | ||||
| 
 | ||||
| const BLOCKED_HOSTS = [ | ||||
|   "google.com", | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import { getWebSearchPrompt } from "~services/ollama" | ||||
| import { getWebSearchPrompt } from "~/services/ollama" | ||||
| import { webSearch } from "./local-google" | ||||
| 
 | ||||
| const getHostName = (url: string) => { | ||||
|  | ||||
| @ -1,11 +1,13 @@ | ||||
| { | ||||
|   "extends": "plasmo/templates/tsconfig.base", | ||||
|   "exclude": ["node_modules"], | ||||
|   "include": [".plasmo/index.d.ts", "./**/*.ts", "./**/*.tsx"], | ||||
|   "extends": "./.wxt/tsconfig.json", | ||||
|   "compilerOptions": { | ||||
|     "paths": { | ||||
|       "~*": ["./src/*"] | ||||
|     }, | ||||
|     "baseUrl": "." | ||||
|   } | ||||
|     "noEmit": true, | ||||
|     "allowImportingTsExtensions": true, | ||||
|     "allowSyntheticDefaultImports": true, | ||||
|     "esModuleInterop": true, | ||||
|     "jsx": "react-jsx" | ||||
|   }, | ||||
|   "exclude": [ | ||||
|     "node_modules" | ||||
|   ], | ||||
| } | ||||
							
								
								
									
										44
									
								
								wxt.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,44 @@ | ||||
| import { defineConfig } from "wxt" | ||||
| import react from "@vitejs/plugin-react" | ||||
| 
 | ||||
| // See https://wxt.dev/api/config.html
 | ||||
| export default defineConfig({ | ||||
|   vite: () => ({ | ||||
|     plugins: [react()], | ||||
|   }), | ||||
|   entrypointsDir: "entries", | ||||
|   srcDir: "src", | ||||
|   outDir: "build", | ||||
|   manifest: { | ||||
|     name: "Page Assist - A Web UI for Local AI Models", | ||||
|     version: "1.1.0", | ||||
|     description: | ||||
|       "Use your locally running AI models to assist you in your web browsing.", | ||||
|     action: {}, | ||||
|     author: "n4ze3m", | ||||
|     host_permissions: ["http://*/*", "https://*/*"], | ||||
|     commands: { | ||||
|       _execute_action: { | ||||
|         suggested_key: { | ||||
|           default: "Ctrl+Shift+L" | ||||
|         } | ||||
|       }, | ||||
|       execute_side_panel: { | ||||
|         description: "Open the side panel", | ||||
|         suggested_key: { | ||||
|           default: "Ctrl+Shift+P" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     permissions: [ | ||||
|       "storage", | ||||
|       "sidePanel", | ||||
|       "activeTab", | ||||
|       "scripting", | ||||
|       "declarativeNetRequest", | ||||
|       "action", | ||||
|       "unlimitedStorage", | ||||
|       "contextMenus" | ||||
|     ] | ||||
|   } | ||||
| }) | ||||