feat(platform): 开源发布 - 初始化 Open AI Canvas

This commit is contained in:
ddCat
2026-07-22 14:18:14 +08:00
commit aea35896eb
365 changed files with 66410 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
import { CanvasNodeType } from "@/types/canvas";
import type { CanvasNodeMetadata } from "@/types/canvas";
type CanvasNodeSpec = {
width: number;
height: number;
title: string;
metadata?: CanvasNodeMetadata;
};
export const NODE_DEFAULT_SIZE = {
[CanvasNodeType.Image]: { width: 340, height: 240, title: "New Generation" },
[CanvasNodeType.Text]: { width: 340, height: 240, title: "Note" },
[CanvasNodeType.Script]: { width: 920, height: 360, title: "分镜脚本" },
[CanvasNodeType.Skill]: { width: 360, height: 220, title: "技能" },
[CanvasNodeType.Config]: { width: 340, height: 300, title: "生成配置" },
[CanvasNodeType.Video]: { width: 420, height: 236, title: "Video" },
[CanvasNodeType.Audio]: { width: 340, height: 120, title: "Audio" },
[CanvasNodeType.Frame]: { width: 760, height: 520, title: "未命名背板" },
} satisfies Record<CanvasNodeType, { width: number; height: number; title: string }>;
export const NODE_SPECS = {
[CanvasNodeType.Image]: {
...NODE_DEFAULT_SIZE[CanvasNodeType.Image],
metadata: { content: "", status: "idle" },
},
[CanvasNodeType.Text]: {
...NODE_DEFAULT_SIZE[CanvasNodeType.Text],
metadata: { content: "", status: "idle", fontSize: 14 },
},
[CanvasNodeType.Script]: {
...NODE_DEFAULT_SIZE[CanvasNodeType.Script],
metadata: {
status: "idle",
workflowKind: "script",
storyboard: {
rows: [],
visibleColumns: ["shotNumber", "durationSeconds", "plotDescription", "dialogue"],
referenceNodeIds: [],
},
},
},
[CanvasNodeType.Skill]: {
...NODE_DEFAULT_SIZE[CanvasNodeType.Skill],
metadata: { status: "success" },
},
[CanvasNodeType.Config]: {
...NODE_DEFAULT_SIZE[CanvasNodeType.Config],
metadata: { content: "", status: "idle", generationMode: "image" },
},
[CanvasNodeType.Video]: {
...NODE_DEFAULT_SIZE[CanvasNodeType.Video],
metadata: { content: "", status: "idle" },
},
[CanvasNodeType.Audio]: {
...NODE_DEFAULT_SIZE[CanvasNodeType.Audio],
metadata: { content: "", status: "idle" },
},
[CanvasNodeType.Frame]: {
...NODE_DEFAULT_SIZE[CanvasNodeType.Frame],
metadata: { frame: { collapsed: false, expandedWidth: NODE_DEFAULT_SIZE[CanvasNodeType.Frame].width, expandedHeight: NODE_DEFAULT_SIZE[CanvasNodeType.Frame].height } },
},
} satisfies Record<CanvasNodeType, CanvasNodeSpec>;
export function getNodeSpec(type: CanvasNodeType) {
return NODE_SPECS[type];
}

View File

@@ -0,0 +1,31 @@
import type { ComponentProps } from "react";
import { Coins } from "lucide-react";
export function CreditSymbol({ className, ...props }: ComponentProps<"span">) {
return (
<span {...props} className={`inline-flex items-center justify-center ${className || ""}`}>
<Coins className="size-[1em]" strokeWidth={2.2} />
</span>
);
}
export type ModelCreditCost = {
model: string;
unitPriceMicrocredits: number;
};
function modelCreditCost(modelCosts: ModelCreditCost[] | undefined, model: string) {
const microcredits = modelCosts?.find((item) => item.model === model)?.unitPriceMicrocredits;
return microcredits === undefined ? null : microcredits / 1_000_000;
}
export function formatCredits(value: number, maximumFractionDigits = 6) {
return (value / 1_000_000).toLocaleString("zh-CN", { maximumFractionDigits });
}
export function requestCreditCost(options: { channelMode: string; modelCosts?: ModelCreditCost[]; model: string; count?: string | number }) {
if (options.channelMode !== "remote") return null;
const unitCredits = modelCreditCost(options.modelCosts, options.model);
const count = Math.max(1, Math.floor(Math.abs(Number(options.count)) || 1));
return unitCredits === null ? null : unitCredits * count;
}

View File

@@ -0,0 +1,26 @@
import { BookOpenCheck, Images, ListChecks, Maximize2 } from "lucide-react";
export const navigationTools = [
{
slug: "canvas",
label: "我的画布",
icon: Maximize2,
},
{
slug: "tasks",
label: "任务中心",
icon: ListChecks,
},
{
slug: "assets",
label: "我的素材",
icon: Images,
},
{
slug: "skills",
label: "技能库大厅",
icon: BookOpenCheck,
},
] as const;
export type NavigationToolSlug = (typeof navigationTools)[number]["slug"];