diff --git a/packages/ui/src/components/basic-tool.tsx b/packages/ui/src/components/basic-tool.tsx index fff6e92f1..4ad91824d 100644 --- a/packages/ui/src/components/basic-tool.tsx +++ b/packages/ui/src/components/basic-tool.tsx @@ -203,6 +203,41 @@ export function BasicTool(props: BasicToolProps) { ) } -export function GenericTool(props: { tool: string; status?: string; hideDetails?: boolean }) { - return +function label(input: Record | undefined) { + const keys = ["description", "query", "url", "filePath", "path", "pattern", "name"] + return keys.map((key) => input?.[key]).find((value): value is string => typeof value === "string" && value.length > 0) +} + +function args(input: Record | undefined) { + if (!input) return [] + const skip = new Set(["description", "query", "url", "filePath", "path", "pattern", "name"]) + return Object.entries(input) + .filter(([key]) => !skip.has(key)) + .flatMap(([key, value]) => { + if (typeof value === "string") return [`${key}=${value}`] + if (typeof value === "number") return [`${key}=${value}`] + if (typeof value === "boolean") return [`${key}=${value}`] + return [] + }) + .slice(0, 3) +} + +export function GenericTool(props: { + tool: string + status?: string + hideDetails?: boolean + input?: Record +}) { + return ( + + ) } diff --git a/packages/ui/src/components/message-part.css b/packages/ui/src/components/message-part.css index 3eee45c75..8fc709013 100644 --- a/packages/ui/src/components/message-part.css +++ b/packages/ui/src/components/message-part.css @@ -577,6 +577,46 @@ justify-content: center; } +[data-component="exa-tool-output"] { + width: 100%; + padding-top: 8px; + display: flex; + flex-direction: column; +} + +[data-slot="basic-tool-tool-subtitle"].exa-tool-query { + display: block; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +[data-slot="exa-tool-links"] { + display: flex; + flex-direction: column; + gap: 4px; +} + +[data-slot="exa-tool-link"] { + display: block; + max-width: 100%; + color: var(--text-interactive-base); + text-decoration: underline; + text-underline-offset: 2px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + &:hover { + color: var(--text-interactive-base); + } + + &:visited { + color: var(--text-interactive-base); + } +} + [data-component="todos"] { padding: 10px 0 24px 0; display: flex; diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index 766060f1b..fbeb8bda2 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -243,6 +243,18 @@ export function getToolInfo(tool: string, input: any = {}): ToolInfo { title: i18n.t("ui.tool.webfetch"), subtitle: input.url, } + case "websearch": + return { + icon: "window-cursor", + title: i18n.t("ui.tool.websearch"), + subtitle: input.query, + } + case "codesearch": + return { + icon: "code", + title: i18n.t("ui.tool.codesearch"), + subtitle: input.query, + } case "task": return { icon: "task", @@ -303,6 +315,18 @@ export function getToolInfo(tool: string, input: any = {}): ToolInfo { } } +function urls(text: string | undefined) { + if (!text) return [] + const seen = new Set() + return [...text.matchAll(/https?:\/\/[^\s<>"'`)\]]+/g)] + .map((item) => item[0].replace(/[),.;:!?]+$/g, "")) + .filter((item) => { + if (seen.has(item)) return false + seen.add(item) + return true + }) +} + const CONTEXT_GROUP_TOOLS = new Set(["read", "glob", "grep", "list"]) const HIDDEN_TOOLS = new Set(["todowrite", "todoread"]) @@ -598,6 +622,32 @@ function contextToolSummary(parts: ToolPart[]) { return { read, search, list } } +function ExaOutput(props: { output?: string }) { + const links = createMemo(() => urls(props.output)) + + return ( + 0}> +
+
+ + {(url) => ( + event.stopPropagation()} + > + {url} + + )} + +
+
+
+ ) +} + export function registerPartComponent(type: string, component: PartComponent) { PART_MAPPING[type] = component } @@ -1467,6 +1517,58 @@ ToolRegistry.register({ }, }) +ToolRegistry.register({ + name: "websearch", + render(props) { + const i18n = useI18n() + const query = createMemo(() => { + const value = props.input.query + if (typeof value !== "string") return "" + return value + }) + + return ( + + + + ) + }, +}) + +ToolRegistry.register({ + name: "codesearch", + render(props) { + const i18n = useI18n() + const query = createMemo(() => { + const value = props.input.query + if (typeof value !== "string") return "" + return value + }) + + return ( + + + + ) + }, +}) + ToolRegistry.register({ name: "task", render(props) { diff --git a/packages/ui/src/i18n/ar.ts b/packages/ui/src/i18n/ar.ts index 3579eff5a..f0a56f772 100644 --- a/packages/ui/src/i18n/ar.ts +++ b/packages/ui/src/i18n/ar.ts @@ -94,6 +94,8 @@ export const dict = { "ui.tool.glob": "Glob", "ui.tool.grep": "Grep", "ui.tool.webfetch": "جلب الويب", + "ui.tool.websearch": "بحث الويب", + "ui.tool.codesearch": "بحث الكود", "ui.tool.shell": "Shell", "ui.tool.patch": "تصحيح", "ui.tool.todos": "المهام", diff --git a/packages/ui/src/i18n/br.ts b/packages/ui/src/i18n/br.ts index 76028878f..d06050605 100644 --- a/packages/ui/src/i18n/br.ts +++ b/packages/ui/src/i18n/br.ts @@ -94,6 +94,8 @@ export const dict = { "ui.tool.glob": "Glob", "ui.tool.grep": "Grep", "ui.tool.webfetch": "Buscar Web", + "ui.tool.websearch": "Pesquisa na Web", + "ui.tool.codesearch": "Pesquisa de Código", "ui.tool.shell": "Shell", "ui.tool.patch": "Patch", "ui.tool.todos": "Tarefas", diff --git a/packages/ui/src/i18n/bs.ts b/packages/ui/src/i18n/bs.ts index 9bc229336..754c6bcef 100644 --- a/packages/ui/src/i18n/bs.ts +++ b/packages/ui/src/i18n/bs.ts @@ -98,6 +98,8 @@ export const dict = { "ui.tool.glob": "Glob", "ui.tool.grep": "Grep", "ui.tool.webfetch": "Web preuzimanje", + "ui.tool.websearch": "Pretraga weba", + "ui.tool.codesearch": "Pretraga koda", "ui.tool.shell": "Shell", "ui.tool.patch": "Patch", "ui.tool.todos": "Lista zadataka", diff --git a/packages/ui/src/i18n/da.ts b/packages/ui/src/i18n/da.ts index 1bb475856..0126a60c8 100644 --- a/packages/ui/src/i18n/da.ts +++ b/packages/ui/src/i18n/da.ts @@ -93,6 +93,8 @@ export const dict = { "ui.tool.glob": "Glob", "ui.tool.grep": "Grep", "ui.tool.webfetch": "Webhentning", + "ui.tool.websearch": "Websøgning", + "ui.tool.codesearch": "Kodesøgning", "ui.tool.shell": "Shell", "ui.tool.patch": "Patch", "ui.tool.todos": "Opgaver", diff --git a/packages/ui/src/i18n/de.ts b/packages/ui/src/i18n/de.ts index 951833c30..24d99ef79 100644 --- a/packages/ui/src/i18n/de.ts +++ b/packages/ui/src/i18n/de.ts @@ -99,6 +99,8 @@ export const dict = { "ui.tool.glob": "Glob", "ui.tool.grep": "Grep", "ui.tool.webfetch": "Webabruf", + "ui.tool.websearch": "Websuche", + "ui.tool.codesearch": "Codesuche", "ui.tool.shell": "Shell", "ui.tool.patch": "Patch", "ui.tool.todos": "Aufgaben", diff --git a/packages/ui/src/i18n/en.ts b/packages/ui/src/i18n/en.ts index 9c9ae6e27..1d92ea507 100644 --- a/packages/ui/src/i18n/en.ts +++ b/packages/ui/src/i18n/en.ts @@ -95,6 +95,8 @@ export const dict: Record = { "ui.tool.glob": "Glob", "ui.tool.grep": "Grep", "ui.tool.webfetch": "Webfetch", + "ui.tool.websearch": "Web Search", + "ui.tool.codesearch": "Code Search", "ui.tool.shell": "Shell", "ui.tool.patch": "Patch", "ui.tool.todos": "To-dos", diff --git a/packages/ui/src/i18n/es.ts b/packages/ui/src/i18n/es.ts index 6fb6eea51..9ee95d824 100644 --- a/packages/ui/src/i18n/es.ts +++ b/packages/ui/src/i18n/es.ts @@ -94,6 +94,8 @@ export const dict = { "ui.tool.glob": "Glob", "ui.tool.grep": "Grep", "ui.tool.webfetch": "Webfetch", + "ui.tool.websearch": "Búsqueda web", + "ui.tool.codesearch": "Búsqueda de código", "ui.tool.shell": "Shell", "ui.tool.patch": "Parche", "ui.tool.todos": "Tareas", diff --git a/packages/ui/src/i18n/fr.ts b/packages/ui/src/i18n/fr.ts index 3a77a3f5c..431abe568 100644 --- a/packages/ui/src/i18n/fr.ts +++ b/packages/ui/src/i18n/fr.ts @@ -94,6 +94,8 @@ export const dict = { "ui.tool.glob": "Glob", "ui.tool.grep": "Grep", "ui.tool.webfetch": "Webfetch", + "ui.tool.websearch": "Recherche Web", + "ui.tool.codesearch": "Recherche de code", "ui.tool.shell": "Shell", "ui.tool.patch": "Patch", "ui.tool.todos": "Tâches", diff --git a/packages/ui/src/i18n/ja.ts b/packages/ui/src/i18n/ja.ts index 9dfb03f76..c6cb2ac40 100644 --- a/packages/ui/src/i18n/ja.ts +++ b/packages/ui/src/i18n/ja.ts @@ -93,6 +93,8 @@ export const dict = { "ui.tool.glob": "Glob", "ui.tool.grep": "Grep", "ui.tool.webfetch": "Webfetch", + "ui.tool.websearch": "Web検索", + "ui.tool.codesearch": "コード検索", "ui.tool.shell": "Shell", "ui.tool.patch": "Patch", "ui.tool.todos": "Todo", diff --git a/packages/ui/src/i18n/ko.ts b/packages/ui/src/i18n/ko.ts index 84d261ac8..cd306e879 100644 --- a/packages/ui/src/i18n/ko.ts +++ b/packages/ui/src/i18n/ko.ts @@ -94,6 +94,8 @@ export const dict = { "ui.tool.glob": "Glob", "ui.tool.grep": "Grep", "ui.tool.webfetch": "웹 가져오기", + "ui.tool.websearch": "웹 검색", + "ui.tool.codesearch": "코드 검색", "ui.tool.shell": "셸", "ui.tool.patch": "패치", "ui.tool.todos": "할 일", diff --git a/packages/ui/src/i18n/no.ts b/packages/ui/src/i18n/no.ts index dd1822bee..ddfe09461 100644 --- a/packages/ui/src/i18n/no.ts +++ b/packages/ui/src/i18n/no.ts @@ -97,6 +97,8 @@ export const dict: Record = { "ui.tool.glob": "Glob", "ui.tool.grep": "Grep", "ui.tool.webfetch": "Webhenting", + "ui.tool.websearch": "Nettsøk", + "ui.tool.codesearch": "Kodesøk", "ui.tool.shell": "Shell", "ui.tool.patch": "Patch", "ui.tool.todos": "Gjøremål", diff --git a/packages/ui/src/i18n/pl.ts b/packages/ui/src/i18n/pl.ts index fcfedb2ef..73fa96afa 100644 --- a/packages/ui/src/i18n/pl.ts +++ b/packages/ui/src/i18n/pl.ts @@ -93,6 +93,8 @@ export const dict = { "ui.tool.glob": "Glob", "ui.tool.grep": "Grep", "ui.tool.webfetch": "Pobieranie sieciowe", + "ui.tool.websearch": "Wyszukiwanie w sieci", + "ui.tool.codesearch": "Wyszukiwanie kodu", "ui.tool.shell": "Terminal", "ui.tool.patch": "Patch", "ui.tool.todos": "Zadania", diff --git a/packages/ui/src/i18n/ru.ts b/packages/ui/src/i18n/ru.ts index 713ff47d1..085be2843 100644 --- a/packages/ui/src/i18n/ru.ts +++ b/packages/ui/src/i18n/ru.ts @@ -93,6 +93,8 @@ export const dict = { "ui.tool.glob": "Glob", "ui.tool.grep": "Grep", "ui.tool.webfetch": "Webfetch", + "ui.tool.websearch": "Веб-поиск", + "ui.tool.codesearch": "Поиск кода", "ui.tool.shell": "Оболочка", "ui.tool.patch": "Патч", "ui.tool.todos": "Задачи", diff --git a/packages/ui/src/i18n/th.ts b/packages/ui/src/i18n/th.ts index 44761a279..705f68d1b 100644 --- a/packages/ui/src/i18n/th.ts +++ b/packages/ui/src/i18n/th.ts @@ -95,6 +95,8 @@ export const dict = { "ui.tool.glob": "Glob", "ui.tool.grep": "Grep", "ui.tool.webfetch": "ดึงจากเว็บ", + "ui.tool.websearch": "ค้นหาเว็บ", + "ui.tool.codesearch": "ค้นหาโค้ด", "ui.tool.shell": "เชลล์", "ui.tool.patch": "แพตช์", "ui.tool.todos": "รายการงาน", diff --git a/packages/ui/src/i18n/tr.ts b/packages/ui/src/i18n/tr.ts index 5ec108d4a..fa3bddb21 100644 --- a/packages/ui/src/i18n/tr.ts +++ b/packages/ui/src/i18n/tr.ts @@ -90,6 +90,8 @@ export const dict = { "ui.tool.glob": "Glob", "ui.tool.grep": "Grep", "ui.tool.webfetch": "Web getir", + "ui.tool.websearch": "Web Araması", + "ui.tool.codesearch": "Kod Araması", "ui.tool.shell": "Kabuk", "ui.tool.patch": "Yama", "ui.tool.todos": "Görevler", diff --git a/packages/ui/src/i18n/zh.ts b/packages/ui/src/i18n/zh.ts index 39226605b..571574d92 100644 --- a/packages/ui/src/i18n/zh.ts +++ b/packages/ui/src/i18n/zh.ts @@ -98,6 +98,8 @@ export const dict = { "ui.tool.glob": "Glob", "ui.tool.grep": "Grep", "ui.tool.webfetch": "Webfetch", + "ui.tool.websearch": "网络搜索", + "ui.tool.codesearch": "代码搜索", "ui.tool.shell": "Shell", "ui.tool.patch": "补丁", "ui.tool.todos": "待办", diff --git a/packages/ui/src/i18n/zht.ts b/packages/ui/src/i18n/zht.ts index 068e222d6..edbc96b12 100644 --- a/packages/ui/src/i18n/zht.ts +++ b/packages/ui/src/i18n/zht.ts @@ -98,6 +98,8 @@ export const dict = { "ui.tool.glob": "Glob", "ui.tool.grep": "Grep", "ui.tool.webfetch": "Webfetch", + "ui.tool.websearch": "網頁搜尋", + "ui.tool.codesearch": "程式碼搜尋", "ui.tool.shell": "Shell", "ui.tool.patch": "修補", "ui.tool.todos": "待辦",