feature: about page, some enhancement.

This commit is contained in:
alikia2x 2024-07-14 03:44:03 +08:00
parent 858a18bf4e
commit 2c1f4f28bf
18 changed files with 10453 additions and 103 deletions

22
backend/route.ts Normal file
View File

@ -0,0 +1,22 @@
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"});
})
}

BIN
bun.lockb

Binary file not shown.

View File

@ -1,26 +1,12 @@
import { useEffect, useState } from "react";
import { useAtom } from "jotai";
import { bgFocusAtom } from "../lib/state/background";
import BackgroundContainer from "./backgroundContainer";
import useDarkMode from "lib/darkModeHook";
export default function Background() {
const [isFocus, setFocus] = useAtom(bgFocusAtom);
const [darkMode, setDarkMode] = useState(false);
useEffect(() => {
const colorSchemeQueryList = window.matchMedia("(prefers-color-scheme: dark)");
setDarkMode(colorSchemeQueryList.matches ? true : false);
const handleChange = () => {
setDarkMode(colorSchemeQueryList.matches ? true : false);
};
colorSchemeQueryList.addEventListener("change", handleChange);
return () => {
colorSchemeQueryList.removeEventListener("change", handleChange);
};
}, []);
const darkMode = useDarkMode();
return (
<div>

View File

@ -36,7 +36,7 @@ export default function OneSearch() {
cleanSuggestion("QUERY", "NAVIGATION");
return;
}
fetch(`/api/suggestion?q=${query}&l=${lang}&t=${time}&engine=${engine}`)
fetch(`/api/v1/suggestion?q=${query}&l=${lang}&t=${time}&engine=${engine}`)
.then((res) => res.json())
.then((data: suggestionsResponse) => {
try {

View File

@ -1,10 +1,10 @@
<!doctype html>
<html lang="en">
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<title>SparkHome</title>
</head>
<body>
<div id="root"></div>

23
lib/darkModeHook.ts Normal file
View File

@ -0,0 +1,23 @@
import { useState, useEffect } from "react";
// Custom React Hook for dark mode detect
export default function useDarkMode() {
const [darkMode, setDarkMode] = useState(false);
useEffect(() => {
const colorSchemeQueryList = window.matchMedia("(prefers-color-scheme: dark)");
setDarkMode(colorSchemeQueryList.matches ? true : false);
const handleChange = () => {
setDarkMode(colorSchemeQueryList.matches ? true : false);
};
colorSchemeQueryList.addEventListener("change", handleChange);
return () => {
colorSchemeQueryList.removeEventListener("change", handleChange);
};
}, []);
return darkMode;
}

10246
lib/license.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -10,13 +10,6 @@ import { LangEn } from "@nlpjs/lang-en-min";
import { LangZh } from "@nlpjs/lang-zh";
import * as fflate from 'fflate';
let zh: TrainData = {};
let en: TrainData = {};
type TrainData = {
[key: string]: string[];
};
export class NLU {
manager: any;
inited: boolean = false;
@ -41,7 +34,6 @@ export class NLU {
const modelText = fflate.strFromU8(decompressed);
manager.fromJSON(JSON.parse(modelText));
this.manager = manager;
// console.log(this.manager);
}
async init() {
await this.loadIntentionModel();

View File

@ -3,7 +3,7 @@ import pjson from "package.json"
const CLIENT_VERSION = pjson.version;
export function sendError(error: Error) {
fetch("/api/error", {
fetch("/api/v1/error", {
method: "POST",
headers: {
"Content-Type": "application/json"

8
lib/version.ts Normal file
View File

@ -0,0 +1,8 @@
import * as pjson from "package.json";
export default function getVersion(){
return pjson.version;
}
export const clientNLUVersion = 2;
export const apiVersion = 1;

View File

@ -1,7 +1,7 @@
{
"name": "sparkhome",
"private": false,
"version": "5.2.3",
"version": "5.3.0",
"type": "module",
"scripts": {
"dev": "bun server.ts",
@ -12,6 +12,7 @@
"dependencies": {
"@iconify/react": "^5.0.1",
"@nextui-org/react": "^2.4.2",
"@types/bun": "^1.1.6",
"@types/express": "^4.17.21",
"cac": "^6.7.14",
"chalk": "^5.3.0",

69
pages/about/index.tsx Normal file
View File

@ -0,0 +1,69 @@
import useDarkMode from "lib/darkModeHook";
import getVersion, { apiVersion, clientNLUVersion } from "lib/version";
export default function AboutPage() {
const darkMode = useDarkMode();
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">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="text-3xl font-bold">SparkHome</span>
<p className="mt-2 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={"/v" + apiVersion}
versionClass="bg-orange-500"
/>
<p className="flex items-center my-3">
<span className="font-bold 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">
<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 h-56 mt-6" />}
{darkMode && <img src="/LuminaraStudioDark.png" className="relative h-56 mt-6" />}
</main>
</div>
);
}
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-2xl mr-4 w-[36rem]">{props.title}</span>
<span
className={
"relative px-2 py-1 text-sm font-bold rounded-md text-nowrap " +
props.versionClass ?? ""
}
>
{props.version}
</span>
</p>
);
}

View File

@ -0,0 +1,17 @@
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
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>
);
}

BIN
public/LuminaraStudio.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

109
server.ts
View File

@ -4,9 +4,52 @@ import ViteExpress from "vite-express";
import pjson from "./package.json";
import { networkInterfaces } from "os";
import cac from "cac";
import { completeGoogle } from "search-engine-autocomplete";
const start = new Date();
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"))
}
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;
}
}
}
const start = new Date();
const cli = cac();
const nets = networkInterfaces();
const ips: string[] = [];
@ -36,68 +79,10 @@ if (parsed.options.host!==undefined && typeof parsed.options.host == "boolean" &
host = "0.0.0.0";
}
app.get("/message", (_, res) => res.send("Hello from express!"));
app.get('/api/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' });
}
});
async function helloMessage() {
const { base } = await ViteExpress.getViteConfig();
//console.clear();
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"))
}
configureBackendRoutes(app);
const server = app.listen(port, host);
ViteExpress.bind(app, server, helloMessage);
async function a() {
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;
}
}
}
a();
handleInput();

View File

@ -7,7 +7,8 @@
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"strict": true,
"noEmit": true
"noEmit": true,
"types": ["bun"]
},
"include": ["vite.config.ts"]
}