From 0c9977d5d3650a9097ebdabdab6669ceaa52049c Mon Sep 17 00:00:00 2001 From: Innei Date: Tue, 19 Aug 2025 23:34:19 +0800 Subject: [PATCH] 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 * 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 * 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 * 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 * chore: update deps Signed-off-by: Innei --------- Signed-off-by: Innei --- .../layer/renderer/src/atoms/settings/ai.ts | 33 +- .../components/layouts/ChatInterface.tsx | 4 +- .../message/ToolInvocationComponent.tsx | 40 +- .../src/modules/settings/modal/context.tsx | 6 + .../src/modules/settings/modal/layout.tsx | 25 +- .../renderer/src/modules/settings/tabs/ai.tsx | 656 ++++++++++++++++-- .../desktop/layer/renderer/src/queries/mcp.ts | 52 ++ apps/ssr/note.md | 3 - apps/ssr/package.json | 2 +- icons/mgc/plugin_2_cute_re.svg | 1 + locales/ai/en.json | 42 +- locales/ai/ja.json | 42 +- locales/ai/zh-CN.json | 1 - locales/errors/en.json | 9 +- packages/configs/package.json | 2 +- packages/internal/components/package.json | 2 +- .../src/ui/json-highlighter/index.tsx | 2 +- .../internal/shared/src/settings/defaults.ts | 4 + .../internal/shared/src/settings/interface.ts | 21 + pnpm-lock.yaml | 117 +++- pnpm-workspace.yaml | 3 +- 21 files changed, 948 insertions(+), 119 deletions(-) create mode 100644 apps/desktop/layer/renderer/src/queries/mcp.ts create mode 100644 icons/mgc/plugin_2_cute_re.svg diff --git a/apps/desktop/layer/renderer/src/atoms/settings/ai.ts b/apps/desktop/layer/renderer/src/atoms/settings/ai.ts index c7a70d733..c851fb1c3 100644 --- a/apps/desktop/layer/renderer/src/atoms/settings/ai.ts +++ b/apps/desktop/layer/renderer/src/atoms/settings/ai.ts @@ -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) => { + 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) => { + 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() diff --git a/apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatInterface.tsx b/apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatInterface.tsx index 8ac379de1..82233f5ed 100644 --- a/apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatInterface.tsx +++ b/apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatInterface.tsx @@ -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]", )} > diff --git a/apps/desktop/layer/renderer/src/modules/ai-chat/components/message/ToolInvocationComponent.tsx b/apps/desktop/layer/renderer/src/modules/ai-chat/components/message/ToolInvocationComponent.tsx index f37b7e27e..f2a986539 100644 --- a/apps/desktop/layer/renderer/src/modules/ai-chat/components/message/ToolInvocationComponent.tsx +++ b/apps/desktop/layer/renderer/src/modules/ai-chat/components/message/ToolInvocationComponent.tsx @@ -16,9 +16,14 @@ interface ToolInvocationComponentProps { export const ToolInvocationComponent: React.FC = React.memo( ({ part }) => { const toolName = getToolName(part) + const hasError = "errorText" in part && part.errorText return ( -
+
@@ -26,9 +31,15 @@ export const ToolInvocationComponent: React.FC = R {/* Tool Info */}
- - Tool Calling: -

{toolName}

+ + + {hasError ? "Tool Failed:" : "Tool Calling:"} + +

+ {toolName} +

