diff --git a/README.md b/README.md index ac566ff..db7ef0b 100644 --- a/README.md +++ b/README.md @@ -85,9 +85,14 @@ This will start a development server and watch for changes in the source files. ## Browser Support -- Any Chromium-based browser that supports Chrome Extensions. - -- Firefox support is planned for the future. +| Browser | Sidebar | Chat With Webpage | Web UI | +| -------- | ------- | ----------------- | ------ | +| Chrome | ✅ | ✅ | ✅ | +| Brave | ✅ | ✅ | ✅ | +| Edge | ✅ | ❌ | ✅ | +| Opera GX | ❌ | ❌ | ✅ | +| Arc | ❌ | ❌ | ✅ | +| Firefox | ❌ | ❌ | ❌ | ## Local AI Provider @@ -105,11 +110,6 @@ This will start a development server and watch for changes in the source files. Contributions are welcome. If you have any feature requests, bug reports, or questions, feel free to create an issue. -## 0.0.1 - -If you are looking for the v0.0.1 of this project, you can find it on v0.0.1 branch. I created it as a hackathon project and it is not maintained anymore. - - ## Support If you like the project and want to support it, you can buy me a coffee. It will help me to keep working on the project. @@ -124,4 +124,4 @@ MIT ## Last but not least -Made in [Alappuzha](https://en.wikipedia.org/wiki/Alappuzha) with ❤️ \ No newline at end of file +Made in [Alappuzha](https://en.wikipedia.org/wiki/Alappuzha) with ❤️ diff --git a/package.json b/package.json index 1097869..8cf6e16 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.5", + "version": "1.0.8", "description": "Use your locally running AI models to assist you in your web browsing.", "author": "n4ze3m", "scripts": { @@ -26,7 +26,7 @@ "dayjs": "^1.11.10", "html-to-text": "^9.0.5", "langchain": "^0.1.9", - "lucide-react": "^0.340.0", + "lucide-react": "^0.350.0", "plasmo": "0.84.1", "property-information": "^6.4.1", "react": "18.2.0", @@ -38,6 +38,7 @@ "rehype-mathjax": "4.0.3", "remark-gfm": "3.0.1", "remark-math": "5.1.1", + "yt-transcript": "^0.0.2", "zustand": "^4.5.0" }, "devDependencies": { diff --git a/page-share.md b/page-share.md new file mode 100644 index 0000000..357dace --- /dev/null +++ b/page-share.md @@ -0,0 +1,37 @@ +# Page Share + +Page Share allows you to share the chat publicly, similar to ChatGPT Share. You can self-host Page Share for privacy and security. + +The default Page Share is hosted at [https://pageassist.xyz](https://pageassist.xyz). + +## Self-Host + +You can self-host Page Share using two methods: + +- Railway +- Docker + +### Railway + +Click the button below to deploy the code to Railway. + +[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/template/VbiS2Q?referralCode=olbszX) + +### Docker + +1. Clone the repository + +```bash +git clone https://github.com/n4ze3m/page-assist-app.git +cd page-assist-app +``` + +2. Run the server + +```bash +docker-compose up +``` + +3. Open the app + +Navigate to [http://localhost:3000](http://localhost:3000) in your browser. \ No newline at end of file diff --git a/src/components/Common/ShareBtn.tsx b/src/components/Common/ShareBtn.tsx new file mode 100644 index 0000000..395b216 --- /dev/null +++ b/src/components/Common/ShareBtn.tsx @@ -0,0 +1,193 @@ +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 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" + +type Props = { + messages: Message[] +} + +const reformatMessages = (messages: Message[], username: string) => { + return messages.map((message, idx) => { + return { + id: idx, + name: message.isBot ? message.name : username, + isBot: message.isBot, + message: message.message, + images: message.images + } + }) +} + +export const PlaygroundMessage = ( + props: Message & { + username: string + } +) => { + return ( +
+
+
+
+
+ {props.isBot ? ( +
+ ) : ( +
+ )} +
+
+
+ + {props.isBot ? props.name : props.username} + + +
+ +
+ {/* source if aviable */} + {props.images && props.images.length > 0 && ( +
+ {props.images + .filter((image) => image.length > 0) + .map((image, index) => ( + Uploaded Image + ))} +
+ )} +
+
+
+
+ ) +} + +export const ShareBtn: React.FC = ({ messages }) => { + const [open, setOpen] = useState(false) + const [form] = Form.useForm() + const name = Form.useWatch("name", form) + + React.useEffect(() => { + if (messages.length > 0) { + form.setFieldsValue({ + title: messages[0].message + }) + } + }, [messages]) + + const onSubmit = async (values: { title: string; name: string }) => { + const owner_id = await getUserId() + const chat = reformatMessages(messages, values.name) + const title = values.title + const url = await getPageShareUrl() + const res = await fetch(`${cleanUrl(url)}/api/v1/share/create`, { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + owner_id, + messages: chat, + title + }) + }) + + if (!res.ok) throw new Error("Failed to create share link") + + const data = await res.json() + + return { + ...data, + url: `${cleanUrl(url)}/share/${data.chat_id}`, + api_url: cleanUrl(url), + share_id: data.chat_id + } + } + + const { mutate: createShareLink, isPending } = useMutation({ + mutationFn: onSubmit, + onSuccess: async (data) => { + const url = data.url + navigator.clipboard.writeText(url) + message.success("Link copied to clipboard") + await saveWebshare({ title: data.title, url, api_url: data.api_url, share_id: data.share_id }) + setOpen(false) + }, + onError: (error) => { + message.error(error?.message || "Failed to create share link") + } + }) + + return ( + <> + + + + + setOpen(false)}> +
+ + + + + + + + +
+
+ {messages.map((message, index) => ( + + ))} +
+
+
+ + +
+ +
+
+
+
+ + ) +} diff --git a/src/components/Layouts/Layout.tsx b/src/components/Layouts/Layout.tsx index 4c08354..d266710 100644 --- a/src/components/Layouts/Layout.tsx +++ b/src/components/Layouts/Layout.tsx @@ -16,6 +16,7 @@ import { ZapIcon } from "lucide-react" import { getAllPrompts } from "~libs/db" +import { ShareBtn } from "~components/Common/ShareBtn" export default function OptionLayout({ children @@ -29,7 +30,9 @@ export default function OptionLayout({ clearChat, selectedSystemPrompt, setSelectedQuickPrompt, - setSelectedSystemPrompt + setSelectedSystemPrompt, + messages, + streaming } = useMessageOption() const { @@ -67,7 +70,7 @@ export default function OptionLayout({
-
+
{pathname !== "/" && (
@@ -88,7 +91,7 @@ export default function OptionLayout({
@@ -155,13 +158,9 @@ export default function OptionLayout({
- {/* - - - - */} + {pathname === "/" && messages.length > 0 && !streaming && ( + + )} - {/* - - - - */} setSidebarOpen(false)} open={sidebarOpen}> - + setSidebarOpen(false)} + />
) diff --git a/src/components/Layouts/SettingsOptionLayout.tsx b/src/components/Layouts/SettingsOptionLayout.tsx index 10fde58..a53bfcb 100644 --- a/src/components/Layouts/SettingsOptionLayout.tsx +++ b/src/components/Layouts/SettingsOptionLayout.tsx @@ -2,7 +2,8 @@ import { Book, BrainCircuit, CircuitBoardIcon, - Orbit + Orbit, + Share } from "lucide-react" import { Link, useLocation } from "react-router-dom" @@ -22,15 +23,15 @@ const LinkComponent = (item: { to={item.href} className={classNames( item.current === item.href - ? "bg-gray-100 text-indigo-600 dark:bg-[#262626] dark:text-white" - : "text-gray-700 hover:text-indigo-600 hover:bg-gray-100 dark:text-gray-200 dark:hover:text-white dark:hover:bg-[#262626]", + ? "bg-gray-100 text-gray-600 dark:bg-[#262626] dark:text-white" + : "text-gray-700 hover:text-gray-600 hover:bg-gray-100 dark:text-gray-200 dark:hover:text-white dark:hover:bg-[#262626]", "group flex gap-x-3 rounded-md py-2 pl-2 pr-3 text-sm leading-6 font-semibold" )}>