Skip to content

Commit 608d26b

Browse files
committed
chore: update ESLint configuration and Next.js settings, add new proxy and Qodana configuration files, and enhance documentation with translated LeetCode problems
1 parent eea8b85 commit 608d26b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+4117
-1511
lines changed

app/components/DocsAssistant.tsx

Lines changed: 27 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import { useCallback, useEffect, useState, useRef } from "react";
3+
import { useCallback, useEffect, useMemo } from "react";
44

55
import { AssistantRuntimeProvider } from "@assistant-ui/react";
66
import { useAISDKRuntime } from "@assistant-ui/react-ai-sdk";
@@ -34,69 +34,44 @@ export function DocsAssistant({ pageContext }: DocsAssistantProps) {
3434
function DocsAssistantInner({ pageContext }: DocsAssistantProps) {
3535
const { provider, openaiApiKey, geminiApiKey } = useAssistantSettings();
3636

37-
// Use refs to ensure we always get the latest values
38-
const providerRef = useRef(provider);
39-
const openaiApiKeyRef = useRef(openaiApiKey);
40-
const geminiApiKeyRef = useRef(geminiApiKey);
41-
42-
// Update refs whenever the values change
43-
providerRef.current = provider;
44-
openaiApiKeyRef.current = openaiApiKey;
45-
geminiApiKeyRef.current = geminiApiKey;
46-
47-
const chat = useChat({
48-
transport: new DefaultChatTransport({
49-
api: "/api/chat",
50-
body: () => {
51-
// Use refs to get the current values at request time
52-
const currentProvider = providerRef.current;
53-
const currentApiKey =
54-
currentProvider === "openai"
55-
? openaiApiKeyRef.current
56-
: currentProvider === "gemini"
57-
? geminiApiKeyRef.current
58-
: ""; // intern provider doesn't need API key
59-
60-
console.log("[DocsAssistant] useChat body function called with:", {
61-
provider: currentProvider,
62-
apiKeyLength: currentApiKey.length,
63-
hasApiKey: currentApiKey.trim().length > 0,
64-
});
65-
66-
return {
67-
pageContext,
68-
provider: currentProvider,
69-
apiKey: currentApiKey,
70-
};
71-
},
72-
}),
73-
});
37+
const transport = useMemo(
38+
() =>
39+
new DefaultChatTransport({
40+
api: "/api/chat",
41+
body: () => {
42+
const apiKey =
43+
provider === "openai"
44+
? openaiApiKey
45+
: provider === "gemini"
46+
? geminiApiKey
47+
: ""; // intern provider doesn't need API key
48+
49+
return { pageContext, provider, apiKey };
50+
},
51+
}),
52+
[geminiApiKey, openaiApiKey, pageContext, provider],
53+
);
54+
55+
const chat = useChat({ transport });
7456

7557
const {
7658
error: chatError,
7759
status: chatStatus,
7860
clearError: clearChatError,
7961
} = chat;
80-
const [assistantError, setAssistantError] =
81-
useState<AssistantErrorState | null>(null);
82-
83-
useEffect(() => {
84-
if (!chatError) {
85-
return;
86-
}
87-
88-
setAssistantError(deriveAssistantError(chatError, provider));
89-
clearChatError();
90-
}, [chatError, clearChatError, provider]);
9162

9263
useEffect(() => {
9364
if (chatStatus === "submitted" || chatStatus === "streaming") {
94-
setAssistantError(null);
65+
clearChatError();
9566
}
96-
}, [chatStatus]);
67+
}, [chatStatus, clearChatError]);
68+
69+
const assistantError =
70+
chatError && chatStatus !== "submitted" && chatStatus !== "streaming"
71+
? deriveAssistantError(chatError, provider)
72+
: null;
9773

9874
const handleClearError = useCallback(() => {
99-
setAssistantError(null);
10075
clearChatError();
10176
}, [clearChatError]);
10277

