feat(ai): implement MCP service integration for enhanced AI capabilities (#4359)
* feat(ai): implement MCP service integration for enhanced AI capabilities - Added functionality to manage MCP services, including adding, updating, removing, and connecting to services. - Introduced UI components for MCP service management, allowing users to enable MCP services and discover service endpoints. - Updated AI settings to include MCP service configurations and integrated translations for new UI elements. - Enhanced error handling and user feedback for service connection and discovery processes. These changes collectively extend the AI capabilities by integrating external services through secure OAuth connections, improving the overall user experience. Signed-off-by: Innei <tukon479@gmail.com> * feat(mcp): update MCP service integration and enhance settings management - Upgraded '@follow-app/client-sdk' to version 0.3.38, reflecting the latest changes in MCP service management. - Removed deprecated MCP service functions and streamlined the integration process for better performance. - Introduced new UI components for managing MCP connections, including transport type selection and header configuration. - Enhanced error handling and user feedback for service connection and discovery processes. - Updated translations for new UI elements related to MCP services. These changes collectively improve the user experience by providing a more robust and intuitive interface for managing external services through secure OAuth connections. Signed-off-by: Innei <tukon479@gmail.com> * feat(ai-chat): enhance ToolInvocationComponent with error handling and UI updates - Added error handling to ToolInvocationComponent, displaying error messages and visual indicators when tool execution fails. - Updated the UI to reflect tool status, changing icons and text based on success or failure. - Improved styling for the back button in ChatInterfaceContent for a more consistent appearance. These changes improve user feedback and interaction within the AI chat interface, enhancing overall usability. Signed-off-by: Innei <tukon479@gmail.com> * refactor(ai): streamline MCPServiceItem layout for improved readability - Removed redundant resource and prompt counts from the MCPServiceItem component to simplify the display. - Adjusted the layout to enhance the presentation of service details, including tools, creation date, and last used date. These changes improve the clarity and usability of the MCP service information within the AI settings interface. Signed-off-by: Innei <tukon479@gmail.com> * chore: update deps Signed-off-by: Innei <tukon479@gmail.com> --------- Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
parent
9872cff3f5
commit
0c9977d5d3
|
|
@ -1,6 +1,6 @@
|
|||
import { createSettingAtom } from "@follow/atoms/helper/setting.js"
|
||||
import { defaultAISettings } from "@follow/shared/settings/defaults"
|
||||
import type { AISettings } from "@follow/shared/settings/interface"
|
||||
import type { AISettings, MCPService } from "@follow/shared/settings/interface"
|
||||
import { jotaiStore } from "@follow/utils"
|
||||
import { atom, useAtomValue } from "jotai"
|
||||
|
||||
|
|
@ -70,6 +70,37 @@ export const setAIPanelVisibility = (visibility: boolean) => {
|
|||
}
|
||||
export const getAIPanelVisibility = () => jotaiStore.get(aiPanelVisibilityAtom)
|
||||
|
||||
////////// MCP Services
|
||||
export const useMCPEnabled = () => useAISettingKey("mcpEnabled")
|
||||
export const setMCPEnabled = (enabled: boolean) => {
|
||||
setAISetting("mcpEnabled", enabled)
|
||||
}
|
||||
|
||||
export const useMCPServices = () => useAISettingKey("mcpServices")
|
||||
export const addMCPService = (service: Omit<MCPService, "id">) => {
|
||||
const services = getAISettings().mcpServices
|
||||
const newService = {
|
||||
...service,
|
||||
id: Date.now().toString(),
|
||||
}
|
||||
setAISetting("mcpServices", [...services, newService])
|
||||
return newService.id
|
||||
}
|
||||
|
||||
export const updateMCPService = (id: string, updates: Partial<MCPService>) => {
|
||||
const services = getAISettings().mcpServices
|
||||
const updatedServices = services.map((service) =>
|
||||
service.id === id ? { ...service, ...updates } : service,
|
||||
)
|
||||
setAISetting("mcpServices", updatedServices)
|
||||
}
|
||||
|
||||
export const removeMCPService = (id: string) => {
|
||||
const services = getAISettings().mcpServices
|
||||
const filteredServices = services.filter((service) => service.id !== id)
|
||||
setAISetting("mcpServices", filteredServices)
|
||||
}
|
||||
|
||||
//// Enhance Init Ai Settings
|
||||
export const initializeDefaultAISettings = () => {
|
||||
initializeDefaultSettings()
|
||||
|
|
|
|||
|
|
@ -215,8 +215,8 @@ const ChatInterfaceContent = () => {
|
|||
onClick={() => resetScrollState()}
|
||||
className={cn(
|
||||
"backdrop-blur-background group flex items-center gap-2 rounded-full border px-3.5 py-2 transition-all",
|
||||
"border-border/40 bg-material-ultra-thin/70 shadow-[0_1px_2px_rgba(0,0,0,0.06),0_8px_24px_rgba(0,0,0,0.08)]",
|
||||
"hover:bg-material-thin/70 hover:border-border/60 active:scale-[0.98]",
|
||||
"border-border/40 bg-material-ultra-thin shadow-[0_1px_2px_rgba(0,0,0,0.06),0_8px_24px_rgba(0,0,0,0.08)]",
|
||||
"hover:bg-material-medium hover:border-border/60 active:scale-[0.98]",
|
||||
)}
|
||||
>
|
||||
<i className="i-mingcute-arrow-down-line text-text/90 size-3" />
|
||||
|
|
|
|||
|
|
@ -16,9 +16,14 @@ interface ToolInvocationComponentProps {
|
|||
export const ToolInvocationComponent: React.FC<ToolInvocationComponentProps> = React.memo(
|
||||
({ part }) => {
|
||||
const toolName = getToolName(part)
|
||||
const hasError = "errorText" in part && part.errorText
|
||||
|
||||
return (
|
||||
<div className="bg-material-medium border-border size-full min-w-0 max-w-prose rounded-lg border text-left">
|
||||
<div
|
||||
className={`bg-material-medium size-full min-w-0 max-w-prose rounded-lg border text-left ${
|
||||
hasError ? "border-red/30" : "border-border"
|
||||
}`}
|
||||
>
|
||||
<div className="w-[9999px] max-w-[calc(var(--ai-chat-layout-width,65ch)_-120px)]" />
|
||||
<Accordion type="single" collapsible>
|
||||
<AccordionItem value="tool-invocation">
|
||||
|
|
@ -26,9 +31,15 @@ export const ToolInvocationComponent: React.FC<ToolInvocationComponentProps> = R
|
|||
{/* Tool Info */}
|
||||
<div className="flex h-6 min-w-0 flex-1 items-center">
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<i className="i-mingcute-tool-line" />
|
||||
<span className="text-text-secondary">Tool Calling:</span>
|
||||
<h4 className="text-text truncate font-medium">{toolName}</h4>
|
||||
<i
|
||||
className={hasError ? "i-mgc-close-cute-re text-red" : "i-mingcute-tool-line"}
|
||||
/>
|
||||
<span className="text-text-secondary">
|
||||
{hasError ? "Tool Failed:" : "Tool Calling:"}
|
||||
</span>
|
||||
<h4 className={`truncate font-medium ${hasError ? "text-red" : "text-text"}`}>
|
||||
{toolName}
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
|
|
@ -44,7 +55,7 @@ export const ToolInvocationComponent: React.FC<ToolInvocationComponentProps> = R
|
|||
</div>
|
||||
)}
|
||||
|
||||
{"output" in part && (
|
||||
{"output" in part && !!part.output && (
|
||||
<div>
|
||||
<div className="text-text-tertiary mb-2 text-xs font-semibold uppercase tracking-wide">
|
||||
Result
|
||||
|
|
@ -52,6 +63,25 @@ export const ToolInvocationComponent: React.FC<ToolInvocationComponentProps> = R
|
|||
<JsonHighlighter json={JSON.stringify(part.output, null, 2)} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{hasError && (
|
||||
<div>
|
||||
<div className="text-red mb-2 text-xs font-semibold uppercase tracking-wide">
|
||||
Error
|
||||
</div>
|
||||
<div className="bg-red/5 border-red/20 text-red rounded-lg border p-3 text-sm">
|
||||
<div className="flex items-start gap-2">
|
||||
<i className="i-mgc-warning-cute-re mt-0.5 flex-shrink-0 text-base" />
|
||||
<div className="min-w-0">
|
||||
<div className="font-medium">Tool Execution Failed</div>
|
||||
<div className="text-red/80 mt-1 break-words font-mono text-xs">
|
||||
{"errorText" in part ? part.errorText : ""}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
import type { PrimitiveAtom } from "jotai"
|
||||
import { atom } from "jotai"
|
||||
import { createContext } from "react"
|
||||
|
||||
import { createAtomHooks } from "~/lib/jotai"
|
||||
|
||||
export const [, , useSettingTab, useSetSettingTab, getSettingTab, setSettingTab] = createAtomHooks(
|
||||
atom(""),
|
||||
)
|
||||
|
||||
export const SettingModalContentPortalableContext = createContext<PrimitiveAtom<HTMLElement>>(
|
||||
atom(null as any),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@ import { LetsIconsResizeDownRightLight } from "@follow/components/icons/resize.j
|
|||
import { IN_ELECTRON } from "@follow/shared/constants"
|
||||
import { preventDefault } from "@follow/utils/dom"
|
||||
import { cn, getOS } from "@follow/utils/utils"
|
||||
import { atom, useAtomValue, useSetAtom } from "jotai"
|
||||
import type { BoundingBox } from "motion/react"
|
||||
import { Resizable } from "re-resizable"
|
||||
import type { PropsWithChildren } from "react"
|
||||
import { memo, Suspense, useCallback, useEffect, useRef } from "react"
|
||||
import { memo, Suspense, use, useCallback, useEffect, useMemo, useRef } from "react"
|
||||
import { createPortal } from "react-dom"
|
||||
|
||||
import { useUISettingSelector } from "~/atoms/settings/ui"
|
||||
import { m } from "~/components/common/Motion"
|
||||
|
|
@ -24,7 +26,7 @@ import { useAvailableSettings, useSettingPageContext } from "../hooks/use-settin
|
|||
import { SettingsSidebarTitle } from "../title"
|
||||
import type { SettingPageConfig } from "../utils"
|
||||
import { DisableWhy } from "../utils"
|
||||
import { useSetSettingTab, useSettingTab } from "./context"
|
||||
import { SettingModalContentPortalableContext, useSetSettingTab, useSettingTab } from "./context"
|
||||
import { defaultCtx, SettingContext } from "./hooks"
|
||||
|
||||
export function SettingModalLayout(
|
||||
|
|
@ -77,6 +79,10 @@ export function SettingModalLayout(
|
|||
return constraints
|
||||
}).current
|
||||
|
||||
const portalableCtxValue = useMemo(() => {
|
||||
return atom(null as any)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div
|
||||
id={SETTING_MODAL_ID}
|
||||
|
|
@ -142,7 +148,10 @@ export function SettingModalLayout(
|
|||
</div>
|
||||
</div>
|
||||
<div className="bg-background relative flex h-full min-w-0 flex-1 flex-col pt-1">
|
||||
<Suspense>{children}</Suspense>
|
||||
<SettingModalContentPortalableContext value={portalableCtxValue}>
|
||||
<Suspense>{children}</Suspense>
|
||||
<SettingModalContentPortalable />
|
||||
</SettingModalContentPortalableContext>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -154,6 +163,11 @@ export function SettingModalLayout(
|
|||
)
|
||||
}
|
||||
|
||||
const SettingModalContentPortalable = () => {
|
||||
const setElement = useSetAtom(use(SettingModalContentPortalableContext))
|
||||
return <div ref={setElement as any} />
|
||||
}
|
||||
|
||||
const SettingItemButtonImpl = (props: {
|
||||
setTab: (tab: string) => void
|
||||
item: SettingPageConfig
|
||||
|
|
@ -220,3 +234,8 @@ export const SidebarItems = memo((props: { onChange?: (tab: string) => void }) =
|
|||
)
|
||||
})
|
||||
})
|
||||
|
||||
export const SettingModalContentPortal: Component = ({ children }) => {
|
||||
const element = useAtomValue(use(SettingModalContentPortalableContext))
|
||||
return createPortal(children, element)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,21 @@
|
|||
import { Spring } from "@follow/components/constants/spring.js"
|
||||
import { Button } from "@follow/components/ui/button/index.js"
|
||||
import { Input, TextArea } from "@follow/components/ui/input/index.js"
|
||||
import { KbdCombined } from "@follow/components/ui/kbd/Kbd.js"
|
||||
import { KeyValueEditor } from "@follow/components/ui/key-value-editor/index.js"
|
||||
import { Label } from "@follow/components/ui/label/index.jsx"
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@follow/components/ui/select/index.js"
|
||||
import { Switch } from "@follow/components/ui/switch/index.jsx"
|
||||
import type { AIShortcut } from "@follow/shared/settings/interface"
|
||||
import type { AIShortcut, MCPService } from "@follow/shared/settings/interface"
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
|
||||
import { AnimatePresence } from "motion/react"
|
||||
import * as React from "react"
|
||||
import { useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { toast } from "sonner"
|
||||
|
|
@ -12,13 +24,26 @@ import {
|
|||
AIChatPanelStyle,
|
||||
setAIChatPanelStyle,
|
||||
setAISetting,
|
||||
setMCPEnabled,
|
||||
useAIChatPanelStyle,
|
||||
useAISettingValue,
|
||||
useMCPEnabled,
|
||||
} from "~/atoms/settings/ai"
|
||||
import { m } from "~/components/common/Motion"
|
||||
import { useDialog, useModalStack } from "~/components/ui/modal/stacked/hooks"
|
||||
import {
|
||||
createMCPConnection,
|
||||
deleteMCPConnection,
|
||||
fetchMCPConnections,
|
||||
mcpQueryKeys,
|
||||
refreshMCPTools,
|
||||
updateMCPConnection,
|
||||
} from "~/queries/mcp"
|
||||
|
||||
import { SettingActionItem, SettingDescription, SettingTabbedSegment } from "../control"
|
||||
import { createDefineSettingItem } from "../helper/builder"
|
||||
import { createSettingBuilder } from "../helper/setting-builder"
|
||||
import { SettingModalContentPortal } from "../modal/layout"
|
||||
|
||||
const SettingBuilder = createSettingBuilder(useAISettingValue)
|
||||
const defineSettingItem = createDefineSettingItem(useAISettingValue, setAISetting)
|
||||
|
|
@ -53,6 +78,12 @@ export const SettingAI = () => {
|
|||
value: t("shortcuts.title"),
|
||||
},
|
||||
AIShortcutsSection,
|
||||
|
||||
{
|
||||
type: "title",
|
||||
value: t("integration.title"),
|
||||
},
|
||||
MCPServicesSection,
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -126,17 +157,31 @@ const PersonalizePromptSetting = () => {
|
|||
</SettingDescription>
|
||||
</div>
|
||||
|
||||
<div className="flex h-9 justify-end">
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
disabled={isSaving || !hasChanges || isOverLimit}
|
||||
buttonClassName={`transition-opacity duration-200 ${
|
||||
hasChanges && !isOverLimit ? "opacity-100" : "pointer-events-none opacity-0"
|
||||
}`}
|
||||
>
|
||||
{isSaving ? "Saving..." : "Save"}
|
||||
</Button>
|
||||
</div>
|
||||
<AnimatePresence>
|
||||
{hasChanges && (
|
||||
<SettingModalContentPortal>
|
||||
<m.div
|
||||
initial={{ y: 20, scale: 0.95 }}
|
||||
animate={{ y: 0, scale: 1 }}
|
||||
exit={{ y: 20, scale: 0.95 }}
|
||||
transition={Spring.presets.snappy}
|
||||
className="absolute inset-x-0 bottom-3 z-10 flex justify-center px-3"
|
||||
>
|
||||
<div className="backdrop-blur-background bg-material-medium border-border shadow-perfect flex w-fit max-w-[92%] items-center justify-between gap-3 rounded-full border py-2 pl-5 pr-2">
|
||||
<span className="text-text-secondary text-xs sm:text-sm">Unsaved changes</span>
|
||||
<Button
|
||||
buttonClassName="bg-accent rounded-full"
|
||||
size="sm"
|
||||
onClick={handleSave}
|
||||
disabled={isSaving || isOverLimit}
|
||||
>
|
||||
{isSaving ? "Saving..." : "Save"}
|
||||
</Button>
|
||||
</div>
|
||||
</m.div>
|
||||
</SettingModalContentPortal>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -144,20 +189,49 @@ const PersonalizePromptSetting = () => {
|
|||
const AIShortcutsSection = () => {
|
||||
const { t } = useTranslation("ai")
|
||||
const { shortcuts } = useAISettingValue()
|
||||
const [isCreating, setIsCreating] = useState(false)
|
||||
const { present } = useModalStack()
|
||||
|
||||
const handleAddShortcut = () => {
|
||||
setIsCreating(true)
|
||||
present({
|
||||
title: "Add AI Shortcut",
|
||||
content: ({ dismiss }: { dismiss: () => void }) => (
|
||||
<ShortcutModalContent
|
||||
shortcut={null}
|
||||
onSave={(shortcut) => {
|
||||
const newShortcut: AIShortcut = {
|
||||
...shortcut,
|
||||
id: Date.now().toString(),
|
||||
}
|
||||
setAISetting("shortcuts", [...shortcuts, newShortcut])
|
||||
toast.success(t("shortcuts.added"))
|
||||
dismiss()
|
||||
}}
|
||||
onCancel={dismiss}
|
||||
/>
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
const handleSaveShortcut = (shortcut: Omit<AIShortcut, "id">) => {
|
||||
const newShortcut: AIShortcut = {
|
||||
...shortcut,
|
||||
id: Date.now().toString(),
|
||||
}
|
||||
setAISetting("shortcuts", [...shortcuts, newShortcut])
|
||||
setIsCreating(false)
|
||||
toast.success(t("shortcuts.added"))
|
||||
const handleEditShortcut = (shortcut: AIShortcut) => {
|
||||
present({
|
||||
title: "Edit AI Shortcut",
|
||||
content: ({ dismiss }: { dismiss: () => void }) => (
|
||||
<ShortcutModalContent
|
||||
shortcut={shortcut}
|
||||
onSave={(updatedShortcut) => {
|
||||
setAISetting(
|
||||
"shortcuts",
|
||||
shortcuts.map((s) =>
|
||||
s.id === shortcut.id ? { ...updatedShortcut, id: shortcut.id } : s,
|
||||
),
|
||||
)
|
||||
toast.success(t("shortcuts.updated"))
|
||||
dismiss()
|
||||
}}
|
||||
onCancel={dismiss}
|
||||
/>
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
const handleDeleteShortcut = (id: string) => {
|
||||
|
|
@ -175,29 +249,15 @@ const AIShortcutsSection = () => {
|
|||
)
|
||||
}
|
||||
|
||||
const handleUpdateShortcut = (id: string, updatedShortcut: Omit<AIShortcut, "id">) => {
|
||||
setAISetting(
|
||||
"shortcuts",
|
||||
shortcuts.map((s) => (s.id === id ? { ...updatedShortcut, id } : s)),
|
||||
)
|
||||
toast.success(t("shortcuts.updated"))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<SettingDescription>{t("shortcuts.description")}</SettingDescription>
|
||||
|
||||
<SettingActionItem
|
||||
label={t("shortcuts.add")}
|
||||
action={handleAddShortcut}
|
||||
buttonText={t("shortcuts.add")}
|
||||
/>
|
||||
|
||||
{isCreating && (
|
||||
<ShortcutEditor onSave={handleSaveShortcut} onCancel={() => setIsCreating(false)} />
|
||||
)}
|
||||
|
||||
{shortcuts.length === 0 && !isCreating && (
|
||||
{shortcuts.length === 0 && (
|
||||
<div className="py-8 text-center">
|
||||
<div className="bg-fill-secondary mx-auto mb-3 flex size-12 items-center justify-center rounded-full">
|
||||
<i className="i-mgc-magic-2-cute-re text-text size-6" />
|
||||
|
|
@ -213,7 +273,7 @@ const AIShortcutsSection = () => {
|
|||
shortcut={shortcut}
|
||||
onDelete={handleDeleteShortcut}
|
||||
onToggle={handleToggleShortcut}
|
||||
onUpdate={handleUpdateShortcut}
|
||||
onEdit={handleEditShortcut}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -224,29 +284,10 @@ interface ShortcutItemProps {
|
|||
shortcut: AIShortcut
|
||||
onDelete: (id: string) => void
|
||||
onToggle: (id: string, enabled: boolean) => void
|
||||
onUpdate: (id: string, shortcut: Omit<AIShortcut, "id">) => void
|
||||
onEdit: (shortcut: AIShortcut) => void
|
||||
}
|
||||
|
||||
const ShortcutItem = ({ shortcut, onDelete, onToggle, onUpdate }: ShortcutItemProps) => {
|
||||
const [isEditing, setIsEditing] = useState(false)
|
||||
|
||||
const handleSave = (updatedShortcut: Omit<AIShortcut, "id">) => {
|
||||
onUpdate(shortcut.id, updatedShortcut)
|
||||
setIsEditing(false)
|
||||
}
|
||||
|
||||
if (isEditing) {
|
||||
return (
|
||||
<div className="before:bg-accent relative pl-4 before:absolute before:inset-y-0 before:left-0 before:w-1 before:rounded-full before:content-['']">
|
||||
<ShortcutEditor
|
||||
shortcut={shortcut}
|
||||
onSave={handleSave}
|
||||
onCancel={() => setIsEditing(false)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const ShortcutItem = ({ shortcut, onDelete, onToggle, onEdit }: ShortcutItemProps) => {
|
||||
return (
|
||||
<div className="hover:bg-material-medium border-border group rounded-lg border p-4 transition-colors">
|
||||
<div className="flex items-start justify-between">
|
||||
|
|
@ -266,7 +307,7 @@ const ShortcutItem = ({ shortcut, onDelete, onToggle, onUpdate }: ShortcutItemPr
|
|||
|
||||
<div className="ml-4 flex items-center gap-3">
|
||||
<div className="flex items-center gap-1 opacity-60 transition-opacity group-hover:opacity-100">
|
||||
<Button variant="ghost" size="sm" onClick={() => setIsEditing(true)}>
|
||||
<Button variant="ghost" size="sm" onClick={() => onEdit(shortcut)}>
|
||||
<i className="i-mgc-edit-cute-re size-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" onClick={() => onDelete(shortcut.id)}>
|
||||
|
|
@ -289,13 +330,13 @@ const ShortcutItem = ({ shortcut, onDelete, onToggle, onUpdate }: ShortcutItemPr
|
|||
)
|
||||
}
|
||||
|
||||
interface ShortcutEditorProps {
|
||||
shortcut?: AIShortcut
|
||||
interface ShortcutModalContentProps {
|
||||
shortcut?: AIShortcut | null
|
||||
onSave: (shortcut: Omit<AIShortcut, "id">) => void
|
||||
onCancel: () => void
|
||||
}
|
||||
|
||||
const ShortcutEditor = ({ shortcut, onSave, onCancel }: ShortcutEditorProps) => {
|
||||
const ShortcutModalContent = ({ shortcut, onSave, onCancel }: ShortcutModalContentProps) => {
|
||||
const { t } = useTranslation("ai")
|
||||
const [name, setName] = useState(shortcut?.name || "")
|
||||
const [prompt, setPrompt] = useState(shortcut?.prompt || "")
|
||||
|
|
@ -316,9 +357,9 @@ const ShortcutEditor = ({ shortcut, onSave, onCancel }: ShortcutEditorProps) =>
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="bg-material-medium space-y-4 rounded-lg p-4">
|
||||
<div className="w-[400px] space-y-4">
|
||||
<div className="grid grid-cols-6 gap-4">
|
||||
<div className="col-span-4 space-y-2">
|
||||
<div className="col-span-6 space-y-2">
|
||||
<Label className="text-text text-xs">{t("shortcuts.name")}</Label>
|
||||
<Input
|
||||
value={name}
|
||||
|
|
@ -387,6 +428,491 @@ const ShortcutEditor = ({ shortcut, onSave, onCancel }: ShortcutEditorProps) =>
|
|||
)
|
||||
}
|
||||
|
||||
const MCPServicesSection = () => {
|
||||
const { t } = useTranslation("ai")
|
||||
const mcpEnabled = useMCPEnabled()
|
||||
const queryClient = useQueryClient()
|
||||
const dialog = useDialog()
|
||||
|
||||
// Reusable OAuth authorization handler using dialog
|
||||
const handleOAuthAuthorization = async (authorizationUrl: string) => {
|
||||
const confirmed = await dialog.ask({
|
||||
title: t("integration.mcp.service.auth_required"),
|
||||
message: t("integration.mcp.service.auth_message"),
|
||||
confirmText: t("integration.mcp.service.open_auth"),
|
||||
cancelText: t("words.cancel", { ns: "common" }),
|
||||
variant: "ask",
|
||||
})
|
||||
|
||||
if (confirmed) {
|
||||
const popup = window.open(
|
||||
authorizationUrl,
|
||||
"_blank",
|
||||
"width=600,height=700,scrollbars=yes,resizable=yes",
|
||||
)
|
||||
if (!popup) {
|
||||
toast.error(t("integration.mcp.service.popup_blocked"))
|
||||
} else {
|
||||
toast.success(t("integration.mcp.service.auth_window_opened"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Query for MCP connections
|
||||
const {
|
||||
data: mcpServices = [],
|
||||
isLoading,
|
||||
error,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
queryKey: mcpQueryKeys.connections(),
|
||||
queryFn: fetchMCPConnections,
|
||||
enabled: mcpEnabled,
|
||||
refetchInterval: 30_000,
|
||||
refetchOnWindowFocus: true,
|
||||
retry: 2,
|
||||
})
|
||||
|
||||
// Mutation for creating MCP connection
|
||||
const createConnectionMutation = useMutation({
|
||||
mutationFn: createMCPConnection,
|
||||
onSuccess: async (result) => {
|
||||
queryClient.invalidateQueries({ queryKey: mcpQueryKeys.connections() })
|
||||
|
||||
// Handle OAuth authorization if needed
|
||||
if (result.authorizationUrl) {
|
||||
await handleOAuthAuthorization(result.authorizationUrl)
|
||||
} else {
|
||||
toast.success(t("integration.mcp.service.added"))
|
||||
}
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(t("integration.mcp.service.discovery_failed"))
|
||||
console.error("Failed to create MCP connection:", error)
|
||||
},
|
||||
})
|
||||
|
||||
// Mutation for updating MCP connection
|
||||
const updateConnectionMutation = useMutation({
|
||||
mutationFn: ({
|
||||
connectionId,
|
||||
updateData,
|
||||
}: {
|
||||
connectionId: string
|
||||
updateData: Parameters<typeof updateMCPConnection>[1]
|
||||
}) => updateMCPConnection(connectionId, updateData),
|
||||
onSuccess: async (result) => {
|
||||
queryClient.invalidateQueries({ queryKey: mcpQueryKeys.connections() })
|
||||
|
||||
// Handle OAuth authorization if needed
|
||||
if (result.authorizationUrl) {
|
||||
await handleOAuthAuthorization(result.authorizationUrl)
|
||||
} else {
|
||||
toast.success(t("integration.mcp.service.updated"))
|
||||
}
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error("Failed to update MCP connection")
|
||||
console.error("Failed to update MCP connection:", error)
|
||||
},
|
||||
})
|
||||
|
||||
// Mutation for deleting MCP connection
|
||||
const deleteConnectionMutation = useMutation({
|
||||
mutationFn: deleteMCPConnection,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: mcpQueryKeys.connections() })
|
||||
toast.success(t("integration.mcp.service.deleted"))
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error("Failed to delete MCP connection")
|
||||
console.error("Failed to delete MCP connection:", error)
|
||||
},
|
||||
})
|
||||
|
||||
// Mutation for refreshing MCP tools
|
||||
const refreshToolsMutation = useMutation({
|
||||
mutationFn: (connectionIds?: string[]) => refreshMCPTools(connectionIds),
|
||||
onSuccess: () => {
|
||||
// Invalidate both connections (for updated counts) and tools queries
|
||||
queryClient.invalidateQueries({ queryKey: mcpQueryKeys.connections() })
|
||||
queryClient.invalidateQueries({ queryKey: mcpQueryKeys.all })
|
||||
toast.success("MCP tools refreshed successfully")
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error("Failed to refresh MCP tools")
|
||||
console.error("Failed to refresh MCP tools:", error)
|
||||
},
|
||||
})
|
||||
|
||||
const { present } = useModalStack()
|
||||
const handleAddService = () => {
|
||||
present({
|
||||
title: "Add MCP Service",
|
||||
content: ({ dismiss }: { dismiss: () => void }) => (
|
||||
<MCPServiceModalContent
|
||||
service={null}
|
||||
onSave={(service) => {
|
||||
createConnectionMutation.mutate(service)
|
||||
dismiss()
|
||||
}}
|
||||
onCancel={dismiss}
|
||||
isLoading={createConnectionMutation.isPending}
|
||||
/>
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
const handleEditService = (service: MCPService) => {
|
||||
present({
|
||||
title: "Edit MCP Service",
|
||||
content: ({ dismiss }: { dismiss: () => void }) => (
|
||||
<MCPServiceModalContent
|
||||
service={service}
|
||||
onSave={(updatedService) => {
|
||||
updateConnectionMutation.mutate({
|
||||
connectionId: service.id,
|
||||
updateData: updatedService,
|
||||
})
|
||||
dismiss()
|
||||
}}
|
||||
onCancel={dismiss}
|
||||
isLoading={updateConnectionMutation.isPending}
|
||||
/>
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
const handleDeleteService = (id: string) => {
|
||||
deleteConnectionMutation.mutate(id)
|
||||
}
|
||||
|
||||
const handleRefreshTools = (connectionId?: string) => {
|
||||
refreshToolsMutation.mutate(connectionId ? [connectionId] : undefined)
|
||||
}
|
||||
|
||||
// Show error message if query failed
|
||||
React.useEffect(() => {
|
||||
if (error) {
|
||||
toast.error("Failed to load MCP connections")
|
||||
console.error("Failed to load MCP connections:", error)
|
||||
}
|
||||
}, [error])
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-1">
|
||||
<Label className="text-text text-sm font-medium">{t("integration.mcp.enabled")}</Label>
|
||||
<div className="text-text-secondary text-xs">{t("integration.mcp.description")}</div>
|
||||
</div>
|
||||
<Switch checked={mcpEnabled} onCheckedChange={setMCPEnabled} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{mcpEnabled && (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="text-text text-sm font-medium">
|
||||
{t("integration.mcp.services.title")}
|
||||
</Label>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => refetch()}
|
||||
disabled={isLoading}
|
||||
title="Refresh connections"
|
||||
>
|
||||
{isLoading ? (
|
||||
<i className="i-mgc-loading-3-cute-re size-4 animate-spin" />
|
||||
) : (
|
||||
<i className="i-mgc-refresh-2-cute-re size-4" />
|
||||
)}
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" onClick={handleAddService}>
|
||||
<i className="i-mgc-add-cute-re mr-2 size-4" />
|
||||
{t("integration.mcp.services.add")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{mcpServices.length === 0 && (
|
||||
<div className="py-8 text-center">
|
||||
<div className="bg-fill-secondary mx-auto mb-3 flex size-12 items-center justify-center rounded-full">
|
||||
<i className="i-mgc-plugin-2-cute-re text-text size-6" />
|
||||
</div>
|
||||
<h4 className="text-text mb-1 text-sm font-medium">
|
||||
{t("integration.mcp.services.empty.title")}
|
||||
</h4>
|
||||
<p className="text-text-secondary text-xs">
|
||||
{t("integration.mcp.services.empty.description")}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isLoading && (
|
||||
<div className="flex items-center justify-center py-8">
|
||||
<i className="i-mgc-loading-3-cute-re size-6 animate-spin" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{mcpServices.map((service) => (
|
||||
<MCPServiceItem
|
||||
key={service.id}
|
||||
service={service}
|
||||
onDelete={handleDeleteService}
|
||||
onRefresh={handleRefreshTools}
|
||||
onEdit={handleEditService}
|
||||
isDeleting={
|
||||
deleteConnectionMutation.isPending &&
|
||||
deleteConnectionMutation.variables === service.id
|
||||
}
|
||||
isRefreshing={
|
||||
refreshToolsMutation.isPending && refreshToolsMutation.variables?.[0] === service.id
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
interface MCPServiceItemProps {
|
||||
service: MCPService
|
||||
onDelete: (id: string) => void
|
||||
onRefresh: (connectionId: string) => void
|
||||
onEdit: (service: MCPService) => void
|
||||
isDeleting?: boolean
|
||||
isRefreshing?: boolean
|
||||
}
|
||||
|
||||
const MCPServiceItem = ({
|
||||
service,
|
||||
onDelete,
|
||||
onRefresh,
|
||||
onEdit,
|
||||
isDeleting = false,
|
||||
isRefreshing = false,
|
||||
}: MCPServiceItemProps) => {
|
||||
const { t } = useTranslation("ai")
|
||||
|
||||
const getConnectionStatusColor = (isConnected: boolean) => {
|
||||
return isConnected ? "bg-green/10 text-green" : "bg-gray/10 text-text-tertiary"
|
||||
}
|
||||
|
||||
const getConnectionStatusText = (isConnected: boolean) => {
|
||||
return isConnected
|
||||
? t("integration.mcp.service.connected")
|
||||
: t("integration.mcp.service.disconnected")
|
||||
}
|
||||
|
||||
const formatDate = (dateString: string | null) => {
|
||||
if (!dateString) return "Never"
|
||||
return new Date(dateString).toLocaleDateString()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="hover:bg-material-medium border-border group rounded-lg border p-4 transition-colors">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1 space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<h4 className="text-text text-sm font-medium">{service.name}</h4>
|
||||
<div
|
||||
className={`rounded-full px-2 py-1 text-xs ${getConnectionStatusColor(service.isConnected)}`}
|
||||
>
|
||||
{getConnectionStatusText(service.isConnected)}
|
||||
</div>
|
||||
<div className="bg-blue/10 text-blue rounded-full px-2 py-1 text-xs">
|
||||
{service.transportType}
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
{service.url && (
|
||||
<p className="text-text-secondary text-xs">
|
||||
<span className="text-text-tertiary">URL:</span> {service.url}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<p className="text-text-secondary text-xs">
|
||||
<span className="text-text-tertiary">Tools:</span> {service.toolCount}
|
||||
<span className="text-text-tertiary ml-4">Created:</span>{" "}
|
||||
{formatDate(service.createdAt)}
|
||||
<span className="text-text-tertiary ml-4">Last Used:</span>{" "}
|
||||
{formatDate(service.lastUsed)}
|
||||
</p>
|
||||
{service.lastError && (
|
||||
<p className="text-red text-xs">
|
||||
<span className="text-text-tertiary">Error:</span> {service.lastError}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="ml-4 flex items-center gap-1 opacity-60 transition-opacity group-hover:opacity-100">
|
||||
<Button variant="ghost" size="sm" onClick={() => onEdit(service)} title="Edit connection">
|
||||
<i className="i-mgc-edit-cute-re size-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onRefresh(service.id)}
|
||||
title="Refresh tools"
|
||||
disabled={isRefreshing}
|
||||
>
|
||||
{isRefreshing ? (
|
||||
<i className="i-mgc-loading-3-cute-re size-4 animate-spin" />
|
||||
) : (
|
||||
<i className="i-mgc-refresh-2-cute-re size-4" />
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onDelete(service.id)}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
{isDeleting ? (
|
||||
<i className="i-mgc-loading-3-cute-re size-4 animate-spin" />
|
||||
) : (
|
||||
<i className="i-mgc-delete-2-cute-re size-4" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
interface MCPServiceModalContentProps {
|
||||
service?: MCPService | null
|
||||
onSave: (service: {
|
||||
name: string
|
||||
transportType: "streamable-http" | "sse"
|
||||
url: string
|
||||
headers?: Record<string, string>
|
||||
}) => void
|
||||
onCancel: () => void
|
||||
isLoading?: boolean
|
||||
}
|
||||
|
||||
const MCPServiceModalContent = ({
|
||||
service,
|
||||
onSave,
|
||||
onCancel,
|
||||
isLoading = false,
|
||||
}: MCPServiceModalContentProps) => {
|
||||
const { t } = useTranslation("ai")
|
||||
const [name, setName] = useState(service?.name || "")
|
||||
const [url, setUrl] = useState(service?.url || "")
|
||||
const [transportType, setTransportType] = useState<"streamable-http" | "sse">(
|
||||
service?.transportType || "streamable-http",
|
||||
)
|
||||
const [headers, setHeaders] = useState<Record<string, string>>(service?.headers || {})
|
||||
|
||||
const handleSave = () => {
|
||||
if (!name.trim()) {
|
||||
toast.error(t("integration.mcp.service.validation.name_required"))
|
||||
return
|
||||
}
|
||||
|
||||
if (!url.trim()) {
|
||||
toast.error(t("integration.mcp.service.validation.baseUrl_required"))
|
||||
return
|
||||
}
|
||||
|
||||
// Basic URL validation
|
||||
try {
|
||||
new URL(url.trim())
|
||||
} catch {
|
||||
toast.error(t("integration.mcp.service.validation.invalid_url"))
|
||||
return
|
||||
}
|
||||
|
||||
onSave({
|
||||
name: name.trim(),
|
||||
transportType,
|
||||
url: url.trim(),
|
||||
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-text text-xs">{t("integration.mcp.service.name")}</Label>
|
||||
<Input
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder={t("integration.mcp.service.name_placeholder")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="text-text text-xs">Transport Type</Label>
|
||||
<Select
|
||||
value={transportType}
|
||||
onValueChange={(value) => setTransportType(value as "streamable-http" | "sse")}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select transport type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent position="item-aligned">
|
||||
<SelectItem value="streamable-http">Streamable HTTP</SelectItem>
|
||||
<SelectItem value="sse">Server-Sent Events</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="text-text text-xs">URL</Label>
|
||||
<Input
|
||||
value={url}
|
||||
onChange={(e) => setUrl(e.target.value)}
|
||||
placeholder="https://example.com/mcp"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="min-w-[500px] space-y-2">
|
||||
<Label className="text-text text-xs">Headers (Optional)</Label>
|
||||
<KeyValueEditor
|
||||
value={headers}
|
||||
onChange={setHeaders}
|
||||
keyPlaceholder="Header name"
|
||||
valuePlaceholder="Header value"
|
||||
addButtonText="Add Header"
|
||||
minRows={0}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div />
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" size="sm" onClick={onCancel} disabled={isLoading}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button size="sm" onClick={handleSave} disabled={isLoading}>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<i className="i-mgc-loading-3-cute-re mr-2 size-4 animate-spin" />
|
||||
Saving...
|
||||
</>
|
||||
) : (
|
||||
"Save"
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const PanelStyleSegment = () => {
|
||||
const { t } = useTranslation("ai")
|
||||
const panelStyle = useAIChatPanelStyle()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
import type { MCPService } from "@follow/shared/settings/interface"
|
||||
|
||||
import { followApi } from "~/lib/api-client"
|
||||
|
||||
// MCP Service API calls - these connect to the actual server endpoints via followClient
|
||||
export const createMCPConnection = async (connectionData: {
|
||||
name: string
|
||||
transportType: "streamable-http" | "sse"
|
||||
url: string
|
||||
headers?: Record<string, string>
|
||||
}): Promise<{ authorizationUrl?: string }> => {
|
||||
const res = await followApi.mcp.createConnection(connectionData)
|
||||
return { authorizationUrl: res.authorizationUrl }
|
||||
}
|
||||
|
||||
export const fetchMCPConnections = async (): Promise<MCPService[]> => {
|
||||
const response = await followApi.mcp.getConnections()
|
||||
return response.data
|
||||
}
|
||||
|
||||
export const updateMCPConnection = async (
|
||||
connectionId: string,
|
||||
updateData: {
|
||||
name?: string
|
||||
transportType?: "streamable-http" | "sse"
|
||||
url?: string
|
||||
headers?: Record<string, string>
|
||||
},
|
||||
): Promise<{ authorizationUrl?: string }> => {
|
||||
const res = await followApi.mcp.updateConnection({ connectionId, ...updateData })
|
||||
return { authorizationUrl: res.authorizationUrl }
|
||||
}
|
||||
|
||||
export const deleteMCPConnection = async (connectionId: string): Promise<void> => {
|
||||
await followApi.mcp.deleteConnection({ connectionId })
|
||||
}
|
||||
|
||||
export const refreshMCPTools = async (connectionIds?: string[]): Promise<void> => {
|
||||
await followApi.mcp.refreshTools({ connectionIds })
|
||||
}
|
||||
|
||||
export const getMCPTools = async (connectionId: string) => {
|
||||
const response = await followApi.mcp.getTools({ connectionId })
|
||||
return response.data
|
||||
}
|
||||
|
||||
// Query key factory for MCP queries
|
||||
export const mcpQueryKeys = {
|
||||
all: ["mcp"] as const,
|
||||
connections: () => [...mcpQueryKeys.all, "connections"] as const,
|
||||
tools: (connectionId: string) => [...mcpQueryKeys.all, "tools", connectionId] as const,
|
||||
}
|
||||
|
|
@ -2,9 +2,6 @@ Rewrite:
|
|||
|
||||
Test url:
|
||||
|
||||
https://css-spring-generator.vercel.app/share/feeds/1
|
||||
https://css-spring-generator.vercel.app/og/feed/1
|
||||
|
||||
http://localhost:2234/share/feeds/41223694984583197
|
||||
http://localhost:2234/share/feeds/41375451836487680?view=2
|
||||
http://localhost:2234/share/feeds/41147805276726317?view=2
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@
|
|||
"masonic": "4.1.0",
|
||||
"nanoid": "5.1.5",
|
||||
"path-to-regexp": "8.2.0",
|
||||
"tailwindcss-uikit-colors": "1.0.0-alpha.1",
|
||||
"tailwindcss-uikit-colors": "catalog:",
|
||||
"tsdown": "0.12.9",
|
||||
"tsx": "4.20.3",
|
||||
"typescript": "catalog:",
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M10.002 2.041c-1.207.168-2.251.992-2.719 2.146-.184.453-.223.672-.223 1.263V6h-.64c-1.033.001-1.569.111-2.26.464A3.978 3.978 0 0 0 2.464 8.16c-.392.765-.475 1.244-.456 2.6.015 1.053.029 1.116.297 1.378.368.358.734.409 1.326.187.235-.089.401-.125.569-.125.81 0 1.429.756 1.266 1.546-.126.612-.581 1.005-1.206 1.044-.239.015-.328 0-.612-.104-.414-.152-.629-.185-.837-.129-.25.067-.534.28-.67.5l-.121.197-.012 1.464c-.016 1.945.035 2.3.456 3.122a3.978 3.978 0 0 0 1.696 1.696c.82.42 1.18.473 3.099.456 1.609-.013 1.6-.012 1.88-.297.348-.355.399-.721.182-1.326-.114-.319-.129-.405-.112-.639.044-.62.438-1.071 1.045-1.196.768-.158 1.48.392 1.537 1.188.017.244.003.324-.114.648-.217.6-.165.969.184 1.325.264.269.326.282 1.379.297 1.356.019 1.835-.064 2.6-.456a3.978 3.978 0 0 0 1.696-1.696c.353-.691.463-1.226.464-2.26v-.64l.55-.001c.591 0 .813-.039 1.263-.222a3.522 3.522 0 0 0 2.072-2.337c.121-.455.121-1.305 0-1.76a3.522 3.522 0 0 0-2.072-2.337c-.45-.183-.672-.222-1.263-.222L18 10.06v-.14c0-.077-.027-.313-.058-.525-.222-1.469-1.266-2.705-2.697-3.192A5.5 5.5 0 0 0 14.08 6h-.14v-.55c0-.591-.039-.81-.223-1.263-.412-1.015-1.306-1.808-2.329-2.066a4.505 4.505 0 0 0-1.386-.08m1.133 2.113c.293.145.562.416.709.715.08.161.094.253.095.591.001.395-.001.405-.189.803-.166.35-.19.435-.19.66.001.328.125.607.363.81.275.236.445.267 1.456.267.959 0 1.166.03 1.551.225.265.135.708.575.84.835.185.366.201.476.226 1.58.023.982.031 1.072.111 1.22.196.365.55.578.965.579.215.001.309-.026.655-.184.222-.102.495-.197.608-.211a1.474 1.474 0 0 1 1.514.828c.119.241.131.3.131.628s-.012.387-.131.628c-.148.3-.416.568-.718.716-.161.08-.253.094-.591.095-.394.001-.406-.002-.808-.189-.352-.164-.444-.19-.66-.19a1.034 1.034 0 0 0-.805.363c-.253.295-.267.399-.267 1.948 0 1.493-.017 1.649-.229 2.066-.133.263-.575.703-.841.838-.277.14-.615.225-.898.225H13.8v-.256c0-1.395-.971-2.674-2.361-3.11-.262-.082-.379-.094-.939-.094-.559 0-.678.012-.94.094-.797.248-1.527.822-1.922 1.51-.261.454-.438 1.101-.438 1.6V20h-.732c-1.052 0-1.381-.105-1.872-.596-.491-.491-.596-.82-.596-1.872V16.8l.27-.001c.338-.001.884-.12 1.242-.27.975-.41 1.744-1.381 1.929-2.434.073-.414.032-1.197-.081-1.555-.245-.78-.824-1.511-1.504-1.902-.454-.261-1.101-.438-1.6-.438H4v-.232c0-.795.529-1.542 1.316-1.856.215-.086.287-.091 1.804-.111 1.523-.02 1.586-.024 1.74-.107.52-.282.717-.859.482-1.414-.333-.785-.312-.71-.293-1.077.02-.397.109-.618.359-.902.305-.348.686-.507 1.159-.487.249.01.358.037.568.14" fill="#10161F" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
|
|
@ -13,6 +13,47 @@
|
|||
"export_error": "Failed to export chat",
|
||||
"export_success": "Chat exported successfully",
|
||||
"features.title": "Features",
|
||||
"integration.mcp.description": "Connect to MCP-compatible services that extend AI capabilities through secure OAuth integration",
|
||||
"integration.mcp.enabled": "Enable MCP Services",
|
||||
"integration.mcp.service.active": "Active",
|
||||
"integration.mcp.service.added": "MCP service added successfully",
|
||||
"integration.mcp.service.auth_message": "This service requires OAuth authorization. Click 'Open Authorization' to proceed with authentication in a new window.",
|
||||
"integration.mcp.service.auth_required": "Authorization Required",
|
||||
"integration.mcp.service.auth_window_opened": "Authorization window opened. Please complete authentication.",
|
||||
"integration.mcp.service.baseUrl": "Service Base URL",
|
||||
"integration.mcp.service.baseUrl_placeholder": "e.g. https://api.example.com",
|
||||
"integration.mcp.service.connect": "Connect",
|
||||
"integration.mcp.service.connected": "Connected",
|
||||
"integration.mcp.service.connected_success": "Successfully connected to MCP service",
|
||||
"integration.mcp.service.connecting": "Connecting...",
|
||||
"integration.mcp.service.connection_failed": "Failed to connect to MCP service",
|
||||
"integration.mcp.service.degraded": "Degraded",
|
||||
"integration.mcp.service.deleted": "MCP service deleted successfully",
|
||||
"integration.mcp.service.disconnect": "Disconnect",
|
||||
"integration.mcp.service.disconnected": "Disconnected",
|
||||
"integration.mcp.service.discover": "Discover Service",
|
||||
"integration.mcp.service.discovery_failed": "Failed to discover service endpoints",
|
||||
"integration.mcp.service.endpoints": "OAuth Endpoints",
|
||||
"integration.mcp.service.error": "Connection Error",
|
||||
"integration.mcp.service.healthy": "Healthy",
|
||||
"integration.mcp.service.inactive": "Inactive",
|
||||
"integration.mcp.service.name": "Service Name",
|
||||
"integration.mcp.service.name_placeholder": "e.g. GitHub Tools, Slack Integration",
|
||||
"integration.mcp.service.open_auth": "Open Authorization",
|
||||
"integration.mcp.service.open_manually": "Open Authorization",
|
||||
"integration.mcp.service.popup_blocked": "Authorization popup was blocked by your browser",
|
||||
"integration.mcp.service.reconnect": "Reconnect",
|
||||
"integration.mcp.service.scopes": "Required Scopes",
|
||||
"integration.mcp.service.unhealthy": "Unhealthy",
|
||||
"integration.mcp.service.updated": "MCP service updated successfully",
|
||||
"integration.mcp.service.validation.baseUrl_required": "Base URL is required",
|
||||
"integration.mcp.service.validation.invalid_url": "Please enter a valid URL",
|
||||
"integration.mcp.service.validation.name_required": "Service name is required",
|
||||
"integration.mcp.services.add": "Add Service",
|
||||
"integration.mcp.services.empty.description": "Connect MCP services to extend AI capabilities with external tools, APIs, and data sources.",
|
||||
"integration.mcp.services.empty.title": "No MCP Services Connected",
|
||||
"integration.mcp.services.title": "MCP Services",
|
||||
"integration.title": "Integration",
|
||||
"personalize.description": "Tell me about yourself to get personalized AI responses",
|
||||
"personalize.prompt.help": "This helps AI provide personalized responses based on your preferences.",
|
||||
"personalize.prompt.label": "Personal Prompt",
|
||||
|
|
@ -35,7 +76,6 @@
|
|||
"shortcuts.added": "Shortcut added successfully",
|
||||
"shortcuts.create_first": "Create your first shortcut",
|
||||
"shortcuts.deleted": "Shortcut deleted successfully",
|
||||
"shortcuts.description": "Create custom AI shortcuts for quick actions",
|
||||
"shortcuts.empty.description": "Create custom AI shortcuts to quickly perform common tasks and get instant AI assistance.",
|
||||
"shortcuts.empty.title": "No Shortcuts Yet",
|
||||
"shortcuts.enabled": "Enabled",
|
||||
|
|
|
|||
|
|
@ -13,6 +13,47 @@
|
|||
"export_error": "Chat のエクスポートに失敗しました",
|
||||
"export_success": "Chatが正常にエクスポートされました",
|
||||
"features.title": "機能",
|
||||
"integration.mcp.description": "安全なOAuth統合により、AI機能を拡張するMCP対応サービスに接続",
|
||||
"integration.mcp.enabled": "MCPサービスを有効にする",
|
||||
"integration.mcp.service.active": "アクティブ",
|
||||
"integration.mcp.service.added": "MCPサービスが正常に追加されました",
|
||||
"integration.mcp.service.auth_message": "このサービスはOAuth認証が必要です。新しいウィンドウで認証を進めるには「認証を開く」をクリックしてください。",
|
||||
"integration.mcp.service.auth_required": "認証が必要",
|
||||
"integration.mcp.service.auth_window_opened": "認証ウィンドウが開きました。認証を完了してください。",
|
||||
"integration.mcp.service.baseUrl": "サービスベースURL",
|
||||
"integration.mcp.service.baseUrl_placeholder": "例:https://api.example.com",
|
||||
"integration.mcp.service.connect": "接続",
|
||||
"integration.mcp.service.connected": "接続済み",
|
||||
"integration.mcp.service.connected_success": "MCPサービスに正常に接続されました",
|
||||
"integration.mcp.service.connecting": "接続中...",
|
||||
"integration.mcp.service.connection_failed": "MCPサービスへの接続に失敗しました",
|
||||
"integration.mcp.service.degraded": "劣化",
|
||||
"integration.mcp.service.deleted": "MCPサービスが正常に削除されました",
|
||||
"integration.mcp.service.disconnect": "切断",
|
||||
"integration.mcp.service.disconnected": "切断済み",
|
||||
"integration.mcp.service.discover": "サービスを発見",
|
||||
"integration.mcp.service.discovery_failed": "サービスエンドポイントの発見に失敗しました",
|
||||
"integration.mcp.service.endpoints": "OAuthエンドポイント",
|
||||
"integration.mcp.service.error": "接続エラー",
|
||||
"integration.mcp.service.healthy": "正常",
|
||||
"integration.mcp.service.inactive": "非アクティブ",
|
||||
"integration.mcp.service.name": "サービス名",
|
||||
"integration.mcp.service.name_placeholder": "例:GitHub Tools、Slack Integration",
|
||||
"integration.mcp.service.open_auth": "認証を開く",
|
||||
"integration.mcp.service.open_manually": "認証を開く",
|
||||
"integration.mcp.service.popup_blocked": "認証ポップアップがブラウザによってブロックされました",
|
||||
"integration.mcp.service.reconnect": "再接続",
|
||||
"integration.mcp.service.scopes": "必要なスコープ",
|
||||
"integration.mcp.service.unhealthy": "異常",
|
||||
"integration.mcp.service.updated": "MCPサービスが正常に更新されました",
|
||||
"integration.mcp.service.validation.baseUrl_required": "ベースURLは必須です",
|
||||
"integration.mcp.service.validation.invalid_url": "有効なURLを入力してください",
|
||||
"integration.mcp.service.validation.name_required": "サービス名は必須です",
|
||||
"integration.mcp.services.add": "サービスを追加",
|
||||
"integration.mcp.services.empty.description": "MCPサービスに接続して、外部ツール、API、データソースでAI機能を拡張します。",
|
||||
"integration.mcp.services.empty.title": "MCPサービスが接続されていません",
|
||||
"integration.mcp.services.title": "MCPサービス",
|
||||
"integration.title": "統合",
|
||||
"personalize.description": "あなた自身について教えてください。そうすれば、パーソナライズされたAIの応答が得られます。",
|
||||
"personalize.prompt.help": "これにより、AIはあなたの好みに基づいてパーソナライズされた応答を提供できます。",
|
||||
"personalize.prompt.label": "個人用プロンプト",
|
||||
|
|
@ -35,7 +76,6 @@
|
|||
"shortcuts.added": "ショートカットが正常に追加されました",
|
||||
"shortcuts.create_first": "最初のショートカットを作成",
|
||||
"shortcuts.deleted": "ショートカットが正常に削除されました",
|
||||
"shortcuts.description": "クイックアクションのためのカスタムAIショートカットを作成します",
|
||||
"shortcuts.empty.description": "一般的なタスクを迅速に実行し、即座にAIアシスタンスを得るためのカスタムAIショートカットを作成します。",
|
||||
"shortcuts.empty.title": "ショートカットはまだありません",
|
||||
"shortcuts.enabled": "有効",
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@
|
|||
"shortcuts.added": "快捷方式添加成功",
|
||||
"shortcuts.create_first": "创建您的第一个快捷方式",
|
||||
"shortcuts.deleted": "快捷方式删除成功",
|
||||
"shortcuts.description": "创建自定义AI快捷方式以快速执行操作",
|
||||
"shortcuts.empty.description": "创建自定义AI快捷方式,快速执行常见任务并获得即时AI协助。",
|
||||
"shortcuts.empty.title": "暂无快捷方式",
|
||||
"shortcuts.enabled": "已启用",
|
||||
|
|
|
|||
|
|
@ -73,5 +73,12 @@
|
|||
"14002": "Upload failed",
|
||||
"15000": "AI token limit exceeded. Please try again later.",
|
||||
"15001": "You are not allowed to access AI chat",
|
||||
"undefined": "MCP configuration error"
|
||||
"16000": "MCP OAuth error",
|
||||
"16001": "MCP discovery error",
|
||||
"16002": "MCP service not found",
|
||||
"16003": "Invalid or expired OAuth state",
|
||||
"16004": "Token exchange failed",
|
||||
"16005": "User MCP service not found",
|
||||
"16006": "MCP configuration error",
|
||||
"16007": "MCP tool execution failed"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
"tailwindcss-motion": "1.1.1",
|
||||
"tailwindcss-multi": "0.4.6",
|
||||
"tailwindcss-safe-area": "0.6.0",
|
||||
"tailwindcss-uikit-colors": "1.0.0-alpha.1"
|
||||
"tailwindcss-uikit-colors": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"postcss": "8.5.6",
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@
|
|||
"remark-gh-alerts": "0.0.3",
|
||||
"remark-rehype": "11.1.2",
|
||||
"sonner": "2.0.6",
|
||||
"tailwindcss-uikit-colors": "1.0.0-alpha.1",
|
||||
"tailwindcss-uikit-colors": "catalog:",
|
||||
"unified": "11.0.5",
|
||||
"unist-util-visit": "5.0.0",
|
||||
"unist-util-visit-parents": "5.1.3",
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ interface Token {
|
|||
function highlightJson(jsonString: string): string {
|
||||
// Escape HTML entities first
|
||||
const escaped = jsonString
|
||||
.replaceAll("&", "&")
|
||||
?.replaceAll("&", "&")
|
||||
.replaceAll("<", "<")
|
||||
.replaceAll(">", ">")
|
||||
|
||||
|
|
|
|||
|
|
@ -156,6 +156,10 @@ export const defaultAISettings: AISettings = {
|
|||
personalizePrompt: "",
|
||||
shortcuts: [],
|
||||
|
||||
// MCP Services
|
||||
mcpEnabled: false,
|
||||
mcpServices: [],
|
||||
|
||||
// Features
|
||||
autoScrollWhenStreaming: true,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,10 +178,31 @@ export interface AIShortcut {
|
|||
hotkey?: string
|
||||
}
|
||||
|
||||
export type MCPTransportType = "streamable-http" | "sse"
|
||||
|
||||
export interface MCPService {
|
||||
id: string
|
||||
name: string
|
||||
transportType: MCPTransportType
|
||||
url?: string
|
||||
headers?: Record<string, string>
|
||||
isConnected: boolean
|
||||
lastError?: string
|
||||
toolCount: number
|
||||
resourceCount: number
|
||||
promptCount: number
|
||||
createdAt: string
|
||||
lastUsed: string | null
|
||||
}
|
||||
|
||||
export interface AISettings {
|
||||
personalizePrompt: string
|
||||
shortcuts: AIShortcut[]
|
||||
|
||||
// MCP Services (stored locally, actual connections managed via server API)
|
||||
mcpEnabled: boolean
|
||||
mcpServices: MCPService[]
|
||||
|
||||
// Features
|
||||
autoScrollWhenStreaming: boolean
|
||||
}
|
||||
|
|
|
|||
117
pnpm-lock.yaml
117
pnpm-lock.yaml
|
|
@ -7,8 +7,11 @@ settings:
|
|||
catalogs:
|
||||
default:
|
||||
'@follow-app/client-sdk':
|
||||
specifier: 0.3.29
|
||||
version: 0.3.29
|
||||
specifier: 0.3.38
|
||||
version: 0.3.38
|
||||
tailwindcss-uikit-colors:
|
||||
specifier: 1.0.0
|
||||
version: 1.0.0
|
||||
typescript:
|
||||
specifier: 5.8.3
|
||||
version: 5.8.3
|
||||
|
|
@ -462,7 +465,7 @@ importers:
|
|||
version: 3.0.2(electron@37.2.0)
|
||||
'@follow-app/client-sdk':
|
||||
specifier: 'catalog:'
|
||||
version: 0.3.29(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
|
||||
version: 0.3.38(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
|
||||
'@follow/database':
|
||||
specifier: workspace:*
|
||||
version: link:../../../../packages/internal/database
|
||||
|
|
@ -1330,8 +1333,8 @@ importers:
|
|||
specifier: 8.2.0
|
||||
version: 8.2.0
|
||||
tailwindcss-uikit-colors:
|
||||
specifier: 1.0.0-alpha.1
|
||||
version: 1.0.0-alpha.1
|
||||
specifier: 'catalog:'
|
||||
version: 1.0.0
|
||||
tsdown:
|
||||
specifier: 0.12.9
|
||||
version: 0.12.9(typescript@5.8.3)
|
||||
|
|
@ -1409,8 +1412,8 @@ importers:
|
|||
specifier: 0.6.0
|
||||
version: 0.6.0(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@24.0.10)(typescript@5.8.3)))
|
||||
tailwindcss-uikit-colors:
|
||||
specifier: 1.0.0-alpha.1
|
||||
version: 1.0.0-alpha.1
|
||||
specifier: 'catalog:'
|
||||
version: 1.0.0
|
||||
devDependencies:
|
||||
postcss:
|
||||
specifier: 8.5.6
|
||||
|
|
@ -1635,8 +1638,8 @@ importers:
|
|||
specifier: 2.0.6
|
||||
version: 2.0.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
tailwindcss-uikit-colors:
|
||||
specifier: 1.0.0-alpha.1
|
||||
version: 1.0.0-alpha.1
|
||||
specifier: 'catalog:'
|
||||
version: 1.0.0
|
||||
unified:
|
||||
specifier: 11.0.5
|
||||
version: 11.0.5
|
||||
|
|
@ -1670,7 +1673,7 @@ importers:
|
|||
dependencies:
|
||||
'@follow-app/client-sdk':
|
||||
specifier: 'catalog:'
|
||||
version: 0.3.29(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
|
||||
version: 0.3.38(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
|
||||
'@follow/configs':
|
||||
specifier: workspace:*
|
||||
version: link:../../configs
|
||||
|
|
@ -1682,7 +1685,7 @@ importers:
|
|||
dependencies:
|
||||
'@follow-app/client-sdk':
|
||||
specifier: 'catalog:'
|
||||
version: 0.3.29(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
|
||||
version: 0.3.38(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
|
||||
'@follow/constants':
|
||||
specifier: workspace:*
|
||||
version: link:../constants
|
||||
|
|
@ -1760,7 +1763,7 @@ importers:
|
|||
dependencies:
|
||||
'@follow-app/client-sdk':
|
||||
specifier: 'catalog:'
|
||||
version: 0.3.29(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
|
||||
version: 0.3.38(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
|
||||
'@follow/constants':
|
||||
specifier: workspace:*
|
||||
version: link:../constants
|
||||
|
|
@ -1824,7 +1827,7 @@ importers:
|
|||
dependencies:
|
||||
'@follow-app/client-sdk':
|
||||
specifier: 'catalog:'
|
||||
version: 0.3.29(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
|
||||
version: 0.3.38(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
|
||||
'@follow/configs':
|
||||
specifier: workspace:*
|
||||
version: link:../../configs
|
||||
|
|
@ -4048,23 +4051,29 @@ packages:
|
|||
'@floating-ui/utils@0.2.10':
|
||||
resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
|
||||
|
||||
'@follow-app/client-sdk@0.3.29':
|
||||
resolution: {integrity: sha512-bQgY0S6xrWO0iLO4eeptU4vxcmQ36iSrLl6omNscK6SCXzlebCYRmqQy7dsFHU4+bditADjjLulCGWmbDX9IJQ==}
|
||||
'@follow-app/client-sdk@0.3.38':
|
||||
resolution: {integrity: sha512-dewSXei/nLfwY+S6a4P8B9xlAgh4LhORyBWphN87b54Lbs3kX3ydx6esrcXliUTwOFH+TkpxbIQR16zPR/ApBw==}
|
||||
|
||||
'@folo-services/ai-tools@0.2.20':
|
||||
resolution: {integrity: sha512-1AJI48H2g64JhuTruSqQKUrn4Tc353hMFdKAbohh65AxTAMN/1pIw9w17Dtjm+g5BVlZUXYKaiIvaN9tHsXLNA==}
|
||||
|
||||
'@folo-services/constants@0.1.18':
|
||||
resolution: {integrity: sha512-3LDxX4Ebvr6GLdL5Ce2ZkAJwfNjfsdqOLpuFRZ3bq2gVwQZbIGsln+qYnInmR5+1i3DtebglrAj/IktnT3BsIg==}
|
||||
'@folo-services/constants@0.1.21':
|
||||
resolution: {integrity: sha512-4ws5nXxYjvm204rHH03ohUAhll5oWXgvlRnVGzxe9BkW+f64kJOshFxEeTxMDm2SMj0cnjEblsGvA29xJfXs/A==}
|
||||
|
||||
'@folo-services/drizzle@0.1.13':
|
||||
resolution: {integrity: sha512-h6tndGnTLrAmfvxK5XsXGdZaMi2GBmFZBFiBqtabC7Y05A1ZSOaWg/d9f60Ydh586LmVonz5VGrhvzTdn5ukhg==}
|
||||
|
||||
'@folo-services/drizzle@0.1.16':
|
||||
resolution: {integrity: sha512-ieo0vAf3KRlz6GZ9WY2BRut80qB9nxplTHS8rRtUJVnrsqP6mXDgChjAoX32vy3YKHk6xJfrzgWLoAlqfsvxyQ==}
|
||||
|
||||
'@folo-services/exceptions@0.1.11':
|
||||
resolution: {integrity: sha512-OMKat8KcIxjw9pBfG3pC1kRcIs0YiuaYmMDA2MRy8AEGcki8F9u5e5QH7OKgP/LlNR7Fs2Csz1XetvcC4nB+RQ==}
|
||||
|
||||
'@folo-services/shared@0.0.8':
|
||||
resolution: {integrity: sha512-rfl1vvGMxUnchckGSKOkeT5hS1NAzZTJ8gPF6yFR+TuSe2SZzq1a10/66nK9OxmqaDFaHdOpQfNXz2vp0gRJpg==}
|
||||
'@folo-services/exceptions@0.1.14':
|
||||
resolution: {integrity: sha512-VFVRkzXg7BytEvdg7iyUXc6qaAudTTtOU8PrTkRUG4ZB9udLE/uVT5q+6poS1CkwMi2GroH27AFC4fnfVp6Fvw==}
|
||||
|
||||
'@folo-services/shared@0.0.11':
|
||||
resolution: {integrity: sha512-WS0OVoswUfPr4X8SCPYdyxx4XL7Khh63NCk0pilCtci3XtO2kMPWlLEM4gJAfg6g5GAs4oVNvsJiNEKEa3Lo1g==}
|
||||
|
||||
'@fontsource/sn-pro@5.2.5':
|
||||
resolution: {integrity: sha512-rBdBv/0ygj6bkO7xDMMFpwobLdSrcQ2Jncb6DIwdeYGoAgeWkQRwVYhGDKasfLjEYCNYxrqr6wsXsM9+aU39RA==}
|
||||
|
|
@ -7119,6 +7128,9 @@ packages:
|
|||
apple-uikit-colors@0.6.2:
|
||||
resolution: {integrity: sha512-sv0b92krbTZGNt4AJQLAPioUvuEI/wW2cLqGuEBWl3KpU2JK/odOftx8+dGSC5jGWhdB5qnwBqF96X2FA8FUQA==}
|
||||
|
||||
apple-uikit-colors@1.0.0:
|
||||
resolution: {integrity: sha512-G2Ti2ogMOOC1phfHacSrLacDiE0RcLI7IG3aNoaw7Ack23WI8/9QczmUuhxxkU9BoIR2euCZ2G4lD5qJ+OknwA==}
|
||||
|
||||
archiver-utils@2.1.0:
|
||||
resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==}
|
||||
engines: {node: '>= 6'}
|
||||
|
|
@ -14571,8 +14583,8 @@ packages:
|
|||
tailwindcss-uikit-colors@0.6.2:
|
||||
resolution: {integrity: sha512-RfF3VVB2nHvJwPZ2ZSTMgKYbZjRny/KW3rP6ufg6/bopxINDQ9TpN/fJXxvKnD86hIU0F2oLIfAv2r6o/ZWcZw==}
|
||||
|
||||
tailwindcss-uikit-colors@1.0.0-alpha.1:
|
||||
resolution: {integrity: sha512-akeDIVZMRO6tFbr6x4YsRkx24xx/ofEfsJdL2lq7AE/sDBnUbs5Pr8by4ngBbxJ2U2eam000B/eJrb6r6YzLmw==}
|
||||
tailwindcss-uikit-colors@1.0.0:
|
||||
resolution: {integrity: sha512-18MGdMVSoXFKjcVkUIu7Q5USqdenAvgFJT9DEocPmdS94b8d/z3s0Uw8LvaNDINfDN9ls24hfg6/hyFqpcZpxw==}
|
||||
|
||||
tailwindcss@3.4.17:
|
||||
resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==}
|
||||
|
|
@ -18921,12 +18933,12 @@ snapshots:
|
|||
|
||||
'@floating-ui/utils@0.2.10': {}
|
||||
|
||||
'@follow-app/client-sdk@0.3.29(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)':
|
||||
'@follow-app/client-sdk@0.3.38(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)':
|
||||
dependencies:
|
||||
'@folo-services/constants': 0.1.18
|
||||
'@folo-services/drizzle': 0.1.13(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)
|
||||
'@folo-services/exceptions': 0.1.11
|
||||
'@folo-services/shared': 0.0.8(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
|
||||
'@folo-services/constants': 0.1.21
|
||||
'@folo-services/drizzle': 0.1.16(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)
|
||||
'@folo-services/exceptions': 0.1.14
|
||||
'@folo-services/shared': 0.0.11(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
|
||||
zod: 3.25.76
|
||||
transitivePeerDependencies:
|
||||
- '@aws-sdk/client-rds-data'
|
||||
|
|
@ -18999,7 +19011,7 @@ snapshots:
|
|||
- sql.js
|
||||
- sqlite3
|
||||
|
||||
'@folo-services/constants@0.1.18':
|
||||
'@folo-services/constants@0.1.21':
|
||||
dependencies:
|
||||
zod: 3.25.76
|
||||
|
||||
|
|
@ -19042,11 +19054,52 @@ snapshots:
|
|||
- sql.js
|
||||
- sqlite3
|
||||
|
||||
'@folo-services/drizzle@0.1.16(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)':
|
||||
dependencies:
|
||||
'@folo-services/exceptions': 0.1.14
|
||||
drizzle-orm: 0.44.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
|
||||
drizzle-zod: 0.7.1(drizzle-orm@0.44.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3))(zod@3.25.76)
|
||||
nanoid: 5.1.5
|
||||
pg: 8.16.3
|
||||
zod: 3.25.76
|
||||
transitivePeerDependencies:
|
||||
- '@aws-sdk/client-rds-data'
|
||||
- '@cloudflare/workers-types'
|
||||
- '@electric-sql/pglite'
|
||||
- '@libsql/client'
|
||||
- '@libsql/client-wasm'
|
||||
- '@neondatabase/serverless'
|
||||
- '@op-engineering/op-sqlite'
|
||||
- '@opentelemetry/api'
|
||||
- '@planetscale/database'
|
||||
- '@prisma/client'
|
||||
- '@tidbcloud/serverless'
|
||||
- '@types/better-sqlite3'
|
||||
- '@types/pg'
|
||||
- '@types/sql.js'
|
||||
- '@upstash/redis'
|
||||
- '@vercel/postgres'
|
||||
- '@xata.io/client'
|
||||
- better-sqlite3
|
||||
- bun-types
|
||||
- expo-sqlite
|
||||
- gel
|
||||
- knex
|
||||
- kysely
|
||||
- mysql2
|
||||
- pg-native
|
||||
- postgres
|
||||
- prisma
|
||||
- sql.js
|
||||
- sqlite3
|
||||
|
||||
'@folo-services/exceptions@0.1.11': {}
|
||||
|
||||
'@folo-services/shared@0.0.8(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)':
|
||||
'@folo-services/exceptions@0.1.14': {}
|
||||
|
||||
'@folo-services/shared@0.0.11(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)':
|
||||
dependencies:
|
||||
'@folo-services/drizzle': 0.1.13(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)
|
||||
'@folo-services/drizzle': 0.1.16(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)
|
||||
drizzle-orm: 0.44.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
|
||||
drizzle-zod: 0.7.1(drizzle-orm@0.44.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3))(zod@3.25.76)
|
||||
zod: 3.25.76
|
||||
|
|
@ -22755,6 +22808,8 @@ snapshots:
|
|||
|
||||
apple-uikit-colors@0.6.2: {}
|
||||
|
||||
apple-uikit-colors@1.0.0: {}
|
||||
|
||||
archiver-utils@2.1.0:
|
||||
dependencies:
|
||||
glob: 7.2.3
|
||||
|
|
@ -31549,9 +31604,9 @@ snapshots:
|
|||
dependencies:
|
||||
apple-uikit-colors: 0.6.2
|
||||
|
||||
tailwindcss-uikit-colors@1.0.0-alpha.1:
|
||||
tailwindcss-uikit-colors@1.0.0:
|
||||
dependencies:
|
||||
apple-uikit-colors: 0.6.2
|
||||
apple-uikit-colors: 1.0.0
|
||||
|
||||
tailwindcss@3.4.17(ts-node@10.9.2(@types/node@24.0.10)(typescript@5.8.3)):
|
||||
dependencies:
|
||||
|
|
|
|||
|
|
@ -58,4 +58,5 @@ overrides:
|
|||
|
||||
catalog:
|
||||
typescript: "5.8.3"
|
||||
"@follow-app/client-sdk": "0.3.29"
|
||||
"@follow-app/client-sdk": "0.3.38"
|
||||
tailwindcss-uikit-colors: "1.0.0"
|
||||
|
|
|
|||
Loading…
Reference in New Issue