ref: use react router
fix: a critical performance issue add: inpage-link for onesearch
This commit is contained in:
parent
263b82c06e
commit
dee9dff8e8
@ -1,30 +1,30 @@
|
|||||||
import { normalizeURL } from "lib/normalizeURL";
|
import { normalizeURL } from "lib/normalizeURL";
|
||||||
|
import { useNavigate } from "react-router";
|
||||||
|
|
||||||
export default function Link(props: { children: React.ReactNode; query: string; selected: boolean }) {
|
interface LinkSuggestionProps {
|
||||||
if (props.selected) {
|
children: React.ReactNode;
|
||||||
|
query: string;
|
||||||
|
selected: boolean;
|
||||||
|
inPage?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function LinkSuggestion(props: LinkSuggestionProps) {
|
||||||
|
const className = props.selected
|
||||||
|
? `w-full h-10 leading-10 bg-zinc-300 dark:bg-zinc-700 px-5 z-10 cursor-pointer duration-100`
|
||||||
|
: `w-full h-10 leading-10 bg-zinc-100 hover:bg-zinc-300 dark:bg-zinc-800 hover:dark:bg-zinc-700 px-5 z-10 cursor-pointer duration-100`;
|
||||||
|
const navigate = useNavigate();
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`w-full h-10 leading-10 bg-zinc-300 dark:bg-zinc-700
|
className={className}
|
||||||
px-5 z-10 cursor-pointer duration-100`}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (props.inPage) {
|
||||||
|
navigate(props.query);
|
||||||
|
} else {
|
||||||
window.open(normalizeURL(props.query));
|
window.open(normalizeURL(props.query));
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={`w-full h-10 leading-10 bg-zinc-100 hover:bg-zinc-300
|
|
||||||
dark:bg-zinc-800 hover:dark:bg-zinc-700 px-5 z-10 cursor-pointer duration-100`}
|
|
||||||
onClick={() => {
|
|
||||||
window.open(normalizeURL(props.query));
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{props.children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -6,7 +6,7 @@ import getSearchEngineName from "lib/onesearch/getSearchEngineName";
|
|||||||
import PlainSearch from "./plainSearch";
|
import PlainSearch from "./plainSearch";
|
||||||
import { suggestionAtom } from "lib/state/suggestion";
|
import { suggestionAtom } from "lib/state/suggestion";
|
||||||
import validLink from "lib/url/validLink";
|
import validLink from "lib/url/validLink";
|
||||||
import Link from "./link";
|
import LinkSuggestion from "./link";
|
||||||
import { selectedSuggestionAtom } from "lib/state/suggestionSelection";
|
import { selectedSuggestionAtom } from "lib/state/suggestionSelection";
|
||||||
import { settingsAtom } from "lib/state/settings";
|
import { settingsAtom } from "lib/state/settings";
|
||||||
import PlainText from "./plainText";
|
import PlainText from "./plainText";
|
||||||
@ -86,12 +86,14 @@ export default function OneSearch() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
(async function () {
|
(async function () {
|
||||||
const NLU = await import("lib/nlp/load");
|
const NLU = await import("lib/nlp/load");
|
||||||
const mainNLUModel = new NLU.NLU();
|
const mainNLUModel = new NLU.NLU();
|
||||||
setNLUModel(mainNLUModel);
|
setNLUModel(mainNLUModel);
|
||||||
setNLUModelLoaded(true);
|
setNLUModelLoaded(true);
|
||||||
})();
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (NLUModel === null || NLUModel === undefined) {
|
if (NLUModel === null || NLUModel === undefined) {
|
||||||
@ -172,7 +174,7 @@ export default function OneSearch() {
|
|||||||
s.type === "link"
|
s.type === "link"
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<Link key={i} query={s.suggestion} selected={i == selected}>
|
<LinkSuggestion key={i} query={s.suggestion} selected={i == selected}>
|
||||||
{s.prompt && (
|
{s.prompt && (
|
||||||
<span className="text-zinc-700 dark:text-zinc-400">{s.prompt}</span>
|
<span className="text-zinc-700 dark:text-zinc-400">{s.prompt}</span>
|
||||||
)}
|
)}
|
||||||
@ -182,7 +184,7 @@ export default function OneSearch() {
|
|||||||
{s.relevance}
|
{s.relevance}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</Link>
|
</LinkSuggestion>
|
||||||
);
|
);
|
||||||
} else if (s.type === "text") {
|
} else if (s.type === "text") {
|
||||||
return (
|
return (
|
||||||
@ -198,6 +200,25 @@ export default function OneSearch() {
|
|||||||
)}
|
)}
|
||||||
</PlainText>
|
</PlainText>
|
||||||
);
|
);
|
||||||
|
} else if (s.type === "inpage-link") {
|
||||||
|
return (
|
||||||
|
<LinkSuggestion
|
||||||
|
key={i}
|
||||||
|
query={s.suggestion}
|
||||||
|
selected={i == selected}
|
||||||
|
inPage={true}
|
||||||
|
>
|
||||||
|
{s.prompt && (
|
||||||
|
<span className="text-zinc-700 dark:text-zinc-400">{s.prompt}</span>
|
||||||
|
)}
|
||||||
|
{s.suggestion}
|
||||||
|
{devMode && (
|
||||||
|
<span className="absolute text-zinc-700 dark:text-zinc-400 text-sm leading-10 h-10 right-2">
|
||||||
|
{s.relevance}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</LinkSuggestion>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
</SuggestionBox>
|
</SuggestionBox>
|
||||||
|
@ -18,8 +18,11 @@ export default function (
|
|||||||
} else if (selected.type === "NAVIGATION" || selected.type === "default-link") {
|
} else if (selected.type === "NAVIGATION" || selected.type === "default-link") {
|
||||||
window.open(normalizeURL(selected.suggestion));
|
window.open(normalizeURL(selected.suggestion));
|
||||||
} else if (selected.type === "text") {
|
} else if (selected.type === "text") {
|
||||||
console.log("????");
|
|
||||||
copyToClipboard(selected.suggestion);
|
copyToClipboard(selected.suggestion);
|
||||||
searchBoxRef.current?.focus();
|
searchBoxRef.current?.focus();
|
||||||
|
} else if (selected.type === "link") {
|
||||||
|
window.open(normalizeURL(selected.suggestion));
|
||||||
|
} else if (selected.type === "inpage-link") {
|
||||||
|
location.href = normalizeURL(selected.suggestion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ export function keywordSuggestion(query: string) {
|
|||||||
for (const keyword in dict_cn) {
|
for (const keyword in dict_cn) {
|
||||||
if (query.includes(keyword)) {
|
if (query.includes(keyword)) {
|
||||||
const result: suggestionItem = {
|
const result: suggestionItem = {
|
||||||
type: "link",
|
type: "inpage-link",
|
||||||
suggestion: dict_cn[keyword],
|
suggestion: dict_cn[keyword],
|
||||||
prompt: keyword,
|
prompt: keyword,
|
||||||
relevance: 3000
|
relevance: 3000
|
||||||
@ -27,7 +27,7 @@ export function keywordSuggestion(query: string) {
|
|||||||
for (const keyword in dict_en) {
|
for (const keyword in dict_en) {
|
||||||
if (query.includes(keyword)) {
|
if (query.includes(keyword)) {
|
||||||
const result: suggestionItem = {
|
const result: suggestionItem = {
|
||||||
type: "link",
|
type: "inpage-link",
|
||||||
suggestion: dict_en[keyword],
|
suggestion: dict_en[keyword],
|
||||||
prompt: keyword,
|
prompt: keyword,
|
||||||
relevance: 3000
|
relevance: 3000
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "sparkhome",
|
"name": "sparkhome",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "5.6.0",
|
"version": "5.7.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun server.ts",
|
"dev": "bun server.ts",
|
||||||
|
@ -58,7 +58,7 @@ function Version(props: { title: string; version: string; versionClass?: string
|
|||||||
<span
|
<span
|
||||||
className={
|
className={
|
||||||
"relative px-2 py-1 text-sm font-bold rounded-md text-nowrap text-white " +
|
"relative px-2 py-1 text-sm font-bold rounded-md text-nowrap text-white " +
|
||||||
props.versionClass ?? ""
|
props.versionClass || ""
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{props.version}
|
{props.version}
|
||||||
|
@ -12,8 +12,9 @@ export default function Homepage() {
|
|||||||
const setBgFocus = useSetAtom(bgFocusAtom);
|
const setBgFocus = useSetAtom(bgFocusAtom);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-screen fixed overflow-hidden w-screen bg-black">
|
<div className="h-screen w-screen overflow-x-hidden bg-white dark:bg-[rgb(23,25,29)]">
|
||||||
<Background />
|
<Background />
|
||||||
|
|
||||||
<EngineSelector
|
<EngineSelector
|
||||||
className="absolute top-20 lg:top-44 short:top-0 translate-x-[-50%] translate-y-[-0.2rem]
|
className="absolute top-20 lg:top-44 short:top-0 translate-x-[-50%] translate-y-[-0.2rem]
|
||||||
left-1/2 w-11/12 sm:w-[700px] text:black text-right
|
left-1/2 w-11/12 sm:w-[700px] text:black text-right
|
||||||
|
91
src/app.tsx
91
src/app.tsx
@ -1,74 +1,31 @@
|
|||||||
import { Suspense } from "react";
|
import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
||||||
import { useRoutes } from "react-router-dom";
|
|
||||||
import routes from "~react-pages";
|
|
||||||
import i18n from "i18next";
|
|
||||||
import { initReactI18next } from "react-i18next";
|
|
||||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
|
||||||
import ICU from 'i18next-icu';
|
|
||||||
import * as en from "i18n/en.json"
|
|
||||||
import * as zh from "i18n/zh.json"
|
|
||||||
import * as ja from "i18n/ja.json"
|
|
||||||
import * as ar from "i18n/ar.json"
|
|
||||||
import * as de from "i18n/de.json"
|
|
||||||
import * as es from "i18n/es.json"
|
|
||||||
import * as fr from "i18n/fr.json"
|
|
||||||
import * as it from "i18n/it.json"
|
|
||||||
import * as ko from "i18n/ko.json"
|
|
||||||
import * as pt from "i18n/pt.json"
|
|
||||||
import * as ru from "i18n/ru.json"
|
|
||||||
|
|
||||||
i18n.use(initReactI18next) // passes i18n down to react-i18next
|
import "./i18n";
|
||||||
.use(LanguageDetector)
|
import Homepage from "pages";
|
||||||
.use(ICU)
|
import AboutPage from "pages/about";
|
||||||
.init({
|
import LicensePage from "pages/about/license";
|
||||||
resources: {
|
|
||||||
en: {
|
const router = createBrowserRouter([
|
||||||
translation: en
|
{
|
||||||
|
path: "/",
|
||||||
|
element: <Homepage />
|
||||||
},
|
},
|
||||||
zh: {
|
{
|
||||||
translation: zh
|
path: "about",
|
||||||
},
|
element: <AboutPage />,
|
||||||
ja: {
|
children: [
|
||||||
translation: ja
|
{
|
||||||
},
|
path: "license",
|
||||||
ar: {
|
element: <LicensePage />
|
||||||
translation: ar
|
|
||||||
},
|
|
||||||
de: {
|
|
||||||
translation: de
|
|
||||||
},
|
|
||||||
es: {
|
|
||||||
translation: es
|
|
||||||
},
|
|
||||||
fr: {
|
|
||||||
translation: fr
|
|
||||||
},
|
|
||||||
it: {
|
|
||||||
translation: it
|
|
||||||
},
|
|
||||||
ko: {
|
|
||||||
translation: ko
|
|
||||||
},
|
|
||||||
pt: {
|
|
||||||
translation: pt
|
|
||||||
},
|
|
||||||
ru: {
|
|
||||||
translation: ru
|
|
||||||
}
|
}
|
||||||
},
|
]
|
||||||
fallbackLng: "en",
|
|
||||||
|
|
||||||
interpolation: {
|
|
||||||
escapeValue: false // react already safes from xss => https://www.i18next.com/translation-function/interpolation#unescape
|
|
||||||
},
|
|
||||||
|
|
||||||
detection: {
|
|
||||||
order: ['navigator'],
|
|
||||||
caches: []
|
|
||||||
}
|
}
|
||||||
});
|
]);
|
||||||
|
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
return <Suspense fallback={<p>Loading...</p>}>{useRoutes(routes)}</Suspense>;
|
return (
|
||||||
|
<div className="relative bg-white dark:bg-black dark:text-white min-h-screen w-screen">
|
||||||
|
<RouterProvider router={router} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
65
src/i18n.ts
Normal file
65
src/i18n.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import * as en from "i18n/en.json"
|
||||||
|
import * as zh from "i18n/zh.json"
|
||||||
|
import * as ja from "i18n/ja.json"
|
||||||
|
import * as ar from "i18n/ar.json"
|
||||||
|
import * as de from "i18n/de.json"
|
||||||
|
import * as es from "i18n/es.json"
|
||||||
|
import * as fr from "i18n/fr.json"
|
||||||
|
import * as it from "i18n/it.json"
|
||||||
|
import * as ko from "i18n/ko.json"
|
||||||
|
import * as pt from "i18n/pt.json"
|
||||||
|
import * as ru from "i18n/ru.json"
|
||||||
|
import i18n from "i18next";
|
||||||
|
import { initReactI18next } from "react-i18next";
|
||||||
|
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||||
|
import ICU from 'i18next-icu';
|
||||||
|
i18n.use(initReactI18next) // passes i18n down to react-i18next
|
||||||
|
.use(LanguageDetector)
|
||||||
|
.use(ICU)
|
||||||
|
.init({
|
||||||
|
resources: {
|
||||||
|
en: {
|
||||||
|
translation: en
|
||||||
|
},
|
||||||
|
zh: {
|
||||||
|
translation: zh
|
||||||
|
},
|
||||||
|
ja: {
|
||||||
|
translation: ja
|
||||||
|
},
|
||||||
|
ar: {
|
||||||
|
translation: ar
|
||||||
|
},
|
||||||
|
de: {
|
||||||
|
translation: de
|
||||||
|
},
|
||||||
|
es: {
|
||||||
|
translation: es
|
||||||
|
},
|
||||||
|
fr: {
|
||||||
|
translation: fr
|
||||||
|
},
|
||||||
|
it: {
|
||||||
|
translation: it
|
||||||
|
},
|
||||||
|
ko: {
|
||||||
|
translation: ko
|
||||||
|
},
|
||||||
|
pt: {
|
||||||
|
translation: pt
|
||||||
|
},
|
||||||
|
ru: {
|
||||||
|
translation: ru
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fallbackLng: "en",
|
||||||
|
|
||||||
|
interpolation: {
|
||||||
|
escapeValue: false // react already safes from xss => https://www.i18next.com/translation-function/interpolation#unescape
|
||||||
|
},
|
||||||
|
|
||||||
|
detection: {
|
||||||
|
order: ['navigator'],
|
||||||
|
caches: []
|
||||||
|
}
|
||||||
|
});
|
@ -1,6 +1,5 @@
|
|||||||
import { StrictMode } from "react";
|
import { StrictMode } from "react";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import { BrowserRouter } from "react-router-dom";
|
|
||||||
import { App } from "./app";
|
import { App } from "./app";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
import { NextUIProvider } from "@nextui-org/react";
|
import { NextUIProvider } from "@nextui-org/react";
|
||||||
@ -9,10 +8,8 @@ const app = createRoot(document.getElementById("root")!);
|
|||||||
|
|
||||||
app.render(
|
app.render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<BrowserRouter>
|
|
||||||
<NextUIProvider>
|
<NextUIProvider>
|
||||||
<App />
|
<App />
|
||||||
</NextUIProvider>
|
</NextUIProvider>
|
||||||
</BrowserRouter>
|
|
||||||
</StrictMode>
|
</StrictMode>
|
||||||
);
|
);
|
||||||
|
@ -7,6 +7,7 @@ const config: Config = {
|
|||||||
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
"./node_modules/@nextui-org/theme/**/*.{js,ts,jsx,tsx}",
|
"./node_modules/@nextui-org/theme/**/*.{js,ts,jsx,tsx}",
|
||||||
|
"./src/**/*.{js,ts,jsx,tsx,mdx}"
|
||||||
],
|
],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import react from "@vitejs/plugin-react-swc";
|
import react from "@vitejs/plugin-react-swc";
|
||||||
import Pages from "vite-plugin-pages";
|
|
||||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||||
import { chunkSplitPlugin } from 'vite-plugin-chunk-split';
|
import { chunkSplitPlugin } from 'vite-plugin-chunk-split';
|
||||||
|
|
||||||
@ -8,9 +7,6 @@ import { chunkSplitPlugin } from 'vite-plugin-chunk-split';
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
react(),
|
react(),
|
||||||
Pages({
|
|
||||||
dirs: "./pages/"
|
|
||||||
}),
|
|
||||||
tsconfigPaths(),
|
tsconfigPaths(),
|
||||||
chunkSplitPlugin()
|
chunkSplitPlugin()
|
||||||
]
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user