app/components/Footer.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import Link from "next/link";
12
import { Github, MessageCircle, ExternalLink } from "lucide-react";
23
import { BrandMark } from "./BrandMark";
34
import { LicenseNotice } from "./LicenseNotice";
@@ -47,13 +48,13 @@ export function Footer() {
4748
<h3 className="font-semibold mb-4">快速链接</h3>
4849
<ul className="space-y-2">
4950
<li>
50-
<a
51+
<Link
5152
href="/docs/ai"
5253
className="text-muted-foreground hover:text-foreground transition-colors flex items-center"
5354
>
5455
知识库
5556
<ExternalLink className="ml-1 h-3 w-3" aria-hidden="true" />
56-
</a>
57+
</Link>
5758
</li>
5859
<li>
5960
<a

app/components/LicenseNotice.tsx

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import Image from "next/image";
12
import { cn } from "@/lib/utils";
23

34
interface LicenseNoticeProps {
@@ -19,44 +20,36 @@ export function LicenseNotice({ className }: LicenseNoticeProps) {
1920
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/">
2021
CC BY-NC-SA 4.0
2122
</a>
22-
<img
23+
<Image
2324
src="https://mirrors.creativecommons.org/presskit/icons/cc.svg"
24-
alt=""
25-
style={{
26-
maxWidth: "1em",
27-
maxHeight: "1em",
28-
marginLeft: "0.2em",
29-
}}
25+
alt="Creative Commons"
26+
width={16}
27+
height={16}
28+
className="ml-[0.2em] inline-block h-4 w-4"
3029
loading="lazy"
3130
/>
32-
<img
31+
<Image
3332
src="https://mirrors.creativecommons.org/presskit/icons/by.svg"
34-
alt=""
35-
style={{
36-
maxWidth: "1em",
37-
maxHeight: "1em",
38-
marginLeft: "0.2em",
39-
}}
33+
alt="Creative Commons BY"
34+
width={16}
35+
height={16}
36+
className="ml-[0.2em] inline-block h-4 w-4"
4037
loading="lazy"
4138
/>
42-
<img
39+
<Image
4340
src="https://mirrors.creativecommons.org/presskit/icons/nc.svg"
44-
alt=""
45-
style={{
46-
maxWidth: "1em",
47-
maxHeight: "1em",
48-
marginLeft: "0.2em",
49-
}}
41+
alt="Creative Commons Non-Commercial"
42+
width={16}
43+
height={16}
44+
className="ml-[0.2em] inline-block h-4 w-4"
5045
loading="lazy"
5146
/>
52-
<img
47+
<Image
5348
src="https://mirrors.creativecommons.org/presskit/icons/sa.svg"
54-
alt=""
55-
style={{
56-
maxWidth: "1em",
57-
maxHeight: "1em",
58-
marginLeft: "0.2em",
59-
}}
49+
alt="Creative Commons Share Alike"
50+
width={16}
51+
height={16}
52+
className="ml-[0.2em] inline-block h-4 w-4"
6053
loading="lazy"
6154
/>
6255
</p>

app/components/ThemeProvider.tsx

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,21 @@ export function ThemeProvider({
2727
storageKey = "vite-ui-theme",
2828
...props
2929
}: ThemeProviderProps) {
30-
// SSR-safe: do not touch localStorage during render
31-
const [theme, setTheme] = useState<Theme>(defaultTheme);
30+
const [theme, setTheme] = useState<Theme>(() => {
31+
if (typeof window === "undefined") {
32+
return defaultTheme;
33+
}
3234

33-
useEffect(() => {
34-
// Read persisted theme on client only
3535
try {
36-
const stored = (
37-
typeof window !== "undefined"
38-
? (localStorage.getItem(storageKey) as Theme | null)
39-
: null
40-
) as Theme | null;
41-
if (stored) {
42-
setTheme(stored);
43-
}
36+
const stored = localStorage.getItem(storageKey) as Theme | null;
37+
return stored ?? defaultTheme;
4438
} catch {
4539
console.error("Error reading theme from localStorage");
40+
return defaultTheme;
4641
}
42+
});
4743

44+
useEffect(() => {
4845
const root = window.document.documentElement;
4946

5047
root.classList.remove("light", "dark");
@@ -60,7 +57,7 @@ export function ThemeProvider({
6057
}
6158

6259
root.classList.add(theme);
63-
}, [theme, storageKey]);
60+
}, [theme]);
6461

6562
const value = {
6663
theme,

app/components/assistant-ui/attachment.tsx

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
"use client";
22

3-
import { PropsWithChildren, useEffect, useState, type FC } from "react";
3+
import {
4+
PropsWithChildren,
5+
useEffect,
6+
useMemo,
7+
useState,
8+
type FC,
9+
} from "react";
410
import Image from "next/image";
511
import { XIcon, PlusIcon, FileText } from "lucide-react";
612
import {
@@ -31,21 +37,17 @@ import { TooltipIconButton } from "@/app/components/assistant-ui/tooltip-icon-bu
3137
import { cn } from "@/lib/utils";
3238

3339
const useFileSrc = (file: File | undefined) => {
34-
const [src, setSrc] = useState<string | undefined>(undefined);
40+
const src = useMemo(
41+
() => (file ? URL.createObjectURL(file) : undefined),
42+
[file],
43+
);
3544

3645
useEffect(() => {
37-
if (!file) {
38-
setSrc(undefined);
39-
return;
40-
}
41-
42-
const objectUrl = URL.createObjectURL(file);
43-
setSrc(objectUrl);
44-
46+
if (!src) return;
4547
return () => {
46-
URL.revokeObjectURL(objectUrl);
48+
URL.revokeObjectURL(src);
4749
};
48-
}, [file]);
50+
}, [src]);
4951

5052
return src;
5153
};

0 commit comments

Comments
 (0)