mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-06-26 18:26:38 +00:00
web search feature added
This commit is contained in:
parent
78dd4c677f
commit
beedd3f6c3
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -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(/</g, '<')
|
.replace(/</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 }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
0
app/types/build.d.ts
vendored
Normal file
Loading…
Reference in New Issue
Block a user