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.
+
+[](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) => (
+
+ ))}
+
+ )}
+
+
+
+
+ )
+}
+
+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(true)}
+ className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
+
+
+
+
+ setOpen(false)}>
+
+
+
+
+
+
+
+
+
+
+ {messages.map((message, index) => (
+
+ ))}
+
+
+
+
+
+
+
+ {isPending ? "Generating link..." : "Generate Link"}
+
+
+
+
+
+ >
+ )
+}
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({
+ className="inline-flex items-center rounded-lg border dark:border-gray-700 bg-transparent px-3 py-3 text-sm font-medium leading-4 text-gray-800 dark:text-white disabled:opacity-50 ">
New Chat
@@ -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"
)}>
{
return (
<>
-
+
{
icon={Book}
current={location.pathname}
/>
+
diff --git a/src/components/Option/Playground/PlaygroundEmpty.tsx b/src/components/Option/Playground/PlaygroundEmpty.tsx
index 64485fd..8e6b1a8 100644
--- a/src/components/Option/Playground/PlaygroundEmpty.tsx
+++ b/src/components/Option/Playground/PlaygroundEmpty.tsx
@@ -35,7 +35,7 @@ export const PlaygroundEmpty = () => {
return (
-
+
{(ollamaStatus === "pending" || isRefetching) && (
diff --git a/src/components/Option/Playground/PlaygroundForm.tsx b/src/components/Option/Playground/PlaygroundForm.tsx
index 786f7f7..4f68c52 100644
--- a/src/components/Option/Playground/PlaygroundForm.tsx
+++ b/src/components/Option/Playground/PlaygroundForm.tsx
@@ -18,6 +18,7 @@ type Props = {
export const PlaygroundForm = ({ dropedFile }: Props) => {
const inputRef = React.useRef
(null)
+ const [typing, setTyping] = React.useState(false)
const {
onSubmit,
selectedModel,
@@ -115,14 +116,14 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
}
})
- const handleKeyDown = (e: KeyboardEvent) => {
- if (e.key === "Process" || e.key === "229") return
+ const handleKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === "Process" || e.key === "229" ) return
if (
+ !typing &&
e.key === "Enter" &&
!e.shiftKey &&
!isSending &&
- sendWhenEnter &&
- !e.isComposing
+ sendWhenEnter
) {
e.preventDefault()
form.onSubmit(async (value) => {
@@ -153,7 +154,7 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
}
}
return (
-
+
{
/>
-
+
Speech Recognition Language
@@ -44,9 +44,7 @@ export const SettingOther = () => {
/>
-
- Change Theme
-
+ Change Theme
{
{mode === "dark" ? "Light" : "Dark"}
+
-
+
Delete Chat History
@@ -83,7 +82,6 @@ export const SettingOther = () => {
Delete
-
)
}
diff --git a/src/components/Option/Settings/search-mode.tsx b/src/components/Option/Settings/search-mode.tsx
index 5a1ab85..15be8dc 100644
--- a/src/components/Option/Settings/search-mode.tsx
+++ b/src/components/Option/Settings/search-mode.tsx
@@ -19,7 +19,7 @@ export const SearchModeSettings = () => {
return (
-
+
Perform Simple Internet Search
diff --git a/src/components/Option/Share/index.tsx b/src/components/Option/Share/index.tsx
new file mode 100644
index 0000000..3128324
--- /dev/null
+++ b/src/components/Option/Share/index.tsx
@@ -0,0 +1,190 @@
+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"
+
+export const OptionShareBody = () => {
+ const queryClient = useQueryClient()
+ const { status, data } = useQuery({
+ queryKey: ["fetchShareInfo"],
+ queryFn: async () => {
+ const [url, shares] = await Promise.all([
+ getPageShareUrl(),
+ getAllWebshares()
+ ])
+ return { url, shares }
+ }
+ })
+
+ const onSubmit = async (values: { url: string }) => {
+ const isOk = await verifyPageShareURL(values.url)
+ if (isOk) {
+ await setPageShareUrl(values.url)
+ }
+ }
+
+ const onDelete = async ({
+ api_url,
+ share_id,
+ id
+ }: {
+ id: string
+ share_id: string
+ api_url: string
+ }) => {
+ const owner_id = await getUserId()
+ const res = await fetch(`${api_url}/api/v1/share/delete`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ share_id,
+ owner_id
+ })
+ })
+ if (!res.ok) throw new Error("Failed to delete share link")
+ await deleteWebshare(id)
+ return "ok"
+ }
+
+ const { mutate: updatePageShareUrl, isPending: isUpdatePending } =
+ useMutation({
+ mutationFn: onSubmit,
+ onSuccess: () => {
+ queryClient.invalidateQueries({
+ queryKey: ["fetchShareInfo"]
+ })
+ message.success("Page Share URL updated successfully")
+ },
+ onError: (error) => {
+ message.error(error?.message || "Failed to update Page Share URL")
+ }
+ })
+
+ const { mutate: deleteMutation } = useMutation({
+ mutationFn: onDelete,
+ onSuccess: () => {
+ queryClient.invalidateQueries({
+ queryKey: ["fetchShareInfo"]
+ })
+ message.success("Webshare deleted successfully")
+ },
+ onError: (error) => {
+ message.error(error?.message || "Failed to delete Webshare")
+ }
+ })
+
+ return (
+
+ {status === "pending" &&
}
+ {status === "success" && (
+
+
+
+
+ Configure Page Share URL
+
+
+
+
+ For privacy reasons, you can self-host the page share and
+ provide the URL here.{" "}
+
+ Learn more
+
+
+ }
+ rules={[
+ {
+ required: true,
+ message: "Please input your Page Share URL!"
+ }
+ ]}
+ label="Page Share URL">
+
+
+
+
+
+
+
+
+
+
+
+
+
(
+
+ {url}
+
+ )
+ },
+ {
+ title: "Actions",
+ render: (_, render) => (
+
+ {
+ if (
+ window.confirm(
+ "Are you sure you want to delete this webshare?"
+ )
+ ) {
+ deleteMutation({
+ id: render.id,
+ share_id: render.share_id,
+ api_url: render.api_url
+ })
+ }
+ }}
+ className="text-red-500 dark:text-red-400">
+
+
+
+ )
+ }
+ ]}
+ />
+
+
+
+ )}
+
+ )
+}
diff --git a/src/components/Option/Sidebar.tsx b/src/components/Option/Sidebar.tsx
index af9be51..2646589 100644
--- a/src/components/Option/Sidebar.tsx
+++ b/src/components/Option/Sidebar.tsx
@@ -10,14 +10,17 @@ import { Empty, Skeleton } from "antd"
import { useMessageOption } from "~hooks/useMessageOption"
import { useState } from "react"
import { PencilIcon, Trash2 } from "lucide-react"
+import { useNavigate } from "react-router-dom"
-type Props = {}
+type Props = {
+ onClose: () => void
+}
-export const Sidebar = ({}: Props) => {
+export const Sidebar = ({ onClose }: Props) => {
const { setMessages, setHistory, setHistoryId, historyId, clearChat } =
useMessageOption()
- const [processingId, setProcessingId] = useState("")
const client = useQueryClient()
+ const navigate = useNavigate()
const { data: chatHistories, status } = useQuery({
queryKey: ["fetchChatHistory"],
@@ -28,21 +31,20 @@ export const Sidebar = ({}: Props) => {
}
})
- const { isPending: isDeleting, mutate: deleteHistory } = useMutation({
+ const { mutate: deleteHistory } = useMutation({
mutationKey: ["deleteHistory"],
mutationFn: deleteByHistoryId,
onSuccess: (history_id) => {
client.invalidateQueries({
queryKey: ["fetchChatHistory"]
})
- setProcessingId("")
if (historyId === history_id) {
clearChat()
}
}
})
- const { isPending: isEditing, mutate: editHistory } = useMutation({
+ const { mutate: editHistory } = useMutation({
mutationKey: ["editHistory"],
mutationFn: async (data: { id: string; title: string }) => {
return await updateHistory(data.id, data.title)
@@ -51,7 +53,6 @@ export const Sidebar = ({}: Props) => {
client.invalidateQueries({
queryKey: ["fetchChatHistory"]
})
- setProcessingId("")
}
})
@@ -86,6 +87,8 @@ export const Sidebar = ({}: Props) => {
setHistoryId(chat.id)
setHistory(formatToChatHistory(history))
setMessages(formatToMessage(history))
+ navigate("/")
+ onClose()
}}>
{chat.title}
@@ -97,8 +100,6 @@ export const Sidebar = ({}: Props) => {
if (newTitle) {
editHistory({ id: chat.id, title: newTitle })
}
-
- setProcessingId(chat.id)
}}
className="text-gray-500 dark:text-gray-400 opacity-80">
@@ -111,7 +112,6 @@ export const Sidebar = ({}: Props) => {
)
return
deleteHistory(chat.id)
- setProcessingId(chat.id)
}}
className="text-red-500 dark:text-red-400 opacity-80">
diff --git a/src/components/Sidepanel/Chat/empty.tsx b/src/components/Sidepanel/Chat/empty.tsx
index 9258385..4042d86 100644
--- a/src/components/Sidepanel/Chat/empty.tsx
+++ b/src/components/Sidepanel/Chat/empty.tsx
@@ -38,7 +38,7 @@ export const EmptySidePanel = () => {
}
}, [ollamaInfo])
- const { setSelectedModel, selectedModel, chatMode, setChatMode } =
+ const { setSelectedModel, selectedModel, chatMode, setChatMode, } =
useMessage()
return (
diff --git a/src/components/Sidepanel/Chat/form.tsx b/src/components/Sidepanel/Chat/form.tsx
index 8a2fe13..fde5a29 100644
--- a/src/components/Sidepanel/Chat/form.tsx
+++ b/src/components/Sidepanel/Chat/form.tsx
@@ -18,6 +18,7 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
const textareaRef = React.useRef(null)
const inputRef = React.useRef(null)
const { sendWhenEnter, setSendWhenEnter } = useWebUI()
+ const [typing, setTyping] = React.useState(false)
const textAreaFocus = () => {
if (textareaRef.current) {
@@ -72,14 +73,14 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
}
})
- const handleKeyDown = (e: KeyboardEvent) => {
+ const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === "Process" || e.key === "229") return
if (
e.key === "Enter" &&
!e.shiftKey &&
!isSending &&
sendWhenEnter &&
- !e.isComposing
+ !typing
) {
e.preventDefault()
form.onSubmit(async (value) => {
@@ -171,13 +172,15 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
/>
)
diff --git a/src/routes/option-settings-share.tsx b/src/routes/option-settings-share.tsx
new file mode 100644
index 0000000..da371d0
--- /dev/null
+++ b/src/routes/option-settings-share.tsx
@@ -0,0 +1,13 @@
+import { SettingsLayout } from "~components/Layouts/SettingsOptionLayout"
+import OptionLayout from "~components/Layouts/Layout"
+import { OptionShareBody } from "~components/Option/Share"
+
+export const OptionShare = () => {
+ return (
+
+
+
+
+
+ )
+}
diff --git a/src/services/ollama.ts b/src/services/ollama.ts
index 6c0ec14..1522317 100644
--- a/src/services/ollama.ts
+++ b/src/services/ollama.ts
@@ -6,6 +6,7 @@ const storage = new Storage()
const DEFAULT_OLLAMA_URL = "http://127.0.0.1:11434"
const DEFAULT_ASK_FOR_MODEL_SELECTION_EVERY_TIME = true
+const DEFAULT_PAGE_SHARE_URL = "https://pageassist.xyz"
const DEFAULT_RAG_QUESTION_PROMPT =
"Given the following conversation and a follow up question, rephrase the follow up question to be a standalone question. Chat History: {chat_history} Follow Up Input: {question} Standalone question:"
@@ -302,4 +303,17 @@ export const getIsSimpleInternetSearch = async () => {
export const setIsSimpleInternetSearch = async (isSimpleInternetSearch: boolean) => {
await storage.set("isSimpleInternetSearch", isSimpleInternetSearch.toString())
+}
+
+export const getPageShareUrl = async () => {
+ const pageShareUrl = await storage.get("pageShareUrl")
+ if (!pageShareUrl || pageShareUrl.length === 0) {
+ return DEFAULT_PAGE_SHARE_URL
+ }
+ return pageShareUrl
+}
+
+
+export const setPageShareUrl = async (pageShareUrl: string) => {
+ await storage.set("pageShareUrl", pageShareUrl)
}
\ No newline at end of file
diff --git a/src/store/index.tsx b/src/store/index.tsx
index d23fea1..8910705 100644
--- a/src/store/index.tsx
+++ b/src/store/index.tsx
@@ -37,6 +37,8 @@ type State = {
setIsEmbedding: (isEmbedding: boolean) => void
speechToTextLanguage: string
setSpeechToTextLanguage: (speechToTextLanguage: string) => void
+ currentURL: string
+ setCurrentURL: (currentURL: string) => void
}
export const useStoreMessage = create((set) => ({
@@ -63,5 +65,7 @@ export const useStoreMessage = create((set) => ({
setIsEmbedding: (isEmbedding) => set({ isEmbedding }),
speechToTextLanguage: "en-US",
setSpeechToTextLanguage: (speechToTextLanguage) =>
- set({ speechToTextLanguage })
+ set({ speechToTextLanguage }),
+ currentURL: "",
+ setCurrentURL: (currentURL) => set({ currentURL })
}))
diff --git a/src/utils/verify-page-share.ts b/src/utils/verify-page-share.ts
new file mode 100644
index 0000000..bf73a83
--- /dev/null
+++ b/src/utils/verify-page-share.ts
@@ -0,0 +1,10 @@
+import { cleanUrl } from "~libs/clean-url"
+
+export const verifyPageShareURL = async (url: string) => {
+ const res = await fetch(`${cleanUrl(url)}/api/v1/ping`)
+ if (!res.ok) {
+ throw new Error("Unable to verify page share")
+ }
+ const data = await res.text()
+ return data === "pong"
+}
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index ca7ad13..9d97196 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4967,10 +4967,10 @@ lru-cache@^6.0.0:
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3"
integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==
-lucide-react@^0.340.0:
- version "0.340.0"
- resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.340.0.tgz#67a6fac6a5e257f2036dffae0dd94d6ccb28ce8e"
- integrity sha512-mWzYhbyy2d+qKuKHh+GWElPwa+kIquTnKbmSLGWOuZy+bjfZCkYD8DQWVFlqI4mQwc4HNxcqcOvtQ7ZS2PwURg==
+lucide-react@^0.350.0:
+ version "0.350.0"
+ resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.350.0.tgz#78b45342f4daff4535290e37b1ea7eb0961a3dab"
+ integrity sha512-5IZVKsxxG8Nn81gpsz4XLNgCAXkppCh0Y0P0GLO39h5iVD2WEaB9of6cPkLtzys1GuSfxJxmwsDh487y7LAf/g==
magic-string@^0.30.0:
version "0.30.6"
@@ -7772,6 +7772,13 @@ ws@^8.11.0:
resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4"
integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==
+xml-js@^1.6.11:
+ version "1.6.11"
+ resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9"
+ integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==
+ dependencies:
+ sax "^1.2.4"
+
xml-name-validator@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"
@@ -7817,6 +7824,14 @@ yaml@^2.2.1, yaml@^2.3.4:
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.4.tgz#53fc1d514be80aabf386dc6001eb29bf3b7523b2"
integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==
+yt-transcript@^0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/yt-transcript/-/yt-transcript-0.0.2.tgz#1c54aede89bb8a03bbca3ba58520dbbd9c828571"
+ integrity sha512-+cNRqW6tSQNDkQDVrWNT6hc6X2TnaQLvUJIepzn9r7XdEvPtUDkfsyhptW5+j0EPIEpnlsKyA/epCUrE4QKn2g==
+ dependencies:
+ axios "^1.6.7"
+ xml-js "^1.6.11"
+
zod-to-json-schema@^3.22.3:
version "3.22.4"
resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.22.4.tgz#f8cc691f6043e9084375e85fb1f76ebafe253d70"