diff --git a/.prettierrc b/.prettierrc
index 255a135..d8cc397 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,8 +1,8 @@
{
- "useTabs": false,
- "tabWidth": 4,
- "trailingComma": "none",
- "singleQuote": false,
- "printWidth": 100,
- "endOfLine": "lf"
+ "useTabs": true,
+ "tabWidth": 4,
+ "trailingComma": "none",
+ "singleQuote": false,
+ "printWidth": 100,
+ "endOfLine": "lf"
}
diff --git a/backend/route.ts b/backend/route.ts
index c7e21cf..bd8dabd 100644
--- a/backend/route.ts
+++ b/backend/route.ts
@@ -2,21 +2,21 @@ import { Express } from "express";
import { completeGoogle } from "search-engine-autocomplete";
export function configureBackendRoutes(app: Express) {
- app.get('/api/v1/suggestion', async (req, res) => {
- const query = req.query.q as string;
- const t = parseInt(req.query.t as string || "0") || null;
- let language = req.query.l as string || 'en-US';
-
- try {
- const data = await completeGoogle(query, language);
- //logger.info({ type: "onesearch_search_autocomplete", query: query, data: data });
- res.json({ ...data, time: t });
- } catch (error) {
- //logger.error({ type: "onesearch_search_autocomplete_error", error: error.message });
- res.status(500).json({ error: 'Internal Server Error' });
- }
- });
- app.get("/api/v1/ping", async (_, res) => {
- res.status(200).json({message: "pong"});
- })
-}
\ No newline at end of file
+ app.get("/api/v1/suggestion", async (req, res) => {
+ const query = req.query.q as string;
+ const t = parseInt((req.query.t as string) || "0") || null;
+ let language = (req.query.l as string) || "en-US";
+
+ try {
+ const data = await completeGoogle(query, language);
+ //logger.info({ type: "onesearch_search_autocomplete", query: query, data: data });
+ res.json({ ...data, time: t });
+ } catch (error) {
+ //logger.error({ type: "onesearch_search_autocomplete_error", error: error.message });
+ res.status(500).json({ error: "Internal Server Error" });
+ }
+ });
+ app.get("/api/v1/ping", async (_, res) => {
+ res.status(200).json({ message: "pong" });
+ });
+}
diff --git a/bun.lockb b/bun.lockb
index fb8a6c9..b7295b8 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/components/background.tsx b/components/background.tsx
index 308ba27..a62592a 100644
--- a/components/background.tsx
+++ b/components/background.tsx
@@ -4,17 +4,27 @@ import BackgroundContainer from "./backgroundContainer";
import useDarkMode from "lib/darkModeHook";
export default function Background() {
- const [isFocus, setFocus] = useAtom(bgFocusAtom);
-
- const darkMode = useDarkMode();
+ const [isFocus, setFocus] = useAtom(bgFocusAtom);
- return (
-
- {darkMode ? (
- setFocus(false)} darkMode={darkMode}/>
- ) : (
- setFocus(false)} darkMode={darkMode}/>
- )}
-
- );
+ const darkMode = useDarkMode();
+
+ return (
+
+ {darkMode ? (
+ setFocus(false)}
+ darkMode={darkMode}
+ />
+ ) : (
+ setFocus(false)}
+ darkMode={darkMode}
+ />
+ )}
+
+ );
}
diff --git a/components/backgroundContainer.tsx b/components/backgroundContainer.tsx
index 6a8ca8b..25a8e52 100644
--- a/components/backgroundContainer.tsx
+++ b/components/backgroundContainer.tsx
@@ -4,47 +4,51 @@ import validateColor from "validate-color";
import { useAtomValue } from "jotai";
export default function BackgroundContainer(props: {
- isFocus: boolean;
- src: string;
- darkMode: boolean;
- onClick: () => void;
+ isFocus: boolean;
+ src: string;
+ darkMode: boolean;
+ onClick: () => void;
}) {
- const settings = useAtomValue(settingsAtom);
- if (validateColor(props.src)) {
- return (
-
- );
- } else if (validUrl.isWebUri(props.src)) {
- return (
-
- );
- } else {
- if (props.darkMode) {
- return (
-
- );
- } else {
- return (
-
- );
- }
- }
+ const settings = useAtomValue(settingsAtom);
+ if (validateColor(props.src)) {
+ return (
+
+ );
+ } else if (validUrl.isWebUri(props.src)) {
+ return (
+
+ );
+ } else {
+ if (props.darkMode) {
+ return (
+
+ );
+ } else {
+ return (
+
+ );
+ }
+ }
}
diff --git a/components/engineSelector.tsx b/components/engineSelector.tsx
index 7959fac..bc278e3 100644
--- a/components/engineSelector.tsx
+++ b/components/engineSelector.tsx
@@ -7,44 +7,44 @@ import { useAtomValue, useSetAtom } from "jotai";
import Picker, { PickedItem } from "./picker";
export default function EngineSelector(props: { className: string }) {
- const { t } = useTranslation();
- const settings: settingsType = useAtomValue(settingsAtom);
- const engines = settings.searchEngines;
- const currentEngine: string = settings.currentSearchEngine;
- const [selected, setSelected] = useState(currentEngine);
- const setSettings = useSetAtom(settingsAtom);
- let engineList: PickedItem = {};
- for (const engineKey of Object.keys(engines)) {
- engineList[engineKey] = getName(engineKey);
- }
+ const { t } = useTranslation();
+ const settings: settingsType = useAtomValue(settingsAtom);
+ const engines = settings.searchEngines;
+ const currentEngine: string = settings.currentSearchEngine;
+ const [selected, setSelected] = useState(currentEngine);
+ const setSettings = useSetAtom(settingsAtom);
+ let engineList: PickedItem = {};
+ for (const engineKey of Object.keys(engines)) {
+ engineList[engineKey] = getName(engineKey);
+ }
- function getName(engineKey: string) {
- return engineTranslation.includes(engineKey) ? t(`search.engine.${engineKey}`) : engineKey;
- }
+ function getName(engineKey: string) {
+ return engineTranslation.includes(engineKey) ? t(`search.engine.${engineKey}`) : engineKey;
+ }
- useEffect(() => {
- function setEngine(engine: string) {
- setSettings((oldSettings: settingsType) => {
- return {
- ...oldSettings,
- currentSearchEngine: engine
- };
- });
- }
- if (selected !== currentEngine) {
- setEngine(selected);
- }
- }, [currentEngine, selected, setSettings]);
+ useEffect(() => {
+ function setEngine(engine: string) {
+ setSettings((oldSettings: settingsType) => {
+ return {
+ ...oldSettings,
+ currentSearchEngine: engine
+ };
+ });
+ }
+ if (selected !== currentEngine) {
+ setEngine(selected);
+ }
+ }, [currentEngine, selected, setSettings]);
- return (
- {
- setSelected(selected);
- }}
- displayContent={getName(selected)}
- className={props.className}
- />
- );
+ return (
+ {
+ setSelected(selected);
+ }}
+ displayContent={getName(selected)}
+ className={props.className}
+ />
+ );
}
diff --git a/components/onesearch/handleEnter.ts b/components/onesearch/handleEnter.ts
index 92d602e..aaa799e 100644
--- a/components/onesearch/handleEnter.ts
+++ b/components/onesearch/handleEnter.ts
@@ -4,23 +4,23 @@ import { normalizeURL } from "lib/normalizeURL";
import search from "lib/search";
export default function (
- index: number,
- suggestion: suggestionItem[],
- _query: string,
- settings: settingsType,
- searchBoxRef: React.RefObject
+ index: number,
+ suggestion: suggestionItem[],
+ _query: string,
+ settings: settingsType,
+ searchBoxRef: React.RefObject
) {
- const selected = suggestion[index];
- const engine = settings.searchEngines[settings.currentSearchEngine];
- const newTab = settings.searchInNewTab;
- //let clipboard: any;
- if (selected.type === "QUERY" || selected.type === "default") {
- search(selected.suggestion, engine, newTab);
- } else if (selected.type === "NAVIGATION" || selected.type === "default-link") {
- window.open(normalizeURL(selected.suggestion));
- } else if (selected.type === "text") {
- console.log("????");
- copyToClipboard(selected.suggestion);
- searchBoxRef.current?.focus();
- }
+ const selected = suggestion[index];
+ const engine = settings.searchEngines[settings.currentSearchEngine];
+ const newTab = settings.searchInNewTab;
+ //let clipboard: any;
+ if (selected.type === "QUERY" || selected.type === "default") {
+ search(selected.suggestion, engine, newTab);
+ } else if (selected.type === "NAVIGATION" || selected.type === "default-link") {
+ window.open(normalizeURL(selected.suggestion));
+ } else if (selected.type === "text") {
+ console.log("????");
+ copyToClipboard(selected.suggestion);
+ searchBoxRef.current?.focus();
+ }
}
diff --git a/components/onesearch/handleNLUResult.ts b/components/onesearch/handleNLUResult.ts
index 2ed9577..fa210dd 100644
--- a/components/onesearch/handleNLUResult.ts
+++ b/components/onesearch/handleNLUResult.ts
@@ -6,36 +6,34 @@ import { WMOCodeTable } from "lib/weather/wmocode";
type UpdateSuggestionFunction = (data: suggestionItem[]) => void;
-export function handleNLUResult(result: any, updateSuggestion: UpdateSuggestionFunction){
- if (result.intent == "weather.summary") {
- getLocationNative((data: GeolocationCoordinates | GeolocationPositionError) => {
- console.log(data);
- if (data instanceof GeolocationCoordinates) {
- getWeather(data.latitude, data.longitude).then((weather) => {
- console.log(weather["hourly"]);
- let hourIndex = findClosestDateIndex(
- weather["hourly"]["time"]
- );
- let temp = weather["hourly"]["apparent_temperature"][hourIndex];
- let weatherCode = weather["hourly"]["weather_code"][hourIndex];
- console.log(temp, weatherCode, hourIndex);
- updateSuggestion([
- {
- type: "text",
- suggestion: `Weather: ${temp}${weather["hourly_units"]["apparent_temperature"]}, ${WMOCodeTable[weatherCode]["day"].description}`,
- relevance: 3000 * result.score
- }
- ]);
- });
- }
- });
- } else if (result.intent !== "None") {
- updateSuggestion([
- {
- type: "text",
- suggestion: result.intent,
- relevance: 2200 * result.score
- }
- ]);
- }
-}
\ No newline at end of file
+export function handleNLUResult(result: any, updateSuggestion: UpdateSuggestionFunction) {
+ if (result.intent == "weather.summary") {
+ getLocationNative((data: GeolocationCoordinates | GeolocationPositionError) => {
+ console.log(data);
+ if (data instanceof GeolocationCoordinates) {
+ getWeather(data.latitude, data.longitude).then((weather) => {
+ console.log(weather["hourly"]);
+ let hourIndex = findClosestDateIndex(weather["hourly"]["time"]);
+ let temp = weather["hourly"]["apparent_temperature"][hourIndex];
+ let weatherCode = weather["hourly"]["weather_code"][hourIndex];
+ console.log(temp, weatherCode, hourIndex);
+ updateSuggestion([
+ {
+ type: "text",
+ suggestion: `Weather: ${temp}${weather["hourly_units"]["apparent_temperature"]}, ${WMOCodeTable[weatherCode]["day"].description}`,
+ relevance: 3000 * result.score
+ }
+ ]);
+ });
+ }
+ });
+ } else if (result.intent !== "None") {
+ updateSuggestion([
+ {
+ type: "text",
+ suggestion: result.intent,
+ relevance: 2200 * result.score
+ }
+ ]);
+ }
+}
diff --git a/components/onesearch/link.tsx b/components/onesearch/link.tsx
index 6820c9b..33d55fd 100644
--- a/components/onesearch/link.tsx
+++ b/components/onesearch/link.tsx
@@ -2,29 +2,29 @@ import { normalizeURL } from "lib/normalizeURL";
import { useNavigate } from "react-router";
interface LinkSuggestionProps {
- children: React.ReactNode;
- query: string;
- selected: boolean;
- inPage?: boolean;
+ 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 (
- {
- if (props.inPage) {
- navigate(props.query);
- } else {
- window.open(normalizeURL(props.query));
- }
- }}
- >
- {props.children}
-
- );
+ 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 (
+ {
+ if (props.inPage) {
+ navigate(props.query);
+ } else {
+ window.open(normalizeURL(props.query));
+ }
+ }}
+ >
+ {props.children}
+
+ );
}
diff --git a/components/onesearch/onesearch.tsx b/components/onesearch/onesearch.tsx
index f97f6de..1b1dec4 100644
--- a/components/onesearch/onesearch.tsx
+++ b/components/onesearch/onesearch.tsx
@@ -19,208 +19,208 @@ import { keywordSuggestion } from "lib/onesearch/keywordSuggestion";
import { NLUType } from "lib/nlp/load";
export default function OneSearch() {
- const [suggestion, setFinalSuggetsion] = useAtom(suggestionAtom);
- const [manager, setManager] = useState(null);
- const [NLUModel, setNLUModel] = useState();
- const [NLUModelLoaded, setNLUModelLoaded] = useState(false);
- const lastRequestTimeRef = useRef(0);
- const selected = useAtomValue(selectedSuggestionAtom);
- const settings = useAtomValue(settingsAtom);
- const devMode = false;
- const query = useAtomValue(queryAtom);
- const engineName = getSearchEngineName();
- const engine = settings.currentSearchEngine;
- const { t } = useTranslation();
- const lang = i18next.language;
+ const [suggestion, setFinalSuggetsion] = useAtom(suggestionAtom);
+ const [manager, setManager] = useState(null);
+ const [NLUModel, setNLUModel] = useState();
+ const [NLUModelLoaded, setNLUModelLoaded] = useState(false);
+ const lastRequestTimeRef = useRef(0);
+ const selected = useAtomValue(selectedSuggestionAtom);
+ const settings = useAtomValue(settingsAtom);
+ const devMode = false;
+ const query = useAtomValue(queryAtom);
+ const engineName = getSearchEngineName();
+ const engine = settings.currentSearchEngine;
+ const { t } = useTranslation();
+ const lang = i18next.language;
- useEffect(() => {
- const time = new Date().getTime().toString();
- if (query.trim() === "" || query.length > 120) {
- cleanSuggestion("QUERY", "NAVIGATION");
- return;
- }
- fetch(`/api/v1/suggestion?q=${query}&l=${lang}&t=${time}&engine=${engine}`)
- .then((res) => res.json())
- .then((data: suggestionsResponse) => {
- try {
- const suggestionToUpdate: suggestionItem[] = data.suggestions;
- if (data.time > lastRequestTimeRef.current) {
- cleanSuggestion("NAVIGATION", "QUERY");
- lastRequestTimeRef.current = data.time;
- updateSuggestion(suggestionToUpdate);
- }
- } catch (error: Error | unknown) {
- if (error instanceof Error) {
- sendError(error);
- }
- }
- })
- .catch((error) => {
- // Handle fetch error
- sendError(error);
- });
- }, [query]);
+ useEffect(() => {
+ const time = new Date().getTime().toString();
+ if (query.trim() === "" || query.length > 120) {
+ cleanSuggestion("QUERY", "NAVIGATION");
+ return;
+ }
+ fetch(`/api/v1/suggestion?q=${query}&l=${lang}&t=${time}&engine=${engine}`)
+ .then((res) => res.json())
+ .then((data: suggestionsResponse) => {
+ try {
+ const suggestionToUpdate: suggestionItem[] = data.suggestions;
+ if (data.time > lastRequestTimeRef.current) {
+ cleanSuggestion("NAVIGATION", "QUERY");
+ lastRequestTimeRef.current = data.time;
+ updateSuggestion(suggestionToUpdate);
+ }
+ } catch (error: Error | unknown) {
+ if (error instanceof Error) {
+ sendError(error);
+ }
+ }
+ })
+ .catch((error) => {
+ // Handle fetch error
+ sendError(error);
+ });
+ }, [query]);
- function updateSuggestion(data: suggestionItem[]) {
- setFinalSuggetsion((cur: suggestionItem[]) => {
- const types: string[] = [];
- for (const sug of data) {
- if (!types.includes(sug.type)) types.push(sug.type);
- }
- for (const type of types) {
- cur = cur.filter((item) => {
- return item.type !== type;
- });
- }
- return cur.concat(data).sort((a, b) => {
- return b.relevance - a.relevance;
- });
- });
- }
+ function updateSuggestion(data: suggestionItem[]) {
+ setFinalSuggetsion((cur: suggestionItem[]) => {
+ const types: string[] = [];
+ for (const sug of data) {
+ if (!types.includes(sug.type)) types.push(sug.type);
+ }
+ for (const type of types) {
+ cur = cur.filter((item) => {
+ return item.type !== type;
+ });
+ }
+ return cur.concat(data).sort((a, b) => {
+ return b.relevance - a.relevance;
+ });
+ });
+ }
- function cleanSuggestion(...types: string[]) {
- setFinalSuggetsion((suggestion: suggestionItem[]) => {
- return suggestion.filter((item) => {
- return !types.includes(item.type);
- });
- });
- }
+ function cleanSuggestion(...types: string[]) {
+ setFinalSuggetsion((suggestion: suggestionItem[]) => {
+ return suggestion.filter((item) => {
+ return !types.includes(item.type);
+ });
+ });
+ }
- useEffect(() => {
- (async function () {
- const NLU = await import("lib/nlp/load");
- const mainNLUModel = new NLU.NLU();
- setNLUModel(mainNLUModel);
- setNLUModelLoaded(true);
- })();
- }, []);
+ useEffect(() => {
+ (async function () {
+ const NLU = await import("lib/nlp/load");
+ const mainNLUModel = new NLU.NLU();
+ setNLUModel(mainNLUModel);
+ setNLUModelLoaded(true);
+ })();
+ }, []);
- useEffect(() => {
- if (NLUModel === null || NLUModel === undefined) {
- return;
- }
- NLUModel.init().then((nlu: typeof NLUModel) => {
- setManager(nlu.manager);
- });
- }, [NLUModelLoaded]);
+ useEffect(() => {
+ if (NLUModel === null || NLUModel === undefined) {
+ return;
+ }
+ NLUModel.init().then((nlu: typeof NLUModel) => {
+ setManager(nlu.manager);
+ });
+ }, [NLUModelLoaded]);
- useEffect(() => {
- cleanSuggestion("default-link", "default", "text", "link");
- if (validLink(query)) {
- updateSuggestion([
- {
- type: "default-link",
- suggestion: query,
- relevance: 3000,
- prompt: Go to:
- },
- { type: "default", suggestion: query, relevance: 1600 }
- ]);
- } else {
- updateSuggestion([
- {
- type: "default",
- suggestion: query,
- relevance: 2000
- }
- ]);
- }
+ useEffect(() => {
+ cleanSuggestion("default-link", "default", "text", "link");
+ if (validLink(query)) {
+ updateSuggestion([
+ {
+ type: "default-link",
+ suggestion: query,
+ relevance: 3000,
+ prompt: Go to:
+ },
+ { type: "default", suggestion: query, relevance: 1600 }
+ ]);
+ } else {
+ updateSuggestion([
+ {
+ type: "default",
+ suggestion: query,
+ relevance: 2000
+ }
+ ]);
+ }
- if (keywordSuggestion(query) !== null) {
- updateSuggestion([keywordSuggestion(query)!]);
- }
+ if (keywordSuggestion(query) !== null) {
+ updateSuggestion([keywordSuggestion(query)!]);
+ }
- if (manager != null) {
- // @ts-ignore
- manager.process(query).then((result) => {
- console.log(result);
- handleNLUResult(result, updateSuggestion);
- });
- }
- }, [query, engineName]);
+ if (manager != null) {
+ // @ts-ignore
+ manager.process(query).then((result) => {
+ console.log(result);
+ handleNLUResult(result, updateSuggestion);
+ });
+ }
+ }, [query, engineName]);
- return (
-
- {suggestion.map((s, i) => {
- if (s.suggestion.trim() === "") return;
- if (s.type === "default") {
- return (
-
- {s.suggestion}
-
- {t("search.search-help-text", { engine: engineName })}
-
- {devMode && (
-
- {s.relevance}
-
- )}
-
- );
- } else if (s.type === "QUERY") {
- return (
-
- {s.suggestion}
- {devMode && (
-
- {s.relevance}
-
- )}
-
- );
- } else if (
- s.type === "NAVIGATION" ||
- s.type === "default-link" ||
- s.type === "link"
- ) {
- return (
-
- {s.prompt && (
- {s.prompt}
- )}
- {s.suggestion}
- {devMode && (
-
- {s.relevance}
-
- )}
-
- );
- } else if (s.type === "text") {
- return (
-
- {s.prompt && (
- {s.prompt}
- )}
- {s.suggestion}
- {devMode && (
-
- {s.relevance}
-
- )}
-
- );
- } else if (s.type === "inpage-link") {
- return (
-
- {s.prompt && (
- {s.prompt}
- )}
- {s.suggestion}
- {devMode && (
-
- {s.relevance}
-
- )}
-
- );
- }
- })}
-
- );
+ return (
+
+ {suggestion.map((s, i) => {
+ if (s.suggestion.trim() === "") return;
+ if (s.type === "default") {
+ return (
+
+ {s.suggestion}
+
+ {t("search.search-help-text", { engine: engineName })}
+
+ {devMode && (
+
+ {s.relevance}
+
+ )}
+
+ );
+ } else if (s.type === "QUERY") {
+ return (
+
+ {s.suggestion}
+ {devMode && (
+
+ {s.relevance}
+
+ )}
+
+ );
+ } else if (
+ s.type === "NAVIGATION" ||
+ s.type === "default-link" ||
+ s.type === "link"
+ ) {
+ return (
+
+ {s.prompt && (
+ {s.prompt}
+ )}
+ {s.suggestion}
+ {devMode && (
+
+ {s.relevance}
+
+ )}
+
+ );
+ } else if (s.type === "text") {
+ return (
+
+ {s.prompt && (
+ {s.prompt}
+ )}
+ {s.suggestion}
+ {devMode && (
+
+ {s.relevance}
+
+ )}
+
+ );
+ } else if (s.type === "inpage-link") {
+ return (
+
+ {s.prompt && (
+ {s.prompt}
+ )}
+ {s.suggestion}
+ {devMode && (
+
+ {s.relevance}
+
+ )}
+
+ );
+ }
+ })}
+
+ );
}
diff --git a/components/onesearch/plainSearch.tsx b/components/onesearch/plainSearch.tsx
index c0e7c8c..d0d6371 100644
--- a/components/onesearch/plainSearch.tsx
+++ b/components/onesearch/plainSearch.tsx
@@ -2,34 +2,37 @@ import { useAtomValue } from "jotai";
import search from "lib/search";
import { settingsAtom } from "lib/state/settings";
-export default function PlainSearch(props: { children: React.ReactNode; query: string; selected: boolean }) {
- const settings = useAtomValue(settingsAtom);
- const engine = settings.searchEngines[settings.currentSearchEngine];
- const newTab = settings.searchInNewTab;
- if (props.selected) {
- return (
- {
- search(props.query, engine, newTab);
- }}
- >
- {props.children}
-
- );
- }
- else {
- return (
- {
+ search(props.query, engine, newTab);
+ }}
+ >
+ {props.children}
+
+ );
+ } else {
+ return (
+ {
- search(props.query, engine, newTab);
- }}
- >
- {props.children}
-
- );
- }
+ onClick={() => {
+ search(props.query, engine, newTab);
+ }}
+ >
+ {props.children}
+
+ );
+ }
}
diff --git a/components/onesearch/plainText.tsx b/components/onesearch/plainText.tsx
index 71cbe48..93e52a4 100644
--- a/components/onesearch/plainText.tsx
+++ b/components/onesearch/plainText.tsx
@@ -1,21 +1,21 @@
export default function PlainText(props: { children: React.ReactNode; selected: boolean }) {
- if (props.selected) {
- return (
-
- {props.children}
-
- );
- } else {
- return (
-
+ {props.children}
+
+ );
+ } else {
+ return (
+
- {props.children}
-
- );
- }
+ >
+ {props.children}
+
+ );
+ }
}
diff --git a/components/onesearch/suggestion.tsx b/components/onesearch/suggestion.tsx
index c10b878..90a20fa 100644
--- a/components/onesearch/suggestion.tsx
+++ b/components/onesearch/suggestion.tsx
@@ -1,6 +1,8 @@
-export default function Suggestion(props: { children: React.ReactNode }) {
- return (
- ${props.children}` as string }} className={`relative 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`}>
-
- );
-}
\ No newline at end of file
+export default function Suggestion(props: { children: React.ReactNode }) {
+ return (
+ ${props.children}` as string }}
+ className={`relative 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`}
+ >
+ );
+}
diff --git a/components/onesearch/suggestionBox.tsx b/components/onesearch/suggestionBox.tsx
index e7cefb3..a6c5d5b 100644
--- a/components/onesearch/suggestionBox.tsx
+++ b/components/onesearch/suggestionBox.tsx
@@ -1,11 +1,11 @@
export default function SuggestionBox(props: { children?: React.ReactNode }) {
- return (
-
- {props.children}
-
- );
+ >
+ {props.children}
+
+ );
}
diff --git a/components/picker.tsx b/components/picker.tsx
index 5e224cc..90a67c4 100644
--- a/components/picker.tsx
+++ b/components/picker.tsx
@@ -7,172 +7,177 @@ import React from "react";
export type selectionType = string;
interface PickerProps extends HTMLAttributes {
- selected: selectionType;
- selectionOnChange: selectedOnChange;
- displayContent: string;
- selectionItems: PickedItem;
+ selected: selectionType;
+ selectionOnChange: selectedOnChange;
+ displayContent: string;
+ selectionItems: PickedItem;
}
export interface PickedItem {
- [key: string]: selectionType;
+ [key: string]: selectionType;
}
export default function Picker(props: PickerProps) {
- const itemListRef: RefObject = useRef(null);
- const buttonRef: RefObject = useRef(null);
- const [displayList, setDisplayList] = useState(false);
+ const itemListRef: RefObject = useRef(null);
+ const buttonRef: RefObject = useRef(null);
+ const [displayList, setDisplayList] = useState(false);
- const updatePosition = () => {
- if (itemListRef.current == null || buttonRef.current == null) {
- return;
- }
- const buttonRect = buttonRef.current.getBoundingClientRect();
- const listRect = itemListRef.current.getBoundingClientRect();
- // Align to center
- itemListRef.current.style.left =
- Math.max(
- Math.min(
- buttonRect.x + buttonRect.width / 2 - listRect.width / 2,
- window.screen.width - listRect.width - 16
- ),
- 0
- ) + "px";
- if (window.screen.height - buttonRect.top < 192) {
- itemListRef.current.style.transformOrigin = "bottom center";
- itemListRef.current.style.top = buttonRect.top - listRect.height - 16 + "px";
- } else {
- itemListRef.current.style.top = buttonRect.y + buttonRect.height + 16 + "px";
- }
- if (listRect.top + listRect.height > window.screen.height - 16) {
- itemListRef.current.style.height = window.screen.height - listRect.top - 12 + "px";
- } else {
- itemListRef.current.style.height = "fit-content";
- }
- };
+ const updatePosition = () => {
+ if (itemListRef.current == null || buttonRef.current == null) {
+ return;
+ }
+ const buttonRect = buttonRef.current.getBoundingClientRect();
+ const listRect = itemListRef.current.getBoundingClientRect();
+ // Align to center
+ itemListRef.current.style.left =
+ Math.max(
+ Math.min(
+ buttonRect.x + buttonRect.width / 2 - listRect.width / 2,
+ window.screen.width - listRect.width - 16
+ ),
+ 0
+ ) + "px";
+ if (window.screen.height - buttonRect.top < 192) {
+ itemListRef.current.style.transformOrigin = "bottom center";
+ itemListRef.current.style.top = buttonRect.top - listRect.height - 16 + "px";
+ } else {
+ itemListRef.current.style.top = buttonRect.y + buttonRect.height + 16 + "px";
+ }
+ if (listRect.top + listRect.height > window.screen.height - 16) {
+ itemListRef.current.style.height = window.screen.height - listRect.top - 12 + "px";
+ } else {
+ itemListRef.current.style.height = "fit-content";
+ }
+ };
- useEffect(() => {
- updatePosition();
- const handleResize = () => {
- updatePosition();
- };
+ useEffect(() => {
+ updatePosition();
+ const handleResize = () => {
+ updatePosition();
+ };
- window.addEventListener("resize", handleResize);
+ window.addEventListener("resize", handleResize);
- // Cleanup event listener on component unmount
- return () => {
- window.removeEventListener("resize", handleResize);
- };
- }, [itemListRef, buttonRef]);
+ // Cleanup event listener on component unmount
+ return () => {
+ window.removeEventListener("resize", handleResize);
+ };
+ }, [itemListRef, buttonRef]);
- function toggleDisplay(targetState?: boolean) {
- function hideList() {
- if (itemListRef.current) {
- itemListRef.current.style.transitionDuration = "200ms";
- itemListRef.current.style.opacity = "0%";
- }
- setTimeout(() => {
- setDisplayList(false);
- }, 200);
- }
- function showList() {
- setDisplayList(true);
- setTimeout(() => {
- if (!itemListRef.current || !buttonRef.current) {
- return;
- }
- updatePosition();
- if (window.screen.height - buttonRef.current.getBoundingClientRect().top < 128) {
- itemListRef.current.style.transformOrigin = "bottom center";
- }
- itemListRef.current.style.transitionDuration = "100ms";
- itemListRef.current.style.opacity = "100%";
- updatePosition();
- const listRect = itemListRef.current.getBoundingClientRect();
- if (listRect.top < 8) {
- itemListRef.current.style.height = window.screen.height - 8 + "px";
- itemListRef.current.style.top = "8px";
- }
- }, 20);
- }
- if (targetState === true) {
- showList();
- } else if (targetState === false) {
- hideList();
- } else if (displayList === true) {
- hideList();
- } else {
- showList();
- }
- }
+ function toggleDisplay(targetState?: boolean) {
+ function hideList() {
+ if (itemListRef.current) {
+ itemListRef.current.style.transitionDuration = "200ms";
+ itemListRef.current.style.opacity = "0%";
+ }
+ setTimeout(() => {
+ setDisplayList(false);
+ }, 200);
+ }
+ function showList() {
+ setDisplayList(true);
+ setTimeout(() => {
+ if (!itemListRef.current || !buttonRef.current) {
+ return;
+ }
+ updatePosition();
+ if (window.screen.height - buttonRef.current.getBoundingClientRect().top < 128) {
+ itemListRef.current.style.transformOrigin = "bottom center";
+ }
+ itemListRef.current.style.transitionDuration = "100ms";
+ itemListRef.current.style.opacity = "100%";
+ updatePosition();
+ const listRect = itemListRef.current.getBoundingClientRect();
+ if (listRect.top < 8) {
+ itemListRef.current.style.height = window.screen.height - 8 + "px";
+ itemListRef.current.style.top = "8px";
+ }
+ }, 20);
+ }
+ if (targetState === true) {
+ showList();
+ } else if (targetState === false) {
+ hideList();
+ } else if (displayList === true) {
+ hideList();
+ } else {
+ showList();
+ }
+ }
- const { displayContent, selectionOnChange, selectionItems, selected, ...rest } = props;
- return (
-
-
- {displayList && (
-
- )}
-
- );
+ ref={buttonRef}
+ onClick={() => {
+ toggleDisplay();
+ }}
+ >
+ {displayContent}
+
+ {displayList && (
+
+ )}
+
+ );
}
interface PickerListProps {
- selected: selectionType;
- selectionOnChange: selectedOnChange;
- selectionItems: PickedItem;
- toggleDisplay: Function;
+ selected: selectionType;
+ selectionOnChange: selectedOnChange;
+ selectionItems: PickedItem;
+ toggleDisplay: Function;
}
const PickerList = React.forwardRef((props, ref) => {
- const { selected, selectionOnChange, selectionItems, toggleDisplay } = props;
+ const { selected, selectionOnChange, selectionItems, toggleDisplay } = props;
- return createPortal(
- {toggleDisplay(false)}}>
-
{
+ toggleDisplay(false);
+ }}
+ >
+
- {Object.keys(selectionItems).map((key: string, index) => {
- return (
-
+ {Object.keys(selectionItems).map((key: string, index) => {
+ return (
+
{
- selectionOnChange(key);
- toggleDisplay(false);
- }}
- >
-
{selectionItems[key]}
-
- {key === selected && (
-
- )}
-
- );
- })}
-
-
,
- document.body
- );
+ onClick={() => {
+ selectionOnChange(key);
+ toggleDisplay(false);
+ }}
+ >
+
{selectionItems[key]}
+
+ {key === selected && (
+
+ )}
+
+ );
+ })}
+
+ ,
+ document.body
+ );
});
PickerList.displayName = "PickerList";
diff --git a/components/search.tsx b/components/search.tsx
index 0097ec7..0bae01b 100644
--- a/components/search.tsx
+++ b/components/search.tsx
@@ -8,82 +8,82 @@ import { suggestionAtom } from "lib/state/suggestion";
import { useTranslation } from "react-i18next";
export default function Search(props: { onFocus: () => void }) {
- const { t } = useTranslation();
- const settings = useAtomValue(settingsAtom);
- const [query, setQuery] = useAtom(queryAtom);
- const [selectedSuggestion, setSelected] = useAtom(selectedSuggestionAtom);
- const suggestions = useAtomValue(suggestionAtom);
- const searchBoxRef = useRef(null);
+ const { t } = useTranslation();
+ const settings = useAtomValue(settingsAtom);
+ const [query, setQuery] = useAtom(queryAtom);
+ const [selectedSuggestion, setSelected] = useAtom(selectedSuggestionAtom);
+ const suggestions = useAtomValue(suggestionAtom);
+ const searchBoxRef = useRef(null);
- const style = "default";
+ const style = "default";
- function handleKeydown(e: KeyboardEvent) {
- if (e.key == "Enter") {
- e.preventDefault();
- handleEnter(selectedSuggestion, suggestions, query, settings, searchBoxRef);
- return;
- } else if (e.key == "ArrowUp") {
- e.preventDefault();
- const len = suggestions.length;
- setSelected((selectedSuggestion - 1 + len) % len);
- } else if (e.key == "ArrowDown") {
- e.preventDefault();
- const len = suggestions.length;
- setSelected((selectedSuggestion + 1) % len);
- }
- }
+ function handleKeydown(e: KeyboardEvent) {
+ if (e.key == "Enter") {
+ e.preventDefault();
+ handleEnter(selectedSuggestion, suggestions, query, settings, searchBoxRef);
+ return;
+ } else if (e.key == "ArrowUp") {
+ e.preventDefault();
+ const len = suggestions.length;
+ setSelected((selectedSuggestion - 1 + len) % len);
+ } else if (e.key == "ArrowDown") {
+ e.preventDefault();
+ const len = suggestions.length;
+ setSelected((selectedSuggestion + 1) % len);
+ }
+ }
- if (style === "default") {
- return (
- // 祖传样式,勿动
-
-
+
- setQuery(() => {
- return e.target.value;
- })
- }
- autoComplete="off"
- autoCorrect="off"
- autoCapitalize="off"
- spellCheck="false"
- ref={searchBoxRef}
- value={query}
- />
-
- );
- } else if (style == "image") {
- return (
- // 祖传样式,勿动
-
-
+ setQuery(() => {
+ return e.target.value;
+ })
+ }
+ autoComplete="off"
+ autoCorrect="off"
+ autoCapitalize="off"
+ spellCheck="false"
+ ref={searchBoxRef}
+ value={query}
+ />
+
+ );
+ } else if (style == "image") {
+ return (
+ // 祖传样式,勿动
+
+
-
- );
- }
+ }
+ id="searchBox"
+ type="text"
+ placeholder="placeholder"
+ onFocus={props.onFocus}
+ ref={searchBoxRef}
+ />
+
+ );
+ }
}
diff --git a/components/selectorItem.tsx b/components/selectorItem.tsx
index 1365af0..587e9e9 100644
--- a/components/selectorItem.tsx
+++ b/components/selectorItem.tsx
@@ -3,10 +3,10 @@ import { selectionType } from "./picker";
export type selectedOnChange = (target: selectionType) => void;
-export default function SelectionItem(props: {key: selectionType, children: ReactNode, onChange: selectedOnChange}){
- return (
- props.onChange(props.key)}>
- {props.children}
-
- )
-}
\ No newline at end of file
+export default function SelectionItem(props: {
+ key: selectionType;
+ children: ReactNode;
+ onChange: selectedOnChange;
+}) {
+ return props.onChange(props.key)}>{props.children}
;
+}
diff --git a/components/time.tsx b/components/time.tsx
index 54ae03a..54ebeff 100644
--- a/components/time.tsx
+++ b/components/time.tsx
@@ -2,47 +2,45 @@
import { useState, useEffect } from "react";
-export default function Time(props: {
- showSecond: boolean
-}) {
- const [currentTime, setCurrentTime] = useState(new Date());
+export default function Time(props: { showSecond: boolean }) {
+ const [currentTime, setCurrentTime] = useState(new Date());
- useEffect(() => {
- const timer = setInterval(() => {
- setCurrentTime(new Date());
- }, 150);
+ useEffect(() => {
+ const timer = setInterval(() => {
+ setCurrentTime(new Date());
+ }, 150);
- return () => {
- clearInterval(timer);
- };
- }, []);
+ return () => {
+ clearInterval(timer);
+ };
+ }, []);
- const formatTime = () => {
- const hours = currentTime.getHours().toString().padStart(2, "0");
- const minutes = currentTime.getMinutes().toString().padStart(2, "0");
- const seconds = currentTime.getSeconds().toString().padStart(2, "0");
+ const formatTime = () => {
+ const hours = currentTime.getHours().toString().padStart(2, "0");
+ const minutes = currentTime.getMinutes().toString().padStart(2, "0");
+ const seconds = currentTime.getSeconds().toString().padStart(2, "0");
- if (props.showSecond) {
- return `${hours}:${minutes}:${seconds}`;
- } else {
- return `${hours}:${minutes}`;
- }
- };
+ if (props.showSecond) {
+ return `${hours}:${minutes}:${seconds}`;
+ } else {
+ return `${hours}:${minutes}`;
+ }
+ };
- return (
-
- {formatTime()}{" "}
-
- {new Intl.DateTimeFormat(navigator.language, {
- year: "numeric",
- month: "short",
- day: "numeric"
- }).format(currentTime)}
-
-
- );
+ >
+ {formatTime()}{" "}
+
+ {new Intl.DateTimeFormat(navigator.language, {
+ year: "numeric",
+ month: "short",
+ day: "numeric"
+ }).format(currentTime)}
+
+
+ );
}
diff --git a/global.d.ts b/global.d.ts
index 1f40826..115aa9a 100644
--- a/global.d.ts
+++ b/global.d.ts
@@ -1,31 +1,31 @@
import { Suggestion } from "search-engine-autocomplete";
-interface settingsType extends object{
- "version": number,
- "elementBackdrop": boolean,
- "bgBlur": boolean,
- "timeShowSecond": boolean,
- "currentSearchEngine": string,
- "searchInNewTab": boolean,
- "searchEngines": {
- [key: string]: string
- },
+interface settingsType extends object {
+ version: number;
+ elementBackdrop: boolean;
+ bgBlur: boolean;
+ timeShowSecond: boolean;
+ currentSearchEngine: string;
+ searchInNewTab: boolean;
+ searchEngines: {
+ [key: string]: string;
+ };
}
-interface suggestionsResponse extends object{
- suggestions: Suggestion[],
- query: string,
- verbatimRelevance: number,
- time: number
+interface suggestionsResponse extends object {
+ suggestions: Suggestion[];
+ query: string;
+ verbatimRelevance: number;
+ time: number;
}
type suggestionItem = {
- suggestion: string,
- type: string,
- relativeRelevance?: number,
- relevance: number,
- prompt?: string | React.ReactElement,
- intention?: string | null,
- probability?: number,
- confidence?: number,
-}
\ No newline at end of file
+ suggestion: string;
+ type: string;
+ relativeRelevance?: number;
+ relevance: number;
+ prompt?: string | React.ReactElement;
+ intention?: string | null;
+ probability?: number;
+ confidence?: number;
+};
diff --git a/i18n/ar.json b/i18n/ar.json
index 0d329a3..14ce9d8 100755
--- a/i18n/ar.json
+++ b/i18n/ar.json
@@ -1,5 +1,5 @@
{
- "search" : {
- "placeholder" : "ابحث أو اكتب عنوان URL"
- }
-}
\ No newline at end of file
+ "search": {
+ "placeholder": "ابحث أو اكتب عنوان URL"
+ }
+}
diff --git a/i18n/de.json b/i18n/de.json
index 47a7c7e..f541287 100755
--- a/i18n/de.json
+++ b/i18n/de.json
@@ -1,5 +1,5 @@
{
- "search" : {
- "placeholder" : "Suche oder gib eine URL ein"
- }
-}
\ No newline at end of file
+ "search": {
+ "placeholder": "Suche oder gib eine URL ein"
+ }
+}
diff --git a/i18n/en.json b/i18n/en.json
index a9fdd73..ecb0c76 100755
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -1,33 +1,33 @@
{
- "search": {
- "placeholder": "Search or type a URL",
- "engine-aria": "Switch search engine",
- "engine": {
- "google": "Google",
- "baidu": "Baidu",
- "bing": "Bing",
- "duckduckgo": "DuckDuckGo",
- "yandex": "Yandex",
- "yahoo": "Yahoo",
- "ecosia": "Ecosia"
- },
- "search-help-text": "Search {engine}"
- },
- "notfound": {
- "title": "page not found",
- "desc": "Please check if there is a typo in the URL.
If SparkHome brought you to this page,
please contact us."
- },
- "about": {
- "title": "SparkHome"
- },
- "tools": {
- "base64": {
- "title": "Base64 tools - LuminaraUtils",
- "decode": "Decode",
- "encode": "Encode",
- "result": "Result: ",
- "copy": "Copy",
- "copied": "Copied"
- }
- }
+ "search": {
+ "placeholder": "Search or type a URL",
+ "engine-aria": "Switch search engine",
+ "engine": {
+ "google": "Google",
+ "baidu": "Baidu",
+ "bing": "Bing",
+ "duckduckgo": "DuckDuckGo",
+ "yandex": "Yandex",
+ "yahoo": "Yahoo",
+ "ecosia": "Ecosia"
+ },
+ "search-help-text": "Search {engine}"
+ },
+ "notfound": {
+ "title": "page not found",
+ "desc": "Please check if there is a typo in the URL.
If SparkHome brought you to this page,
please contact us."
+ },
+ "about": {
+ "title": "SparkHome"
+ },
+ "tools": {
+ "base64": {
+ "title": "Base64 tools - LuminaraUtils",
+ "decode": "Decode",
+ "encode": "Encode",
+ "result": "Result: ",
+ "copy": "Copy",
+ "copied": "Copied"
+ }
+ }
}
diff --git a/i18n/es.json b/i18n/es.json
index c79c404..2316e61 100755
--- a/i18n/es.json
+++ b/i18n/es.json
@@ -1,5 +1,5 @@
{
- "search" : {
- "placeholder" : "Buscar o escribir una URL"
- }
-}
\ No newline at end of file
+ "search": {
+ "placeholder": "Buscar o escribir una URL"
+ }
+}
diff --git a/i18n/fr.json b/i18n/fr.json
index d9f6e0b..f8244e6 100755
--- a/i18n/fr.json
+++ b/i18n/fr.json
@@ -1,5 +1,5 @@
{
- "search" : {
- "placeholder" : "Rechercher ou saisir une URL"
- }
-}
\ No newline at end of file
+ "search": {
+ "placeholder": "Rechercher ou saisir une URL"
+ }
+}
diff --git a/i18n/it.json b/i18n/it.json
index 23f0358..9416fa3 100755
--- a/i18n/it.json
+++ b/i18n/it.json
@@ -1,5 +1,5 @@
{
- "search" : {
- "placeholder" : "Cerca o digita un URL"
- }
-}
\ No newline at end of file
+ "search": {
+ "placeholder": "Cerca o digita un URL"
+ }
+}
diff --git a/i18n/ja.json b/i18n/ja.json
index bb8ae84..d251a59 100644
--- a/i18n/ja.json
+++ b/i18n/ja.json
@@ -1,12 +1,12 @@
{
- "search": {
- "placeholder": "検索またはURLを入力"
- },
- "Search": {
- "engine-aria": "検索エンジンを切り替える",
- "engine": {
- "google": "Google"
- },
- "placeholder": "検索またはURLを入力"
- }
+ "search": {
+ "placeholder": "検索またはURLを入力"
+ },
+ "Search": {
+ "engine-aria": "検索エンジンを切り替える",
+ "engine": {
+ "google": "Google"
+ },
+ "placeholder": "検索またはURLを入力"
+ }
}
diff --git a/i18n/ko.json b/i18n/ko.json
index f93197a..df3351a 100755
--- a/i18n/ko.json
+++ b/i18n/ko.json
@@ -1,5 +1,5 @@
{
- "search" : {
- "placeholder" : "검색 또는 URL 입력"
- }
-}
\ No newline at end of file
+ "search": {
+ "placeholder": "검색 또는 URL 입력"
+ }
+}
diff --git a/i18n/pt.json b/i18n/pt.json
index 2fad8f3..add94e4 100755
--- a/i18n/pt.json
+++ b/i18n/pt.json
@@ -1,5 +1,5 @@
{
- "search" : {
- "placeholder" : "Pesquisar ou digitar uma URL"
- }
-}
\ No newline at end of file
+ "search": {
+ "placeholder": "Pesquisar ou digitar uma URL"
+ }
+}
diff --git a/i18n/ru.json b/i18n/ru.json
index b23cc86..893aba7 100755
--- a/i18n/ru.json
+++ b/i18n/ru.json
@@ -1,5 +1,5 @@
{
- "search" : {
- "placeholder" : "Искать или ввести URL"
- }
-}
\ No newline at end of file
+ "search": {
+ "placeholder": "Искать или ввести URL"
+ }
+}
diff --git a/i18n/zh.json b/i18n/zh.json
index d021e98..54e00c8 100644
--- a/i18n/zh.json
+++ b/i18n/zh.json
@@ -1,53 +1,53 @@
{
- "search": {
- "placeholder": "搜索或输入网址",
- "engine-aria": "切换搜索引擎",
- "engine": {
- "google": "谷歌",
- "baidu": "百度",
- "bing": "必应",
- "duckduckgo": "DuckDuckGo",
- "yandex": "Yandex",
- "yahoo": "雅虎",
- "ecosia": "Ecosia"
- },
- "search-help-text": "用 {engine} 搜索"
- },
- "Search": {
- "placeholder": "搜索或输入网址",
- "engine-aria": "切换搜索引擎",
- "engine": {
- "google": "谷歌",
- "baidu": "百度",
- "bing": "必应",
- "duckduckgo": "DuckDuckGo",
- "yandex": "Yandex",
- "yahoo": "雅虎",
- "ecosia": "Ecosia"
- },
- "search-help-text": "用 {engine} 搜索"
- },
- "404": {
- "title": "页面未找到"
- },
- "About": {
- "title": "星火主页"
- },
- "tools": {
- "base64": {
- "title": "Base64 工具",
- "decode": "解码",
- "encode": "编码",
- "result": "结果: ",
- "copy": "复制",
- "copied": "已复制"
- }
- },
- "notfound": {
- "desc": "请检查网址是否出错。
如果你从星火主页跳转到这里,
请 联系我们",
- "title": "网页不存在"
- },
- "about": {
- "title": "星火主页"
- }
+ "search": {
+ "placeholder": "搜索或输入网址",
+ "engine-aria": "切换搜索引擎",
+ "engine": {
+ "google": "谷歌",
+ "baidu": "百度",
+ "bing": "必应",
+ "duckduckgo": "DuckDuckGo",
+ "yandex": "Yandex",
+ "yahoo": "雅虎",
+ "ecosia": "Ecosia"
+ },
+ "search-help-text": "用 {engine} 搜索"
+ },
+ "Search": {
+ "placeholder": "搜索或输入网址",
+ "engine-aria": "切换搜索引擎",
+ "engine": {
+ "google": "谷歌",
+ "baidu": "百度",
+ "bing": "必应",
+ "duckduckgo": "DuckDuckGo",
+ "yandex": "Yandex",
+ "yahoo": "雅虎",
+ "ecosia": "Ecosia"
+ },
+ "search-help-text": "用 {engine} 搜索"
+ },
+ "404": {
+ "title": "页面未找到"
+ },
+ "About": {
+ "title": "星火主页"
+ },
+ "tools": {
+ "base64": {
+ "title": "Base64 工具",
+ "decode": "解码",
+ "encode": "编码",
+ "result": "结果: ",
+ "copy": "复制",
+ "copied": "已复制"
+ }
+ },
+ "notfound": {
+ "desc": "请检查网址是否出错。
如果你从星火主页跳转到这里,
请 联系我们",
+ "title": "网页不存在"
+ },
+ "about": {
+ "title": "星火主页"
+ }
}
diff --git a/index.html b/index.html
index a061336..af9602f 100644
--- a/index.html
+++ b/index.html
@@ -1,13 +1,13 @@
-
+
-
-
-
-
- SparkHome
-
-
-
-
-
+
+
+
+
+ SparkHome
+
+
+
+
+
diff --git a/lib/copy.ts b/lib/copy.ts
index dc71d27..feddfa5 100644
--- a/lib/copy.ts
+++ b/lib/copy.ts
@@ -1,8 +1,8 @@
-export default function copyToClipboard(value: string){
- const textarea = document.createElement("textarea");
- textarea.value = value;
- document.body.appendChild(textarea);
- textarea.select();
- document.execCommand("copy");
- document.body.removeChild(textarea);
-}
\ No newline at end of file
+export default function copyToClipboard(value: string) {
+ const textarea = document.createElement("textarea");
+ textarea.value = value;
+ document.body.appendChild(textarea);
+ textarea.select();
+ document.execCommand("copy");
+ document.body.removeChild(textarea);
+}
diff --git a/lib/darkModeHook.ts b/lib/darkModeHook.ts
index e2f6433..2f4620f 100644
--- a/lib/darkModeHook.ts
+++ b/lib/darkModeHook.ts
@@ -2,22 +2,22 @@ import { useState, useEffect } from "react";
// Custom React Hook for dark mode detect
export default function useDarkMode() {
- const [darkMode, setDarkMode] = useState(false);
+ const [darkMode, setDarkMode] = useState(false);
- useEffect(() => {
- const colorSchemeQueryList = window.matchMedia("(prefers-color-scheme: dark)");
- setDarkMode(colorSchemeQueryList.matches);
+ useEffect(() => {
+ const colorSchemeQueryList = window.matchMedia("(prefers-color-scheme: dark)");
+ setDarkMode(colorSchemeQueryList.matches);
- const handleChange = () => {
- setDarkMode(colorSchemeQueryList.matches);
- };
+ const handleChange = () => {
+ setDarkMode(colorSchemeQueryList.matches);
+ };
- colorSchemeQueryList.addEventListener("change", handleChange);
+ colorSchemeQueryList.addEventListener("change", handleChange);
- return () => {
- colorSchemeQueryList.removeEventListener("change", handleChange);
- };
- }, []);
+ return () => {
+ colorSchemeQueryList.removeEventListener("change", handleChange);
+ };
+ }, []);
- return darkMode;
+ return darkMode;
}
diff --git a/lib/nlp/data/en.json b/lib/nlp/data/en.json
index bb453ae..fe21e9c 100644
--- a/lib/nlp/data/en.json
+++ b/lib/nlp/data/en.json
@@ -1,147 +1,141 @@
{
- "weather.summary": [
- "how's the weather",
- "What's going on with the weather?",
- "Can you give me an update on the weather?",
- "How's the forecast looking today?",
- "Give me a summary of the current weather.",
- "Can you tell me the current weather?",
- "What is the weather situation at the moment?",
- "Could you provide a quick weather update?",
- "Is it raining or sunny outside?",
- "What's the weather like right now?",
- "Tell me the current weather conditions.",
- "How about the weather today?",
- "Is it a good day to be outside?",
- "What should I expect in terms of weather today?",
- "Is there any severe weather to be aware of?",
- "Can you summarize today's weather forecast?",
- "What's the weather looking like for the next few hours?",
- "Is it going to stay this way all day?",
- "Could you give me a brief overview of the weather?",
- "What's the general weather situation in our area?",
- "Is it cloudy or clear outside?",
- "Any weather alerts I should know about?",
- "How's the weather looking for outdoor activities?",
- "What's the forecast saying for today's weather?",
- "Is it going to be a warm day?",
- "Are we expecting any storms today?",
- "What's the weather condition outside my window?",
- "Is it a typical day for this season in terms of weather?",
- "how's the weather now?"
- ],
+ "weather.summary": [
+ "how's the weather",
+ "What's going on with the weather?",
+ "Can you give me an update on the weather?",
+ "How's the forecast looking today?",
+ "Give me a summary of the current weather.",
+ "Can you tell me the current weather?",
+ "What is the weather situation at the moment?",
+ "Could you provide a quick weather update?",
+ "Is it raining or sunny outside?",
+ "What's the weather like right now?",
+ "Tell me the current weather conditions.",
+ "How about the weather today?",
+ "Is it a good day to be outside?",
+ "What should I expect in terms of weather today?",
+ "Is there any severe weather to be aware of?",
+ "Can you summarize today's weather forecast?",
+ "What's the weather looking like for the next few hours?",
+ "Is it going to stay this way all day?",
+ "Could you give me a brief overview of the weather?",
+ "What's the general weather situation in our area?",
+ "Is it cloudy or clear outside?",
+ "Any weather alerts I should know about?",
+ "How's the weather looking for outdoor activities?",
+ "What's the forecast saying for today's weather?",
+ "Is it going to be a warm day?",
+ "Are we expecting any storms today?",
+ "What's the weather condition outside my window?",
+ "Is it a typical day for this season in terms of weather?",
+ "how's the weather now?"
+ ],
- "weather.temp": [
- "What's the temperature like right now?",
- "Can you tell me the current temperature?",
- "How hot is it outside?",
- "What's the temperature supposed to be today?",
- "What is the current temp outside?",
- "Could you tell me the outdoor temperature?",
- "Is it cold or warm outside?",
- "What's the high temperature for today?",
- "What's the low temperature expected tonight?",
- "How does the temperature feel outside?",
- "Is it going to get warmer or cooler today?",
- "What's the temperature in the shade?",
- "Can you provide the current temp in Celsius?",
- "What's the temperature in Fahrenheit right now?",
- "Is it too hot to be outside?",
- "What's the temperature like in the morning?",
- "How about the temperature in the evening?",
- "Is it warm enough to go swimming?",
- "What's the temperature in the city center?",
- "Can you tell me the temp in the nearby area?",
- "Is it below freezing outside?",
- "What's the average temperature for today?",
- "Is the temperature dropping or rising?",
- "What should I wear considering the temperature?"
- ],
+ "weather.temp": [
+ "What's the temperature like right now?",
+ "Can you tell me the current temperature?",
+ "How hot is it outside?",
+ "What's the temperature supposed to be today?",
+ "What is the current temp outside?",
+ "Could you tell me the outdoor temperature?",
+ "Is it cold or warm outside?",
+ "What's the high temperature for today?",
+ "What's the low temperature expected tonight?",
+ "How does the temperature feel outside?",
+ "Is it going to get warmer or cooler today?",
+ "What's the temperature in the shade?",
+ "Can you provide the current temp in Celsius?",
+ "What's the temperature in Fahrenheit right now?",
+ "Is it too hot to be outside?",
+ "What's the temperature like in the morning?",
+ "How about the temperature in the evening?",
+ "Is it warm enough to go swimming?",
+ "What's the temperature in the city center?",
+ "Can you tell me the temp in the nearby area?",
+ "Is it below freezing outside?",
+ "What's the average temperature for today?",
+ "Is the temperature dropping or rising?",
+ "What should I wear considering the temperature?"
+ ],
- "base64.encode": [
- "Please encode this data with base64: %s",
- "I need to encode the following data in base64: %s",
- "Could you encode this string using base64? %s",
- "Convert this data to b64 encoding: %s",
- "I want to encode this information with base64: %s",
- "Help me encode this in base64: %s",
- "Can you encode this data to base64 format? %s",
- "b64 encode",
- "base64 encode",
- "encode base64 %s"
- ],
+ "base64.encode": [
+ "Please encode this data with base64: %s",
+ "I need to encode the following data in base64: %s",
+ "Could you encode this string using base64? %s",
+ "Convert this data to b64 encoding: %s",
+ "I want to encode this information with base64: %s",
+ "Help me encode this in base64: %s",
+ "Can you encode this data to base64 format? %s",
+ "b64 encode",
+ "base64 encode",
+ "encode base64 %s"
+ ],
- "base64.decode": [
- "Please decode this base64 data: %s",
- "I have a base64 encoded string that needs decoding: %s",
- "Could you decode this base64 string for me? %s",
- "Convert this base64 encoded data back to its original form: %s",
- "I need to decode this base64 information: %s",
- "Help me decode this base64 data: %s",
- "Can you translate this base64 back to normal text? %s",
- "b64 decode",
- "base64 decode",
- "decode base64 %s"
- ],
+ "base64.decode": [
+ "Please decode this base64 data: %s",
+ "I have a base64 encoded string that needs decoding: %s",
+ "Could you decode this base64 string for me? %s",
+ "Convert this base64 encoded data back to its original form: %s",
+ "I need to decode this base64 information: %s",
+ "Help me decode this base64 data: %s",
+ "Can you translate this base64 back to normal text? %s",
+ "b64 decode",
+ "base64 decode",
+ "decode base64 %s"
+ ],
- "url.encode": [
- "Please encode this URL: %s",
- "I need to encode this URL component: %s",
- "Could you encode this part of the URL? %s",
- "Convert this URL to its encoded form: %s",
- "I want to encode this URL for safe transmission: %s",
- "Help me encode this URL segment: %s",
- "Can you encode this URL data? %s"
- ],
+ "url.encode": [
+ "Please encode this URL: %s",
+ "I need to encode this URL component: %s",
+ "Could you encode this part of the URL? %s",
+ "Convert this URL to its encoded form: %s",
+ "I want to encode this URL for safe transmission: %s",
+ "Help me encode this URL segment: %s",
+ "Can you encode this URL data? %s"
+ ],
- "url.decode": [
- "Please decode this URL: %s",
- "I have an encoded URL that needs decoding: %s",
- "Could you decode this URL for me? %s",
- "Convert this encoded URL back to its original form: %s",
- "I need to decode this URL component: %s",
- "Help me decode this URL segment: %s",
- "Can you translate this encoded URL back to normal? %s"
- ],
+ "url.decode": [
+ "Please decode this URL: %s",
+ "I have an encoded URL that needs decoding: %s",
+ "Could you decode this URL for me? %s",
+ "Convert this encoded URL back to its original form: %s",
+ "I need to decode this URL component: %s",
+ "Help me decode this URL segment: %s",
+ "Can you translate this encoded URL back to normal? %s"
+ ],
- "html.encode": [
- "Please encode this HTML entity: %s",
- "I need to encode this text to HTML entity: %s",
- "Could you encode this as an HTML entity? %s",
- "Convert this text to HTML entity encoding: %s",
- "I want to encode this to prevent HTML interpretation: %s",
- "Help me encode this into HTML entity: %s",
- "Can you encode this for HTML usage? %s"
- ],
+ "html.encode": [
+ "Please encode this HTML entity: %s",
+ "I need to encode this text to HTML entity: %s",
+ "Could you encode this as an HTML entity? %s",
+ "Convert this text to HTML entity encoding: %s",
+ "I want to encode this to prevent HTML interpretation: %s",
+ "Help me encode this into HTML entity: %s",
+ "Can you encode this for HTML usage? %s"
+ ],
- "html.decode": [
- "Please decode this HTML entity: %s",
- "I have an HTML entity that needs decoding: %s",
- "Could you decode this HTML entity for me? %s",
- "Convert this HTML entity back to its original text: %s",
- "I need to decode this HTML entity to plain text: %s",
- "Help me decode this HTML entity: %s",
- "Can you translate this HTML entity back to normal text? %s"
- ],
+ "html.decode": [
+ "Please decode this HTML entity: %s",
+ "I have an HTML entity that needs decoding: %s",
+ "Could you decode this HTML entity for me? %s",
+ "Convert this HTML entity back to its original text: %s",
+ "I need to decode this HTML entity to plain text: %s",
+ "Help me decode this HTML entity: %s",
+ "Can you translate this HTML entity back to normal text? %s"
+ ],
- "ai.command": [
- "write a typescript helloworld code",
- "Check the following content for grammar and clarity",
- "Help me study vocabulary: write a sentence for me to fill in the blank, and I'll try to pick the correct option.",
- "Improve this markdown content in asepcts like grammar and expression, for a GitHub repo README.",
- "can u think of a short name of my package",
- "simplify this code"
- ],
+ "ai.command": [
+ "write a typescript helloworld code",
+ "Check the following content for grammar and clarity",
+ "Help me study vocabulary: write a sentence for me to fill in the blank, and I'll try to pick the correct option.",
+ "Improve this markdown content in asepcts like grammar and expression, for a GitHub repo README.",
+ "can u think of a short name of my package",
+ "simplify this code"
+ ],
- "ai.answer": [
- "Which framework do you think is the most suitable for performance sensitive projects?"
- ],
+ "ai.answer": [
+ "Which framework do you think is the most suitable for performance sensitive projects?"
+ ],
- "None": [
- "free weather api",
- "js get timezone",
- "how",
- "how's",
- "how's the"
- ]
+ "None": ["free weather api", "js get timezone", "how", "how's", "how's the"]
}
diff --git a/lib/nlp/data/zh.json b/lib/nlp/data/zh.json
index 77cfbf3..5bb1c0c 100644
--- a/lib/nlp/data/zh.json
+++ b/lib/nlp/data/zh.json
@@ -1,127 +1,121 @@
{
- "weather.summary": [
- "天气如何",
- "现在的天气",
- "今天的天气预报",
- "现在的天气状况",
- "今天天气怎么样",
- "目前是什么天气",
- "今天的天气概述",
- "当前天气状况如何",
- "今天会下雨吗",
- "今天会下雪吗",
- "今天晴天吗",
- "今天的天气状况如何",
- "现在外面是什么天气",
- "今天天气好么",
- "今天适合外出吗",
- "今天的天气适宜做什么",
- "今天有没有雾霾",
- "今天的空气质量如何",
- "今天的紫外线指数是多少",
- "今天有没有大风",
- "今天会不会很冷",
- "今天的天气会变化吗",
- "今天晚上的天气如何",
- "今天夜里会下雨吗",
- "今天的天气对出行有影响吗",
- "今天的天气对运动有影响吗",
- "今天的天气对工作有影响吗",
- "今天的天气对旅游有影响吗",
- "今天的天气对健康有影响吗"
- ],
- "weather.temp": [
- "现在的温度",
- "现在多少度",
- "外面有多热",
- "明天热不热?",
- "现在的气温是多少",
- "今天最高温度是多少",
- "今天最低温度是多少",
- "现在外面感觉冷吗",
- "现在需要穿外套吗",
- "现在适合穿短袖吗",
- "现在的温度适合外出吗",
- "现在的温度适合运动吗",
- "现在的温度适合睡觉吗",
- "明天会比今天热吗",
- "明天会比今天冷吗",
- "今天的温度变化大吗",
- "现在的温度适合开空调吗",
- "现在的温度适合开暖气吗",
- "室外的温度是多少",
- "室内的温度是多少",
- "现在的温度适合种植吗",
- "现在的温度适合养宠物吗",
- "现在的温度对健康有影响吗",
- "现在的温度是否舒适",
- "现在的温度是否适合工作"
- ],
- "base64.encode": [
- "请将数据使用base64编码:%s",
- "需要将以下数据base64编码:%s",
- "请将此字符串转为base64:%s",
- "将数据转为base64编码:%s",
- "信息base64编码:%s",
- "请帮忙编码base64:%s",
- "将数据编码为base64:%s"
- ],
+ "weather.summary": [
+ "天气如何",
+ "现在的天气",
+ "今天的天气预报",
+ "现在的天气状况",
+ "今天天气怎么样",
+ "目前是什么天气",
+ "今天的天气概述",
+ "当前天气状况如何",
+ "今天会下雨吗",
+ "今天会下雪吗",
+ "今天晴天吗",
+ "今天的天气状况如何",
+ "现在外面是什么天气",
+ "今天天气好么",
+ "今天适合外出吗",
+ "今天的天气适宜做什么",
+ "今天有没有雾霾",
+ "今天的空气质量如何",
+ "今天的紫外线指数是多少",
+ "今天有没有大风",
+ "今天会不会很冷",
+ "今天的天气会变化吗",
+ "今天晚上的天气如何",
+ "今天夜里会下雨吗",
+ "今天的天气对出行有影响吗",
+ "今天的天气对运动有影响吗",
+ "今天的天气对工作有影响吗",
+ "今天的天气对旅游有影响吗",
+ "今天的天气对健康有影响吗"
+ ],
+ "weather.temp": [
+ "现在的温度",
+ "现在多少度",
+ "外面有多热",
+ "明天热不热?",
+ "现在的气温是多少",
+ "今天最高温度是多少",
+ "今天最低温度是多少",
+ "现在外面感觉冷吗",
+ "现在需要穿外套吗",
+ "现在适合穿短袖吗",
+ "现在的温度适合外出吗",
+ "现在的温度适合运动吗",
+ "现在的温度适合睡觉吗",
+ "明天会比今天热吗",
+ "明天会比今天冷吗",
+ "今天的温度变化大吗",
+ "现在的温度适合开空调吗",
+ "现在的温度适合开暖气吗",
+ "室外的温度是多少",
+ "室内的温度是多少",
+ "现在的温度适合种植吗",
+ "现在的温度适合养宠物吗",
+ "现在的温度对健康有影响吗",
+ "现在的温度是否舒适",
+ "现在的温度是否适合工作"
+ ],
+ "base64.encode": [
+ "请将数据使用base64编码:%s",
+ "需要将以下数据base64编码:%s",
+ "请将此字符串转为base64:%s",
+ "将数据转为base64编码:%s",
+ "信息base64编码:%s",
+ "请帮忙编码base64:%s",
+ "将数据编码为base64:%s"
+ ],
- "base64.decode": [
- "请解码这个base64数据:%s",
- "有base64编码字符串需要解码:%s",
- "帮忙解码base64:%s",
- "将base64编码转回原数据:%s",
- "解码base64信息:%s",
- "解码这个base64:%s",
- "将base64转文本:%s"
- ],
+ "base64.decode": [
+ "请解码这个base64数据:%s",
+ "有base64编码字符串需要解码:%s",
+ "帮忙解码base64:%s",
+ "将base64编码转回原数据:%s",
+ "解码base64信息:%s",
+ "解码这个base64:%s",
+ "将base64转文本:%s"
+ ],
- "url.encode": [
- "请编码这个URL:%s",
- "URL部分需要编码:%s",
- "请将URL部分编码:%s",
- "URL编码转换:%s",
- "安全传输需编码URL:%s",
- "编码URL段:%s",
- "URL数据编码:%s"
- ],
+ "url.encode": [
+ "请编码这个URL:%s",
+ "URL部分需要编码:%s",
+ "请将URL部分编码:%s",
+ "URL编码转换:%s",
+ "安全传输需编码URL:%s",
+ "编码URL段:%s",
+ "URL数据编码:%s"
+ ],
- "url.decode": [
- "请解码这个URL:%s",
- "有URL编码需要解码:%s",
- "解码这个URL:%s",
- "URL编码转回原URL:%s",
- "解码URL部分:%s",
- "解码URL段:%s",
- "URL编码转文本:%s"
- ],
+ "url.decode": [
+ "请解码这个URL:%s",
+ "有URL编码需要解码:%s",
+ "解码这个URL:%s",
+ "URL编码转回原URL:%s",
+ "解码URL部分:%s",
+ "解码URL段:%s",
+ "URL编码转文本:%s"
+ ],
- "html.encode": [
- "请编码HTML实体:%s",
- "文本转为HTML实体:%s",
- "编码为HTML实体:%s",
- "文本HTML实体编码:%s",
- "预防HTML解析编码:%s",
- "HTML实体编码:%s",
- "文本HTML使用编码:%s"
- ],
+ "html.encode": [
+ "请编码HTML实体:%s",
+ "文本转为HTML实体:%s",
+ "编码为HTML实体:%s",
+ "文本HTML实体编码:%s",
+ "预防HTML解析编码:%s",
+ "HTML实体编码:%s",
+ "文本HTML使用编码:%s"
+ ],
- "html.decode": [
- "请解码HTML实体:%s",
- "HTML实体需要解码:%s",
- "解码HTML实体:%s",
- "HTML实体转回文本:%s",
- "HTML实体解码:%s",
- "解码HTML实体:%s",
- "HTML实体转文本:%s"
- ],
+ "html.decode": [
+ "请解码HTML实体:%s",
+ "HTML实体需要解码:%s",
+ "解码HTML实体:%s",
+ "HTML实体转回文本:%s",
+ "HTML实体解码:%s",
+ "解码HTML实体:%s",
+ "HTML实体转文本:%s"
+ ],
- "None": [
- "你好",
- "为什么计算机使用二进制",
- "什么是",
- "热",
- "怎么"
- ]
+ "None": ["你好", "为什么计算机使用二进制", "什么是", "热", "怎么"]
}
diff --git a/lib/nlp/load.ts b/lib/nlp/load.ts
index b99a72d..c39670e 100644
--- a/lib/nlp/load.ts
+++ b/lib/nlp/load.ts
@@ -8,49 +8,48 @@ import { NluManager, NluNeural } from "@nlpjs/nlu";
import { LangEn } from "@nlpjs/lang-en-min";
// @ts-ignore
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;
- init(): Promise;
- process(lang: string, text: string): Promise;
+ manager: any;
+ inited: boolean;
+ loadIntentionModel(): Promise;
+ init(): Promise;
+ process(lang: string, text: string): Promise;
}
-
export class NLU {
- manager: any;
- inited: boolean = false;
- async loadIntentionModel() {
- const container = await containerBootstrap();
- container.use(Nlp);
- container.use(LangEn);
- container.use(LangZh);
- container.use(NluNeural);
- const manager = new NluManager({
- container,
- locales: ["en", "zh"],
- nlu: {
- useNoneFeature: true
- }
- });
- const response = await fetch("/model");
-
- const responseBuf = await response.arrayBuffer();
- const compressed = new Uint8Array(responseBuf);
- const decompressed = fflate.decompressSync(compressed);
- const modelText = fflate.strFromU8(decompressed);
- manager.fromJSON(JSON.parse(modelText));
- this.manager = manager;
- }
- async init() {
- await this.loadIntentionModel();
- this.inited = true;
- return this;
- }
- async process(lang: string, text: string): Promise {
- const actual = await this.manager.process(lang, text);
- return actual;
- }
-}
\ No newline at end of file
+ manager: any;
+ inited: boolean = false;
+ async loadIntentionModel() {
+ const container = await containerBootstrap();
+ container.use(Nlp);
+ container.use(LangEn);
+ container.use(LangZh);
+ container.use(NluNeural);
+ const manager = new NluManager({
+ container,
+ locales: ["en", "zh"],
+ nlu: {
+ useNoneFeature: true
+ }
+ });
+ const response = await fetch("/model");
+
+ const responseBuf = await response.arrayBuffer();
+ const compressed = new Uint8Array(responseBuf);
+ const decompressed = fflate.decompressSync(compressed);
+ const modelText = fflate.strFromU8(decompressed);
+ manager.fromJSON(JSON.parse(modelText));
+ this.manager = manager;
+ }
+ async init() {
+ await this.loadIntentionModel();
+ this.inited = true;
+ return this;
+ }
+ async process(lang: string, text: string): Promise {
+ const actual = await this.manager.process(lang, text);
+ return actual;
+ }
+}
diff --git a/lib/nlp/train.ts b/lib/nlp/train.ts
index 72f0a56..1988b58 100644
--- a/lib/nlp/train.ts
+++ b/lib/nlp/train.ts
@@ -9,63 +9,63 @@ import { LangEn } from "@nlpjs/lang-en-min";
// @ts-ignore
import { LangZh } from "@nlpjs/lang-zh";
import fs from "node:fs";
-import * as fflate from 'fflate';
+import * as fflate from "fflate";
let zh: TrainData = {};
let en: TrainData = {};
type TrainData = {
- [key: string]: string[];
+ [key: string]: string[];
};
export async function trainIntentionModel() {
- try {
- const dataZH = fs.readFileSync("./lib/nlp/data/zh.json", "utf8");
- const dataEN = fs.readFileSync("./lib/nlp/data/en.json", "utf8");
- zh = JSON.parse(dataZH);
- en = JSON.parse(dataEN);
- } catch (err) {
- console.error(err);
- }
+ try {
+ const dataZH = fs.readFileSync("./lib/nlp/data/zh.json", "utf8");
+ const dataEN = fs.readFileSync("./lib/nlp/data/en.json", "utf8");
+ zh = JSON.parse(dataZH);
+ en = JSON.parse(dataEN);
+ } catch (err) {
+ console.error(err);
+ }
- const container = await containerBootstrap();
- container.use(Nlp);
- container.use(LangEn);
- container.use(LangZh);
- container.use(NluNeural);
- const manager = new NluManager({
- container,
- locales: ["en", "zh"],
- nlu: {
- useNoneFeature: true
- }
- });
- // Adds the utterances and intents for the NLP
+ const container = await containerBootstrap();
+ container.use(Nlp);
+ container.use(LangEn);
+ container.use(LangZh);
+ container.use(NluNeural);
+ const manager = new NluManager({
+ container,
+ locales: ["en", "zh"],
+ nlu: {
+ useNoneFeature: true
+ }
+ });
+ // Adds the utterances and intents for the NLP
- for (const key in zh) {
- for (const value of zh[key]) {
- manager.add("zh", value, key);
- }
- }
+ for (const key in zh) {
+ for (const value of zh[key]) {
+ manager.add("zh", value, key);
+ }
+ }
- for (const key in en) {
- for (const value of en[key]) {
- manager.add("en", value, key);
- }
- }
+ for (const key in en) {
+ for (const value of en[key]) {
+ manager.add("en", value, key);
+ }
+ }
- await manager.train();
+ await manager.train();
- const resultModel = manager.toJSON();
+ const resultModel = manager.toJSON();
- const buf = fflate.strToU8(JSON.stringify(resultModel));
+ const buf = fflate.strToU8(JSON.stringify(resultModel));
- const gzipped = fflate.gzipSync(buf, {
- filename: 'model.json',
- mtime: new Date().getTime()
- });
-
- fs.writeFileSync("./public/model", Buffer.from(gzipped));
+ const gzipped = fflate.gzipSync(buf, {
+ filename: "model.json",
+ mtime: new Date().getTime()
+ });
+
+ fs.writeFileSync("./public/model", Buffer.from(gzipped));
}
trainIntentionModel();
diff --git a/lib/normalizeURL.ts b/lib/normalizeURL.ts
index 91404c5..8588ed6 100644
--- a/lib/normalizeURL.ts
+++ b/lib/normalizeURL.ts
@@ -1,18 +1,18 @@
export function normalizeURL(input: string): string {
- try {
- // try to create a URL object
- const url = new URL(input, window.location.href);
- // if the URL is valid, return it
- return url.href;
- } catch (error) {
- // if the URL is invalid, try to add the protocol
- const withHTTP = "http://" + input;
- try {
- const urlWithHTTP = new URL(withHTTP);
- return urlWithHTTP.href;
- } catch (error) {
- // if the URL is still invalid, return the original input
- return input;
- }
- }
+ try {
+ // try to create a URL object
+ const url = new URL(input, window.location.href);
+ // if the URL is valid, return it
+ return url.href;
+ } catch (error) {
+ // if the URL is invalid, try to add the protocol
+ const withHTTP = "http://" + input;
+ try {
+ const urlWithHTTP = new URL(withHTTP);
+ return urlWithHTTP.href;
+ } catch (error) {
+ // if the URL is still invalid, return the original input
+ return input;
+ }
+ }
}
diff --git a/lib/onesearch/getSearchEngineName.ts b/lib/onesearch/getSearchEngineName.ts
index 251a151..93662e0 100644
--- a/lib/onesearch/getSearchEngineName.ts
+++ b/lib/onesearch/getSearchEngineName.ts
@@ -4,14 +4,14 @@ import { settingsType } from "global";
import { useAtomValue } from "jotai";
import { useTranslation } from "react-i18next";
-export default function(){
- const settings: settingsType = useAtomValue(settingsAtom);
- const currentEngine = settings.currentSearchEngine;
- const displayEngine = getName(currentEngine);
- return displayEngine;
+export default function () {
+ const settings: settingsType = useAtomValue(settingsAtom);
+ const currentEngine = settings.currentSearchEngine;
+ const displayEngine = getName(currentEngine);
+ return displayEngine;
}
function getName(engineKey: string) {
- const { t } = useTranslation();
- return engineTranslation.includes(engineKey) ? t(`search.engine.${engineKey}`) : engineKey;
-}
\ No newline at end of file
+ const { t } = useTranslation();
+ return engineTranslation.includes(engineKey) ? t(`search.engine.${engineKey}`) : engineKey;
+}
diff --git a/lib/onesearch/handleEnter.ts b/lib/onesearch/handleEnter.ts
index 239b925..1dbc8e4 100644
--- a/lib/onesearch/handleEnter.ts
+++ b/lib/onesearch/handleEnter.ts
@@ -4,25 +4,25 @@ import { normalizeURL } from "lib/normalizeURL";
import search from "lib/search";
export default function (
- index: number,
- suggestion: suggestionItem[],
- _query: string,
- settings: settingsType,
- searchBoxRef: React.RefObject
+ index: number,
+ suggestion: suggestionItem[],
+ _query: string,
+ settings: settingsType,
+ searchBoxRef: React.RefObject
) {
- const selected = suggestion[index];
- const engine = settings.searchEngines[settings.currentSearchEngine];
- const newTab = settings.searchInNewTab;
- if (selected.type === "QUERY" || selected.type === "default") {
- search(selected.suggestion, engine, newTab);
- } else if (selected.type === "NAVIGATION" || selected.type === "default-link") {
- window.open(normalizeURL(selected.suggestion));
- } else if (selected.type === "text") {
- copyToClipboard(selected.suggestion);
- searchBoxRef.current?.focus();
- } else if (selected.type === "link") {
- window.open(normalizeURL(selected.suggestion));
- } else if (selected.type === "inpage-link") {
- location.href = normalizeURL(selected.suggestion);
- }
+ const selected = suggestion[index];
+ const engine = settings.searchEngines[settings.currentSearchEngine];
+ const newTab = settings.searchInNewTab;
+ if (selected.type === "QUERY" || selected.type === "default") {
+ search(selected.suggestion, engine, newTab);
+ } else if (selected.type === "NAVIGATION" || selected.type === "default-link") {
+ window.open(normalizeURL(selected.suggestion));
+ } else if (selected.type === "text") {
+ copyToClipboard(selected.suggestion);
+ searchBoxRef.current?.focus();
+ } else if (selected.type === "link") {
+ window.open(normalizeURL(selected.suggestion));
+ } else if (selected.type === "inpage-link") {
+ location.href = normalizeURL(selected.suggestion);
+ }
}
diff --git a/lib/onesearch/keywordSuggestion.ts b/lib/onesearch/keywordSuggestion.ts
index 0736f23..c13cfc0 100644
--- a/lib/onesearch/keywordSuggestion.ts
+++ b/lib/onesearch/keywordSuggestion.ts
@@ -1,39 +1,39 @@
import { suggestionItem } from "global";
interface keywordLinkDict {
- [key: string]: string;
+ [key: string]: string;
}
const dict_en: keywordLinkDict = {
- about: "/about"
+ about: "/about"
};
const dict_cn: keywordLinkDict = {
- 关于: "/about"
+ 关于: "/about"
};
export function keywordSuggestion(query: string) {
- for (const keyword in dict_cn) {
- if (query.includes(keyword)) {
- const result: suggestionItem = {
- type: "inpage-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: "inpage-link",
- suggestion: dict_en[keyword],
- prompt: keyword,
- relevance: 3000
- };
- return result
- }
- }
- return null;
+ for (const keyword in dict_cn) {
+ if (query.includes(keyword)) {
+ const result: suggestionItem = {
+ type: "inpage-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: "inpage-link",
+ suggestion: dict_en[keyword],
+ prompt: keyword,
+ relevance: 3000
+ };
+ return result;
+ }
+ }
+ return null;
}
diff --git a/lib/onesearch/translatedEngineList.ts b/lib/onesearch/translatedEngineList.ts
index d486591..86886f3 100644
--- a/lib/onesearch/translatedEngineList.ts
+++ b/lib/onesearch/translatedEngineList.ts
@@ -1 +1,9 @@
-export const engineTranslation = ["google", "bing", "baidu", "duckduckgo", "yandex", "ecosia", "yahoo"];
\ No newline at end of file
+export const engineTranslation = [
+ "google",
+ "bing",
+ "baidu",
+ "duckduckgo",
+ "yandex",
+ "ecosia",
+ "yahoo"
+];
diff --git a/lib/search.ts b/lib/search.ts
index fee5d50..9f38edc 100644
--- a/lib/search.ts
+++ b/lib/search.ts
@@ -1,4 +1,4 @@
-export default function(query: string, engine: string, newTab: boolean = true) {
- if(newTab) window.open(engine.replace("%s", query));
- else window.location.href = engine.replace("%s", query);
-}
\ No newline at end of file
+export default function (query: string, engine: string, newTab: boolean = true) {
+ if (newTab) window.open(engine.replace("%s", query));
+ else window.location.href = engine.replace("%s", query);
+}
diff --git a/lib/state/settings.ts b/lib/state/settings.ts
index 60e5f53..d2c9e6c 100644
--- a/lib/state/settings.ts
+++ b/lib/state/settings.ts
@@ -1,24 +1,24 @@
import { settingsType } from "global";
-import { atomWithStorage } from 'jotai/utils'
+import { atomWithStorage } from "jotai/utils";
const defaultSettings: settingsType = {
- "version": 2,
- "elementBackdrop": true,
- "bgBlur": true,
- "timeShowSecond": false,
- "currentSearchEngine": "google",
- "searchInNewTab": true,
- "searchEngines": {
- "google": "https://www.google.com/search?q=%s",
- "bing": "https://www.bing.com/search?q=%s",
- "baidu": "https://www.baidu.com/s?wd=%s",
- "duckduckgo": "https://duckduckgo.com/?q=%s",
- "yandex": "https://yandex.com/search/?text=%s",
- "yahoo": "https://search.yahoo.com/search?p=%s",
- "ecosia": "https://www.ecosia.org/search?q=%s"
- }
+ version: 2,
+ elementBackdrop: true,
+ bgBlur: true,
+ timeShowSecond: false,
+ currentSearchEngine: "google",
+ searchInNewTab: true,
+ searchEngines: {
+ google: "https://www.google.com/search?q=%s",
+ bing: "https://www.bing.com/search?q=%s",
+ baidu: "https://www.baidu.com/s?wd=%s",
+ duckduckgo: "https://duckduckgo.com/?q=%s",
+ yandex: "https://yandex.com/search/?text=%s",
+ yahoo: "https://search.yahoo.com/search?p=%s",
+ ecosia: "https://www.ecosia.org/search?q=%s"
+ }
};
-const settingsAtom = atomWithStorage('settings', defaultSettings);
+const settingsAtom = atomWithStorage("settings", defaultSettings);
export { settingsAtom };
diff --git a/lib/telemetering/sendError.ts b/lib/telemetering/sendError.ts
index 2ccb700..9851950 100644
--- a/lib/telemetering/sendError.ts
+++ b/lib/telemetering/sendError.ts
@@ -1,21 +1,21 @@
-import pjson from "package.json"
+import pjson from "package.json";
const CLIENT_VERSION = pjson.version;
export function sendError(error: Error) {
- fetch("/api/v1/error", {
- method: "POST",
- headers: {
- "Content-Type": "application/json"
- },
- body: JSON.stringify({
- message: error.message,
- name: error.name,
- time: new Date().getTime()/1000,
- version: CLIENT_VERSION,
- ua: navigator.userAgent,
- cause: error.cause,
- stack: error.stack
- })
- })
-}
\ No newline at end of file
+ fetch("/api/v1/error", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ message: error.message,
+ name: error.name,
+ time: new Date().getTime() / 1000,
+ version: CLIENT_VERSION,
+ ua: navigator.userAgent,
+ cause: error.cause,
+ stack: error.stack
+ })
+ });
+}
diff --git a/lib/url/tldList.ts b/lib/url/tldList.ts
index 1709b6a..0868bca 100644
--- a/lib/url/tldList.ts
+++ b/lib/url/tldList.ts
@@ -1,5 +1,5 @@
-import TLDtxt from "./tlds.txt?raw"
+import TLDtxt from "./tlds.txt?raw";
-export function getTLD(){
- return TLDtxt.split("\n").filter((line) => line[0] !== "#")
-}
\ No newline at end of file
+export function getTLD() {
+ return TLDtxt.split("\n").filter((line) => line[0] !== "#");
+}
diff --git a/lib/url/updateTLD.ts b/lib/url/updateTLD.ts
index 32f173d..487c277 100644
--- a/lib/url/updateTLD.ts
+++ b/lib/url/updateTLD.ts
@@ -1,3 +1,3 @@
const response = await fetch("https://data.iana.org/TLD/tlds-alpha-by-domain.txt");
const tldText = await response.text();
-await Bun.write("./lib/url/tlds.txt", tldText);
\ No newline at end of file
+await Bun.write("./lib/url/tlds.txt", tldText);
diff --git a/lib/url/validLink.ts b/lib/url/validLink.ts
index 5112091..6ffa85b 100644
--- a/lib/url/validLink.ts
+++ b/lib/url/validLink.ts
@@ -2,89 +2,89 @@ import { toASCII } from "tr46";
import { getTLD } from "./tldList";
export default function validLink(link: string) {
- let finalURL;
- try {
- const url = new URL(link);
- finalURL = url;
- return true;
- } catch (error) {
- // if the URL is invalid, try to add the protocol
- try {
- const urlWithHTTP = new URL("http://" + link);
- finalURL = urlWithHTTP;
- } catch (error) {
- return false;
- }
- }
- if (
- validTLD(finalURL.host) ||
- isValidIPv6(finalURL.host.slice(1, finalURL.host.length - 1)) ||
- isValidIPv4(finalURL.host)
- ) {
- return true;
- } else {
- return false;
- }
+ let finalURL;
+ try {
+ const url = new URL(link);
+ finalURL = url;
+ return true;
+ } catch (error) {
+ // if the URL is invalid, try to add the protocol
+ try {
+ const urlWithHTTP = new URL("http://" + link);
+ finalURL = urlWithHTTP;
+ } catch (error) {
+ return false;
+ }
+ }
+ if (
+ validTLD(finalURL.host) ||
+ isValidIPv6(finalURL.host.slice(1, finalURL.host.length - 1)) ||
+ isValidIPv4(finalURL.host)
+ ) {
+ return true;
+ } else {
+ return false;
+ }
}
export function validTLD(domain: string): boolean {
- if (!domain.includes(".")) return false;
- const tld = toASCII(domain.split(".").reverse()[0]);
- const tldList = getTLD();
- if (tldList.includes(tld.toUpperCase())) {
- return true;
- } else {
- return false;
- }
+ if (!domain.includes(".")) return false;
+ const tld = toASCII(domain.split(".").reverse()[0]);
+ const tldList = getTLD();
+ if (tldList.includes(tld.toUpperCase())) {
+ return true;
+ } else {
+ return false;
+ }
}
export function isValidIPv6(ip: string): boolean {
- const length = ip.length;
- let groups = 1;
- let groupDigits = 0;
- let doubleColonCount = 0;
- for (let i = 0; i < length; i++) {
- const char = ip[i];
- if ("0" <= char && char <= "9") {
- groupDigits++;
- } else if ("a" <= char && char <= "f") {
- groupDigits++;
- } else if ("A" <= char && char <= "F") {
- groupDigits++;
- } else if (char === ":" && i + 1 < length && ip[i + 1] !== ":") {
- groups++;
- groupDigits = 0;
- } else if (char === ":" && i + 1 < length && ip[i + 1] === ":") {
- doubleColonCount++;
- i++;
- groupDigits = 0;
- } else {
- return false;
- }
- if (groups > 8) {
- return false;
- } else if (groupDigits > 4) {
- return false;
- } else if (doubleColonCount > 1) {
- return false;
- }
- }
- if (doubleColonCount === 0 && groups !== 8) {
- return false;
- }
- return true;
+ const length = ip.length;
+ let groups = 1;
+ let groupDigits = 0;
+ let doubleColonCount = 0;
+ for (let i = 0; i < length; i++) {
+ const char = ip[i];
+ if ("0" <= char && char <= "9") {
+ groupDigits++;
+ } else if ("a" <= char && char <= "f") {
+ groupDigits++;
+ } else if ("A" <= char && char <= "F") {
+ groupDigits++;
+ } else if (char === ":" && i + 1 < length && ip[i + 1] !== ":") {
+ groups++;
+ groupDigits = 0;
+ } else if (char === ":" && i + 1 < length && ip[i + 1] === ":") {
+ doubleColonCount++;
+ i++;
+ groupDigits = 0;
+ } else {
+ return false;
+ }
+ if (groups > 8) {
+ return false;
+ } else if (groupDigits > 4) {
+ return false;
+ } else if (doubleColonCount > 1) {
+ return false;
+ }
+ }
+ if (doubleColonCount === 0 && groups !== 8) {
+ return false;
+ }
+ return true;
}
export function isValidIPv4(ip: string): boolean {
- const parts = ip.split(".");
- if (parts.length !== 4) {
- return false;
- }
- for (const part of parts) {
- const num = Number(part);
- if (isNaN(num) || num < 0 || num > 255 || !part.match(/^\d+$/)) {
- return false;
- }
- }
- return true;
+ const parts = ip.split(".");
+ if (parts.length !== 4) {
+ return false;
+ }
+ for (const part of parts) {
+ const num = Number(part);
+ if (isNaN(num) || num < 0 || num > 255 || !part.match(/^\d+$/)) {
+ return false;
+ }
+ }
+ return true;
}
diff --git a/lib/version.ts b/lib/version.ts
index 4b45f49..badfb98 100644
--- a/lib/version.ts
+++ b/lib/version.ts
@@ -1,8 +1,8 @@
import * as pjson from "package.json";
-export default function getVersion(){
- return pjson.version;
+export default function getVersion() {
+ return pjson.version;
}
export const clientNLUVersion = 2;
-export const apiVersion = 1;
\ No newline at end of file
+export const apiVersion = 1;
diff --git a/lib/weather/getCurrentWeather.ts b/lib/weather/getCurrentWeather.ts
index a20a330..e40ac8c 100644
--- a/lib/weather/getCurrentWeather.ts
+++ b/lib/weather/getCurrentWeather.ts
@@ -1,38 +1,38 @@
export function getClosestHourTimestamp(): string {
- const now = new Date();
- now.setMinutes(0, 0, 0); // 设置分钟、秒和毫秒为0
+ const now = new Date();
+ now.setMinutes(0, 0, 0); // 设置分钟、秒和毫秒为0
- // 获取本地时间的年份、月份、日期、小时
- const year = now.getFullYear();
- const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份从0开始
- const day = String(now.getDate()).padStart(2, '0');
- const hour = String(now.getHours()).padStart(2, '0');
+ // 获取本地时间的年份、月份、日期、小时
+ const year = now.getFullYear();
+ const month = String(now.getMonth() + 1).padStart(2, "0"); // 月份从0开始
+ const day = String(now.getDate()).padStart(2, "0");
+ const hour = String(now.getHours()).padStart(2, "0");
- // 拼接成所需的格式
- const localHourTimestamp = `${year}-${month}-${day}T${hour}:00`;
+ // 拼接成所需的格式
+ const localHourTimestamp = `${year}-${month}-${day}T${hour}:00`;
- return localHourTimestamp;
+ return localHourTimestamp;
}
export function findClosestDateIndex(dates: string[]): number {
- const now = new Date();
- const nowTimestamp = now.getTime();
+ const now = new Date();
+ const nowTimestamp = now.getTime();
- let closestIndex = -1;
- let closestDiff = Infinity;
+ let closestIndex = -1;
+ let closestDiff = Infinity;
- for (let i = 0; i < dates.length; i++) {
- const date = new Date(dates[i]);
- const adjustedTimestamp = date.getTime();
+ for (let i = 0; i < dates.length; i++) {
+ const date = new Date(dates[i]);
+ const adjustedTimestamp = date.getTime();
- if (adjustedTimestamp <= nowTimestamp) {
- const diff = nowTimestamp - adjustedTimestamp;
- if (diff < closestDiff) {
- closestDiff = diff;
- closestIndex = i;
- }
- }
- }
+ if (adjustedTimestamp <= nowTimestamp) {
+ const diff = nowTimestamp - adjustedTimestamp;
+ if (diff < closestDiff) {
+ closestDiff = diff;
+ closestIndex = i;
+ }
+ }
+ }
- return closestIndex;
-}
\ No newline at end of file
+ return closestIndex;
+}
diff --git a/lib/weather/getLocation.ts b/lib/weather/getLocation.ts
index 93774cc..5bfa0e8 100644
--- a/lib/weather/getLocation.ts
+++ b/lib/weather/getLocation.ts
@@ -1,17 +1,17 @@
const options = {
- enableHighAccuracy: true,
- timeout: 10000,
- maximumAge: 3600
+ enableHighAccuracy: true,
+ timeout: 10000,
+ maximumAge: 3600
};
export function getLocationNative(callback: Function) {
- navigator.geolocation.getCurrentPosition(
- (pos: GeolocationPosition) => {
- callback(pos.coords);
- },
- (err: GeolocationPositionError) => {
- callback(err);
- },
- options
- );
+ navigator.geolocation.getCurrentPosition(
+ (pos: GeolocationPosition) => {
+ callback(pos.coords);
+ },
+ (err: GeolocationPositionError) => {
+ callback(err);
+ },
+ options
+ );
}
diff --git a/lib/weather/getWeather.ts b/lib/weather/getWeather.ts
index b9cc28c..ebc6102 100644
--- a/lib/weather/getWeather.ts
+++ b/lib/weather/getWeather.ts
@@ -1,23 +1,24 @@
export async function getWeather(lat: number, lon: number) {
- const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
- const cacheKey = `weather-cache-${lat.toFixed(2)}-${lon.toFixed(2)}-${timezone}`;
- const localData = localStorage.getItem(cacheKey);
- if (localData != null) {
- console.log('Using cache');
- const parsedLocalData = JSON.parse(localData);
- if (parsedLocalData["hourly"]["time"][0] != undefined &&
- new Date().getTime() - new Date(parsedLocalData["hourly"]["time"][0]).getTime() < 86400 * 1000
- ) {
- return parsedLocalData;
- }
- else {
- console.log('Cache expired');
- localStorage.removeItem(cacheKey);
- }
- }
- const url = `https://api.open-meteo.com/v1/cma?latitude=${lat.toString()}&longitude=${lon.toString()}&hourly=apparent_temperature,precipitation,weather_code&timezone=${encodeURIComponent(timezone)}&forecast_days=1`;
- const response = await fetch(url);
- const responseJson = await response.json();
- localStorage.setItem(cacheKey, JSON.stringify(responseJson));
- return responseJson;
-}
\ No newline at end of file
+ const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
+ const cacheKey = `weather-cache-${lat.toFixed(2)}-${lon.toFixed(2)}-${timezone}`;
+ const localData = localStorage.getItem(cacheKey);
+ if (localData != null) {
+ console.log("Using cache");
+ const parsedLocalData = JSON.parse(localData);
+ if (
+ parsedLocalData["hourly"]["time"][0] != undefined &&
+ new Date().getTime() - new Date(parsedLocalData["hourly"]["time"][0]).getTime() <
+ 86400 * 1000
+ ) {
+ return parsedLocalData;
+ } else {
+ console.log("Cache expired");
+ localStorage.removeItem(cacheKey);
+ }
+ }
+ const url = `https://api.open-meteo.com/v1/cma?latitude=${lat.toString()}&longitude=${lon.toString()}&hourly=apparent_temperature,precipitation,weather_code&timezone=${encodeURIComponent(timezone)}&forecast_days=1`;
+ const response = await fetch(url);
+ const responseJson = await response.json();
+ localStorage.setItem(cacheKey, JSON.stringify(responseJson));
+ return responseJson;
+}
diff --git a/lib/weather/wmocode.ts b/lib/weather/wmocode.ts
index d852747..19a77ef 100644
--- a/lib/weather/wmocode.ts
+++ b/lib/weather/wmocode.ts
@@ -1,294 +1,294 @@
type WeatherInfo = {
- description: string;
- image: string;
+ description: string;
+ image: string;
};
type WMOCodeTable = {
- [key: string]: {
- day: WeatherInfo;
- night: WeatherInfo;
- };
+ [key: string]: {
+ day: WeatherInfo;
+ night: WeatherInfo;
+ };
};
export let WMOCodeTable: WMOCodeTable = {
- "0": {
- day: {
- description: "Sunny",
- image: "http://openweathermap.org/img/wn/01d@2x.png"
- },
- night: {
- description: "Clear",
- image: "http://openweathermap.org/img/wn/01n@2x.png"
- }
- },
- "1": {
- day: {
- description: "Mainly Sunny",
- image: "http://openweathermap.org/img/wn/01d@2x.png"
- },
- night: {
- description: "Mainly Clear",
- image: "http://openweathermap.org/img/wn/01n@2x.png"
- }
- },
- "2": {
- day: {
- description: "Partly Cloudy",
- image: "http://openweathermap.org/img/wn/02d@2x.png"
- },
- night: {
- description: "Partly Cloudy",
- image: "http://openweathermap.org/img/wn/02n@2x.png"
- }
- },
- "3": {
- day: {
- description: "Cloudy",
- image: "http://openweathermap.org/img/wn/03d@2x.png"
- },
- night: {
- description: "Cloudy",
- image: "http://openweathermap.org/img/wn/03n@2x.png"
- }
- },
- "45": {
- day: {
- description: "Foggy",
- image: "http://openweathermap.org/img/wn/50d@2x.png"
- },
- night: {
- description: "Foggy",
- image: "http://openweathermap.org/img/wn/50n@2x.png"
- }
- },
- "48": {
- day: {
- description: "Rime Fog",
- image: "http://openweathermap.org/img/wn/50d@2x.png"
- },
- night: {
- description: "Rime Fog",
- image: "http://openweathermap.org/img/wn/50n@2x.png"
- }
- },
- "51": {
- day: {
- description: "Light Drizzle",
- image: "http://openweathermap.org/img/wn/09d@2x.png"
- },
- night: {
- description: "Light Drizzle",
- image: "http://openweathermap.org/img/wn/09n@2x.png"
- }
- },
- "53": {
- day: {
- description: "Drizzle",
- image: "http://openweathermap.org/img/wn/09d@2x.png"
- },
- night: {
- description: "Drizzle",
- image: "http://openweathermap.org/img/wn/09n@2x.png"
- }
- },
- "55": {
- day: {
- description: "Heavy Drizzle",
- image: "http://openweathermap.org/img/wn/09d@2x.png"
- },
- night: {
- description: "Heavy Drizzle",
- image: "http://openweathermap.org/img/wn/09n@2x.png"
- }
- },
- "56": {
- day: {
- description: "Light Freezing Drizzle",
- image: "http://openweathermap.org/img/wn/09d@2x.png"
- },
- night: {
- description: "Light Freezing Drizzle",
- image: "http://openweathermap.org/img/wn/09n@2x.png"
- }
- },
- "57": {
- day: {
- description: "Freezing Drizzle",
- image: "http://openweathermap.org/img/wn/09d@2x.png"
- },
- night: {
- description: "Freezing Drizzle",
- image: "http://openweathermap.org/img/wn/09n@2x.png"
- }
- },
- "61": {
- day: {
- description: "Light Rain",
- image: "http://openweathermap.org/img/wn/10d@2x.png"
- },
- night: {
- description: "Light Rain",
- image: "http://openweathermap.org/img/wn/10n@2x.png"
- }
- },
- "63": {
- day: {
- description: "Rain",
- image: "http://openweathermap.org/img/wn/10d@2x.png"
- },
- night: {
- description: "Rain",
- image: "http://openweathermap.org/img/wn/10n@2x.png"
- }
- },
- "65": {
- day: {
- description: "Heavy Rain",
- image: "http://openweathermap.org/img/wn/10d@2x.png"
- },
- night: {
- description: "Heavy Rain",
- image: "http://openweathermap.org/img/wn/10n@2x.png"
- }
- },
- "66": {
- day: {
- description: "Light Freezing Rain",
- image: "http://openweathermap.org/img/wn/10d@2x.png"
- },
- night: {
- description: "Light Freezing Rain",
- image: "http://openweathermap.org/img/wn/10n@2x.png"
- }
- },
- "67": {
- day: {
- description: "Freezing Rain",
- image: "http://openweathermap.org/img/wn/10d@2x.png"
- },
- night: {
- description: "Freezing Rain",
- image: "http://openweathermap.org/img/wn/10n@2x.png"
- }
- },
- "71": {
- day: {
- description: "Light Snow",
- image: "http://openweathermap.org/img/wn/13d@2x.png"
- },
- night: {
- description: "Light Snow",
- image: "http://openweathermap.org/img/wn/13n@2x.png"
- }
- },
- "73": {
- day: {
- description: "Snow",
- image: "http://openweathermap.org/img/wn/13d@2x.png"
- },
- night: {
- description: "Snow",
- image: "http://openweathermap.org/img/wn/13n@2x.png"
- }
- },
- "75": {
- day: {
- description: "Heavy Snow",
- image: "http://openweathermap.org/img/wn/13d@2x.png"
- },
- night: {
- description: "Heavy Snow",
- image: "http://openweathermap.org/img/wn/13n@2x.png"
- }
- },
- "77": {
- day: {
- description: "Snow Grains",
- image: "http://openweathermap.org/img/wn/13d@2x.png"
- },
- night: {
- description: "Snow Grains",
- image: "http://openweathermap.org/img/wn/13n@2x.png"
- }
- },
- "80": {
- day: {
- description: "Light Showers",
- image: "http://openweathermap.org/img/wn/09d@2x.png"
- },
- night: {
- description: "Light Showers",
- image: "http://openweathermap.org/img/wn/09n@2x.png"
- }
- },
- "81": {
- day: {
- description: "Showers",
- image: "http://openweathermap.org/img/wn/09d@2x.png"
- },
- night: {
- description: "Showers",
- image: "http://openweathermap.org/img/wn/09n@2x.png"
- }
- },
- "82": {
- day: {
- description: "Heavy Showers",
- image: "http://openweathermap.org/img/wn/09d@2x.png"
- },
- night: {
- description: "Heavy Showers",
- image: "http://openweathermap.org/img/wn/09n@2x.png"
- }
- },
- "85": {
- day: {
- description: "Light Snow Showers",
- image: "http://openweathermap.org/img/wn/13d@2x.png"
- },
- night: {
- description: "Light Snow Showers",
- image: "http://openweathermap.org/img/wn/13n@2x.png"
- }
- },
- "86": {
- day: {
- description: "Snow Showers",
- image: "http://openweathermap.org/img/wn/13d@2x.png"
- },
- night: {
- description: "Snow Showers",
- image: "http://openweathermap.org/img/wn/13n@2x.png"
- }
- },
- "95": {
- day: {
- description: "Thunderstorm",
- image: "http://openweathermap.org/img/wn/11d@2x.png"
- },
- night: {
- description: "Thunderstorm",
- image: "http://openweathermap.org/img/wn/11n@2x.png"
- }
- },
- "96": {
- day: {
- description: "Light Thunderstorms With Hail",
- image: "http://openweathermap.org/img/wn/11d@2x.png"
- },
- night: {
- description: "Light Thunderstorms With Hail",
- image: "http://openweathermap.org/img/wn/11n@2x.png"
- }
- },
- "99": {
- day: {
- description: "Thunderstorm With Hail",
- image: "http://openweathermap.org/img/wn/11d@2x.png"
- },
- night: {
- description: "Thunderstorm With Hail",
- image: "http://openweathermap.org/img/wn/11n@2x.png"
- }
- }
+ "0": {
+ day: {
+ description: "Sunny",
+ image: "http://openweathermap.org/img/wn/01d@2x.png"
+ },
+ night: {
+ description: "Clear",
+ image: "http://openweathermap.org/img/wn/01n@2x.png"
+ }
+ },
+ "1": {
+ day: {
+ description: "Mainly Sunny",
+ image: "http://openweathermap.org/img/wn/01d@2x.png"
+ },
+ night: {
+ description: "Mainly Clear",
+ image: "http://openweathermap.org/img/wn/01n@2x.png"
+ }
+ },
+ "2": {
+ day: {
+ description: "Partly Cloudy",
+ image: "http://openweathermap.org/img/wn/02d@2x.png"
+ },
+ night: {
+ description: "Partly Cloudy",
+ image: "http://openweathermap.org/img/wn/02n@2x.png"
+ }
+ },
+ "3": {
+ day: {
+ description: "Cloudy",
+ image: "http://openweathermap.org/img/wn/03d@2x.png"
+ },
+ night: {
+ description: "Cloudy",
+ image: "http://openweathermap.org/img/wn/03n@2x.png"
+ }
+ },
+ "45": {
+ day: {
+ description: "Foggy",
+ image: "http://openweathermap.org/img/wn/50d@2x.png"
+ },
+ night: {
+ description: "Foggy",
+ image: "http://openweathermap.org/img/wn/50n@2x.png"
+ }
+ },
+ "48": {
+ day: {
+ description: "Rime Fog",
+ image: "http://openweathermap.org/img/wn/50d@2x.png"
+ },
+ night: {
+ description: "Rime Fog",
+ image: "http://openweathermap.org/img/wn/50n@2x.png"
+ }
+ },
+ "51": {
+ day: {
+ description: "Light Drizzle",
+ image: "http://openweathermap.org/img/wn/09d@2x.png"
+ },
+ night: {
+ description: "Light Drizzle",
+ image: "http://openweathermap.org/img/wn/09n@2x.png"
+ }
+ },
+ "53": {
+ day: {
+ description: "Drizzle",
+ image: "http://openweathermap.org/img/wn/09d@2x.png"
+ },
+ night: {
+ description: "Drizzle",
+ image: "http://openweathermap.org/img/wn/09n@2x.png"
+ }
+ },
+ "55": {
+ day: {
+ description: "Heavy Drizzle",
+ image: "http://openweathermap.org/img/wn/09d@2x.png"
+ },
+ night: {
+ description: "Heavy Drizzle",
+ image: "http://openweathermap.org/img/wn/09n@2x.png"
+ }
+ },
+ "56": {
+ day: {
+ description: "Light Freezing Drizzle",
+ image: "http://openweathermap.org/img/wn/09d@2x.png"
+ },
+ night: {
+ description: "Light Freezing Drizzle",
+ image: "http://openweathermap.org/img/wn/09n@2x.png"
+ }
+ },
+ "57": {
+ day: {
+ description: "Freezing Drizzle",
+ image: "http://openweathermap.org/img/wn/09d@2x.png"
+ },
+ night: {
+ description: "Freezing Drizzle",
+ image: "http://openweathermap.org/img/wn/09n@2x.png"
+ }
+ },
+ "61": {
+ day: {
+ description: "Light Rain",
+ image: "http://openweathermap.org/img/wn/10d@2x.png"
+ },
+ night: {
+ description: "Light Rain",
+ image: "http://openweathermap.org/img/wn/10n@2x.png"
+ }
+ },
+ "63": {
+ day: {
+ description: "Rain",
+ image: "http://openweathermap.org/img/wn/10d@2x.png"
+ },
+ night: {
+ description: "Rain",
+ image: "http://openweathermap.org/img/wn/10n@2x.png"
+ }
+ },
+ "65": {
+ day: {
+ description: "Heavy Rain",
+ image: "http://openweathermap.org/img/wn/10d@2x.png"
+ },
+ night: {
+ description: "Heavy Rain",
+ image: "http://openweathermap.org/img/wn/10n@2x.png"
+ }
+ },
+ "66": {
+ day: {
+ description: "Light Freezing Rain",
+ image: "http://openweathermap.org/img/wn/10d@2x.png"
+ },
+ night: {
+ description: "Light Freezing Rain",
+ image: "http://openweathermap.org/img/wn/10n@2x.png"
+ }
+ },
+ "67": {
+ day: {
+ description: "Freezing Rain",
+ image: "http://openweathermap.org/img/wn/10d@2x.png"
+ },
+ night: {
+ description: "Freezing Rain",
+ image: "http://openweathermap.org/img/wn/10n@2x.png"
+ }
+ },
+ "71": {
+ day: {
+ description: "Light Snow",
+ image: "http://openweathermap.org/img/wn/13d@2x.png"
+ },
+ night: {
+ description: "Light Snow",
+ image: "http://openweathermap.org/img/wn/13n@2x.png"
+ }
+ },
+ "73": {
+ day: {
+ description: "Snow",
+ image: "http://openweathermap.org/img/wn/13d@2x.png"
+ },
+ night: {
+ description: "Snow",
+ image: "http://openweathermap.org/img/wn/13n@2x.png"
+ }
+ },
+ "75": {
+ day: {
+ description: "Heavy Snow",
+ image: "http://openweathermap.org/img/wn/13d@2x.png"
+ },
+ night: {
+ description: "Heavy Snow",
+ image: "http://openweathermap.org/img/wn/13n@2x.png"
+ }
+ },
+ "77": {
+ day: {
+ description: "Snow Grains",
+ image: "http://openweathermap.org/img/wn/13d@2x.png"
+ },
+ night: {
+ description: "Snow Grains",
+ image: "http://openweathermap.org/img/wn/13n@2x.png"
+ }
+ },
+ "80": {
+ day: {
+ description: "Light Showers",
+ image: "http://openweathermap.org/img/wn/09d@2x.png"
+ },
+ night: {
+ description: "Light Showers",
+ image: "http://openweathermap.org/img/wn/09n@2x.png"
+ }
+ },
+ "81": {
+ day: {
+ description: "Showers",
+ image: "http://openweathermap.org/img/wn/09d@2x.png"
+ },
+ night: {
+ description: "Showers",
+ image: "http://openweathermap.org/img/wn/09n@2x.png"
+ }
+ },
+ "82": {
+ day: {
+ description: "Heavy Showers",
+ image: "http://openweathermap.org/img/wn/09d@2x.png"
+ },
+ night: {
+ description: "Heavy Showers",
+ image: "http://openweathermap.org/img/wn/09n@2x.png"
+ }
+ },
+ "85": {
+ day: {
+ description: "Light Snow Showers",
+ image: "http://openweathermap.org/img/wn/13d@2x.png"
+ },
+ night: {
+ description: "Light Snow Showers",
+ image: "http://openweathermap.org/img/wn/13n@2x.png"
+ }
+ },
+ "86": {
+ day: {
+ description: "Snow Showers",
+ image: "http://openweathermap.org/img/wn/13d@2x.png"
+ },
+ night: {
+ description: "Snow Showers",
+ image: "http://openweathermap.org/img/wn/13n@2x.png"
+ }
+ },
+ "95": {
+ day: {
+ description: "Thunderstorm",
+ image: "http://openweathermap.org/img/wn/11d@2x.png"
+ },
+ night: {
+ description: "Thunderstorm",
+ image: "http://openweathermap.org/img/wn/11n@2x.png"
+ }
+ },
+ "96": {
+ day: {
+ description: "Light Thunderstorms With Hail",
+ image: "http://openweathermap.org/img/wn/11d@2x.png"
+ },
+ night: {
+ description: "Light Thunderstorms With Hail",
+ image: "http://openweathermap.org/img/wn/11n@2x.png"
+ }
+ },
+ "99": {
+ day: {
+ description: "Thunderstorm With Hail",
+ image: "http://openweathermap.org/img/wn/11d@2x.png"
+ },
+ night: {
+ description: "Thunderstorm With Hail",
+ image: "http://openweathermap.org/img/wn/11n@2x.png"
+ }
+ }
};
diff --git a/package.json b/package.json
index 8a08c4d..f30aa71 100644
--- a/package.json
+++ b/package.json
@@ -1,61 +1,63 @@
{
- "name": "sparkhome",
- "private": false,
- "version": "5.8.0",
- "type": "module",
- "scripts": {
- "dev": "bun server.ts",
- "build": "bun license-gen && tsc -b && vite build",
- "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
- "preview": "NODE_ENV=production bun server.ts",
- "license-gen": "bunx generate-license-file --input package.json --output lib/license.txt --overwrite"
- },
- "dependencies": {
- "@iconify/react": "^5.0.1",
- "@nextui-org/react": "^2.4.2",
- "@types/bun": "^1.1.6",
- "@types/express": "^4.17.21",
- "@types/tr46": "^5.0.0",
- "cac": "^6.7.14",
- "chalk": "^5.3.0",
- "express": "^4.19.2",
- "fflate": "^0.8.2",
- "framer-motion": "^11.2.12",
- "generate-license-file": "^3.5.1",
- "i18next": "^23.11.5",
- "i18next-browser-languagedetector": "^8.0.0",
- "i18next-icu": "^2.3.0",
- "jest": "^29.7.0",
- "jotai": "^2.8.3",
- "node-nlp": "^4.27.0",
- "react": "^18.3.1",
- "react-dom": "^18.3.1",
- "react-i18next": "^14.1.2",
- "react-router": "^6.23.1",
- "react-router-dom": "^6.23.1",
- "search-engine-autocomplete": "^0.4.3",
- "tr46": "^5.0.0",
- "valid-url": "^1.0.9",
- "validate-color": "^2.2.4",
- "vite-express": "^0.17.0"
- },
- "devDependencies": {
- "@types/react": "^18.3.3",
- "@types/react-dom": "^18.3.0",
- "@types/valid-url": "^1.0.7",
- "@typescript-eslint/eslint-plugin": "^7.13.1",
- "@typescript-eslint/parser": "^7.13.1",
- "@vitejs/plugin-react-swc": "^3.5.0",
- "autoprefixer": "^10.4.19",
- "eslint": "^8.57.0",
- "eslint-plugin-react-hooks": "^4.6.2",
- "eslint-plugin-react-refresh": "^0.4.7",
- "postcss": "^8.4.38",
- "tailwindcss": "^3.4.4",
- "typescript": "^5.2.2",
- "vite": "^5.3.1",
- "vite-plugin-chunk-split": "^0.5.0",
- "vite-plugin-pages": "^0.32.2",
- "vite-tsconfig-paths": "^4.3.2"
- }
+ "name": "sparkhome",
+ "private": false,
+ "version": "5.8.0",
+ "type": "module",
+ "scripts": {
+ "dev": "bun server.ts",
+ "build": "bun license-gen && tsc -b && vite build",
+ "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
+ "preview": "NODE_ENV=production bun server.ts",
+ "license-gen": "bunx generate-license-file --input package.json --output lib/license.txt --overwrite",
+ "format": "prettier --write ."
+ },
+ "dependencies": {
+ "@iconify/react": "^5.0.1",
+ "@nextui-org/react": "^2.4.2",
+ "@types/bun": "^1.1.6",
+ "@types/express": "^4.17.21",
+ "@types/tr46": "^5.0.0",
+ "cac": "^6.7.14",
+ "chalk": "^5.3.0",
+ "express": "^4.19.2",
+ "fflate": "^0.8.2",
+ "framer-motion": "^11.2.12",
+ "generate-license-file": "^3.5.1",
+ "i18next": "^23.11.5",
+ "i18next-browser-languagedetector": "^8.0.0",
+ "i18next-icu": "^2.3.0",
+ "jest": "^29.7.0",
+ "jotai": "^2.8.3",
+ "node-nlp": "^4.27.0",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-i18next": "^14.1.2",
+ "react-router": "^6.23.1",
+ "react-router-dom": "^6.23.1",
+ "search-engine-autocomplete": "^0.4.3",
+ "tr46": "^5.0.0",
+ "valid-url": "^1.0.9",
+ "validate-color": "^2.2.4",
+ "vite-express": "^0.17.0"
+ },
+ "devDependencies": {
+ "@types/react": "^18.3.3",
+ "@types/react-dom": "^18.3.0",
+ "@types/valid-url": "^1.0.7",
+ "@typescript-eslint/eslint-plugin": "^7.13.1",
+ "@typescript-eslint/parser": "^7.13.1",
+ "@vitejs/plugin-react-swc": "^3.5.0",
+ "autoprefixer": "^10.4.19",
+ "eslint": "^8.57.0",
+ "eslint-plugin-react-hooks": "^4.6.2",
+ "eslint-plugin-react-refresh": "^0.4.7",
+ "postcss": "^8.4.38",
+ "prettier": "^3.3.3",
+ "tailwindcss": "^3.4.4",
+ "typescript": "^5.2.2",
+ "vite": "^5.3.1",
+ "vite-plugin-chunk-split": "^0.5.0",
+ "vite-plugin-pages": "^0.32.2",
+ "vite-tsconfig-paths": "^4.3.2"
+ }
}
diff --git a/pages/[...].tsx b/pages/[...].tsx
index 13233a7..b1b471e 100644
--- a/pages/[...].tsx
+++ b/pages/[...].tsx
@@ -1,17 +1,20 @@
import { useTranslation } from "react-i18next";
export default function NotFound() {
- const { t } = useTranslation();
- return (
-
-
-
404
-
-
-
{t("notfound.title")}
-
-
-
-
- );
+ const { t } = useTranslation();
+ return (
+
+
+
404
+
+
+
{t("notfound.title")}
+
+
+
+
+ );
}
diff --git a/pages/about/index.tsx b/pages/about/index.tsx
index 134a5ca..33027bb 100644
--- a/pages/about/index.tsx
+++ b/pages/about/index.tsx
@@ -3,66 +3,66 @@ import getVersion, { apiVersion, clientNLUVersion } from "lib/version";
import AboutLayout from "./layout";
export default function AboutPage() {
- const darkMode = useDarkMode();
+ const darkMode = useDarkMode();
- return (
-
- About SparkHome
-
-

-
-
SparkHome
-
- Made with ♥️ by
-
- alikia2x
-
- from Luminara Studio
-
-
-
+ return (
+
+ About SparkHome
+
+

+
+
SparkHome
+
+ Made with ♥️ by
+
+ alikia2x
+
+ from Luminara Studio
+
+
+
-
-
-
-
- License
-
+
+
+
+ License
+
- → view
-
-
+ >
+ → view
+
+
- Presented By
- {!darkMode &&
}
- {darkMode &&
}
-
- );
+ Presented By
+ {!darkMode &&
}
+ {darkMode &&
}
+
+ );
}
function Version(props: { title: string; version: string; versionClass?: string }) {
- document.title = "About SparkHome";
- return (
-
- {props.title}
-
- {props.version}
-
-
- );
+ document.title = "About SparkHome";
+ return (
+
+ {props.title}
+
+ {props.version}
+
+
+ );
}
diff --git a/pages/about/layout.tsx b/pages/about/layout.tsx
index 61a0eda..1a697bb 100644
--- a/pages/about/layout.tsx
+++ b/pages/about/layout.tsx
@@ -1,10 +1,12 @@
export default function AboutLayout({ children }: { children: React.ReactNode }) {
- return (
-
-
- {children}
-
-
- );
+ return (
+
+
+ {children}
+
+
+ );
}
diff --git a/pages/about/license/index.tsx b/pages/about/license/index.tsx
index f5e25c3..7385ebe 100644
--- a/pages/about/license/index.tsx
+++ b/pages/about/license/index.tsx
@@ -1,17 +1,15 @@
import LICENSE from "lib/license.txt?raw";
export default function LicensePage() {
- return (
-
-
+
- LICENSE
-
- {LICENSE}
-
-
-
- );
-}
\ No newline at end of file
+ >
+ LICENSE
+ {LICENSE}
+
+
+ );
+}
diff --git a/pages/index.tsx b/pages/index.tsx
index 6b55cc9..a7e8bfe 100644
--- a/pages/index.tsx
+++ b/pages/index.tsx
@@ -8,21 +8,21 @@ import EngineSelector from "components/engineSelector";
import OneSearch from "components/onesearch/onesearch";
export default function Homepage() {
- const settings = useAtomValue(settingsAtom);
- const setBgFocus = useSetAtom(bgFocusAtom);
+ const settings = useAtomValue(settingsAtom);
+ const setBgFocus = useSetAtom(bgFocusAtom);
- return (
-
-
+ return (
+
+
-
- setBgFocus(true)} />
-
-
-
- );
+ />
+
setBgFocus(true)} />
+
+
+
+ );
}
diff --git a/postcss.config.js b/postcss.config.js
index badd100..0f77216 100644
--- a/postcss.config.js
+++ b/postcss.config.js
@@ -1,6 +1,6 @@
export default {
- plugins: {
- tailwindcss: {},
- autoprefixer: {}
- }
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {}
+ }
};
diff --git a/server.ts b/server.ts
index 38cad65..43785b6 100644
--- a/server.ts
+++ b/server.ts
@@ -6,47 +6,72 @@ import { networkInterfaces } from "os";
import cac from "cac";
import { configureBackendRoutes } from "./backend/route";
-
async function helloMessage() {
- const { base } = await ViteExpress.getViteConfig();
- const timeCost = new Date().getTime() - start.getTime();
- console.log("");
- console.log(
- " ",
- chalk.redBright("SparkHome"),
- chalk.redBright("v" + pjson.version),
- chalk.whiteBright(" ready in"),
- `${Math.round(timeCost)} ms`
- );
- console.log("");
- console.log(" ", chalk.redBright("➜ "), "Local:\t", chalk.cyan(`http://${host}:${port}${base}`));
- if (host !== "localhost") {
- for (const ip of ips) {
- console.log(" ", chalk.redBright("➜ "), "Network:\t", chalk.cyan(`http://${ip}:${port}${base}`));
- }
- }
- console.log(" ", chalk.red("➜ "), chalk.whiteBright("press"), "h + enter", chalk.whiteBright("to show help"))
+ const { base } = await ViteExpress.getViteConfig();
+ const timeCost = new Date().getTime() - start.getTime();
+ console.log("");
+ console.log(
+ " ",
+ chalk.redBright("SparkHome"),
+ chalk.redBright("v" + pjson.version),
+ chalk.whiteBright(" ready in"),
+ `${Math.round(timeCost)} ms`
+ );
+ console.log("");
+ console.log(
+ " ",
+ chalk.redBright("➜ "),
+ "Local:\t",
+ chalk.cyan(`http://${host}:${port}${base}`)
+ );
+ if (host !== "localhost") {
+ for (const ip of ips) {
+ console.log(
+ " ",
+ chalk.redBright("➜ "),
+ "Network:\t",
+ chalk.cyan(`http://${ip}:${port}${base}`)
+ );
+ }
+ }
+ console.log(
+ " ",
+ chalk.red("➜ "),
+ chalk.whiteBright("press"),
+ "h + enter",
+ chalk.whiteBright("to show help")
+ );
}
async function handleInput() {
- for await (const line of console) {
- switch (line) {
- case "h":
- console.log(" Shortcuts");
- console.log(" ", chalk.whiteBright("press"), "c + enter ", chalk.whiteBright("to clear console"));
- console.log(" ", chalk.whiteBright("press"), "q + enter ", chalk.whiteBright("to quit"));
- break;
- case "c":
- console.clear();
- break;
- case "q":
- server.on("vite:close", ()=>{});
- server.close();
- return;
- default:
- break;
- }
- }
+ for await (const line of console) {
+ switch (line) {
+ case "h":
+ console.log(" Shortcuts");
+ console.log(
+ " ",
+ chalk.whiteBright("press"),
+ "c + enter ",
+ chalk.whiteBright("to clear console")
+ );
+ console.log(
+ " ",
+ chalk.whiteBright("press"),
+ "q + enter ",
+ chalk.whiteBright("to quit")
+ );
+ break;
+ case "c":
+ console.clear();
+ break;
+ case "q":
+ server.on("vite:close", () => {});
+ server.close();
+ return;
+ default:
+ break;
+ }
+ }
}
const start = new Date();
@@ -54,29 +79,33 @@ const cli = cac();
const nets = networkInterfaces();
const ips: string[] = [];
for (const name of Object.keys(nets)) {
- if (nets[name] === undefined) {
- continue;
- }
- for (const net of nets[name]) {
- // Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
- // 'IPv4' is in Node <= 17, from 18 it's a number 4 or 6
- const familyV4Value = typeof net.family === "string" ? "IPv4" : 4;
- if (net.family === familyV4Value && !net.internal) {
- ips.push(net.address);
- }
- }
+ if (nets[name] === undefined) {
+ continue;
+ }
+ for (const net of nets[name]) {
+ // Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
+ // 'IPv4' is in Node <= 17, from 18 it's a number 4 or 6
+ const familyV4Value = typeof net.family === "string" ? "IPv4" : 4;
+ if (net.family === familyV4Value && !net.internal) {
+ ips.push(net.address);
+ }
+ }
}
const app = express();
const port = 3000;
let host = "localhost";
-cli.option("--host [host]", "Sepcify host name")
-cli.help()
+cli.option("--host [host]", "Sepcify host name");
+cli.help();
cli.version(pjson.version);
const parsed = cli.parse();
-if (parsed.options.host!==undefined && typeof parsed.options.host == "boolean" && parsed.options.host) {
- host = "0.0.0.0";
+if (
+ parsed.options.host !== undefined &&
+ typeof parsed.options.host == "boolean" &&
+ parsed.options.host
+) {
+ host = "0.0.0.0";
}
configureBackendRoutes(app);
@@ -85,4 +114,4 @@ const server = app.listen(port, host);
ViteExpress.bind(app, server, helloMessage);
-handleInput();
\ No newline at end of file
+handleInput();
diff --git a/src/app.tsx b/src/app.tsx
index dd605f6..9bb0659 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -6,26 +6,26 @@ import AboutPage from "pages/about";
import LicensePage from "pages/about/license";
const router = createBrowserRouter([
- {
- path: "/",
- element:
- },
- {
- path: "about",
- element: ,
- children: [
- {
- path: "license",
- element:
- }
- ]
- }
+ {
+ path: "/",
+ element:
+ },
+ {
+ path: "about",
+ element: ,
+ children: [
+ {
+ path: "license",
+ element:
+ }
+ ]
+ }
]);
export function App() {
- return (
-
-
-
- );
+ return (
+
+
+
+ );
}
diff --git a/src/i18n.ts b/src/i18n.ts
index 630a51b..c612251 100644
--- a/src/i18n.ts
+++ b/src/i18n.ts
@@ -1,65 +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 * 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';
+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",
+ .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
- },
+ interpolation: {
+ escapeValue: false // react already safes from xss => https://www.i18next.com/translation-function/interpolation#unescape
+ },
- detection: {
- order: ['navigator'],
- caches: []
- }
- });
\ No newline at end of file
+ detection: {
+ order: ["navigator"],
+ caches: []
+ }
+ });
diff --git a/src/index.css b/src/index.css
index bd6213e..b5c61c9 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,3 +1,3 @@
@tailwind base;
@tailwind components;
-@tailwind utilities;
\ No newline at end of file
+@tailwind utilities;
diff --git a/src/main.tsx b/src/main.tsx
index d4ecb1c..a5b2101 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -7,9 +7,9 @@ import { NextUIProvider } from "@nextui-org/react";
const app = createRoot(document.getElementById("root")!);
app.render(
-
-
-
-
-
+
+
+
+
+
);
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
index 32f4e37..f07ec82 100644
--- a/src/vite-env.d.ts
+++ b/src/vite-env.d.ts
@@ -1,2 +1,2 @@
///
-///
\ No newline at end of file
+///
diff --git a/tailwind.config.ts b/tailwind.config.ts
index e7856ab..71440a2 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -3,20 +3,20 @@ import type { Config } from "tailwindcss";
import { nextui } from "@nextui-org/react";
const config: Config = {
- content: [
- "./pages/**/*.{js,ts,jsx,tsx,mdx}",
- "./components/**/*.{js,ts,jsx,tsx,mdx}",
- "./node_modules/@nextui-org/theme/**/*.{js,ts,jsx,tsx}",
- "./src/**/*.{js,ts,jsx,tsx,mdx}"
- ],
- theme: {
- extend: {
- backgroundImage: {
- "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
- "gradient-conic": "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))"
- }
- }
- },
- plugins: [nextui()]
+ content: [
+ "./pages/**/*.{js,ts,jsx,tsx,mdx}",
+ "./components/**/*.{js,ts,jsx,tsx,mdx}",
+ "./node_modules/@nextui-org/theme/**/*.{js,ts,jsx,tsx}",
+ "./src/**/*.{js,ts,jsx,tsx,mdx}"
+ ],
+ theme: {
+ extend: {
+ backgroundImage: {
+ "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
+ "gradient-conic": "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))"
+ }
+ }
+ },
+ plugins: [nextui()]
};
export default config;
diff --git a/test/validLink.test.ts b/test/validLink.test.ts
index e80d8ce..df8ab39 100644
--- a/test/validLink.test.ts
+++ b/test/validLink.test.ts
@@ -2,76 +2,80 @@ import { describe, expect, test } from "@jest/globals";
import validLink, { validTLD } from "../lib/url/validLink";
describe("Check if a string is an accessible domain/URL/IP", () => {
- test("Plain, full URL", () => {
- // Plain form
- expect(validLink("http://example.com")).toBe(true);
- // With https and path
- expect(validLink("https://jestjs.io/docs/getting-started/")).toBe(true);
- // With anchor
- expect(validLink("https://difftastic.wilfred.me.uk/zh-CN/git.html#git-difftool")).toBe(true);
- // With params
- expect(validLink("https://www.bilibili.com/list/ml2252204359?oid=990610203&bvid=BV1sx4y1g7Hh")).toBe(true);
- });
- test("Punycode URL", () => {
- expect(validLink("https://原神大学.com/")).toBe(true);
- expect(validLink("中国原神大学.com")).toBe(true);
- });
- test("Invalid TLD with protocol", () => {
- expect(validLink("https://www.example.notexist")).toBe(true);
- });
- test("Invalid TLD with no protocol", () => {
- expect(validLink("www.example.notexist")).toBe(false);
- });
- test("IPv4 without protocol", () => {
- expect(validLink("127.0.0.1")).toBe(true);
- });
- test("IPv6 without protocol", () => {
- expect(validLink("[::]")).toBe(true);
- });
- test("special test for 铜锣湾.chn.moe", () => {
- expect(validLink("铜锣湾.chn.moe")).toBe(true);
- });
- test("Not a valid host/URL.", () => {
- expect(validLink("weather")).toBe(false);
- });
+ test("Plain, full URL", () => {
+ // Plain form
+ expect(validLink("http://example.com")).toBe(true);
+ // With https and path
+ expect(validLink("https://jestjs.io/docs/getting-started/")).toBe(true);
+ // With anchor
+ expect(validLink("https://difftastic.wilfred.me.uk/zh-CN/git.html#git-difftool")).toBe(
+ true
+ );
+ // With params
+ expect(
+ validLink("https://www.bilibili.com/list/ml2252204359?oid=990610203&bvid=BV1sx4y1g7Hh")
+ ).toBe(true);
+ });
+ test("Punycode URL", () => {
+ expect(validLink("https://原神大学.com/")).toBe(true);
+ expect(validLink("中国原神大学.com")).toBe(true);
+ });
+ test("Invalid TLD with protocol", () => {
+ expect(validLink("https://www.example.notexist")).toBe(true);
+ });
+ test("Invalid TLD with no protocol", () => {
+ expect(validLink("www.example.notexist")).toBe(false);
+ });
+ test("IPv4 without protocol", () => {
+ expect(validLink("127.0.0.1")).toBe(true);
+ });
+ test("IPv6 without protocol", () => {
+ expect(validLink("[::]")).toBe(true);
+ });
+ test("special test for 铜锣湾.chn.moe", () => {
+ expect(validLink("铜锣湾.chn.moe")).toBe(true);
+ });
+ test("Not a valid host/URL.", () => {
+ expect(validLink("weather")).toBe(false);
+ });
});
// Reference: https://www.iana.org/domains/root/db
describe("Check if the given TLD exist and assigned.", () => {
- test("Valid normal TLD", () => {
- expect(validTLD("example.com")).toBe(true);
- expect(validTLD("example.top")).toBe(true);
- expect(validTLD("example.net")).toBe(true);
- expect(validTLD("example.org")).toBe(true);
- });
- test("Valid new TLDs", () => {
- // they really exist!
- expect(validTLD("example.foo")).toBe(true);
- expect(validTLD("example.bar")).toBe(true);
- expect(validTLD('example.zip')).toBe(true);
- });
- test("Exist but not assigned TLD", () => {
- expect(validTLD("example.active")).toBe(false);
- expect(validTLD("example.off")).toBe(false);
- });
- test("with dot", () => {
- expect(validTLD("example.com")).toBe(true);
- expect(validTLD("example.us")).toBe(true);
- expect(validTLD("example.cn")).toBe(true);
- expect(validTLD("example.io")).toBe(true);
- });
- test("Punycode TLDs", () => {
- expect(validTLD("example.中国")).toBe(true);
- expect(validTLD("example.РФ")).toBe(true);
- expect(validTLD("example.कॉम")).toBe(true);
- expect(validTLD("example.ایران")).toBe(true);
- expect(validTLD("example.இலங்கை")).toBe(true);
- expect(validTLD("example.გე")).toBe(true);
- expect(validTLD("example.ポイント")).toBe(true);
- });
- test("Punycode TLDs but not assigned", () => {
- expect(validTLD("example.テスト")).toBe(false);
- expect(validTLD("example.परीक्षा")).toBe(false);
- expect(validTLD("example.测试")).toBe(false);
- });
+ test("Valid normal TLD", () => {
+ expect(validTLD("example.com")).toBe(true);
+ expect(validTLD("example.top")).toBe(true);
+ expect(validTLD("example.net")).toBe(true);
+ expect(validTLD("example.org")).toBe(true);
+ });
+ test("Valid new TLDs", () => {
+ // they really exist!
+ expect(validTLD("example.foo")).toBe(true);
+ expect(validTLD("example.bar")).toBe(true);
+ expect(validTLD("example.zip")).toBe(true);
+ });
+ test("Exist but not assigned TLD", () => {
+ expect(validTLD("example.active")).toBe(false);
+ expect(validTLD("example.off")).toBe(false);
+ });
+ test("with dot", () => {
+ expect(validTLD("example.com")).toBe(true);
+ expect(validTLD("example.us")).toBe(true);
+ expect(validTLD("example.cn")).toBe(true);
+ expect(validTLD("example.io")).toBe(true);
+ });
+ test("Punycode TLDs", () => {
+ expect(validTLD("example.中国")).toBe(true);
+ expect(validTLD("example.РФ")).toBe(true);
+ expect(validTLD("example.कॉम")).toBe(true);
+ expect(validTLD("example.ایران")).toBe(true);
+ expect(validTLD("example.இலங்கை")).toBe(true);
+ expect(validTLD("example.გე")).toBe(true);
+ expect(validTLD("example.ポイント")).toBe(true);
+ });
+ test("Punycode TLDs but not assigned", () => {
+ expect(validTLD("example.テスト")).toBe(false);
+ expect(validTLD("example.परीक्षा")).toBe(false);
+ expect(validTLD("example.测试")).toBe(false);
+ });
});
diff --git a/tsconfig.app.json b/tsconfig.app.json
index c93fa36..28758d8 100644
--- a/tsconfig.app.json
+++ b/tsconfig.app.json
@@ -1,28 +1,28 @@
{
- "compilerOptions": {
- "baseUrl": ".",
- "composite": true,
- "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
- "target": "ES2020",
- "useDefineForClassFields": true,
- "lib": ["ES2020", "DOM", "DOM.Iterable"],
- "module": "ESNext",
- "skipLibCheck": true,
+ "compilerOptions": {
+ "baseUrl": ".",
+ "composite": true,
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
- /* Bundler mode */
- "moduleResolution": "bundler",
- "allowImportingTsExtensions": true,
- "resolveJsonModule": true,
- "isolatedModules": true,
- "moduleDetection": "force",
- "noEmit": true,
- "jsx": "react-jsx",
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
- /* Linting */
- "strict": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "noFallthroughCasesInSwitch": true
- },
- "include": ["src", "**/*.ts", "**/*.tsx", "global.d.ts"]
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["src", "**/*.ts", "**/*.tsx", "global.d.ts"]
}
diff --git a/tsconfig.json b/tsconfig.json
index 41ccf0f..a5b06bf 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,11 +1,11 @@
{
- "files": [],
- "references": [
- {
- "path": "./tsconfig.app.json"
- },
- {
- "path": "./tsconfig.node.json"
- }
- ]
+ "files": [],
+ "references": [
+ {
+ "path": "./tsconfig.app.json"
+ },
+ {
+ "path": "./tsconfig.node.json"
+ }
+ ]
}
diff --git a/tsconfig.node.json b/tsconfig.node.json
index 2cc878d..2c1b0a9 100644
--- a/tsconfig.node.json
+++ b/tsconfig.node.json
@@ -1,14 +1,14 @@
{
- "compilerOptions": {
- "composite": true,
- "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
- "skipLibCheck": true,
- "module": "ESNext",
- "moduleResolution": "bundler",
- "allowSyntheticDefaultImports": true,
- "strict": true,
- "noEmit": true,
- "types": ["bun"]
- },
- "include": ["vite.config.ts"]
+ "compilerOptions": {
+ "composite": true,
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "skipLibCheck": true,
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "noEmit": true,
+ "types": ["bun"]
+ },
+ "include": ["vite.config.ts"]
}
diff --git a/vite.config.ts b/vite.config.ts
index 8f41c4f..3b918b7 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,13 +1,9 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
-import tsconfigPaths from 'vite-tsconfig-paths';
-import { chunkSplitPlugin } from 'vite-plugin-chunk-split';
+import tsconfigPaths from "vite-tsconfig-paths";
+import { chunkSplitPlugin } from "vite-plugin-chunk-split";
// https://vitejs.dev/config/
export default defineConfig({
- plugins: [
- react(),
- tsconfigPaths(),
- chunkSplitPlugin()
- ]
+ plugins: [react(), tsconfigPaths(), chunkSplitPlugin()]
});