web search feature added

This commit is contained in:
migavel508 2025-05-14 12:45:38 +05:30
parent 78dd4c677f
commit beedd3f6c3
4 changed files with 44 additions and 43 deletions

View File

@ -27,11 +27,13 @@ export const WebSearch = ({ onSearchResult, disabled = false }: WebSearchProps)
const [isSearching, setIsSearching] = useState(false); const [isSearching, setIsSearching] = useState(false);
const formatSearchResult = (data: WebSearchResponse['data']) => { const formatSearchResult = (data: WebSearchResponse['data']) => {
if (!data) return ''; if (!data) {
return '';
}
let result = `# Web Search Results from ${data.sourceUrl}\n\n`; let result = `# Web Search Results from ${data.sourceUrl}\n\n`;
result += `## ${data.title}\n\n`; result += `## ${data.title}\n\n`;
if (data.description) { if (data.description) {
result += `**Description:** ${data.description}\n\n`; result += `**Description:** ${data.description}\n\n`;
} }
@ -40,14 +42,14 @@ export const WebSearch = ({ onSearchResult, disabled = false }: WebSearchProps)
if (data.codeBlocks.length > 0) { if (data.codeBlocks.length > 0) {
result += `## Code Examples\n\n`; result += `## Code Examples\n\n`;
data.codeBlocks.forEach((block, index) => { data.codeBlocks.forEach((block, _index) => {
result += `\`\`\`\n${block}\n\`\`\`\n\n`; result += `\`\`\`\n${block}\n\`\`\`\n\n`;
}); });
} }
if (data.relevantLinks.length > 0) { if (data.relevantLinks.length > 0) {
result += `## Relevant Links\n\n`; result += `## Relevant Links\n\n`;
data.relevantLinks.forEach(link => { data.relevantLinks.forEach((link) => {
result += `- [${link.text}](${link.url})\n`; result += `- [${link.text}](${link.url})\n`;
}); });
} }
@ -56,12 +58,15 @@ export const WebSearch = ({ onSearchResult, disabled = false }: WebSearchProps)
}; };
const handleWebSearch = async () => { const handleWebSearch = async () => {
if (disabled) return; if (disabled) {
return;
}
try { try {
setIsSearching(true); setIsSearching(true);
const url = window.prompt('Enter URL to search:'); const url = window.prompt('Enter URL to search:');
if (!url) { if (!url) {
setIsSearching(false); setIsSearching(false);
return; return;
@ -75,7 +80,7 @@ export const WebSearch = ({ onSearchResult, disabled = false }: WebSearchProps)
body: formData, body: formData,
}); });
const data = await response.json() as WebSearchResponse; const data = (await response.json()) as WebSearchResponse;
if (!response.ok) { if (!response.ok) {
throw new Error(data.error || 'Failed to perform web search'); throw new Error(data.error || 'Failed to perform web search');
@ -110,4 +115,4 @@ export const WebSearch = ({ onSearchResult, disabled = false }: WebSearchProps)
)} )}
</IconButton> </IconButton>
); );
}; };

View File

