ref: format

This commit is contained in:
alikia2x (寒寒) 2024-09-21 21:31:08 +08:00
parent 35f90b42d9
commit d97e678206
Signed by: alikia2x
GPG Key ID: 56209E0CCD8420C6
72 changed files with 2240 additions and 2190 deletions

View File

@ -1,5 +1,5 @@
{ {
"useTabs": false, "useTabs": true,
"tabWidth": 4, "tabWidth": 4,
"trailingComma": "none", "trailingComma": "none",
"singleQuote": false, "singleQuote": false,

View File

@ -2,10 +2,10 @@ import { Express } from "express";
import { completeGoogle } from "search-engine-autocomplete"; import { completeGoogle } from "search-engine-autocomplete";
export function configureBackendRoutes(app: Express) { export function configureBackendRoutes(app: Express) {
app.get('/api/v1/suggestion', async (req, res) => { app.get("/api/v1/suggestion", async (req, res) => {
const query = req.query.q as string; const query = req.query.q as string;
const t = parseInt(req.query.t as string || "0") || null; const t = parseInt((req.query.t as string) || "0") || null;
let language = req.query.l as string || 'en-US'; let language = (req.query.l as string) || "en-US";
try { try {
const data = await completeGoogle(query, language); const data = await completeGoogle(query, language);
@ -13,10 +13,10 @@ export function configureBackendRoutes(app: Express) {
res.json({ ...data, time: t }); res.json({ ...data, time: t });
} catch (error) { } catch (error) {
//logger.error({ type: "onesearch_search_autocomplete_error", error: error.message }); //logger.error({ type: "onesearch_search_autocomplete_error", error: error.message });
res.status(500).json({ error: 'Internal Server Error' }); res.status(500).json({ error: "Internal Server Error" });
} }
}); });
app.get("/api/v1/ping", async (_, res) => { app.get("/api/v1/ping", async (_, res) => {
res.status(200).json({message: "pong"}); res.status(200).json({ message: "pong" });
}) });
} }

BIN
bun.lockb

Binary file not shown.

View File

@ -11,9 +11,19 @@ export default function Background() {
return ( return (
<div> <div>
{darkMode ? ( {darkMode ? (
<BackgroundContainer src="rgb(23,25,29)" isFocus={isFocus} onClick={() => setFocus(false)} darkMode={darkMode}/> <BackgroundContainer
src="rgb(23,25,29)"
isFocus={isFocus}
onClick={() => setFocus(false)}
darkMode={darkMode}
/>
) : ( ) : (
<BackgroundContainer src="white" isFocus={isFocus} onClick={() => setFocus(false)} darkMode={darkMode}/> <BackgroundContainer
src="white"
isFocus={isFocus}
onClick={() => setFocus(false)}
darkMode={darkMode}
/>
)} )}
</div> </div>
); );

View File

@ -24,7 +24,11 @@ export default function BackgroundContainer(props: {
src={props.src} src={props.src}
className={ className={
"w-full h-full fixed object-cover inset-0 duration-200 z-0 " + "w-full h-full fixed object-cover inset-0 duration-200 z-0 " +
(props.isFocus ? (settings.bgBlur ? "blur-lg scale-110" : "brightness-50 scale-105") : "") (props.isFocus
? settings.bgBlur
? "blur-lg scale-110"
: "brightness-50 scale-105"
: "")
} }
alt="background" alt="background"
onClick={props.onClick} onClick={props.onClick}

View File

@ -6,16 +6,14 @@ import { WMOCodeTable } from "lib/weather/wmocode";
type UpdateSuggestionFunction = (data: suggestionItem[]) => void; type UpdateSuggestionFunction = (data: suggestionItem[]) => void;
export function handleNLUResult(result: any, updateSuggestion: UpdateSuggestionFunction){ export function handleNLUResult(result: any, updateSuggestion: UpdateSuggestionFunction) {
if (result.intent == "weather.summary") { if (result.intent == "weather.summary") {
getLocationNative((data: GeolocationCoordinates | GeolocationPositionError) => { getLocationNative((data: GeolocationCoordinates | GeolocationPositionError) => {
console.log(data); console.log(data);
if (data instanceof GeolocationCoordinates) { if (data instanceof GeolocationCoordinates) {
getWeather(data.latitude, data.longitude).then((weather) => { getWeather(data.latitude, data.longitude).then((weather) => {
console.log(weather["hourly"]); console.log(weather["hourly"]);
let hourIndex = findClosestDateIndex( let hourIndex = findClosestDateIndex(weather["hourly"]["time"]);
weather["hourly"]["time"]
);
let temp = weather["hourly"]["apparent_temperature"][hourIndex]; let temp = weather["hourly"]["apparent_temperature"][hourIndex];
let weatherCode = weather["hourly"]["weather_code"][hourIndex]; let weatherCode = weather["hourly"]["weather_code"][hourIndex];
console.log(temp, weatherCode, hourIndex); console.log(temp, weatherCode, hourIndex);

View File

@ -2,7 +2,11 @@ import { useAtomValue } from "jotai";
import search from "lib/search"; import search from "lib/search";
import { settingsAtom } from "lib/state/settings"; import { settingsAtom } from "lib/state/settings";
export default function PlainSearch(props: { children: React.ReactNode; query: string; selected: boolean }) { export default function PlainSearch(props: {
children: React.ReactNode;
query: string;
selected: boolean;
}) {
const settings = useAtomValue(settingsAtom); const settings = useAtomValue(settingsAtom);
const engine = settings.searchEngines[settings.currentSearchEngine]; const engine = settings.searchEngines[settings.currentSearchEngine];
const newTab = settings.searchInNewTab; const newTab = settings.searchInNewTab;
@ -18,8 +22,7 @@ export default function PlainSearch(props: { children: React.ReactNode; query: s
{props.children} {props.children}
</div> </div>
); );
} } else {
else {
return ( return (
<div <div
className={`relative w-full h-10 leading-10 bg-zinc-100 hover:bg-zinc-300 className={`relative w-full h-10 leading-10 bg-zinc-100 hover:bg-zinc-300

View File

@ -1,6 +1,8 @@
export default function Suggestion(props: { children: React.ReactNode }) { export default function Suggestion(props: { children: React.ReactNode }) {
return ( 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
</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>
); );
} }

View File

@ -142,7 +142,12 @@ const PickerList = React.forwardRef<HTMLDivElement, PickerListProps>((props, ref
const { selected, selectionOnChange, selectionItems, toggleDisplay } = props; const { selected, selectionOnChange, selectionItems, toggleDisplay } = props;
return createPortal( return createPortal(
<div className="absolute w-screen h-screen" onClick={()=>{toggleDisplay(false)}}> <div
className="absolute w-screen h-screen"
onClick={() => {
toggleDisplay(false);
}}
>
<div <div
ref={ref} ref={ref}
className="overflow-y-auto fixed w-fit text-black dark:text-white opacity-0 duration-200 className="overflow-y-auto fixed w-fit text-black dark:text-white opacity-0 duration-200

View File

@ -3,10 +3,10 @@ import { selectionType } from "./picker";
export type selectedOnChange = (target: selectionType) => void; export type selectedOnChange = (target: selectionType) => void;
export default function SelectionItem(props: {key: selectionType, children: ReactNode, onChange: selectedOnChange}){ export default function SelectionItem(props: {
return ( key: selectionType;
<div onClick={() => props.onChange(props.key)}> children: ReactNode;
{props.children} onChange: selectedOnChange;
</div> }) {
) return <div onClick={() => props.onChange(props.key)}>{props.children}</div>;
} }

View File

@ -2,9 +2,7 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
export default function Time(props: { export default function Time(props: { showSecond: boolean }) {
showSecond: boolean
}) {
const [currentTime, setCurrentTime] = useState(new Date()); const [currentTime, setCurrentTime] = useState(new Date());
useEffect(() => { useEffect(() => {

48
global.d.ts vendored
View File

@ -1,31 +1,31 @@
import { Suggestion } from "search-engine-autocomplete"; import { Suggestion } from "search-engine-autocomplete";
interface settingsType extends object{ interface settingsType extends object {
"version": number, version: number;
"elementBackdrop": boolean, elementBackdrop: boolean;
"bgBlur": boolean, bgBlur: boolean;
"timeShowSecond": boolean, timeShowSecond: boolean;
"currentSearchEngine": string, currentSearchEngine: string;
"searchInNewTab": boolean, searchInNewTab: boolean;
"searchEngines": { searchEngines: {
[key: string]: string [key: string]: string;
}, };
} }
interface suggestionsResponse extends object{ interface suggestionsResponse extends object {
suggestions: Suggestion[], suggestions: Suggestion[];
query: string, query: string;
verbatimRelevance: number, verbatimRelevance: number;
time: number time: number;
} }
type suggestionItem = { type suggestionItem = {
suggestion: string, suggestion: string;
type: string, type: string;
relativeRelevance?: number, relativeRelevance?: number;
relevance: number, relevance: number;
prompt?: string | React.ReactElement, prompt?: string | React.ReactElement;
intention?: string | null, intention?: string | null;
probability?: number, probability?: number;
confidence?: number, confidence?: number;
} };

View File

@ -1,5 +1,5 @@
{ {
"search" : { "search": {
"placeholder" : "ابحث أو اكتب عنوان URL" "placeholder": "ابحث أو اكتب عنوان URL"
} }
} }

View File

@ -1,5 +1,5 @@
{ {
"search" : { "search": {
"placeholder" : "Suche oder gib eine URL ein" "placeholder": "Suche oder gib eine URL ein"
} }
} }

View File

@ -1,5 +1,5 @@
{ {
"search" : { "search": {
"placeholder" : "Buscar o escribir una URL" "placeholder": "Buscar o escribir una URL"
} }
} }

View File

@ -1,5 +1,5 @@
{ {
"search" : { "search": {
"placeholder" : "Rechercher ou saisir une URL" "placeholder": "Rechercher ou saisir une URL"
} }
} }

View File

@ -1,5 +1,5 @@
{ {
"search" : { "search": {
"placeholder" : "Cerca o digita un URL" "placeholder": "Cerca o digita un URL"
} }
} }

View File

@ -1,5 +1,5 @@
{ {
"search" : { "search": {
"placeholder" : "검색 또는 URL 입력" "placeholder": "검색 또는 URL 입력"
} }
} }

View File

@ -1,5 +1,5 @@
{ {
"search" : { "search": {
"placeholder" : "Pesquisar ou digitar uma URL" "placeholder": "Pesquisar ou digitar uma URL"
} }
} }

View File

@ -1,5 +1,5 @@
{ {
"search" : { "search": {
"placeholder" : "Искать или ввести URL" "placeholder": "Искать или ввести URL"
} }
} }

View File

@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />

View File

@ -1,4 +1,4 @@
export default function copyToClipboard(value: string){ export default function copyToClipboard(value: string) {
const textarea = document.createElement("textarea"); const textarea = document.createElement("textarea");
textarea.value = value; textarea.value = value;
document.body.appendChild(textarea); document.body.appendChild(textarea);

View File

@ -137,11 +137,5 @@
"Which framework do you think is the most suitable for performance sensitive projects?" "Which framework do you think is the most suitable for performance sensitive projects?"
], ],
"None": [ "None": ["free weather api", "js get timezone", "how", "how's", "how's the"]
"free weather api",
"js get timezone",
"how",
"how's",
"how's the"
]
} }

View File

@ -117,11 +117,5 @@
"HTML实体转文本%s" "HTML实体转文本%s"
], ],
"None": [ "None": ["你好", "为什么计算机使用二进制", "什么是", "热", "怎么"]
"你好",
"为什么计算机使用二进制",
"什么是",
"热",
"怎么"
]
} }

View File

@ -8,7 +8,7 @@ import { NluManager, NluNeural } from "@nlpjs/nlu";
import { LangEn } from "@nlpjs/lang-en-min"; import { LangEn } from "@nlpjs/lang-en-min";
// @ts-ignore // @ts-ignore
import { LangZh } from "@nlpjs/lang-zh"; import { LangZh } from "@nlpjs/lang-zh";
import * as fflate from 'fflate'; import * as fflate from "fflate";
export interface NLUType { export interface NLUType {
manager: any; manager: any;
@ -18,7 +18,6 @@ export interface NLUType {
process(lang: string, text: string): Promise<any>; process(lang: string, text: string): Promise<any>;
} }
export class NLU { export class NLU {
manager: any; manager: any;
inited: boolean = false; inited: boolean = false;

View File

@ -9,7 +9,7 @@ import { LangEn } from "@nlpjs/lang-en-min";
// @ts-ignore // @ts-ignore
import { LangZh } from "@nlpjs/lang-zh"; import { LangZh } from "@nlpjs/lang-zh";
import fs from "node:fs"; import fs from "node:fs";
import * as fflate from 'fflate'; import * as fflate from "fflate";
let zh: TrainData = {}; let zh: TrainData = {};
let en: TrainData = {}; let en: TrainData = {};
@ -61,7 +61,7 @@ export async function trainIntentionModel() {
const buf = fflate.strToU8(JSON.stringify(resultModel)); const buf = fflate.strToU8(JSON.stringify(resultModel));
const gzipped = fflate.gzipSync(buf, { const gzipped = fflate.gzipSync(buf, {
filename: 'model.json', filename: "model.json",
mtime: new Date().getTime() mtime: new Date().getTime()
}); });

View File

@ -4,7 +4,7 @@ import { settingsType } from "global";
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
export default function(){ export default function () {
const settings: settingsType = useAtomValue(settingsAtom); const settings: settingsType = useAtomValue(settingsAtom);
const currentEngine = settings.currentSearchEngine; const currentEngine = settings.currentSearchEngine;
const displayEngine = getName(currentEngine); const displayEngine = getName(currentEngine);

View File

@ -21,7 +21,7 @@ export function keywordSuggestion(query: string) {
prompt: keyword, prompt: keyword,
relevance: 3000 relevance: 3000
}; };
return result return result;
} }
} }
for (const keyword in dict_en) { for (const keyword in dict_en) {
@ -32,7 +32,7 @@ export function keywordSuggestion(query: string) {
prompt: keyword, prompt: keyword,
relevance: 3000 relevance: 3000
}; };
return result return result;
} }
} }
return null; return null;

View File

@ -1 +1,9 @@
export const engineTranslation = ["google", "bing", "baidu", "duckduckgo", "yandex", "ecosia", "yahoo"]; export const engineTranslation = [
"google",
"bing",
"baidu",
"duckduckgo",
"yandex",
"ecosia",
"yahoo"
];

View File

@ -1,4 +1,4 @@
export default function(query: string, engine: string, newTab: boolean = true) { export default function (query: string, engine: string, newTab: boolean = true) {
if(newTab) window.open(engine.replace("%s", query)); if (newTab) window.open(engine.replace("%s", query));
else window.location.href = engine.replace("%s", query); else window.location.href = engine.replace("%s", query);
} }

View File

@ -1,24 +1,24 @@
import { settingsType } from "global"; import { settingsType } from "global";
import { atomWithStorage } from 'jotai/utils' import { atomWithStorage } from "jotai/utils";
const defaultSettings: settingsType = { const defaultSettings: settingsType = {
"version": 2, version: 2,
"elementBackdrop": true, elementBackdrop: true,
"bgBlur": true, bgBlur: true,
"timeShowSecond": false, timeShowSecond: false,
"currentSearchEngine": "google", currentSearchEngine: "google",
"searchInNewTab": true, searchInNewTab: true,
"searchEngines": { searchEngines: {
"google": "https://www.google.com/search?q=%s", google: "https://www.google.com/search?q=%s",
"bing": "https://www.bing.com/search?q=%s", bing: "https://www.bing.com/search?q=%s",
"baidu": "https://www.baidu.com/s?wd=%s", baidu: "https://www.baidu.com/s?wd=%s",
"duckduckgo": "https://duckduckgo.com/?q=%s", duckduckgo: "https://duckduckgo.com/?q=%s",
"yandex": "https://yandex.com/search/?text=%s", yandex: "https://yandex.com/search/?text=%s",
"yahoo": "https://search.yahoo.com/search?p=%s", yahoo: "https://search.yahoo.com/search?p=%s",
"ecosia": "https://www.ecosia.org/search?q=%s" ecosia: "https://www.ecosia.org/search?q=%s"
} }
}; };
const settingsAtom = atomWithStorage('settings', defaultSettings); const settingsAtom = atomWithStorage("settings", defaultSettings);
export { settingsAtom }; export { settingsAtom };

View File

@ -1,4 +1,4 @@
import pjson from "package.json" import pjson from "package.json";
const CLIENT_VERSION = pjson.version; const CLIENT_VERSION = pjson.version;
@ -11,11 +11,11 @@ export function sendError(error: Error) {
body: JSON.stringify({ body: JSON.stringify({
message: error.message, message: error.message,
name: error.name, name: error.name,
time: new Date().getTime()/1000, time: new Date().getTime() / 1000,
version: CLIENT_VERSION, version: CLIENT_VERSION,
ua: navigator.userAgent, ua: navigator.userAgent,
cause: error.cause, cause: error.cause,
stack: error.stack stack: error.stack
}) })
}) });
} }

View File

@ -1,5 +1,5 @@
import TLDtxt from "./tlds.txt?raw" import TLDtxt from "./tlds.txt?raw";
export function getTLD(){ export function getTLD() {
return TLDtxt.split("\n").filter((line) => line[0] !== "#") return TLDtxt.split("\n").filter((line) => line[0] !== "#");
} }

View File

@ -1,6 +1,6 @@
import * as pjson from "package.json"; import * as pjson from "package.json";
export default function getVersion(){ export default function getVersion() {
return pjson.version; return pjson.version;
} }

View File

@ -4,9 +4,9 @@ export function getClosestHourTimestamp(): string {
// 获取本地时间的年份、月份、日期、小时 // 获取本地时间的年份、月份、日期、小时
const year = now.getFullYear(); const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份从0开始 const month = String(now.getMonth() + 1).padStart(2, "0"); // 月份从0开始
const day = String(now.getDate()).padStart(2, '0'); const day = String(now.getDate()).padStart(2, "0");
const hour = String(now.getHours()).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`;

View File

@ -3,15 +3,16 @@ export async function getWeather(lat: number, lon: number) {
const cacheKey = `weather-cache-${lat.toFixed(2)}-${lon.toFixed(2)}-${timezone}`; const cacheKey = `weather-cache-${lat.toFixed(2)}-${lon.toFixed(2)}-${timezone}`;
const localData = localStorage.getItem(cacheKey); const localData = localStorage.getItem(cacheKey);
if (localData != null) { if (localData != null) {
console.log('Using cache'); console.log("Using cache");
const parsedLocalData = JSON.parse(localData); const parsedLocalData = JSON.parse(localData);
if (parsedLocalData["hourly"]["time"][0] != undefined && if (
new Date().getTime() - new Date(parsedLocalData["hourly"]["time"][0]).getTime() < 86400 * 1000 parsedLocalData["hourly"]["time"][0] != undefined &&
new Date().getTime() - new Date(parsedLocalData["hourly"]["time"][0]).getTime() <
86400 * 1000
) { ) {
return parsedLocalData; return parsedLocalData;
} } else {
else { console.log("Cache expired");
console.log('Cache expired');
localStorage.removeItem(cacheKey); localStorage.removeItem(cacheKey);
} }
} }

View File

@ -8,7 +8,8 @@
"build": "bun license-gen && tsc -b && vite build", "build": "bun license-gen && tsc -b && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "NODE_ENV=production bun server.ts", "preview": "NODE_ENV=production bun server.ts",
"license-gen": "bunx generate-license-file --input package.json --output lib/license.txt --overwrite" "license-gen": "bunx generate-license-file --input package.json --output lib/license.txt --overwrite",
"format": "prettier --write ."
}, },
"dependencies": { "dependencies": {
"@iconify/react": "^5.0.1", "@iconify/react": "^5.0.1",
@ -51,6 +52,7 @@
"eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.7", "eslint-plugin-react-refresh": "^0.4.7",
"postcss": "^8.4.38", "postcss": "^8.4.38",
"prettier": "^3.3.3",
"tailwindcss": "^3.4.4", "tailwindcss": "^3.4.4",
"typescript": "^5.2.2", "typescript": "^5.2.2",
"vite": "^5.3.1", "vite": "^5.3.1",

View File

@ -9,7 +9,10 @@ export default function NotFound() {
<div className="relative h-20 mx-4 w-[0.15rem] bg-black dark:bg-white"></div> <div className="relative h-20 mx-4 w-[0.15rem] bg-black dark:bg-white"></div>
<div className="flex flex-col"> <div className="flex flex-col">
<div className="uppercase text-3xl font-light">{t("notfound.title")}</div> <div className="uppercase text-3xl font-light">{t("notfound.title")}</div>
<div className="text-sm" dangerouslySetInnerHTML={{__html:t("notfound.desc")}}></div> <div
className="text-sm"
dangerouslySetInnerHTML={{ __html: t("notfound.desc") }}
></div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,8 +1,10 @@
export default function AboutLayout({ children }: { children: React.ReactNode }) { export default function AboutLayout({ children }: { children: React.ReactNode }) {
return ( return (
<div className="h-screen w-screen overflow-x-hidden bg-white dark:bg-[rgb(23,25,29)]"> <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 <main
pt-12 px-3 md:px-0"> 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} {children}
</main> </main>
</div> </div>

View File

@ -8,9 +8,7 @@ export default function LicensePage() {
pt-12" pt-12"
> >
<h1 className="text-4xl font-bold mb-6">LICENSE</h1> <h1 className="text-4xl font-bold mb-6">LICENSE</h1>
<div className="font-mono text-justify whitespace-break-spaces"> <div className="font-mono text-justify whitespace-break-spaces">{LICENSE}</div>
{LICENSE}
</div>
</main> </main>
</div> </div>
); );

View File

@ -6,7 +6,6 @@ import { networkInterfaces } from "os";
import cac from "cac"; import cac from "cac";
import { configureBackendRoutes } from "./backend/route"; import { configureBackendRoutes } from "./backend/route";
async function helloMessage() { async function helloMessage() {
const { base } = await ViteExpress.getViteConfig(); const { base } = await ViteExpress.getViteConfig();
const timeCost = new Date().getTime() - start.getTime(); const timeCost = new Date().getTime() - start.getTime();
@ -19,13 +18,29 @@ async function helloMessage() {
`${Math.round(timeCost)} ms` `${Math.round(timeCost)} ms`
); );
console.log(""); console.log("");
console.log(" ", chalk.redBright("➜ "), "Local:\t", chalk.cyan(`http://${host}:${port}${base}`)); console.log(
" ",
chalk.redBright("➜ "),
"Local:\t",
chalk.cyan(`http://${host}:${port}${base}`)
);
if (host !== "localhost") { if (host !== "localhost") {
for (const ip of ips) { for (const ip of ips) {
console.log(" ", chalk.redBright("➜ "), "Network:\t", chalk.cyan(`http://${ip}:${port}${base}`)); 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")) console.log(
" ",
chalk.red("➜ "),
chalk.whiteBright("press"),
"h + enter",
chalk.whiteBright("to show help")
);
} }
async function handleInput() { async function handleInput() {
@ -33,14 +48,24 @@ async function handleInput() {
switch (line) { switch (line) {
case "h": case "h":
console.log(" Shortcuts"); console.log(" Shortcuts");
console.log(" ", chalk.whiteBright("press"), "c + enter ", chalk.whiteBright("to clear console")); console.log(
console.log(" ", chalk.whiteBright("press"), "q + enter ", chalk.whiteBright("to quit")); " ",
chalk.whiteBright("press"),
"c + enter ",
chalk.whiteBright("to clear console")
);
console.log(
" ",
chalk.whiteBright("press"),
"q + enter ",
chalk.whiteBright("to quit")
);
break; break;
case "c": case "c":
console.clear(); console.clear();
break; break;
case "q": case "q":
server.on("vite:close", ()=>{}); server.on("vite:close", () => {});
server.close(); server.close();
return; return;
default: default:
@ -71,11 +96,15 @@ const app = express();
const port = 3000; const port = 3000;
let host = "localhost"; let host = "localhost";
cli.option("--host [host]", "Sepcify host name") cli.option("--host [host]", "Sepcify host name");
cli.help() cli.help();
cli.version(pjson.version); cli.version(pjson.version);
const parsed = cli.parse(); const parsed = cli.parse();
if (parsed.options.host!==undefined && typeof parsed.options.host == "boolean" && parsed.options.host) { if (
parsed.options.host !== undefined &&
typeof parsed.options.host == "boolean" &&
parsed.options.host
) {
host = "0.0.0.0"; host = "0.0.0.0";
} }

View File

@ -1,18 +1,18 @@
import * as en from "i18n/en.json" import * as en from "i18n/en.json";
import * as zh from "i18n/zh.json" import * as zh from "i18n/zh.json";
import * as ja from "i18n/ja.json" import * as ja from "i18n/ja.json";
import * as ar from "i18n/ar.json" import * as ar from "i18n/ar.json";
import * as de from "i18n/de.json" import * as de from "i18n/de.json";
import * as es from "i18n/es.json" import * as es from "i18n/es.json";
import * as fr from "i18n/fr.json" import * as fr from "i18n/fr.json";
import * as it from "i18n/it.json" import * as it from "i18n/it.json";
import * as ko from "i18n/ko.json" import * as ko from "i18n/ko.json";
import * as pt from "i18n/pt.json" import * as pt from "i18n/pt.json";
import * as ru from "i18n/ru.json" import * as ru from "i18n/ru.json";
import i18n from "i18next"; import i18n from "i18next";
import { initReactI18next } from "react-i18next"; import { initReactI18next } from "react-i18next";
import LanguageDetector from 'i18next-browser-languagedetector'; import LanguageDetector from "i18next-browser-languagedetector";
import ICU from 'i18next-icu'; import ICU from "i18next-icu";
i18n.use(initReactI18next) // passes i18n down to react-i18next i18n.use(initReactI18next) // passes i18n down to react-i18next
.use(LanguageDetector) .use(LanguageDetector)
.use(ICU) .use(ICU)
@ -59,7 +59,7 @@ i18n.use(initReactI18next) // passes i18n down to react-i18next
}, },
detection: { detection: {
order: ['navigator'], order: ["navigator"],
caches: [] caches: []
} }
}); });

View File

@ -8,9 +8,13 @@ describe("Check if a string is an accessible domain/URL/IP", () => {
// With https and path // With https and path
expect(validLink("https://jestjs.io/docs/getting-started/")).toBe(true); expect(validLink("https://jestjs.io/docs/getting-started/")).toBe(true);
// With anchor // With anchor
expect(validLink("https://difftastic.wilfred.me.uk/zh-CN/git.html#git-difftool")).toBe(true); expect(validLink("https://difftastic.wilfred.me.uk/zh-CN/git.html#git-difftool")).toBe(
true
);
// With params // With params
expect(validLink("https://www.bilibili.com/list/ml2252204359?oid=990610203&bvid=BV1sx4y1g7Hh")).toBe(true); expect(
validLink("https://www.bilibili.com/list/ml2252204359?oid=990610203&bvid=BV1sx4y1g7Hh")
).toBe(true);
}); });
test("Punycode URL", () => { test("Punycode URL", () => {
expect(validLink("https://原神大学.com/")).toBe(true); expect(validLink("https://原神大学.com/")).toBe(true);
@ -48,7 +52,7 @@ describe("Check if the given TLD exist and assigned.", () => {
// they really exist! // they really exist!
expect(validTLD("example.foo")).toBe(true); expect(validTLD("example.foo")).toBe(true);
expect(validTLD("example.bar")).toBe(true); expect(validTLD("example.bar")).toBe(true);
expect(validTLD('example.zip')).toBe(true); expect(validTLD("example.zip")).toBe(true);
}); });
test("Exist but not assigned TLD", () => { test("Exist but not assigned TLD", () => {
expect(validTLD("example.active")).toBe(false); expect(validTLD("example.active")).toBe(false);

View File

@ -1,13 +1,9 @@
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc"; import react from "@vitejs/plugin-react-swc";
import tsconfigPaths from 'vite-tsconfig-paths'; import tsconfigPaths from "vite-tsconfig-paths";
import { chunkSplitPlugin } from 'vite-plugin-chunk-split'; import { chunkSplitPlugin } from "vite-plugin-chunk-split";
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [react(), tsconfigPaths(), chunkSplitPlugin()]
react(),
tsconfigPaths(),
chunkSplitPlugin()
]
}); });