feature: keyword2link suggestion

- better optimization for NLU model loading
- fix some bugs to ensure that the build passes
This commit is contained in:
alikia2x 2024-07-16 21:29:17 +08:00
parent bf7867b263
commit 263b82c06e
11 changed files with 96 additions and 20 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -6,14 +6,14 @@ import search from "lib/search";
export default function ( export default function (
index: number, index: number,
suggestion: suggestionItem[], suggestion: suggestionItem[],
query: string, _query: string,
settings: settingsType, settings: settingsType,
searchBoxRef: React.RefObject<HTMLInputElement> searchBoxRef: React.RefObject<HTMLInputElement>
) { ) {
const selected = suggestion[index]; const selected = suggestion[index];
const engine = settings.searchEngines[settings.currentSearchEngine]; const engine = settings.searchEngines[settings.currentSearchEngine];
const newTab = settings.searchInNewTab; const newTab = settings.searchInNewTab;
let clipboard: any; //let clipboard: any;
if (selected.type === "QUERY" || selected.type === "default") { if (selected.type === "QUERY" || selected.type === "default") {
search(selected.suggestion, engine, newTab); search(selected.suggestion, engine, newTab);
} else if (selected.type === "NAVIGATION" || selected.type === "default-link") { } else if (selected.type === "NAVIGATION" || selected.type === "default-link") {

View File

@ -14,8 +14,7 @@ export function handleNLUResult(result: any, updateSuggestion: UpdateSuggestionF
getWeather(data.latitude, data.longitude).then((weather) => { getWeather(data.latitude, data.longitude).then((weather) => {
console.log(weather["hourly"]); console.log(weather["hourly"]);
let hourIndex = findClosestDateIndex( let hourIndex = findClosestDateIndex(
weather["hourly"]["time"], weather["hourly"]["time"]
weather["utc_offset_seconds"]
); );
let temp = weather["hourly"]["apparent_temperature"][hourIndex]; let temp = weather["hourly"]["apparent_temperature"][hourIndex];
let weatherCode = weather["hourly"]["weather_code"][hourIndex]; let weatherCode = weather["hourly"]["weather_code"][hourIndex];

View File

@ -11,15 +11,18 @@ 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";
import { sendError } from "lib/telemetering/sendError"; import { sendError } from "lib/telemetering/sendError";
import { NLU } from "lib/nlp/load";
import { handleNLUResult } from "./handleNLUResult"; import { handleNLUResult } from "./handleNLUResult";
import { useAtom, useAtomValue } from "jotai"; import { useAtom, useAtomValue } from "jotai";
import i18next from "i18next"; import i18next from "i18next";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { keywordSuggestion } from "lib/onesearch/keywordSuggestion";
import { NLUType } from "lib/nlp/load";
export default function OneSearch() { export default function OneSearch() {
const [suggestion, setFinalSuggetsion] = useAtom(suggestionAtom); const [suggestion, setFinalSuggetsion] = useAtom(suggestionAtom);
const [manager, setManager] = useState(null); const [manager, setManager] = useState(null);
const [NLUModel, setNLUModel] = useState<NLUType>();
const [NLUModelLoaded, setNLUModelLoaded] = useState(false);
const lastRequestTimeRef = useRef(0); const lastRequestTimeRef = useRef(0);
const selected = useAtomValue(selectedSuggestionAtom); const selected = useAtomValue(selectedSuggestionAtom);
const settings = useAtomValue(settingsAtom); const settings = useAtomValue(settingsAtom);
@ -83,20 +86,32 @@ export default function OneSearch() {
}); });
} }
const NLUModel = new NLU(); (async function () {
const NLU = await import("lib/nlp/load");
const mainNLUModel = new NLU.NLU();
setNLUModel(mainNLUModel);
setNLUModelLoaded(true);
})();
useEffect(() => { useEffect(() => {
NLUModel.init().then((nlu) => { if (NLUModel === null || NLUModel === undefined) {
return;
}
NLUModel.init().then((nlu: typeof NLUModel) => {
setManager(nlu.manager); setManager(nlu.manager);
console.log(nlu.manager);
}); });
}, []); }, [NLUModelLoaded]);
useEffect(() => { useEffect(() => {
cleanSuggestion("default-link", "default", "text"); cleanSuggestion("default-link", "default", "text", "link");
if (validLink(query)) { if (validLink(query)) {
updateSuggestion([ updateSuggestion([
{ type: "default-link", suggestion: query, relevance: 3000, prompt: <span>Go to: </span> }, {
type: "default-link",
suggestion: query,
relevance: 3000,
prompt: <span>Go to: </span>
},
{ type: "default", suggestion: query, relevance: 1600 } { type: "default", suggestion: query, relevance: 1600 }
]); ]);
} else { } else {
@ -109,6 +124,10 @@ export default function OneSearch() {
]); ]);
} }
if (keywordSuggestion(query) !== null) {
updateSuggestion([keywordSuggestion(query)!]);
}
if (manager != null) { if (manager != null) {
// @ts-ignore // @ts-ignore
manager.process(query).then((result) => { manager.process(query).then((result) => {
@ -147,10 +166,16 @@ export default function OneSearch() {
)} )}
</PlainSearch> </PlainSearch>
); );
} else if (s.type === "NAVIGATION" || s.type === "default-link") { } else if (
s.type === "NAVIGATION" ||
s.type === "default-link" ||
s.type === "link"
) {
return ( return (
<Link key={i} query={s.suggestion} selected={i == selected}> <Link key={i} query={s.suggestion} selected={i == selected}>
{s.prompt && <span className="text-zinc-700 dark:text-zinc-400">{s.prompt}</span>} {s.prompt && (
<span className="text-zinc-700 dark:text-zinc-400">{s.prompt}</span>
)}
{s.suggestion} {s.suggestion}
{devMode && ( {devMode && (
<span className="absolute text-zinc-700 dark:text-zinc-400 text-sm leading-10 h-10 right-2"> <span className="absolute text-zinc-700 dark:text-zinc-400 text-sm leading-10 h-10 right-2">
@ -162,7 +187,9 @@ export default function OneSearch() {
} else if (s.type === "text") { } else if (s.type === "text") {
return ( return (
<PlainText key={i} selected={i == selected}> <PlainText key={i} selected={i == selected}>
{s.prompt && <span className="text-zinc-700 dark:text-zinc-400">{s.prompt}</span>} {s.prompt && (
<span className="text-zinc-700 dark:text-zinc-400">{s.prompt}</span>
)}
<p>{s.suggestion}</p> <p>{s.suggestion}</p>
{devMode && ( {devMode && (
<span className="bottom-0 absolute text-zinc-700 dark:text-zinc-400 text-sm leading-10 h-10 right-2"> <span className="bottom-0 absolute text-zinc-700 dark:text-zinc-400 text-sm leading-10 h-10 right-2">

View File

@ -10,6 +10,15 @@ import { LangEn } from "@nlpjs/lang-en-min";
import { LangZh } from "@nlpjs/lang-zh"; import { LangZh } from "@nlpjs/lang-zh";
import * as fflate from 'fflate'; import * as fflate from 'fflate';
export interface NLUType {
manager: any;
inited: boolean;
loadIntentionModel(): Promise<void>;
init(): Promise<this>;
process(lang: string, text: string): Promise<any>;
}
export class NLU { export class NLU {
manager: any; manager: any;
inited: boolean = false; inited: boolean = false;

View File

@ -1,7 +1,7 @@
export function normalizeURL(input: string): string { export function normalizeURL(input: string): string {
try { try {
// try to create a URL object // try to create a URL object
const url = new URL(input); const url = new URL(input, window.location.href);
// if the URL is valid, return it // if the URL is valid, return it
return url.href; return url.href;
} catch (error) { } catch (error) {

View File

@ -0,0 +1,39 @@
import { suggestionItem } from "global";
interface keywordLinkDict {
[key: string]: string;
}
const dict_en: keywordLinkDict = {
about: "/about"
};
const dict_cn: keywordLinkDict = {
: "/about"
};
export function keywordSuggestion(query: string) {
for (const keyword in dict_cn) {
if (query.includes(keyword)) {
const result: suggestionItem = {
type: "link",
suggestion: dict_cn[keyword],
prompt: keyword,
relevance: 3000
};
return result
}
}
for (const keyword in dict_en) {
if (query.includes(keyword)) {
const result: suggestionItem = {
type: "link",
suggestion: dict_en[keyword],
prompt: keyword,
relevance: 3000
};
return result
}
}
return null;
}

View File

@ -14,10 +14,9 @@ export function getClosestHourTimestamp(): string {
return localHourTimestamp; return localHourTimestamp;
} }
export function findClosestDateIndex(dates: string[], utc_offset_seconds: number): number { export function findClosestDateIndex(dates: string[]): number {
const now = new Date(); const now = new Date();
const nowTimestamp = now.getTime(); const nowTimestamp = now.getTime();
const offsetMilliseconds = utc_offset_seconds * 1000;
let closestIndex = -1; let closestIndex = -1;
let closestDiff = Infinity; let closestDiff = Infinity;

View File

@ -1,7 +1,7 @@
{ {
"name": "sparkhome", "name": "sparkhome",
"private": false, "private": false,
"version": "5.5.3", "version": "5.6.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "bun server.ts", "dev": "bun server.ts",
@ -52,6 +52,7 @@
"tailwindcss": "^3.4.4", "tailwindcss": "^3.4.4",
"typescript": "^5.2.2", "typescript": "^5.2.2",
"vite": "^5.3.1", "vite": "^5.3.1",
"vite-plugin-chunk-split": "^0.5.0",
"vite-plugin-pages": "^0.32.2", "vite-plugin-pages": "^0.32.2",
"vite-tsconfig-paths": "^4.3.2" "vite-tsconfig-paths": "^4.3.2"
} }

View File

@ -44,7 +44,7 @@ export default function AboutPage() {
</p> </p>
<p className="relative font-bold text-2xl mt-12">Presented By</p> <p className="relative font-bold text-2xl mt-12">Presented By</p>
{!darkMode && <img src="/LuminaraStudio.png" className="relative md:h-56 mt-6" />} {!darkMode && <img src="/LuminaraStudio.png" className="relative md:h-64 mt-6" />}
{darkMode && <img src="/LuminaraStudioDark.png" className="relative md:h-56 mt-6" />} {darkMode && <img src="/LuminaraStudioDark.png" className="relative md:h-56 mt-6" />}
</AboutLayout> </AboutLayout>
); );

View File

@ -2,6 +2,7 @@ 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 Pages from "vite-plugin-pages";
import tsconfigPaths from 'vite-tsconfig-paths'; import tsconfigPaths from 'vite-tsconfig-paths';
import { chunkSplitPlugin } from 'vite-plugin-chunk-split';
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
@ -10,6 +11,7 @@ export default defineConfig({
Pages({ Pages({
dirs: "./pages/" dirs: "./pages/"
}), }),
tsconfigPaths() tsconfigPaths(),
chunkSplitPlugin()
] ]
}); });