refactor(canvas): 替换自定义ID生成函数为nanoid并优化配置逻辑

- 移除自定义createId函数,统一使用nanoid库生成唯一标识符
- 将useEffectiveAiConfig Hook替换为resolveEffectiveConfig工具函数
- 直接从config store获取publicSettings避免额外的Hook依赖
- 更新所有组件中的ID生成调用以使用nanoid
- 简化buildApiUrl函数中的基础URL规范化逻辑
This commit is contained in:
HouYunFei
2026-05-21 14:43:09 +08:00
parent 22d34a0c99
commit ef9e87fb29
12 changed files with 78 additions and 67 deletions
+4 -10
View File
@@ -9,7 +9,7 @@ import { GitHubLink } from "@/components/github-link";
import { UserStatusActions } from "@/components/user-status-actions";
import { AnimatedThemeToggler } from "@/components/ui/animated-theme-toggler";
import { VersionReleaseModal } from "@/components/version-release-modal";
import { useConfigStore, type AiConfig } from "@/stores/use-config-store";
import { useConfigStore, useEffectiveConfig, type AiConfig } from "@/stores/use-config-store";
import { navigationTools, type NavigationToolSlug } from "@/lib/navigation-tools";
import { fetchImageModels } from "@/services/api/image";
import { useThemeStore } from "@/stores/use-theme-store";
@@ -28,7 +28,6 @@ export function AppTopNav({ activeToolSlug, config, onConfigChange, hideHeader =
const { message } = App.useApp();
const [loadingModels, setLoadingModels] = useState(false);
const [mobileNavOpen, setMobileNavOpen] = useState(false);
const appVersion = process.env.NEXT_PUBLIC_APP_VERSION || "dev";
const isConfigOpen = useConfigStore((state) => state.isConfigOpen);
const shouldPromptContinue = useConfigStore((state) => state.shouldPromptContinue);
const openConfigDialog = useConfigStore((state) => state.openConfigDialog);
@@ -40,16 +39,11 @@ export function AppTopNav({ activeToolSlug, config, onConfigChange, hideHeader =
const isReady = useUserStore((state) => state.isReady);
const logout = useUserStore((state) => state.clearSession);
const publicSettings = useConfigStore((state) => state.publicSettings);
const effectiveConfig = useEffectiveConfig();
const modelChannel = publicSettings?.modelChannel;
const allowCustomChannel = modelChannel?.allowCustomChannel === true;
const effectiveMode = allowCustomChannel ? config.channelMode : "remote";
const modelConfig = effectiveMode === "remote" && modelChannel ? {
...config,
models: modelChannel.availableModels,
imageModel: modelChannel.availableModels.includes(config.imageModel) ? config.imageModel : modelChannel.defaultImageModel || modelChannel.defaultModel,
textModel: modelChannel.availableModels.includes(config.textModel) ? config.textModel : modelChannel.defaultTextModel || modelChannel.defaultModel,
systemPrompt: modelChannel.systemPrompt,
} : config;
const modelConfig = effectiveMode === "remote" ? effectiveConfig : config;
const finishConfig = () => {
setConfigDialogOpen(false);
@@ -163,7 +157,7 @@ export function AppTopNav({ activeToolSlug, config, onConfigChange, hideHeader =
aria-label={theme === "dark" ? "切换到浅色主题" : "切换到深色主题"}
title={theme === "dark" ? "切换到浅色主题" : "切换到深色主题"}
/>
<VersionReleaseModal currentVersion={appVersion} />
<VersionReleaseModal />
<GitHubLink />
<Link href="/login" className="text-sm font-medium text-stone-600 underline-offset-4 transition hover:text-stone-950 hover:underline dark:text-stone-300 dark:hover:text-stone-100">
+1 -2
View File
@@ -52,7 +52,6 @@ export function UserStatusActions({
const theme = useThemeStore((state) => state.theme);
const setTheme = useThemeStore((state) => state.setTheme);
const storedUserName = useUserStore((state) => state.user?.username);
const appVersion = process.env.NEXT_PUBLIC_APP_VERSION || "dev";
const resolvedUserName = userName || storedUserName;
const avatarText = initial || (resolvedUserName?.trim()[0] || "U").toUpperCase();
const naturalIconClass = "inline-flex size-8 shrink-0 items-center justify-center text-stone-600 transition hover:text-stone-950 dark:text-stone-300 dark:hover:text-white [&_svg]:size-4";
@@ -79,7 +78,7 @@ export function UserStatusActions({
aria-label={theme === "dark" ? "切换到浅色主题" : "切换到深色主题"}
title={theme === "dark" ? "切换到浅色主题" : "切换到深色主题"}
/>
<VersionReleaseModal currentVersion={appVersion} style={versionStyle} />
<VersionReleaseModal style={versionStyle} />
<GitHubLink className={cn("bg-transparent hover:bg-transparent dark:hover:bg-transparent", gitHubClassName)} style={gitHubStyle} />
<div ref={accountRef}>
<Dropdown
+9 -25
View File
@@ -1,9 +1,9 @@
"use client";
import { useCallback, useEffect, useState } from "react";
import type { CSSProperties } from "react";
import { App, Modal, Tag, Timeline } from "antd";
import { Modal, Tag, Timeline } from "antd";
import { useVersionCheck } from "@/hooks/use-version-check";
import { APP_VERSION } from "@/constant/env";
function getTagColor(type: string) {
if (type === "新增") return "green";
@@ -18,28 +18,12 @@ function getReleaseTitle(version: string) {
}
type VersionReleaseModalProps = {
currentVersion: string;
className?: string;
style?: CSSProperties;
};
export function VersionReleaseModal({ currentVersion, className, style }: VersionReleaseModalProps) {
const { message } = App.useApp();
const [open, setOpen] = useState(false);
const { latestVersion, releases, checking, hasNewVersion, checkLatestRelease } = useVersionCheck(currentVersion);
const handleCheckLatestRelease = useCallback((showMessage = false) => {
void checkLatestRelease().then((success) => {
if (!showMessage) return;
if (success) message.success("已获取最新版本信息");
else message.error("获取最新版本信息失败");
});
}, [checkLatestRelease, message]);
useEffect(() => {
if (!open) return;
handleCheckLatestRelease();
}, [handleCheckLatestRelease, open]);
export function VersionReleaseModal({ className, style }: VersionReleaseModalProps) {
const { open, setOpen, openReleaseModal, latestVersion, releases, checking, hasNewVersion, checkLatestRelease } = useVersionCheck();
return (
<>
@@ -47,11 +31,11 @@ export function VersionReleaseModal({ currentVersion, className, style }: Versio
type="button"
className={className || "shrink-0 cursor-pointer text-xs font-medium text-stone-500 transition hover:text-stone-950 dark:text-stone-400 dark:hover:text-white"}
style={style}
onClick={() => setOpen(true)}
onClick={openReleaseModal}
title="查看版本更新"
>
<span className="relative inline-flex">
{currentVersion}
{APP_VERSION}
{hasNewVersion ? <span className="absolute -right-1.5 -top-1 size-1.5 rounded-full bg-green-500" /> : null}
</span>
</button>
@@ -66,7 +50,7 @@ export function VersionReleaseModal({ currentVersion, className, style }: Versio
<div className="mb-5 grid grid-cols-2 gap-3">
<div className="rounded-lg border border-stone-200 p-3 dark:border-stone-800">
<div className="text-xs text-stone-500 dark:text-stone-400"></div>
<div className="mt-1 text-base font-semibold text-stone-950 dark:text-stone-100">{currentVersion}</div>
<div className="mt-1 text-base font-semibold text-stone-950 dark:text-stone-100">{APP_VERSION}</div>
</div>
<div className="rounded-lg border border-stone-200 p-3 dark:border-stone-800">
<div className="flex items-center justify-between gap-3">
@@ -74,7 +58,7 @@ export function VersionReleaseModal({ currentVersion, className, style }: Versio
<button
type="button"
className="cursor-pointer bg-transparent p-0 text-[11px] font-normal text-stone-400 underline-offset-2 transition hover:text-stone-700 hover:underline dark:text-stone-500 dark:hover:text-stone-300"
onClick={() => handleCheckLatestRelease(true)}
onClick={() => void checkLatestRelease(true)}
>
{checking ? "检查中..." : "检查更新"}
</button>
@@ -92,7 +76,7 @@ export function VersionReleaseModal({ currentVersion, className, style }: Versio
<span className="text-xs text-stone-500 dark:text-stone-400">{release.date}</span>
<div className="flex min-w-0 items-center gap-1.5">
{release.version === latestVersion ? <Tag color="green"></Tag> : null}
{release.version === currentVersion ? <Tag></Tag> : null}
{release.version === APP_VERSION ? <Tag></Tag> : null}
</div>
</div>
<div className="mt-2 space-y-1.5">