diff --git a/client/components/App.tsx b/client/components/App.tsx
index ab5d090..a303efb 100644
--- a/client/components/App.tsx
+++ b/client/components/App.tsx
@@ -1,71 +1,24 @@
-import { usePubSub } from "create-pubsub/react";
-import {
- promptPubSub,
- responsePubSub,
- searchResultsPubSub,
- urlsDescriptionsPubSub,
-} from "../modules/pubSub";
-import { SearchForm } from "./SearchForm";
-import { Toaster } from "react-hot-toast";
-import { SettingsButton } from "./SettingsButton";
-import Markdown from "markdown-to-jsx";
-import { getDisableAiResponseSetting } from "../modules/pubSub";
-import { SearchResultsList } from "./SearchResultsList";
-import { useEffect } from "react";
-import { prepareTextGeneration } from "../modules/textGeneration";
+import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
-export function App() {
- const [query, updateQuery] = usePubSub(promptPubSub);
- const [response] = usePubSub(responsePubSub);
- const [searchResults] = usePubSub(searchResultsPubSub);
- const [urlsDescriptions] = usePubSub(urlsDescriptionsPubSub);
+import { SearchPage } from "../pages/search/SearchPage";
+import { ComparisonPage } from "../pages/static/ComparisonPage";
- useEffect(() => {
- prepareTextGeneration();
- }, []);
+function InnerApp() {
return (
<>
-
- {!getDisableAiResponseSetting() && response.length > 0 && (
-
- {response}
-
- )}
- {searchResults.length > 0 && (
-
-
-
- )}
-
-
-
-
+
+ } />
+ } />
+
>
);
}
+
+export const App = () => {
+ return (
+
+
+
+ );
+}
diff --git a/client/components/SearchForm.tsx b/client/components/SearchForm.tsx
index 7e9bd5a..436fcba 100644
--- a/client/components/SearchForm.tsx
+++ b/client/components/SearchForm.tsx
@@ -1,6 +1,8 @@
-import { useEffect, useRef, FormEvent, useState, useCallback } from "react";
+import { useEffect, useRef, useState, useCallback } from "react";
+import { useNavigate } from 'react-router-dom';
import TextareaAutosize from "react-textarea-autosize";
import { getRandomQuerySuggestion } from "../modules/querySuggestions";
+import { debounce } from "../utils/debounce";
export function SearchForm({
query,
@@ -15,45 +17,42 @@ export function SearchForm({
getRandomQuerySuggestion(),
);
+const navigate = useNavigate();
+
+const startSearching = useCallback((queryToEncode: string) => {
+ updateQuery(queryToEncode);
+ navigate(`/?q=${encodeURIComponent(queryToEncode)}`);
+ }, [updateQuery, navigate]);
+
+ const debouncedStartSearching = debounce(startSearching, 3000); // 3000ms = 3s
+
const handleInputChange = (event: React.ChangeEvent) => {
- const userQueryIsBlank = event.target.value.trim().length === 0;
+ const userQuery = event.target.value.trim();
+ const userQueryIsBlank = userQuery.length === 0;
const suggestedQueryIsBlank = suggestedQuery.trim().length === 0;
-
+
if (userQueryIsBlank && suggestedQueryIsBlank) {
setSuggestedQuery(getRandomQuerySuggestion());
} else if (!userQueryIsBlank && !suggestedQueryIsBlank) {
setSuggestedQuery("");
}
- };
- const startSearching = useCallback(() => {
- let queryToEncode = suggestedQuery;
-
- if (textAreaRef.current && textAreaRef.current.value.trim().length > 0) {
- queryToEncode = textAreaRef.current.value;
+ // Start searching immediately when user types
+ if (!userQueryIsBlank) {
+ debouncedStartSearching(userQuery);
}
-
- self.history.pushState(
- null,
- "",
- `/?q=${encodeURIComponent(queryToEncode)}`,
- );
-
- updateQuery(queryToEncode);
-
- location.reload();
- }, [suggestedQuery, updateQuery]);
-
- const handleSubmit = (event: FormEvent) => {
- event.preventDefault();
- startSearching();
};
useEffect(() => {
const keyboardEventHandler = (event: KeyboardEvent) => {
if (event.code === "Enter" && !event.shiftKey) {
event.preventDefault();
- startSearching();
+ if (textAreaRef.current) {
+ const userQuery = textAreaRef.current.value.trim();
+ if (userQuery.length > 0) {
+ startSearching(userQuery);
+ }
+ }
}
};
const textArea = textAreaRef.current;
@@ -76,7 +75,7 @@ export function SearchForm({
: undefined
}
>
-
);
diff --git a/client/pages/search/SearchPage.tsx b/client/pages/search/SearchPage.tsx
new file mode 100644
index 0000000..fedb3cc
--- /dev/null
+++ b/client/pages/search/SearchPage.tsx
@@ -0,0 +1,80 @@
+import { usePubSub } from "create-pubsub/react";
+import {
+ promptPubSub,
+ responsePubSub,
+ searchResultsPubSub,
+ urlsDescriptionsPubSub,
+} from "../../modules/pubSub";
+import { SearchForm } from "../../components/SearchForm";
+import { Toaster } from "react-hot-toast";
+import { SettingsButton } from "../../components/SettingsButton";
+import Markdown from "markdown-to-jsx";
+import { getDisableAiResponseSetting } from "../../modules/pubSub";
+import { SearchResultsList } from "../../components/SearchResultsList";
+import { useEffect } from "react";
+import { prepareTextGeneration } from "../../modules/textGeneration";
+import { useLocation } from 'react-router-dom';
+
+export const SearchPage = () => {
+ const [query, setQuery] = usePubSub(promptPubSub);
+ const [response] = usePubSub(responsePubSub);
+ const [searchResults] = usePubSub(searchResultsPubSub);
+ const [urlsDescriptions] = usePubSub(urlsDescriptionsPubSub);
+
+ useEffect(() => {
+ prepareTextGeneration();
+ }, []);
+
+ const location = useLocation();
+
+ useEffect(() => {
+ const params = new URLSearchParams(location.search);
+ const newQuery = params.get('q');
+ if (newQuery !== null) {
+ setQuery(newQuery);
+ }
+ }, [location.search, setQuery]);
+
+
+ return (
+ <>
+
+ {!getDisableAiResponseSetting() && response.length > 0 && (
+
+
AI's thoughts:
+
+ {response}
+
+
+ )}
+ {searchResults.length > 0 && (
+
+
+
+ )}
+
+
+
+
+ >
+ );
+};
diff --git a/client/pages/static/ComparisonPage.tsx b/client/pages/static/ComparisonPage.tsx
new file mode 100644
index 0000000..44c9665
--- /dev/null
+++ b/client/pages/static/ComparisonPage.tsx
@@ -0,0 +1,10 @@
+import React from 'react';
+
+export const ComparisonPage = () => {
+ return (
+
+
Comparison Page
+ {/* Add your comparison logic here */}
+
+ );
+};
diff --git a/client/utils/debounce.ts b/client/utils/debounce.ts
new file mode 100644
index 0000000..788ac57
--- /dev/null
+++ b/client/utils/debounce.ts
@@ -0,0 +1,16 @@
+export const debounce = void>(
+ func: T,
+ wait: number,
+): ((...args: Parameters) => void) => {
+ let timeout: NodeJS.Timeout;
+
+ return (...args: Parameters): void => {
+ const later = () => {
+ clearTimeout(timeout);
+ func(...args);
+ };
+
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ };
+};
diff --git a/package-lock.json b/package-lock.json
index a14c81b..8063b2b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26,6 +26,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hot-toast": "^2.4.1",
+ "react-router-dom": "^6.23.0",
"react-textarea-autosize": "^8.5.3",
"react-tooltip": "^5.21.6",
"temp-dir": "^3.0.0",
@@ -1091,6 +1092,14 @@
"node": ">= 8"
}
},
+ "node_modules/@remix-run/router": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.0.tgz",
+ "integrity": "sha512-Quz1KOffeEf/zwkCBM3kBtH4ZoZ+pT3xIXBG4PPW/XFtDP7EGhtTiC2+gpL9GnR7+Qdet5Oa6cYSvwKYg6kN9Q==",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.13.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz",
@@ -3138,6 +3147,36 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-router": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.0.tgz",
+ "integrity": "sha512-wPMZ8S2TuPadH0sF5irFGjkNLIcRvOSaEe7v+JER8508dyJumm6XZB1u5kztlX0RVq6AzRVndzqcUh6sFIauzA==",
+ "dependencies": {
+ "@remix-run/router": "1.16.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.0.tgz",
+ "integrity": "sha512-Q9YaSYvubwgbal2c9DJKfx6hTNoBp3iJDsl+Duva/DwxoJH+OTXkxGpql4iUK2sla/8z4RpjAm6EWx1qUDuopQ==",
+ "dependencies": {
+ "@remix-run/router": "1.16.0",
+ "react-router": "6.23.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
"node_modules/react-textarea-autosize": {
"version": "8.5.3",
"resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.3.tgz",
diff --git a/package.json b/package.json
index c5f01f2..8c2f2a1 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hot-toast": "^2.4.1",
+ "react-router-dom": "^6.23.0",
"react-textarea-autosize": "^8.5.3",
"react-tooltip": "^5.21.6",
"temp-dir": "^3.0.0",