feat(*): 创作配置 - 支持 Midjourney、多图保存与自定义公告
Co-authored-by: ddcat666 <26564566+ddcat666@users.noreply.github.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 || [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user