diff --git a/src/components/ProModeSelector.tsx b/src/components/ProModeSelector.tsx index 4fe28f5..0796d97 100644 --- a/src/components/ProModeSelector.tsx +++ b/src/components/ProModeSelector.tsx @@ -19,6 +19,12 @@ import { hasDyadProKey, type UserSettings } from "@/lib/schemas"; export function ProModeSelector() { const { settings, updateSettings } = useSettings(); + const toggleWebSearch = () => { + updateSettings({ + enableProWebSearch: !settings?.enableProWebSearch, + }); + }; + const toggleLazyEdits = () => { updateSettings({ enableProLazyEditsMode: !settings?.enableProLazyEditsMode, @@ -105,6 +111,15 @@ export function ProModeSelector() { settingEnabled={Boolean(settings?.enableDyadPro)} toggle={toggleProEnabled} /> + + {content} + + ); + case "dyad-web-search": + return ( + + {content} + + ); + case "dyad-web-search-result": + return ( + + {content} + + ); case "think": return ( = ({ + children, + node, + path: pathProp, +}) => { + const path = pathProp || node?.properties?.path || ""; + const fileName = path ? path.split("/").pop() : ""; + + return ( +
+
+
+ + {fileName && ( + + {fileName} + + )} +
Read
+
+
+ {path && ( +
+ {path} +
+ )} + {children && ( +
+ {children} +
+ )} +
+ ); +}; diff --git a/src/components/chat/DyadWebSearch.tsx b/src/components/chat/DyadWebSearch.tsx new file mode 100644 index 0000000..65ae008 --- /dev/null +++ b/src/components/chat/DyadWebSearch.tsx @@ -0,0 +1,31 @@ +import type React from "react"; +import type { ReactNode } from "react"; +import { Globe } from "lucide-react"; + +interface DyadWebSearchProps { + children?: ReactNode; + node?: any; + query?: string; +} + +export const DyadWebSearch: React.FC = ({ + children, + node: _node, + query: queryProp, +}) => { + const query = queryProp || (typeof children === "string" ? children : ""); + + return ( +
+
+
+ +
Web Search
+
+
+
+ {query || children} +
+
+ ); +}; diff --git a/src/components/chat/DyadWebSearchResult.tsx b/src/components/chat/DyadWebSearchResult.tsx new file mode 100644 index 0000000..2e1dfce --- /dev/null +++ b/src/components/chat/DyadWebSearchResult.tsx @@ -0,0 +1,78 @@ +import React, { useEffect, useState } from "react"; +import { ChevronDown, ChevronUp, Globe, Loader } from "lucide-react"; +import { VanillaMarkdownParser } from "./DyadMarkdownParser"; +import { CustomTagState } from "./stateTypes"; + +interface DyadWebSearchResultProps { + node?: any; + children?: React.ReactNode; +} + +export const DyadWebSearchResult: React.FC = ({ + children, + node, +}) => { + const state = node?.properties?.state as CustomTagState; + const inProgress = state === "pending"; + const [isExpanded, setIsExpanded] = useState(inProgress); + + // Collapse when transitioning from in-progress to not-in-progress + useEffect(() => { + if (!inProgress && isExpanded) { + setIsExpanded(false); + } + }, [inProgress]); + + return ( +
setIsExpanded(!isExpanded)} + role="button" + aria-expanded={isExpanded} + tabIndex={0} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + setIsExpanded(!isExpanded); + } + }} + > + {/* Top-left label badge */} +
+ + Web Search Result + {inProgress && ( + + )} +
+ + {/* Indicator icon */} +
+ {isExpanded ? : } +
+ + {/* Main content with smooth transition */} +
+
+ {typeof children === "string" ? ( + + ) : ( + children + )} +
+
+
+ ); +}; diff --git a/src/ipc/utils/get_model_client.ts b/src/ipc/utils/get_model_client.ts index 57f66c4..9fcbcd8 100644 --- a/src/ipc/utils/get_model_client.ts +++ b/src/ipc/utils/get_model_client.ts @@ -86,7 +86,8 @@ export async function getModelClient( if (providerConfig.gatewayPrefix != null || dyadEngineUrl) { const isEngineEnabled = settings.enableProSmartFilesContextMode || - settings.enableProLazyEditsMode; + settings.enableProLazyEditsMode || + settings.enableProWebSearch; const provider = isEngineEnabled ? createDyadEngine({ apiKey: dyadApiKey, @@ -100,6 +101,7 @@ export async function getModelClient( enableSmartFilesContext: settings.enableProSmartFilesContextMode, // Keep in sync with getCurrentValue in ProModeSelector.tsx smartContextMode: settings.proSmartContextOption ?? "balanced", + enableWebSearch: settings.enableProWebSearch, }, settings, }) diff --git a/src/ipc/utils/llm_engine_provider.ts b/src/ipc/utils/llm_engine_provider.ts index d9ca698..bf47076 100644 --- a/src/ipc/utils/llm_engine_provider.ts +++ b/src/ipc/utils/llm_engine_provider.ts @@ -44,6 +44,7 @@ or to provide a custom fetch implementation for e.g. testing. dyadOptions: { enableLazyEdits?: boolean; enableSmartFilesContext?: boolean; + enableWebSearch?: boolean; smartContextMode?: "balanced" | "conservative"; }; settings: UserSettings; @@ -158,6 +159,7 @@ export function createDyadEngine( enable_smart_files_context: options.dyadOptions.enableSmartFilesContext, smart_context_mode: options.dyadOptions.smartContextMode, + enable_web_search: options.dyadOptions.enableWebSearch, }; } diff --git a/src/lib/schemas.ts b/src/lib/schemas.ts index e02efb9..f13d9d9 100644 --- a/src/lib/schemas.ts +++ b/src/lib/schemas.ts @@ -219,6 +219,7 @@ export const UserSettingsSchema = z.object({ thinkingBudget: z.enum(["low", "medium", "high"]).optional(), enableProLazyEditsMode: z.boolean().optional(), enableProSmartFilesContextMode: z.boolean().optional(), + enableProWebSearch: z.boolean().optional(), proSmartContextOption: z.enum(["balanced", "conservative"]).optional(), selectedTemplateId: z.string(), enableSupabaseWriteSqlMigration: z.boolean().optional(),