@ -13,10 +13,11 @@ export async function action({ request }: ActionFunctionArgs) {
// Add proper headers to handle CORS and content type // Add proper headers to handle CORS and content type
const response = await fetch(url, { const response = await fetch(url, {
headers: { headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 'User-Agent':
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5', 'Accept-Language': 'en-US,en;q=0.5',
} },
}); });
if (!response.ok) { if (!response.ok) {
@ -24,12 +25,13 @@ export async function action({ request }: ActionFunctionArgs) {
} }
const contentType = response.headers.get('content-type'); const contentType = response.headers.get('content-type');
if (!contentType?.includes('text/html')) { if (!contentType?.includes('text/html')) {
throw new Error('URL must point to an HTML page'); throw new Error('URL must point to an HTML page');
} }
const html = await response.text(); const html = await response.text();
// Extract title // Extract title
const titleMatch = html.match(/<title[^>]*>([^<]+)<\/title>/i); const titleMatch = html.match(/<title[^>]*>([^<]+)<\/title>/i);
const title = titleMatch ? titleMatch[1].trim() : 'No title found'; const title = titleMatch ? titleMatch[1].trim() : 'No title found';
@ -48,7 +50,7 @@ export async function action({ request }: ActionFunctionArgs) {
// Extract code blocks // Extract code blocks
const codeBlocks = html.match(/<pre[^>]*>[\s\S]*?<\/pre>|<code[^>]*>[\s\S]*?<\/code>/gi) || []; const codeBlocks = html.match(/<pre[^>]*>[\s\S]*?<\/pre>|<code[^>]*>[\s\S]*?<\/code>/gi) || [];
const formattedCodeBlocks = codeBlocks.map(block => { const formattedCodeBlocks = codeBlocks.map((block) => {
return block return block
.replace(/<[^>]+>/g, '') .replace(/<[^>]+>/g, '')
.replace(/&lt;/g, '<') .replace(/&lt;/g, '<')
@ -59,12 +61,13 @@ export async function action({ request }: ActionFunctionArgs) {
// Extract links // Extract links
const links = html.match(/<a[^>]*href="([^"]*)"[^>]*>([^<]*)<\/a>/gi) || []; const links = html.match(/<a[^>]*href="([^"]*)"[^>]*>([^<]*)<\/a>/gi) || [];
const formattedLinks = links.map(link => { const formattedLinks = links.map((link) => {
const hrefMatch = link.match(/href="([^"]*)"/i); const hrefMatch = link.match(/href="([^"]*)"/i);
const textMatch = link.match(/>([^<]*)</i); const textMatch = link.match(/>([^<]*)</i);
return { return {
url: hrefMatch ? hrefMatch[1] : '', url: hrefMatch ? hrefMatch[1] : '',
text: textMatch ? textMatch[1].trim() : '' text: textMatch ? textMatch[1].trim() : '',
}; };
}); });
@ -74,24 +77,18 @@ export async function action({ request }: ActionFunctionArgs) {
description, description,
mainContent: mainContent.slice(0, 1000) + '...', mainContent: mainContent.slice(0, 1000) + '...',
codeBlocks: formattedCodeBlocks, codeBlocks: formattedCodeBlocks,
relevantLinks: formattedLinks.filter(link => relevantLinks: formattedLinks.filter(
link.url && (link) => link.url && !link.url.startsWith('#') && !link.url.startsWith('javascript:') && link.text.trim(),
!link.url.startsWith('#') &&
!link.url.startsWith('javascript:') &&
link.text.trim()
), ),
sourceUrl: url sourceUrl: url,
}; };
return json({ return json({
success: true, success: true,
data: structuredContent data: structuredContent,
}); });
} catch (error) { } catch (error) {
console.error('Web search error:', error); console.error('Web search error:', error);
return json( return json({ error: error instanceof Error ? error.message : 'Unknown error occurred' }, { status: 500 });
{ error: error instanceof Error ? error.message : 'Unknown error occurred' },
{ status: 500 }
);
} }
} }

View File

@ -11,38 +11,37 @@ export async function action({ request }: ActionFunctionArgs) {
} }
const response = await fetch(url); const response = await fetch(url);
if (!response.ok) { if (!response.ok) {
throw new Error(`Failed to fetch URL: ${response.status} ${response.statusText}`); throw new Error(`Failed to fetch URL: ${response.status} ${response.statusText}`);
} }
const html = await response.text(); const html = await response.text();
// Basic HTML parsing to extract title and content // Basic HTML parsing to extract title and content
const titleMatch = html.match(/<title[^>]*>([^<]+)<\/title>/i); const titleMatch = html.match(/<title[^>]*>([^<]+)<\/title>/i);
const title = titleMatch ? titleMatch[1].trim() : 'No title found'; const title = titleMatch ? titleMatch[1].trim() : 'No title found';
// Extract content by removing script and style tags, then getting text content // Extract content by removing script and style tags, then getting text content
const content = html const content =
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '') html
.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '') .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
.replace(/<[^>]+>/g, ' ') .replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '')
.replace(/\s+/g, ' ') .replace(/<[^>]+>/g, ' ')
.trim() .replace(/\s+/g, ' ')
.slice(0, 1000) + '...'; // Limit content length .trim()
.slice(0, 1000) + '...'; // Limit content length
return json({ return json({
success: true, success: true,
data: { data: {
title, title,
content, content,
url url,
} },
}); });
} catch (error) { } catch (error) {
console.error('Web search error:', error); console.error('Web search error:', error);
return json( return json({ error: error instanceof Error ? error.message : 'Unknown error occurred' }, { status: 500 });
{ error: error instanceof Error ? error.message : 'Unknown error occurred' },
{ status: 500 }
);
} }
} }

0
app/types/build.d.ts vendored Normal file
View File