@@ -44,7 +55,7 @@ export const ToolInvocationComponent: React.FC = R
)} - {"output" in part && ( + {"output" in part && !!part.output && (
Result @@ -52,6 +63,25 @@ export const ToolInvocationComponent: React.FC = R
)} + + {hasError && ( +
+
+ Error +
+
+
+ +
+
Tool Execution Failed
+
+ {"errorText" in part ? part.errorText : ""} +
+
+
+
+
+ )}
diff --git a/apps/desktop/layer/renderer/src/modules/settings/modal/context.tsx b/apps/desktop/layer/renderer/src/modules/settings/modal/context.tsx index 96f1f1645..be75864a1 100644 --- a/apps/desktop/layer/renderer/src/modules/settings/modal/context.tsx +++ b/apps/desktop/layer/renderer/src/modules/settings/modal/context.tsx @@ -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>( + atom(null as any), +) diff --git a/apps/desktop/layer/renderer/src/modules/settings/modal/layout.tsx b/apps/desktop/layer/renderer/src/modules/settings/modal/layout.tsx index b6873b8e5..c730d4db3 100644 --- a/apps/desktop/layer/renderer/src/modules/settings/modal/layout.tsx +++ b/apps/desktop/layer/renderer/src/modules/settings/modal/layout.tsx @@ -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 (
- {children} + + {children} + +
@@ -154,6 +163,11 @@ export function SettingModalLayout( ) } +const SettingModalContentPortalable = () => { + const setElement = useSetAtom(use(SettingModalContentPortalableContext)) + return
+} + 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) +} diff --git a/apps/desktop/layer/renderer/src/modules/settings/tabs/ai.tsx b/apps/desktop/layer/renderer/src/modules/settings/tabs/ai.tsx index 8b2dcaebf..d0abd9693 100644 --- a/apps/desktop/layer/renderer/src/modules/settings/tabs/ai.tsx +++ b/apps/desktop/layer/renderer/src/modules/settings/tabs/ai.tsx @@ -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, ]} />
@@ -126,17 +157,31 @@ const PersonalizePromptSetting = () => {
-
- -
+ + {hasChanges && ( + + +
+ Unsaved changes + +
+
+
+ )} +
) } @@ -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 }) => ( + { + const newShortcut: AIShortcut = { + ...shortcut, + id: Date.now().toString(), + } + setAISetting("shortcuts", [...shortcuts, newShortcut]) + toast.success(t("shortcuts.added")) + dismiss() + }} + onCancel={dismiss} + /> + ), + }) } - const handleSaveShortcut = (shortcut: Omit) => { - 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 }) => ( + { + 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) => { - setAISetting( - "shortcuts", - shortcuts.map((s) => (s.id === id ? { ...updatedShortcut, id } : s)), - ) - toast.success(t("shortcuts.updated")) - } - return (
- {t("shortcuts.description")} - - {isCreating && ( - setIsCreating(false)} /> - )} - - {shortcuts.length === 0 && !isCreating && ( + {shortcuts.length === 0 && (
@@ -213,7 +273,7 @@ const AIShortcutsSection = () => { shortcut={shortcut} onDelete={handleDeleteShortcut} onToggle={handleToggleShortcut} - onUpdate={handleUpdateShortcut} + onEdit={handleEditShortcut} /> ))}
@@ -224,29 +284,10 @@ interface ShortcutItemProps { shortcut: AIShortcut onDelete: (id: string) => void onToggle: (id: string, enabled: boolean) => void - onUpdate: (id: string, shortcut: Omit) => void + onEdit: (shortcut: AIShortcut) => void } -const ShortcutItem = ({ shortcut, onDelete, onToggle, onUpdate }: ShortcutItemProps) => { - const [isEditing, setIsEditing] = useState(false) - - const handleSave = (updatedShortcut: Omit) => { - onUpdate(shortcut.id, updatedShortcut) - setIsEditing(false) - } - - if (isEditing) { - return ( -
- setIsEditing(false)} - /> -
- ) - } - +const ShortcutItem = ({ shortcut, onDelete, onToggle, onEdit }: ShortcutItemProps) => { return (
@@ -266,7 +307,7 @@ const ShortcutItem = ({ shortcut, onDelete, onToggle, onUpdate }: ShortcutItemPr
- + +
+
+ + {mcpServices.length === 0 && ( +
+
+ +
+

+ {t("integration.mcp.services.empty.title")} +

+

+ {t("integration.mcp.services.empty.description")} +

+
+ )} + + {isLoading && ( +
+ +
+ )} + + {mcpServices.map((service) => ( + + ))} +
+ )} +
+ ) +} + +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 ( +
+
+
+
+

{service.name}

+
+ {getConnectionStatusText(service.isConnected)} +
+
+ {service.transportType} +
+
+
+ {service.url && ( +

+ URL: {service.url} +

+ )} + +

+ Tools: {service.toolCount} + Created:{" "} + {formatDate(service.createdAt)} + Last Used:{" "} + {formatDate(service.lastUsed)} +

+ {service.lastError && ( +

+ Error: {service.lastError} +

+ )} +
+
+ +
+ + + +
+
+
+ ) +} + +interface MCPServiceModalContentProps { + service?: MCPService | null + onSave: (service: { + name: string + transportType: "streamable-http" | "sse" + url: string + headers?: Record + }) => 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>(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 ( +
+
+
+
+ + setName(e.target.value)} + placeholder={t("integration.mcp.service.name_placeholder")} + /> +
+ +
+ + +
+ +
+ + setUrl(e.target.value)} + placeholder="https://example.com/mcp" + /> +
+ +
+ + +
+
+
+ +
+
+
+ + +
+
+
+ ) +} + export const PanelStyleSegment = () => { const { t } = useTranslation("ai") const panelStyle = useAIChatPanelStyle() diff --git a/apps/desktop/layer/renderer/src/queries/mcp.ts b/apps/desktop/layer/renderer/src/queries/mcp.ts new file mode 100644 index 000000000..a896195c1 --- /dev/null +++ b/apps/desktop/layer/renderer/src/queries/mcp.ts @@ -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 +}): Promise<{ authorizationUrl?: string }> => { + const res = await followApi.mcp.createConnection(connectionData) + return { authorizationUrl: res.authorizationUrl } +} + +export const fetchMCPConnections = async (): Promise => { + 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 + }, +): Promise<{ authorizationUrl?: string }> => { + const res = await followApi.mcp.updateConnection({ connectionId, ...updateData }) + return { authorizationUrl: res.authorizationUrl } +} + +export const deleteMCPConnection = async (connectionId: string): Promise => { + await followApi.mcp.deleteConnection({ connectionId }) +} + +export const refreshMCPTools = async (connectionIds?: string[]): Promise => { + 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, +} diff --git a/apps/ssr/note.md b/apps/ssr/note.md index 9860a09b6..8e74500d5 100644 --- a/apps/ssr/note.md +++ b/apps/ssr/note.md @@ -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 diff --git a/apps/ssr/package.json b/apps/ssr/package.json index cf2f2470f..3b1a7cca6 100644 --- a/apps/ssr/package.json +++ b/apps/ssr/package.json @@ -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:", diff --git a/icons/mgc/plugin_2_cute_re.svg b/icons/mgc/plugin_2_cute_re.svg new file mode 100644 index 000000000..8d2d9312a --- /dev/null +++ b/icons/mgc/plugin_2_cute_re.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/locales/ai/en.json b/locales/ai/en.json index ab8d97957..a466be52c 100644 --- a/locales/ai/en.json +++ b/locales/ai/en.json @@ -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", diff --git a/locales/ai/ja.json b/locales/ai/ja.json index 87dc53f20..36c026216 100644 --- a/locales/ai/ja.json +++ b/locales/ai/ja.json @@ -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": "有効", diff --git a/locales/ai/zh-CN.json b/locales/ai/zh-CN.json index 9b570b8d2..aecc84f84 100644 --- a/locales/ai/zh-CN.json +++ b/locales/ai/zh-CN.json @@ -35,7 +35,6 @@ "shortcuts.added": "快捷方式添加成功", "shortcuts.create_first": "创建您的第一个快捷方式", "shortcuts.deleted": "快捷方式删除成功", - "shortcuts.description": "创建自定义AI快捷方式以快速执行操作", "shortcuts.empty.description": "创建自定义AI快捷方式,快速执行常见任务并获得即时AI协助。", "shortcuts.empty.title": "暂无快捷方式", "shortcuts.enabled": "已启用", diff --git a/locales/errors/en.json b/locales/errors/en.json index 483f9455b..096d2c990 100644 --- a/locales/errors/en.json +++ b/locales/errors/en.json @@ -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" } diff --git a/packages/configs/package.json b/packages/configs/package.json index 1df51373f..f9fc2dc81 100644 --- a/packages/configs/package.json +++ b/packages/configs/package.json @@ -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", diff --git a/packages/internal/components/package.json b/packages/internal/components/package.json index 70f4ff732..284f8d660 100644 --- a/packages/internal/components/package.json +++ b/packages/internal/components/package.json @@ -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", diff --git a/packages/internal/components/src/ui/json-highlighter/index.tsx b/packages/internal/components/src/ui/json-highlighter/index.tsx index e6e45e7c5..c86e4ba4b 100644 --- a/packages/internal/components/src/ui/json-highlighter/index.tsx +++ b/packages/internal/components/src/ui/json-highlighter/index.tsx @@ -96,7 +96,7 @@ interface Token { function highlightJson(jsonString: string): string { // Escape HTML entities first const escaped = jsonString - .replaceAll("&", "&") + ?.replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") diff --git a/packages/internal/shared/src/settings/defaults.ts b/packages/internal/shared/src/settings/defaults.ts index be2158999..6c6bcf73b 100644 --- a/packages/internal/shared/src/settings/defaults.ts +++ b/packages/internal/shared/src/settings/defaults.ts @@ -156,6 +156,10 @@ export const defaultAISettings: AISettings = { personalizePrompt: "", shortcuts: [], + // MCP Services + mcpEnabled: false, + mcpServices: [], + // Features autoScrollWhenStreaming: true, } diff --git a/packages/internal/shared/src/settings/interface.ts b/packages/internal/shared/src/settings/interface.ts index c827740b6..0a492182a 100644 --- a/packages/internal/shared/src/settings/interface.ts +++ b/packages/internal/shared/src/settings/interface.ts @@ -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 + 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 } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3466db906..b03cba188 100644 --- a/pnpm-lock.yaml +++ b/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: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index aa757362d..2b8811de5 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -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"