Skip to content

Commit 5376b9e

Browse files
committed
web search feature added
1 parent 290dddc commit 5376b9e

File tree

4 files changed

+44
-43
lines changed

4 files changed

+44
-43
lines changed

app/components/chat/WebSearch.client.tsx

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ export const WebSearch = ({ onSearchResult, disabled = false }: WebSearchProps)
2727
const [isSearching, setIsSearching] = useState(false);
2828

2929
const formatSearchResult = (data: WebSearchResponse['data']) => {
30-
if (!data) return '';
30+
if (!data) {
31+
return '';
32+
}
3133

3234
let result = `# Web Search Results from ${data.sourceUrl}\n\n`;
3335
result += `## ${data.title}\n\n`;
34-
36+
3537
if (data.description) {
3638
result += `**Description:** ${data.description}\n\n`;
3739
}
@@ -40,14 +42,14 @@ export const WebSearch = ({ onSearchResult, disabled = false }: WebSearchProps)
4042

4143
if (data.codeBlocks.length > 0) {
4244
result += `## Code Examples\n\n`;
43-
data.codeBlocks.forEach((block, index) => {
45+
data.codeBlocks.forEach((block, _index) => {
4446
result += `\`\`\`\n${block}\n\`\`\`\n\n`;
4547
});
4648
}
4749

4850
if (data.relevantLinks.length > 0) {
4951
result += `## Relevant Links\n\n`;
50-
data.relevantLinks.forEach(link => {
52+
data.relevantLinks.forEach((link) => {
5153
result += `- [${link.text}](${link.url})\n`;
5254
});
5355
}
@@ -56,12 +58,15 @@ export const WebSearch = ({ onSearchResult, disabled = false }: WebSearchProps)
5658
};
5759

5860
const handleWebSearch = async () => {
59-
if (disabled) return;
61+
if (disabled) {
62+
return;
63+
}
6064

6165
try {
6266
setIsSearching(true);
67+
6368
const url = window.prompt('Enter URL to search:');
64-
69+
6570
if (!url) {
6671
setIsSearching(false);
6772
return;
@@ -75,7 +80,7 @@ export const WebSearch = ({ onSearchResult, disabled = false }: WebSearchProps)
7580
body: formData,
7681
});
7782

78-
const data = await response.json() as WebSearchResponse;
83+
const data = (await response.json()) as WebSearchResponse;
7984

8085
if (!response.ok) {
8186
throw new Error(data.error || 'Failed to perform web search');
@@ -110,4 +115,4 @@ export const WebSearch = ({ onSearchResult, disabled = false }: WebSearchProps)
110115
)}
111116
</IconButton>
112117
);
113-
};
118+
};

app/routes/api-web-search.ts

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,25 @@ export async function action({ request }: ActionFunctionArgs) {
1313
// Add proper headers to handle CORS and content type
1414
const response = await fetch(url, {
1515
headers: {
16-
'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',
17-
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
16+
'User-Agent':
17+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
18+
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
1819
'Accept-Language': 'en-US,en;q=0.5',
19-
}
20+
},
2021
});
2122

2223
if (!response.ok) {
2324
throw new Error(`Failed to fetch URL: ${response.status} ${response.statusText}`);
2425
}
2526

2627
const contentType = response.headers.get('content-type');
28+
2729
if (!contentType?.includes('text/html')) {
2830
throw new Error('URL must point to an HTML page');
2931
}
3032

3133
const html = await response.text();
32-
34+
3335
// Extract title
3436
const titleMatch = html.match(/<title[^>]*>([^<]+)<\/title>/i);
3537
const title = titleMatch ? titleMatch[1].trim() : 'No title found';
@@ -48,7 +50,7 @@ export async function action({ request }: ActionFunctionArgs) {
4850

4951
// Extract code blocks
5052
const codeBlocks = html.match(/<pre[^>]*>[\s\S]*?<\/pre>|<code[^>]*>[\s\S]*?<\/code>/gi) || [];
51-
const formattedCodeBlocks = codeBlocks.map(block => {
53+
const formattedCodeBlocks = codeBlocks.map((block) => {
5254
return block
5355
.replace(/<[^>]+>/g, '')
5456
.replace(/&lt;/g, '<')
@@ -59,12 +61,13 @@ export async function action({ request }: ActionFunctionArgs) {
5961

6062
// Extract links
6163
const links = html.match(/<a[^>]*href="([^"]*)"[^>]*>([^<]*)<\/a>/gi) || [];
62-
const formattedLinks = links.map(link => {
64+
const formattedLinks = links.map((link) => {
6365
const hrefMatch = link.match(/href="([^"]*)"/i);
6466
const textMatch = link.match(/>([^<]*)</i);
67+
6568
return {
6669
url: hrefMatch ? hrefMatch[1] : '',
67-
text: textMatch ? textMatch[1].trim() : ''
70+
text: textMatch ? textMatch[1].trim() : '',
6871
};
6972
});
7073

@@ -74,24 +77,18 @@ export async function action({ request }: ActionFunctionArgs) {
7477
description,
7578
mainContent: mainContent.slice(0, 1000) + '...',
7679
codeBlocks: formattedCodeBlocks,
77-
relevantLinks: formattedLinks.filter(link =>
78-
link.url &&
79-
!link.url.startsWith('#') &&
80-
!link.url.startsWith('javascript:') &&
81-
link.text.trim()
80+
relevantLinks: formattedLinks.filter(
81+
(link) => link.url && !link.url.startsWith('#') && !link.url.startsWith('javascript:') && link.text.trim(),
8282
),
83-
sourceUrl: url
83+
sourceUrl: url,
8484
};
8585

8686
return json({
8787
success: true,
88-
data: structuredContent
88+
data: structuredContent,
8989
});
9090
} catch (error) {
9191
console.error('Web search error:', error);
92-
return json(
93-
{ error: error instanceof Error ? error.message : 'Unknown error occurred' },
94-
{ status: 500 }
95-
);
92+
return json({ error: error instanceof Error ? error.message : 'Unknown error occurred' }, { status: 500 });
9693
}
97-
}
94+
}

app/routes/api/web-search.ts

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,38 +11,37 @@ export async function action({ request }: ActionFunctionArgs) {
1111
}
1212

1313
const response = await fetch(url);
14+
1415
if (!response.ok) {
1516
throw new Error(`Failed to fetch URL: ${response.status} ${response.statusText}`);
1617
}
1718

1819
const html = await response.text();
19-
20+
2021
// Basic HTML parsing to extract title and content
2122
const titleMatch = html.match(/<title[^>]*>([^<]+)<\/title>/i);
2223
const title = titleMatch ? titleMatch[1].trim() : 'No title found';
23-
24+
2425
// Extract content by removing script and style tags, then getting text content
25-
const content = html
26-
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
27-
.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '')
28-
.replace(/<[^>]+>/g, ' ')
29-
.replace(/\s+/g, ' ')
30-
.trim()
31-
.slice(0, 1000) + '...'; // Limit content length
26+
const content =
27+
html
28+
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
29+
.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '')
30+
.replace(/<[^>]+>/g, ' ')
31+
.replace(/\s+/g, ' ')
32+
.trim()
33+
.slice(0, 1000) + '...'; // Limit content length
3234

3335
return json({
3436
success: true,
3537
data: {
3638
title,
3739
content,
38-
url
39-
}
40+
url,
41+
},
4042
});
4143
} catch (error) {
4244
console.error('Web search error:', error);
43-
return json(
44-
{ error: error instanceof Error ? error.message : 'Unknown error occurred' },
45-
{ status: 500 }
46-
);
45+
return json({ error: error instanceof Error ? error.message : 'Unknown error occurred' }, { status: 500 });
4746
}
48-
}
47+
}

app/types/build.d.ts

Whitespace-only changes.

0 commit comments

Comments
 (0)