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 ( - background - ); - } else { - if (props.darkMode) { - return ( -
- ); - } else { - return ( -
- ); - } - } + const settings = useAtomValue(settingsAtom); + if (validateColor(props.src)) { + return ( +
+ ); + } else if (validUrl.isWebUri(props.src)) { + return ( + background + ); + } 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 && ( - <span className="text-zinc-700 dark:text-zinc-400">{s.prompt}</span> - )} - <p>{s.suggestion}</p> - {devMode && ( - <span className="bottom-0 absolute text-zinc-700 dark:text-zinc-400 text-sm leading-10 h-10 right-2"> - {s.relevance} - </span> - )} - </PlainText> - ); - } else if (s.type === "inpage-link") { - return ( - <LinkSuggestion - key={i} - query={s.suggestion} - selected={i == selected} - inPage={true} - > - {s.prompt && ( - <span className="text-zinc-700 dark:text-zinc-400">{s.prompt}</span> - )} - {s.suggestion} - {devMode && ( - <span className="absolute text-zinc-700 dark:text-zinc-400 text-sm leading-10 h-10 right-2"> - {s.relevance} - </span> - )} - </LinkSuggestion> - ); - } - })} - </SuggestionBox> - ); + return ( + <SuggestionBox> + {suggestion.map((s, i) => { + if (s.suggestion.trim() === "") return; + if (s.type === "default") { + return ( + <PlainSearch key={i} query={s.suggestion} selected={i == selected}> + {s.suggestion}&nbsp; + <span className="text-zinc-700 dark:text-zinc-400 text-sm"> + {t("search.search-help-text", { engine: engineName })} + </span> + {devMode && ( + <span className="absolute text-zinc-700 dark:text-zinc-400 text-sm leading-10 h-10 right-2"> + {s.relevance} + </span> + )} + </PlainSearch> + ); + } else if (s.type === "QUERY") { + return ( + <PlainSearch key={i} query={s.suggestion} selected={i == selected}> + {s.suggestion} + {devMode && ( + <span className="absolute text-zinc-700 dark:text-zinc-400 text-sm leading-10 h-10 right-2"> + {s.relevance} + </span> + )} + </PlainSearch> + ); + } else if ( + s.type === "NAVIGATION" || + s.type === "default-link" || + s.type === "link" + ) { + return ( + <LinkSuggestion key={i} query={s.suggestion} selected={i == selected}> + {s.prompt && ( + <span className="text-zinc-700 dark:text-zinc-400">{s.prompt}</span> + )} + {s.suggestion} + {devMode && ( + <span className="absolute text-zinc-700 dark:text-zinc-400 text-sm leading-10 h-10 right-2"> + {s.relevance} + </span> + )} + </LinkSuggestion> + ); + } else if (s.type === "text") { + return ( + <PlainText key={i} selected={i == selected}> + {s.prompt && ( + <span className="text-zinc-700 dark:text-zinc-400">{s.prompt}</span> + )} + <p>{s.suggestion}</p> + {devMode && ( + <span className="bottom-0 absolute text-zinc-700 dark:text-zinc-400 text-sm leading-10 h-10 right-2"> + {s.relevance} + </span> + )} + </PlainText> + ); + } else if (s.type === "inpage-link") { + return ( + <LinkSuggestion + key={i} + query={s.suggestion} + selected={i == selected} + inPage={true} + > + {s.prompt && ( + <span className="text-zinc-700 dark:text-zinc-400">{s.prompt}</span> + )} + {s.suggestion} + {devMode && ( + <span className="absolute text-zinc-700 dark:text-zinc-400 text-sm leading-10 h-10 right-2"> + {s.relevance} + </span> + )} + </LinkSuggestion> + ); + } + })} + </SuggestionBox> + ); } 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 ( - <div - className={`relative w-full h-10 leading-10 bg-zinc-300 dark:bg-zinc-700 +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 ( + <div + className={`relative w-full h-10 leading-10 bg-zinc-300 dark:bg-zinc-700 px-5 z-10 cursor-pointer duration-100 truncate`} - onClick={() => { - search(props.query, engine, newTab); - }} - > - {props.children} - </div> - ); - } - else { - return ( - <div - className={`relative w-full h-10 leading-10 bg-zinc-100 hover:bg-zinc-300 + onClick={() => { + search(props.query, engine, newTab); + }} + > + {props.children} + </div> + ); + } else { + return ( + <div + 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 truncate`} - onClick={() => { - search(props.query, engine, newTab); - }} - > - {props.children} - </div> - ); - } + onClick={() => { + search(props.query, engine, newTab); + }} + > + {props.children} + </div> + ); + } } 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 ( - <div - className={`relative w-full h-auto leading-6 break-all py-[0.6rem] bg-zinc-300 dark:bg-zinc-700 + if (props.selected) { + return ( + <div + className={`relative w-full h-auto leading-6 break-all py-[0.6rem] bg-zinc-300 dark:bg-zinc-700 px-5 z-10 cursor-pointer duration-100`} - > - {props.children} - </div> - ); - } else { - return ( - <div - className={`relative w-full h-auto leading-6 break-all py-[0.6rem] bg-zinc-100 hover:bg-zinc-300 + > + {props.children} + </div> + ); + } else { + return ( + <div + className={`relative w-full h-auto leading-6 break-all py-[0.6rem] bg-zinc-100 hover:bg-zinc-300 dark:bg-zinc-800 hover:dark:bg-zinc-700 px-5 z-10 cursor-pointer duration-100`} - > - {props.children} - </div> - ); - } + > + {props.children} + </div> + ); + } } 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 ( - <div dangerouslySetInnerHTML={{ __html: `<p>${props.children}</p>` 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`}> - </div> - ); -} \ No newline at end of file +export default function Suggestion(props: { children: React.ReactNode }) { + return ( + <div + dangerouslySetInnerHTML={{ __html: `<p>${props.children}</p>` 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`} + ></div> + ); +} 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 ( - <div - className={`relative bg-zinc-100 dark:bg-zinc-800 w-11/12 sm:w-[700px] h-auto max-h-[calc(100vh-20rem)] + return ( + <div + className={`relative bg-zinc-100 dark:bg-zinc-800 w-11/12 sm:w-[700px] h-auto max-h-[calc(100vh-20rem)] overflow-y-auto left-1/2 translate-x-[-50%] top-72 z-20 rounded-md overflow-hidden duration-250 dark:text-white ${props.children ? "opacity-100" : "opacity-0"}`} - > - {props.children} - </div> - ); + > + {props.children} + </div> + ); } 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<HTMLDivElement> { - 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<HTMLDivElement> = useRef(null); - const buttonRef: RefObject<HTMLButtonElement> = useRef(null); - const [displayList, setDisplayList] = useState(false); + const itemListRef: RefObject<HTMLDivElement> = useRef(null); + const buttonRef: RefObject<HTMLButtonElement> = 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 ( - <div {...rest}> - <button - className="relative border-2 border-gray-500 dark:border-gray-300 + const { displayContent, selectionOnChange, selectionItems, selected, ...rest } = props; + return ( + <div {...rest}> + <button + className="relative border-2 border-gray-500 dark:border-gray-300 rounded-xl dark:text-white px-4 py-2" - ref={buttonRef} - onClick={() => { - toggleDisplay(); - }} - > - {displayContent} - </button> - {displayList && ( - <PickerList - ref={itemListRef} - selected={selected} - selectionOnChange={selectionOnChange} - selectionItems={selectionItems} - toggleDisplay={toggleDisplay} - /> - )} - </div> - ); + ref={buttonRef} + onClick={() => { + toggleDisplay(); + }} + > + {displayContent} + </button> + {displayList && ( + <PickerList + ref={itemListRef} + selected={selected} + selectionOnChange={selectionOnChange} + selectionItems={selectionItems} + toggleDisplay={toggleDisplay} + /> + )} + </div> + ); } interface PickerListProps { - selected: selectionType; - selectionOnChange: selectedOnChange; - selectionItems: PickedItem; - toggleDisplay: Function; + selected: selectionType; + selectionOnChange: selectedOnChange; + selectionItems: PickedItem; + toggleDisplay: Function; } const PickerList = React.forwardRef<HTMLDivElement, PickerListProps>((props, ref) => { - const { selected, selectionOnChange, selectionItems, toggleDisplay } = props; + const { selected, selectionOnChange, selectionItems, toggleDisplay } = props; - return createPortal( - <div className="absolute w-screen h-screen" onClick={()=>{toggleDisplay(false)}}> - <div - ref={ref} - className="overflow-y-auto fixed w-fit text-black dark:text-white opacity-0 duration-200 + return createPortal( + <div + className="absolute w-screen h-screen" + onClick={() => { + toggleDisplay(false); + }} + > + <div + ref={ref} + className="overflow-y-auto fixed w-fit text-black dark:text-white opacity-0 duration-200 bg-zinc-50 shadow-lg border-1 border-zinc-200 dark:border-zinc-600 dark:bg-zinc-800 px-2 py-2 rounded-xl text-align-left" - style={{ transformOrigin: "top center" }} - > - {Object.keys(selectionItems).map((key: string, index) => { - return ( - <div - key={index} - className="relative py-2 w-full min-w-32 pl-2 cursor-pointer rounded-lg + style={{ transformOrigin: "top center" }} + > + {Object.keys(selectionItems).map((key: string, index) => { + return ( + <div + key={index} + className="relative py-2 w-full min-w-32 pl-2 cursor-pointer rounded-lg hover:bg-zinc-200 dark:hover:bg-zinc-700 flex justify-between items-center" - onClick={() => { - selectionOnChange(key); - toggleDisplay(false); - }} - > - <span>{selectionItems[key]}</span> - <div className="relative w-16"></div> - {key === selected && ( - <Icon className="relative right-2" icon="carbon:checkmark" /> - )} - </div> - ); - })} - </div> - </div>, - document.body - ); + onClick={() => { + selectionOnChange(key); + toggleDisplay(false); + }} + > + <span>{selectionItems[key]}</span> + <div className="relative w-16"></div> + {key === selected && ( + <Icon className="relative right-2" icon="carbon:checkmark" /> + )} + </div> + ); + })} + </div> + </div>, + 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<HTMLInputElement>(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<HTMLInputElement>(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 ( - // 祖传样式,勿动 - <div className="absolute w-full top-[8.5rem] lg:top-56 short:top-24 z-1 left-1/2 translate-x-[-50%] "> - <input - className="absolute z-1 w-11/12 sm:w-[700px] h-10 rounded-lg left-1/2 translate-x-[-50%] + if (style === "default") { + return ( + // 祖传样式,勿动 + <div className="absolute w-full top-[8.5rem] lg:top-56 short:top-24 z-1 left-1/2 translate-x-[-50%] "> + <input + className="absolute z-1 w-11/12 sm:w-[700px] h-10 rounded-lg left-1/2 translate-x-[-50%] text-center outline-none border-2 focus:border-2 duration-200 pr-2 shadow-md focus:shadow-none dark:shadow-zinc-800 dark:shadow-md bg-white dark:bg-[rgb(23,25,29)] dark:border-neutral-500 dark:focus:border-neutral-300 placeholder:text-slate-500 dark:placeholder:text-slate-400 text-slate-900 dark:text-white" - id="searchBox" - type="text" - placeholder={t("search.placeholder")} - onFocus={props.onFocus} - onKeyDown={handleKeydown} - onChange={(e) => - setQuery(() => { - return e.target.value; - }) - } - autoComplete="off" - autoCorrect="off" - autoCapitalize="off" - spellCheck="false" - ref={searchBoxRef} - value={query} - /> - </div> - ); - } else if (style == "image") { - return ( - // 祖传样式,勿动 - <div className="absolute w-full top-[8.5rem] lg:top-56 short:top-24 z-1 left-1/2 translate-x-[-50%] "> - <input - className={ - `absolute z-1 w-2/3 sm:w-80 md:w-[400px] focus:w-11/12 focus:sm:w-[700px] hover:w-11/12 + id="searchBox" + type="text" + placeholder={t("search.placeholder")} + onFocus={props.onFocus} + onKeyDown={handleKeydown} + onChange={(e) => + setQuery(() => { + return e.target.value; + }) + } + autoComplete="off" + autoCorrect="off" + autoCapitalize="off" + spellCheck="false" + ref={searchBoxRef} + value={query} + /> + </div> + ); + } else if (style == "image") { + return ( + // 祖传样式,勿动 + <div className="absolute w-full top-[8.5rem] lg:top-56 short:top-24 z-1 left-1/2 translate-x-[-50%] "> + <input + className={ + `absolute z-1 w-2/3 sm:w-80 md:w-[400px] focus:w-11/12 focus:sm:w-[700px] hover:w-11/12 hover:sm:w-[700px] h-10 rounded-3xl left-1/2 translate-x-[-50%] text-center outline-none border-solid border-0 duration-200 pr-2 shadow-md focus:shadow-none` + - (settings.bgBlur - ? `bg-[rgba(255,255,255,0.5)] dark:bg-[rgba(24,24,24,0.75)] backdrop-blur-xl + (settings.bgBlur + ? `bg-[rgba(255,255,255,0.5)] dark:bg-[rgba(24,24,24,0.75)] backdrop-blur-xl placeholder:text-slate-500 dark:placeholder:text-slate-400 text-slate-900 dark:text-white` - : `bg-[rgba(235,235,235,0.9)] dark:bg-[rgba(20,20,20,0.9)] placeholder:text-slate-500 + : `bg-[rgba(235,235,235,0.9)] dark:bg-[rgba(20,20,20,0.9)] placeholder:text-slate-500 text-slate-800 dark:text-white`) - } - id="searchBox" - type="text" - placeholder="placeholder" - onFocus={props.onFocus} - ref={searchBoxRef} - /> - </div> - ); - } + } + id="searchBox" + type="text" + placeholder="placeholder" + onFocus={props.onFocus} + ref={searchBoxRef} + /> + </div> + ); + } } 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 ( - <div onClick={() => props.onChange(props.key)}> - {props.children} - </div> - ) -} \ No newline at end of file +export default function SelectionItem(props: { + key: selectionType; + children: ReactNode; + onChange: selectedOnChange; +}) { + return <div onClick={() => props.onChange(props.key)}>{props.children}</div>; +} 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 ( - <div - className="absolute top-20 lg:top-44 short:top-0 translate-x-[-50%] + return ( + <div + className="absolute top-20 lg:top-44 short:top-0 translate-x-[-50%] left-1/2 w-11/12 sm:w-[700px] text:black dark:text-white text-3xl text-left text-shadow-lg" - > - {formatTime()}{" "} - <span className="text-lg leading-9 relative"> - {new Intl.DateTimeFormat(navigator.language, { - year: "numeric", - month: "short", - day: "numeric" - }).format(currentTime)} - </span> - </div> - ); + > + {formatTime()}{" "} + <span className="text-lg leading-9 relative"> + {new Intl.DateTimeFormat(navigator.language, { + year: "numeric", + month: "short", + day: "numeric" + }).format(currentTime)} + </span> + </div> + ); } 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. <br/>If SparkHome brought you to this page,<br/> please <a style=\"text-decoration:underline;\" href=\"mailto:contact@alikia2x.com\">contact us.</a>" - }, - "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. <br/>If SparkHome brought you to this page,<br/> please <a style=\"text-decoration:underline;\" href=\"mailto:contact@alikia2x.com\">contact us.</a>" + }, + "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": "请检查网址是否出错。 <br/>如果你从星火主页跳转到这里,<br/> 请 <a style=\"text-decoration:underline;\" href=\"mailto:contact@alikia2x.com\">联系我们</a>", - "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": "请检查网址是否出错。 <br/>如果你从星火主页跳转到这里,<br/> 请 <a style=\"text-decoration:underline;\" href=\"mailto:contact@alikia2x.com\">联系我们</a>", + "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 @@ -<!DOCTYPE html> +<!doctype html> <html> - <head> - <meta charset="UTF-8" /> - <link rel="icon" href="/favicon.ico" /> - <meta name="viewport" content="width=device-width, initial-scale=1.0" /> - <title>SparkHome</title> - </head> - <body> - <div id="root"></div> - <script type="module" src="/src/main.tsx"></script> - </body> + <head> + <meta charset="UTF-8" /> + <link rel="icon" href="/favicon.ico" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>SparkHome</title> + </head> + <body> + <div id="root"></div> + <script type="module" src="/src/main.tsx"></script> + </body> </html> 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<void>; - init(): Promise<this>; - process(lang: string, text: string): Promise<any>; + manager: any; + inited: boolean; + loadIntentionModel(): Promise<void>; + init(): Promise<this>; + process(lang: string, text: string): Promise<any>; } - 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<any> { - 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<any> { + 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<HTMLInputElement> + index: number, + suggestion: suggestionItem[], + _query: string, + settings: settingsType, + searchBoxRef: React.RefObject<HTMLInputElement> ) { - 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 ( - <div className="relative w-screen h-screen flex justify-center items-center"> - <div className="flex items-center"> - <h1 className="text-7xl font-thin">404</h1> - <div className="relative h-20 mx-4 w-[0.15rem] bg-black dark:bg-white"></div> - <div className="flex flex-col"> - <div className="uppercase text-3xl font-light">{t("notfound.title")}</div> - <div className="text-sm" dangerouslySetInnerHTML={{__html:t("notfound.desc")}}></div> - </div> - </div> - </div> - ); + const { t } = useTranslation(); + return ( + <div className="relative w-screen h-screen flex justify-center items-center"> + <div className="flex items-center"> + <h1 className="text-7xl font-thin">404</h1> + <div className="relative h-20 mx-4 w-[0.15rem] bg-black dark:bg-white"></div> + <div className="flex flex-col"> + <div className="uppercase text-3xl font-light">{t("notfound.title")}</div> + <div + className="text-sm" + dangerouslySetInnerHTML={{ __html: t("notfound.desc") }} + ></div> + </div> + </div> + </div> + ); } 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 ( - <AboutLayout> - <h1 className="text-4xl font-bold mb-6">About SparkHome</h1> - <div className="flex mb-8"> - <img src="/favicon.ico" className="relative w-20 h-20" /> - <div className="flex flex-col ml-4"> - <span className="leading-7 md:leading-9 text-3xl font-bold">SparkHome</span> - <p className="mt-2 leading-5 text-base md:text-xl"> - Made with <span className="text-red-500">♥️</span> by - <a className="underline text-red-500 mx-1" href="https://alikia2x.com"> - alikia2x - </a> - from Luminara Studio - </p> - </div> - </div> + return ( + <AboutLayout> + <h1 className="text-4xl font-bold mb-6">About SparkHome</h1> + <div className="flex mb-8"> + <img src="/favicon.ico" className="relative w-20 h-20" /> + <div className="flex flex-col ml-4"> + <span className="leading-7 md:leading-9 text-3xl font-bold">SparkHome</span> + <p className="mt-2 leading-5 text-base md:text-xl"> + Made with <span className="text-red-500">♥️</span> by + <a className="underline text-red-500 mx-1" href="https://alikia2x.com"> + alikia2x + </a> + from Luminara Studio + </p> + </div> + </div> - <Version title="Overall Version" version={getVersion()} versionClass="bg-red-500" /> - <Version - title="Browser NLU Model Version" - version={"Build " + clientNLUVersion} - versionClass="bg-purple-500" - /> - <Version - title="Backend API Version" - version={"/api/v" + apiVersion} - versionClass="bg-orange-500" - /> - <p className="flex items-center my-3"> - <span className="font-bold text-xl md:text-2xl mr-4 w-[36rem]">License</span> - <span - className="relative px-2 py-1 text-sm font-bold rounded-md text-nowrap underline + <Version title="Overall Version" version={getVersion()} versionClass="bg-red-500" /> + <Version + title="Browser NLU Model Version" + version={"Build " + clientNLUVersion} + versionClass="bg-purple-500" + /> + <Version + title="Backend API Version" + version={"/api/v" + apiVersion} + versionClass="bg-orange-500" + /> + <p className="flex items-center my-3"> + <span className="font-bold text-xl md:text-2xl mr-4 w-[36rem]">License</span> + <span + className="relative px-2 py-1 text-sm font-bold rounded-md text-nowrap underline bg-green-600 text-white" - > - <a href="/about/license">→ view</a> - </span> - </p> + > + <a href="/about/license">→ view</a> + </span> + </p> - <p className="relative font-bold text-2xl mt-12">Presented By</p> - {!darkMode && <img src="/LuminaraStudio.png" className="relative md:h-64 mt-6" />} - {darkMode && <img src="/LuminaraStudioDark.png" className="relative md:h-56 mt-6" />} - </AboutLayout> - ); + <p className="relative font-bold text-2xl mt-12">Presented By</p> + {!darkMode && <img src="/LuminaraStudio.png" className="relative md:h-64 mt-6" />} + {darkMode && <img src="/LuminaraStudioDark.png" className="relative md:h-56 mt-6" />} + </AboutLayout> + ); } function Version(props: { title: string; version: string; versionClass?: string }) { - document.title = "About SparkHome"; - return ( - <p className="flex items-center my-3"> - <span className="font-bold text-xl md:text-2xl mr-4 w-[36rem]">{props.title}</span> - <span - className={ - "relative px-2 py-1 text-sm font-bold rounded-md text-nowrap text-white " + - props.versionClass || "" - } - > - {props.version} - </span> - </p> - ); + document.title = "About SparkHome"; + return ( + <p className="flex items-center my-3"> + <span className="font-bold text-xl md:text-2xl mr-4 w-[36rem]">{props.title}</span> + <span + className={ + "relative px-2 py-1 text-sm font-bold rounded-md text-nowrap text-white " + + props.versionClass || "" + } + > + {props.version} + </span> + </p> + ); } 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 ( - <div className="h-screen w-screen overflow-x-hidden bg-white dark:bg-[rgb(23,25,29)]"> - <main className="relative h-full w-full md:w-3/4 lg:w-1/2 left-0 md:left-[12.5%] lg:left-1/4 - pt-12 px-3 md:px-0"> - {children} - </main> - </div> - ); + return ( + <div className="h-screen w-screen overflow-x-hidden bg-white dark:bg-[rgb(23,25,29)]"> + <main + className="relative h-full w-full md:w-3/4 lg:w-1/2 left-0 md:left-[12.5%] lg:left-1/4 + pt-12 px-3 md:px-0" + > + {children} + </main> + </div> + ); } 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 ( - <div className="dark:bg-[rgb(23,25,29)] dark:text-white min-h-screen w-screen overflow-x-hidden"> - <main - className="relative h-full w-full md:w-3/4 lg:w-1/2 left-0 md:left-[12.5%] lg:left-1/4 + return ( + <div className="dark:bg-[rgb(23,25,29)] dark:text-white min-h-screen w-screen overflow-x-hidden"> + <main + className="relative h-full w-full md:w-3/4 lg:w-1/2 left-0 md:left-[12.5%] lg:left-1/4 pt-12" - > - <h1 className="text-4xl font-bold mb-6">LICENSE</h1> - <div className="font-mono text-justify whitespace-break-spaces"> - {LICENSE} - </div> - </main> - </div> - ); -} \ No newline at end of file + > + <h1 className="text-4xl font-bold mb-6">LICENSE</h1> + <div className="font-mono text-justify whitespace-break-spaces">{LICENSE}</div> + </main> + </div> + ); +} 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 ( - <div className="h-screen w-screen overflow-x-hidden bg-white dark:bg-[rgb(23,25,29)]"> - <Background /> + return ( + <div className="h-screen w-screen overflow-x-hidden bg-white dark:bg-[rgb(23,25,29)]"> + <Background /> - <EngineSelector - className="absolute top-20 lg:top-44 short:top-0 translate-x-[-50%] translate-y-[-0.2rem] + <EngineSelector + className="absolute top-20 lg:top-44 short:top-0 translate-x-[-50%] translate-y-[-0.2rem] left-1/2 w-11/12 sm:w-[700px] text:black text-right dark:text-white text-shadow-lg z-10" - /> - <Search onFocus={() => setBgFocus(true)} /> - <Time showSecond={settings.timeShowSecond} /> - <OneSearch /> - </div> - ); + /> + <Search onFocus={() => setBgFocus(true)} /> + <Time showSecond={settings.timeShowSecond} /> + <OneSearch /> + </div> + ); } 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: <Homepage /> - }, - { - path: "about", - element: <AboutPage />, - children: [ - { - path: "license", - element: <LicensePage /> - } - ] - } + { + path: "/", + element: <Homepage /> + }, + { + path: "about", + element: <AboutPage />, + children: [ + { + path: "license", + element: <LicensePage /> + } + ] + } ]); export function App() { - return ( - <div className="relative bg-white dark:bg-black dark:text-white min-h-screen w-screen"> - <RouterProvider router={router} /> - </div> - ); + return ( + <div className="relative bg-white dark:bg-black dark:text-white min-h-screen w-screen"> + <RouterProvider router={router} /> + </div> + ); } 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( - <StrictMode> - <NextUIProvider> - <App /> - </NextUIProvider> - </StrictMode> + <StrictMode> + <NextUIProvider> + <App /> + </NextUIProvider> + </StrictMode> ); 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 @@ /// <reference types="vite/client" /> -/// <reference types="vite-plugin-pages/client-react" /> \ No newline at end of file +/// <reference types="vite-plugin-pages/client-react" /> 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()] });