feat(canvas): add switch to hide skill tags in chat
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-03-04 01:01:56 +08:00
parent 94b27f25d5
commit 787840dcb2
4 changed files with 59 additions and 13 deletions

View File

@ -1002,11 +1002,13 @@ export function AdminSettingsClient() {
// 快捷短语状态
const [questions, setQuestions] = useState<string[]>([]);
const { data: questionsData, mutate: mutateQuestions } = useSWR<string[]>("/api/starter-questions", fetcher);
const [canvasSkillTagsEnabled, setCanvasSkillTagsEnabled] = useState(true);
const [canvasSkillTags, setCanvasSkillTags] = useState<CanvasSkillTag[]>(
DEFAULT_CANVAS_SKILL_TAGS
);
const [canvasSkillTagUploadingId, setCanvasSkillTagUploadingId] = useState<string | null>(null);
const { data: canvasSkillTagsData, mutate: mutateCanvasSkillTags } = useSWR<{
enabled?: boolean;
tags?: CanvasSkillTag[];
}>("/api/canvas-skill-tags", fetcher);
@ -1051,6 +1053,7 @@ export function AdminSettingsClient() {
useEffect(() => {
if (canvasSkillTagsData) {
setCanvasSkillTagsEnabled(canvasSkillTagsData.enabled !== false);
setCanvasSkillTags(normalizeCanvasSkillTags(canvasSkillTagsData.tags));
}
}, [canvasSkillTagsData]);
@ -1148,11 +1151,12 @@ export function AdminSettingsClient() {
const res = await fetch("/api/canvas-skill-tags", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ tags: payload }),
body: JSON.stringify({ enabled: canvasSkillTagsEnabled, tags: payload }),
});
if (!res.ok) throw new Error("Failed");
const data = await res.json().catch(() => ({}));
setCanvasSkillTagsEnabled(data?.enabled !== false);
setCanvasSkillTags(normalizeCanvasSkillTags(data?.tags ?? payload));
toast.success("快捷标签已更新");
mutateCanvasSkillTags();
@ -5747,6 +5751,18 @@ export function AdminSettingsClient() {
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between rounded-lg border p-3">
<div className="space-y-0.5">
<Label>在画布对话中显示 Skills 标签</Label>
<p className="text-xs text-muted-foreground">
关闭后,对话框将不显示快捷标签区域。
</p>
</div>
<Switch
checked={canvasSkillTagsEnabled}
onCheckedChange={setCanvasSkillTagsEnabled}
/>
</div>
{canvasSkillTags.map((tag, index) => {
const TagIcon = getCanvasSkillIconComponent(tag.iconName);
return (

View File

@ -5,6 +5,7 @@ import { db } from "@/lib/db";
import { systemSettings } from "@/lib/db/schema";
import {
CANVAS_SKILL_TAGS_SETTING_KEY,
normalizeCanvasSkillTagSettings,
normalizeCanvasSkillTags,
} from "@/lib/canvas-skill-tags";
@ -16,14 +17,11 @@ export async function GET() {
.where(eq(systemSettings.key, CANVAS_SKILL_TAGS_SETTING_KEY))
.limit(1);
return NextResponse.json({
tags: normalizeCanvasSkillTags(setting?.value),
});
const settings = normalizeCanvasSkillTagSettings(setting?.value);
return NextResponse.json(settings);
} catch (error) {
console.error("[CanvasSkillTags API] Get error:", error);
return NextResponse.json({
tags: normalizeCanvasSkillTags(null),
});
return NextResponse.json(normalizeCanvasSkillTagSettings(null));
}
}
@ -37,6 +35,8 @@ export async function PUT(request: NextRequest) {
const body = await request.json();
const rawTags = Array.isArray(body) ? body : body?.tags;
const tags = normalizeCanvasSkillTags(rawTags);
const enabled = Array.isArray(body) ? true : body?.enabled !== false;
const nextValue = { enabled, tags };
const [existing] = await db
.select()
@ -48,7 +48,7 @@ export async function PUT(request: NextRequest) {
await db
.update(systemSettings)
.set({
value: tags,
value: nextValue,
updatedAt: new Date(),
updatedBy: session.user.id,
})
@ -56,12 +56,12 @@ export async function PUT(request: NextRequest) {
} else {
await db.insert(systemSettings).values({
key: CANVAS_SKILL_TAGS_SETTING_KEY,
value: tags,
value: nextValue,
updatedBy: session.user.id,
});
}
return NextResponse.json({ success: true, tags });
return NextResponse.json({ success: true, enabled, tags });
} catch (error) {
console.error("[CanvasSkillTags API] Update error:", error);
return NextResponse.json({ error: "Failed to update" }, { status: 500 });

View File

@ -2250,7 +2250,7 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
canvasAutoModeDefaultModelId?: string | null;
canvasDetailDefaultModelId?: string | null;
}>("/api/site-settings", fetcher);
const { data: canvasSkillTagsData } = useSWR<{ tags?: CanvasSkillTag[] }>(
const { data: canvasSkillTagsData } = useSWR<{ enabled?: boolean; tags?: CanvasSkillTag[] }>(
"/api/canvas-skill-tags",
fetcher
);
@ -2289,9 +2289,10 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
: "Hi, 我是你的AI设计师";
const totalGeneratedImages = Number(userImageStats?.totalImages ?? NaN);
const aiDetailGenerationEnabled = siteSettings?.aiDetailGenerationEnabled === true;
const canvasSkillTagsEnabled = canvasSkillTagsData?.enabled !== false;
const canvasSkillTags = useMemo(
() => normalizeCanvasSkillTags(canvasSkillTagsData?.tags),
[canvasSkillTagsData?.tags]
() => (canvasSkillTagsEnabled ? normalizeCanvasSkillTags(canvasSkillTagsData?.tags) : []),
[canvasSkillTagsData?.tags, canvasSkillTagsEnabled]
);
const selectedSkillTag = useMemo(
() => canvasSkillTags.find((tag) => tag.id === selectedSkillTagId) || null,

View File

@ -1,5 +1,10 @@
export const CANVAS_SKILL_TAGS_SETTING_KEY = "canvas_skill_tags";
export type CanvasSkillTagSettings = {
enabled: boolean;
tags: CanvasSkillTag[];
};
export type CanvasSkillTag = {
id: string;
name: string;
@ -108,3 +113,27 @@ export function normalizeCanvasSkillTags(value: unknown): CanvasSkillTag[] {
return normalized.slice(0, 30);
}
export function normalizeCanvasSkillTagSettings(
value: unknown
): CanvasSkillTagSettings {
if (Array.isArray(value)) {
return {
enabled: true,
tags: normalizeCanvasSkillTags(value),
};
}
if (value && typeof value === "object") {
const row = value as Record<string, unknown>;
return {
enabled: row.enabled !== false,
tags: normalizeCanvasSkillTags(row.tags),
};
}
return {
enabled: true,
tags: normalizeCanvasSkillTags(null),
};
}