diff --git a/package.json b/package.json
index 50f980b..1097869 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "pageassist",
"displayName": "Page Assist - A Web UI for Local AI Models",
- "version": "1.0.4",
+ "version": "1.0.5",
"description": "Use your locally running AI models to assist you in your web browsing.",
"author": "n4ze3m",
"scripts": {
@@ -21,7 +21,6 @@
"@tailwindcss/forms": "^0.5.7",
"@tailwindcss/typography": "^0.5.10",
"@tanstack/react-query": "^5.17.19",
- "@types/pdf-parse": "^1.1.4",
"antd": "^5.13.3",
"axios": "^1.6.7",
"dayjs": "^1.11.10",
@@ -39,7 +38,6 @@
"rehype-mathjax": "4.0.3",
"remark-gfm": "3.0.1",
"remark-math": "5.1.1",
- "voy-search": "^0.6.3",
"zustand": "^4.5.0"
},
"devDependencies": {
diff --git a/src/background.ts b/src/background.ts
index 43b8dde..598d577 100644
--- a/src/background.ts
+++ b/src/background.ts
@@ -96,7 +96,6 @@ chrome.runtime.onMessage.addListener(async (message) => {
clearBadge()
}, 5000)
}
- console.log("Pulling model", message.modelName)
await streamDownload(ollamaURL, message.modelName)
}
diff --git a/src/components/Layouts/Layout.tsx b/src/components/Layouts/Layout.tsx
new file mode 100644
index 0000000..4c08354
--- /dev/null
+++ b/src/components/Layouts/Layout.tsx
@@ -0,0 +1,205 @@
+import React, { useState } from "react"
+
+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 {
+ ChevronLeft,
+ CogIcon,
+ ComputerIcon,
+ GithubIcon,
+ PanelLeftIcon,
+ SquarePen,
+ ZapIcon
+} from "lucide-react"
+import { getAllPrompts } from "~libs/db"
+
+export default function OptionLayout({
+ children
+}: {
+ children: React.ReactNode
+}) {
+ const [sidebarOpen, setSidebarOpen] = useState(false)
+ const {
+ selectedModel,
+ setSelectedModel,
+ clearChat,
+ selectedSystemPrompt,
+ setSelectedQuickPrompt,
+ setSelectedSystemPrompt
+ } = useMessageOption()
+
+ const {
+ data: models,
+ isLoading: isModelsLoading,
+ isFetching: isModelsFetching
+ } = useQuery({
+ queryKey: ["fetchModel"],
+ queryFn: () => getAllModels({ returnEmpty: true }),
+ refetchInterval: 15000
+ })
+
+ const { data: prompts, isLoading: isPromptLoading } = useQuery({
+ queryKey: ["fetchAllPromptsLayout"],
+ queryFn: getAllPrompts
+ })
+
+ const { pathname } = useLocation()
+
+ const getPromptInfoById = (id: string) => {
+ return prompts?.find((prompt) => prompt.id === id)
+ }
+
+ const handlePromptChange = (value: string) => {
+ const prompt = getPromptInfoById(value)
+ if (prompt?.is_system) {
+ setSelectedSystemPrompt(prompt.id)
+ } else {
+ setSelectedQuickPrompt(prompt.content)
+ setSelectedSystemPrompt(null)
+ }
+ }
+
+ return (
+
+
+
+
+
+ {pathname !== "/" && (
+
+
+
+
+
+ )}
+
+
+
+
+
+
+
+ {"/"}
+
+
+
+
+ {"/"}
+
+
+
+
+
+
+
+ {/*
+
+
+
+ */}
+
+
+
+
+
+ {/*
+
+
+
+ */}
+
+
+
+
+
+
+
+
+
+
{children}
+
+
+
+
setSidebarOpen(false)}
+ open={sidebarOpen}>
+
+
+
+ )
+}
diff --git a/src/components/Layouts/SettingsOptionLayout.tsx b/src/components/Layouts/SettingsOptionLayout.tsx
new file mode 100644
index 0000000..10fde58
--- /dev/null
+++ b/src/components/Layouts/SettingsOptionLayout.tsx
@@ -0,0 +1,90 @@
+import {
+ Book,
+ BrainCircuit,
+ CircuitBoardIcon,
+ Orbit
+} from "lucide-react"
+import { Link, useLocation } from "react-router-dom"
+
+function classNames(...classes: string[]) {
+ return classes.filter(Boolean).join(" ")
+}
+
+const LinkComponent = (item: {
+ href: string
+ name: string
+ icon: any
+ current: string
+}) => {
+ return (
+
+
+
+ {item.name}
+
+
+ )
+}
+
+export const SettingsLayout = ({ children }: { children: React.ReactNode }) => {
+ const location = useLocation()
+ return (
+ <>
+
+
+
+
+
+ {children}
+
+
+
+ >
+ )
+}
diff --git a/src/components/Option/Layout.tsx b/src/components/Option/Layout.tsx
deleted file mode 100644
index d85a1a3..0000000
--- a/src/components/Option/Layout.tsx
+++ /dev/null
@@ -1,131 +0,0 @@
-import React, { useState } from "react"
-
-import { useLocation, NavLink } from "react-router-dom"
-import { Sidebar } from "./Sidebar"
-import { Drawer, Layout, Modal, Select, Tooltip } from "antd"
-import { useQuery } from "@tanstack/react-query"
-import { getAllModels } from "~services/ollama"
-import { useMessageOption } from "~hooks/useMessageOption"
-import { Settings } from "./Settings"
-import { BrainCircuit, ChevronLeft, CogIcon, GithubIcon, PanelLeftIcon, SquarePen } from "lucide-react"
-
-
-export default function OptionLayout({
- children
-}: {
- children: React.ReactNode
-}) {
- const [sidebarOpen, setSidebarOpen] = useState(false)
- const [open, setOpen] = useState(false)
-
- const {
- data: models,
- isLoading: isModelsLoading,
- isFetching: isModelsFetching
- } = useQuery({
- queryKey: ["fetchModel"],
- queryFn: getAllModels,
- refetchInterval: 15000
- })
-
- const { pathname } = useLocation()
- const { selectedModel, setSelectedModel, clearChat } = useMessageOption()
-
- return (
-
-
-
- {pathname !== "/" && (
-
-
-
-
-
- )}
-
-
-
-
-
-
-
- {"/"}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {children}
-
- setSidebarOpen(false)}
- open={sidebarOpen}>
-
-
-
- setOpen(false)}
- footer={null}
- onCancel={() => setOpen(false)}>
- setOpen(false)} />
-
-
- )
-}
diff --git a/src/components/Option/Models/index.tsx b/src/components/Option/Models/index.tsx
index b637d7f..e1d16e8 100644
--- a/src/components/Option/Models/index.tsx
+++ b/src/components/Option/Models/index.tsx
@@ -22,7 +22,7 @@ export const ModelsBody = () => {
const { data, status } = useQuery({
queryKey: ["fetchAllModels"],
- queryFn: getAllModels
+ queryFn: () => getAllModels({ returnEmpty: true })
})
const { mutate: deleteOllamaModel } = useMutation({
@@ -67,15 +67,15 @@ export const ModelsBody = () => {
})
return (
-
-
+
+
{/* Add new model button */}
@@ -212,7 +212,7 @@ export const ModelsBody = () => {
diff --git a/src/components/Option/Playground/Playground.tsx b/src/components/Option/Playground/Playground.tsx
index fd59544..58a5446 100644
--- a/src/components/Option/Playground/Playground.tsx
+++ b/src/components/Option/Playground/Playground.tsx
@@ -70,17 +70,15 @@ export const Playground = () => {
ref={drop}
className={`${
dropState === "dragging" ? "bg-gray-100 dark:bg-gray-800 z-10" : ""
- } min-h-screen`}>
-
+ } bg-white dark:bg-[#171717]`}>
+
diff --git a/src/components/Option/Playground/PlaygroundChat.tsx b/src/components/Option/Playground/PlaygroundChat.tsx
index ac0f5b0..1d7948a 100644
--- a/src/components/Option/Playground/PlaygroundChat.tsx
+++ b/src/components/Option/Playground/PlaygroundChat.tsx
@@ -18,7 +18,7 @@ export const PlaygroundChat = () => {
)}
- {messages.length > 0 &&
}
+ {/* {messages.length > 0 &&
} */}
{messages.map((message, index) => (
{
- const textareaRef = React.useRef(null)
const inputRef = React.useRef(null)
+ const {
+ onSubmit,
+ selectedModel,
+ chatMode,
+ speechToTextLanguage,
+ stopStreamingRequest,
+ streaming: isSending,
+ webSearch,
+ setWebSearch,
+ selectedQuickPrompt,
+ textareaRef,
+ setSelectedQuickPrompt
+ } = useMessageOption()
- const resetHeight = () => {
- const textarea = textareaRef.current
- if (textarea) {
- textarea.style.height = "auto"
+ const textAreaFocus = () => {
+ if (textareaRef.current) {
+ if (
+ textareaRef.current.selectionStart === textareaRef.current.selectionEnd
+ ) {
+ textareaRef.current.focus()
+ }
}
}
const form = useForm({
@@ -33,9 +49,7 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
})
React.useEffect(() => {
- if (textareaRef.current) {
- textareaRef.current.focus()
- }
+ textAreaFocus()
}, [])
const onInputChange = async (
@@ -60,17 +74,6 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
useDynamicTextareaSize(textareaRef, form.values.message, 300)
- const {
- onSubmit,
- selectedModel,
- chatMode,
- speechToTextLanguage,
- stopStreamingRequest,
- streaming: isSending,
- webSearch,
- setWebSearch
- } = useMessageOption()
-
const { isListening, start, stop, transcript } = useSpeechRecognition()
const { sendWhenEnter, setSendWhenEnter } = useWebUI()
@@ -80,17 +83,75 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
}
}, [transcript])
+ React.useEffect(() => {
+ if (selectedQuickPrompt) {
+ const word = getVariable(selectedQuickPrompt)
+ form.setFieldValue("message", selectedQuickPrompt)
+ if (word) {
+ textareaRef.current?.focus()
+ const interval = setTimeout(() => {
+ textareaRef.current?.setSelectionRange(word.start, word.end)
+ setSelectedQuickPrompt(null)
+ }, 100)
+ return () => {
+ clearInterval(interval)
+ }
+ }
+ }
+ }, [selectedQuickPrompt])
+
const queryClient = useQueryClient()
const { mutateAsync: sendMessage } = useMutation({
mutationFn: onSubmit,
onSuccess: () => {
+ textAreaFocus()
queryClient.invalidateQueries({
queryKey: ["fetchChatHistory"]
})
+ },
+ onError: (error) => {
+ textAreaFocus()
}
})
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if (e.key === "Process" || e.key === "229") return
+ if (
+ e.key === "Enter" &&
+ !e.shiftKey &&
+ !isSending &&
+ sendWhenEnter &&
+ !e.isComposing
+ ) {
+ e.preventDefault()
+ form.onSubmit(async (value) => {
+ if (value.message.trim().length === 0) {
+ return
+ }
+ if (!selectedModel || selectedModel.length === 0) {
+ form.setFieldError("message", "Please select a model")
+ return
+ }
+ if (webSearch) {
+ const defaultEM = await defaultEmbeddingModelForRag()
+ if (!defaultEM) {
+ form.setFieldError(
+ "message",
+ "Please set an embedding model on the Settings > Ollama page"
+ )
+ return
+ }
+ }
+ form.reset()
+ textAreaFocus()
+ await sendMessage({
+ image: value.image,
+ message: value.message.trim()
+ })
+ })()
+ }
+ }
return (
{
}
}
form.reset()
- resetHeight()
+ textAreaFocus()
await sendMessage({
image: value.image,
message: value.message.trim()
@@ -152,41 +213,7 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
/>