chore: enable WebUI ESLint

This commit is contained in:
Stellar鱼 2026-05-24 14:44:42 +08:00 committed by Xubin Ren
parent a4a2c55120
commit 1eddc129a1
6 changed files with 1257 additions and 42 deletions

31
webui/eslint.config.js Normal file
View File

@ -0,0 +1,31 @@
import js from "@eslint/js";
import reactHooks from "eslint-plugin-react-hooks";
import globals from "globals";
import tseslint from "typescript-eslint";
export default tseslint.config(
{
ignores: ["dist", "coverage", "node_modules", "*.config.js"],
linterOptions: {
reportUnusedDisableDirectives: "off",
},
},
js.configs.recommended,
...tseslint.configs.recommended,
{
files: ["src/**/*.{ts,tsx}"],
languageOptions: {
ecmaVersion: 2020,
globals: {
...globals.browser,
...globals.es2020,
},
},
plugins: {
"react-hooks": reactHooks,
},
rules: {
"react-hooks/rules-of-hooks": "error",
},
},
);

1207
webui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -34,6 +34,7 @@
"tailwind-merge": "^2.6.0" "tailwind-merge": "^2.6.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^10.0.1",
"@tailwindcss/typography": "^0.5.19", "@tailwindcss/typography": "^0.5.19",
"@testing-library/jest-dom": "^6.6.3", "@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.1.0", "@testing-library/react": "^16.1.0",
@ -44,12 +45,16 @@
"@types/react-syntax-highlighter": "^15.5.13", "@types/react-syntax-highlighter": "^15.5.13",
"@vitejs/plugin-react": "^4.3.4", "@vitejs/plugin-react": "^4.3.4",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"eslint": "^10.4.0",
"eslint-plugin-react-hooks": "^7.1.1",
"globals": "^17.6.0",
"happy-dom": "^16.3.0", "happy-dom": "^16.3.0",
"katex": "^0.16.21", "katex": "^0.16.21",
"postcss": "^8.5.0", "postcss": "^8.5.0",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.17",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"typescript": "^5.7.2", "typescript": "^5.7.2",
"typescript-eslint": "^8.59.4",
"vite": "^5.4.11", "vite": "^5.4.11",
"vitest": "^2.1.8" "vitest": "^2.1.8"
} }

View File

@ -164,8 +164,7 @@ export default function App() {
if (cancelled) return; if (cancelled) return;
if (secret) saveSecret(secret); if (secret) saveSecret(secret);
const url = deriveWsUrl(boot.ws_path, boot.token); const url = deriveWsUrl(boot.ws_path, boot.token);
let client: NanobotClient; const client = new NanobotClient({
client = new NanobotClient({
url, url,
onReauth: async () => { onReauth: async () => {
try { try {
@ -663,12 +662,13 @@ function Shell({
useEffect(() => { useEffect(() => {
return client.onStatus((status) => { return client.onStatus((status) => {
let startedAt = 0; const startedAt = (() => {
try { try {
startedAt = Number(window.localStorage.getItem(RESTART_STARTED_KEY) ?? "0"); return Number(window.localStorage.getItem(RESTART_STARTED_KEY) ?? "0");
} catch { } catch {
startedAt = 0; return 0;
} }
})();
if (!startedAt) return; if (!startedAt) return;
if (status !== "open") { if (status !== "open") {
restartSawDisconnectRef.current = true; restartSawDisconnectRef.current = true;

View File

@ -286,7 +286,6 @@ function RunElapsedStrip({
const showTimer = startedAt != null; const showTimer = startedAt != null;
const stripLabel = goalStateStripPreview(goalState, t); const stripLabel = goalStateStripPreview(goalState, t);
const showGoal = !!stripLabel?.trim(); const showGoal = !!stripLabel?.trim();
if (!showTimer && !showGoal) return null;
const objectiveFull = goalState?.objective?.trim() ?? ""; const objectiveFull = goalState?.objective?.trim() ?? "";
const summaryFull = goalState?.ui_summary?.trim() ?? ""; const summaryFull = goalState?.ui_summary?.trim() ?? "";
@ -349,6 +348,8 @@ function RunElapsedStrip({
}; };
}, [goalPanelOpen]); }, [goalPanelOpen]);
if (!showTimer && !showGoal) return null;
const elapsed = const elapsed =
startedAt != null ? Math.max(0, Math.floor(Date.now() / 1000 - startedAt)) : 0; startedAt != null ? Math.max(0, Math.floor(Date.now() / 1000 - startedAt)) : 0;
const m = Math.floor(elapsed / 60); const m = Math.floor(elapsed / 60);

View File

@ -3,6 +3,43 @@ import { beforeEach } from "vitest";
import i18n from "@/i18n"; import i18n from "@/i18n";
function createTestStorage(): Storage {
const store = new Map<string, string>();
return {
get length() {
return store.size;
},
clear() {
store.clear();
},
getItem(key: string) {
return store.get(String(key)) ?? null;
},
key(index: number) {
return Array.from(store.keys())[index] ?? null;
},
removeItem(key: string) {
store.delete(String(key));
},
setItem(key: string, value: string) {
store.set(String(key), String(value));
},
};
}
if (typeof window !== "undefined" && typeof localStorage.setItem !== "function") {
const storage = createTestStorage();
Object.defineProperty(window, "localStorage", {
value: storage,
configurable: true,
});
Object.defineProperty(globalThis, "localStorage", {
value: storage,
configurable: true,
writable: true,
});
}
// happy-dom doesn't ship with ``crypto.randomUUID``; shim a tiny v4-ish helper. // happy-dom doesn't ship with ``crypto.randomUUID``; shim a tiny v4-ish helper.
if (!("randomUUID" in globalThis.crypto)) { if (!("randomUUID" in globalThis.crypto)) {
Object.defineProperty(globalThis.crypto, "randomUUID", { Object.defineProperty(globalThis.crypto, "randomUUID", {