feat(*): 创作配置 - 支持 Midjourney、多图保存与自定义公告

Co-authored-by: ddcat666 <26564566+ddcat666@users.noreply.github.com>
This commit is contained in:
DAO-QIN
2026-10-04 10:43:31 +08:00
committed by ddcat666
parent 002a45f0c3
commit 853c7d2ce7
107 changed files with 9261 additions and 408 deletions

View File

@@ -2,8 +2,10 @@ import { http } from "@/services/api/request";
import type { SkinDefinition } from "@/lib/skin-themes";
import type { CanvasAppearance } from "@/lib/canvas/agent-appearance";
import { apiBaseURL } from "@/services/api/request";
import type { UpdateAnnouncement } from "@/lib/update-announcement";
export type PublicAppearance = {
updates?: UpdateAnnouncement;
canvas?: CanvasAppearance;
schemaVersion: number;
brandName: string;
@@ -34,6 +36,7 @@ export type PublicAppearance = {
};
export type AdminAppearance = {
updates?: UpdateAnnouncement;
canvas?: CanvasAppearance;
schemaVersion: number;
brandName: string;
@@ -86,6 +89,7 @@ export async function updateAdminAppearance(
AdminAppearance,
| "brandName"
| "canvas"
| "updates"
| "brandSlug"
| "authHeroTitle"
| "authHeroDescription"
@@ -114,15 +118,20 @@ export async function resetAdminAppearance() {
return result.setting;
}
export async function uploadAppearanceAsset(slot: AppearanceAssetSlot, file: File) {
export async function uploadAppearanceAsset(slot: AppearanceAssetSlot | "update-image" | "update-video", file: File) {
const body = new FormData();
body.append("file", file);
const result = await http.post<{ resource: AppearanceResource }>(`/admin/settings/appearance/assets/${slot}`, body);
return result.resource;
}
export function updateAnnouncementPreviewURL(resourceId: string) {
return `${apiBaseURL.replace(/\/$/, "")}/admin/settings/appearance/updates/${encodeURIComponent(resourceId)}`;
}
export async function uploadLive2D(file: File) {
const body = new FormData(); body.append("file", file);
const body = new FormData();
body.append("file", file);
const result = await http.post<{ model: { resourceId: string; entry: string } }>("/admin/settings/appearance/live2d", body);
return result.model;
}

View File

@@ -14,6 +14,14 @@ type PluginProviderCatalogItem = {
enabled: boolean;
unavailableReason?: string;
baseUrl?: string;
parameters?: Array<{
name: string;
type: string;
required?: boolean;
description?: string;
values?: string[];
mapping?: string;
}>;
workflows?: Array<{
id: string;
label: string;
@@ -41,6 +49,7 @@ function toProviderDefinition(item: PluginProviderCatalogItem): ModelProtocolDef
media: `${item.vendor} · ${item.version}`,
enabled: item.enabled && !item.unavailableReason,
baseUrl: item.baseUrl,
parameters: item.parameters || [],
workflows: item.workflows || [],
};
}

View File

@@ -1,7 +1,30 @@
import { getPublicAppearance, type PublicAppearance } from "@/services/api/appearance";
import { commitPublicAppearance, DEFAULT_PUBLIC_APPEARANCE } from "@/stores/use-appearance-store";
import { commitPublicAppearance, DEFAULT_PUBLIC_APPEARANCE, useAppearanceStore } from "@/stores/use-appearance-store";
const APPEARANCE_BOOTSTRAP_TIMEOUT_MS = 4_000;
let refreshing: Promise<PublicAppearance | undefined> | undefined;
// A failed refresh must keep the last saved identity rather than restore defaults.
export function refreshPublicAppearance(fetchAppearance: (signal: AbortSignal) => Promise<PublicAppearance> = getPublicAppearance) {
if (refreshing) return refreshing;
refreshing = (async () => {
const previous = useAppearanceStore.getState().appearance;
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), APPEARANCE_BOOTSTRAP_TIMEOUT_MS);
try {
const appearance = await fetchAppearance(controller.signal);
if (useAppearanceStore.getState().appearance !== previous) return undefined;
return commitPublicAppearance(appearance);
} catch {
return undefined;
} finally {
clearTimeout(timer);
}
})().finally(() => {
refreshing = undefined;
});
return refreshing;
}
export async function resolvePublicAppearance(fetchAppearance: (signal: AbortSignal) => Promise<PublicAppearance> = getPublicAppearance) {
const controller = new AbortController();

View File

@@ -123,7 +123,7 @@ export async function applyCanvasGenerationTaskNodeEffect(input: {
if (!applied.updated || !applied.node) throw new Error("画布中找不到对应任务节点");
throwIfAborted(input.signal);
const previousNodes = input.nodesRef.current;
const nodes = commitCanvasGenerationResult(previousNodes, before, applied.node, input.task.id);
const nodes = commitCanvasGenerationResult(previousNodes, before, applied.node, input.task.id, applied.additionalNodes);
await persistCanvasGenerationEffect({
projectId: input.projectId,
effectKey: input.effectKey,
@@ -133,7 +133,7 @@ export async function applyCanvasGenerationTaskNodeEffect(input: {
});
throwIfAborted(input.signal);
// 持久化已完成;最后一次 await 之后仍按节点合并,保留期间发生的编辑。
input.setNodes((current) => commitCanvasGenerationResult(current, before, applied.node!, input.task.id));
input.setNodes((current) => commitCanvasGenerationResult(current, before, applied.node!, input.task.id, applied.additionalNodes));
}
export async function persistCanvasOperationContinuationEffect(input: {