feat: 完成短剧项目空间与结构化资产创作闭环 (#38)

* feat(canvas): 短剧项目空间 - 建立项目章节与画布关联 MVP

* feat(canvas): 短剧项目空间 - 完善项目资产、镜头与流程闭环

Refs #30

* fix(deploy): 本地部署 - 修复前端构建并忽略本地 PostgreSQL 配置

* feat(*): 短剧项目空间 - 完成项目、章节、资产、角色与画布创作闭环

Refs #30

---------

Co-authored-by: ddCat <ddcat666@88.com>
This commit is contained in:
ddcat666
2026-07-27 10:45:11 +08:00
committed by GitHub
parent 431b9e3294
commit d2fbc413b3
208 changed files with 22655 additions and 4234 deletions

View File

@@ -81,7 +81,7 @@ export default function RegisterPage() {
return (
<form onSubmit={submit} className="space-y-4">
{settings?.firstUser ? <Notice icon={<Info className="size-3.5" />} tone="cyan">首个账号自动成为管理员,邮箱验证码暂不要求。</Notice> : null}
{settings?.firstUser ? <Notice icon={<Info className="size-3.5" />} tone="blue">首个账号自动成为管理员,邮箱验证码暂不要求。</Notice> : null}
{registrationClosed ? <Notice icon={<TriangleAlert className="size-3.5" />} tone="amber">当前已关闭普通注册,请联系管理员创建账号。</Notice> : null}
{mailUnavailable ? <Notice icon={<TriangleAlert className="size-3.5" />} tone="amber">管理员尚未配置注册邮件,普通邮箱注册暂不可用。</Notice> : null}
@@ -116,11 +116,11 @@ function AuthField({ label, children }: { label: string; children: ReactNode })
return <label className="block space-y-2"><span className="text-xs font-medium text-white/62">{label}</span>{children}</label>;
}
function Notice({ icon, tone, children }: { icon: ReactNode; tone: "cyan" | "amber"; children: ReactNode }) {
return <div className={`flex items-start gap-2 rounded-xl border px-3 py-2.5 text-xs leading-5 ${tone === "cyan" ? "border-cyan-300/15 bg-cyan-300/[0.06] text-cyan-100/78" : "border-amber-300/15 bg-amber-300/[0.06] text-amber-100/78"}`}><span className="mt-0.5 shrink-0">{icon}</span>{children}</div>;
function Notice({ icon, tone, children }: { icon: ReactNode; tone: "blue" | "amber"; children: ReactNode }) {
return <div className={`flex items-start gap-2 rounded-lg border px-3 py-2.5 text-xs leading-5 ${tone === "blue" ? "border-blue-300/15 bg-blue-300/[0.06] text-blue-100/78" : "border-amber-300/15 bg-amber-300/[0.06] text-amber-100/78"}`}><span className="mt-0.5 shrink-0">{icon}</span>{children}</div>;
}
function safeNext(value: string | null) {
if (!value || !value.startsWith("/") || value.startsWith("//")) return "/canvas";
if (!value || !value.startsWith("/") || value.startsWith("//")) return "/projects";
return value;
}