Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yaoai-video
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
姚珂
yaoai-video
Commits
fbe3f0e7
Commit
fbe3f0e7
authored
May 26, 2026
by
黄同智
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'main' of
http://git.ruanyiit.com/yaoke/yaoai-video
parents
06978b88
c2bcc050
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
457 additions
and
61 deletions
+457
-61
Layout.tsx
doc/html/src/app/components/Layout.tsx
+54
-19
CharacterGeneration.tsx
doc/html/src/app/pages/CharacterGeneration.tsx
+0
-0
Dashboard.tsx
doc/html/src/app/pages/Dashboard.tsx
+0
-0
NewProject.tsx
doc/html/src/app/pages/NewProject.tsx
+0
-0
ProjectDetail.tsx
doc/html/src/app/pages/ProjectDetail.tsx
+185
-0
StoryboardWorkspace.tsx
doc/html/src/app/pages/StoryboardWorkspace.tsx
+0
-0
theme.css
doc/html/src/styles/theme.css
+47
-24
docker-compose.yml
docker-compose.yml
+5
-18
2026-05-25-doc-html-figma-handoff.md
docs/superpowers/specs/2026-05-25-doc-html-figma-handoff.md
+166
-0
No files found.
doc/html/src/app/components/Layout.tsx
View file @
fbe3f0e7
...
@@ -120,7 +120,7 @@ export function Layout() {
...
@@ -120,7 +120,7 @@ export function Layout() {
<
div
className=
"flex h-7 w-7 items-center justify-center rounded-md bg-gradient-to-br from-[#0073ff] to-[#7b2cff]"
>
<
div
className=
"flex h-7 w-7 items-center justify-center rounded-md bg-gradient-to-br from-[#0073ff] to-[#7b2cff]"
>
<
Film
className=
"h-4 w-4 text-white"
/>
<
Film
className=
"h-4 w-4 text-white"
/>
</
div
>
</
div
>
<
span
className=
"text-base font-semibold tracking-normal text-[#
111827]"
>
Dramix
</
span
>
<
span
className=
"text-base font-semibold tracking-normal text-[#
7a5cf6]"
>
Dramix.ai
</
span
>
</
Link
>
</
Link
>
</
div
>
</
div
>
...
@@ -309,31 +309,66 @@ export function Layout() {
...
@@ -309,31 +309,66 @@ export function Layout() {
<
section
className=
"relative flex min-w-0 flex-1 flex-col overflow-hidden"
>
<
section
className=
"relative flex min-w-0 flex-1 flex-col overflow-hidden"
>
{
isProjectRoute
&&
(
{
isProjectRoute
&&
(
<
div
className=
"flex h-12 items-center gap-5 border-b border-[#dfe3ee] bg-white px-6"
>
<
div
className=
"flex h-16 items-center border-b border-[#e5e8f0] bg-[#fbfcff] px-5"
>
<
div
className=
"flex min-w-0 items-center gap-2 border-r border-[#dfe3ee] pr-5"
>
<
div
className=
"flex min-w-[310px] items-center gap-3"
>
<
Film
className=
"h-4 w-4 flex-shrink-0 text-[#1664ff]"
/>
<
button
<
span
className=
"max-w-[240px] truncate text-sm font-medium text-[#111827]"
>
type=
"button"
{
currentProject
?.
name
??
projectId
}
onClick=
{
()
=>
navigate
(
"/"
)
}
</
span
>
className=
"flex h-7 w-7 items-center justify-center rounded-md text-[#111827] hover:bg-[#edf1f8]"
aria
-
label=
"返回项目列表"
>
<
ChevronDown
className=
"h-4 w-4 rotate-90"
/>
</
button
>
<
div
className=
"min-w-0"
>
<
div
className=
"flex items-center gap-1.5"
>
<
span
className=
"max-w-[270px] truncate text-sm font-semibold text-[#111827]"
>
{
currentProject
?.
name
??
projectId
}
</
span
>
<
button
className=
"text-[#98a2b3] hover:text-[#6f55f2]"
aria
-
label=
"编辑项目名"
>
<
FileText
className=
"h-3.5 w-3.5"
/>
</
button
>
</
div
>
<
div
className=
"mt-1 flex items-center gap-3 text-[11px] text-[#667085]"
>
<
span
>
9:16
</
span
>
<
span
>
720
</
span
>
<
span
>
写实电影感
</
span
>
</
div
>
</
div
>
</
div
>
</
div
>
<
div
className=
"flex min-w-0 items-center gap-1.5 overflow-x-auto"
>
{
projectSections
.
map
((
section
)
=>
{
<
div
className=
"mx-auto hidden items-center gap-5 xl:flex"
>
{
projectSections
.
filter
((
section
)
=>
section
.
id
!==
"agent"
).
map
((
section
,
index
,
arr
)
=>
{
const
Icon
=
section
.
icon
;
const
Icon
=
section
.
icon
;
const
active
=
getActiveSection
()
===
section
.
id
;
const
active
=
getActiveSection
()
===
section
.
id
;
return
(
return
(
<
button
<
div
key=
{
section
.
id
}
className=
"flex items-center gap-5"
>
key=
{
section
.
id
}
<
button
onClick=
{
()
=>
navigate
(
section
.
path
)
}
onClick=
{
()
=>
navigate
(
section
.
path
)
}
className=
{
`flex h-8 flex-shrink-0 items-center gap-2 rounded-md px-3 text-sm transition ${
className=
{
`flex h-12 min-w-[96px] flex-col items-center justify-center gap-1 rounded-full border px-6 text-xs transition ${
active ? "bg-[#eef3ff] text-[#1664ff]" : "text-[#475467] hover:bg-[#f2f4f8]"
active
}`
}
? "border-[#7657ff] bg-white text-[#7657ff] shadow-[0_6px_18px_rgba(118,87,255,0.18)]"
>
: "border-[#e2e6ef] bg-white text-[#111827] hover:border-[#b8c1d4]"
<
Icon
className=
"h-4 w-4"
/>
}`
}
{
section
.
label
}
>
</
button
>
<
Icon
className=
"h-4 w-4"
/>
{
section
.
label
}
</
button
>
{
index
<
arr
.
length
-
1
&&
<
span
className=
"h-px w-8 bg-[#d9dee9]"
/>
}
</
div
>
);
);
})
}
})
}
</
div
>
</
div
>
<
div
className=
"ml-auto flex min-w-[260px] items-center justify-end gap-3"
>
<
button
className=
"text-xs text-[#667085] hover:text-[#6f55f2]"
>
优化建议
</
button
>
<
button
onClick=
{
()
=>
navigate
(
"/usage"
)
}
className=
"flex h-8 items-center gap-1.5 rounded-md border border-[#d9d2ff] bg-[#f6f2ff] px-3 text-xs font-semibold text-[#6f55f2]"
>
<
Sparkles
className=
"h-3.5 w-3.5"
/>
{
Number
(
balance
?.
balance
??
7492538
).
toLocaleString
()
}
</
button
>
</
div
>
</
div
>
</
div
>
)
}
)
}
...
...
doc/html/src/app/pages/CharacterGeneration.tsx
View file @
fbe3f0e7
This diff is collapsed.
Click to expand it.
doc/html/src/app/pages/Dashboard.tsx
View file @
fbe3f0e7
This diff is collapsed.
Click to expand it.
doc/html/src/app/pages/NewProject.tsx
View file @
fbe3f0e7
This diff is collapsed.
Click to expand it.
doc/html/src/app/pages/ProjectDetail.tsx
View file @
fbe3f0e7
...
@@ -59,6 +59,17 @@ export function ProjectDetail() {
...
@@ -59,6 +59,17 @@ export function ProjectDetail() {
}
}
return
(
return
(
<
ProjectScriptView
project=
{
project
}
outline=
{
outline
}
episodes=
{
episodes
}
characters=
{
characters
}
projectId=
{
projectId
}
onNavigate=
{
(
path
)
=>
navigate
(
path
)
}
/>
);
return
(
<
div
className=
"h-full overflow-auto bg-background relative"
>
<
div
className=
"h-full overflow-auto bg-background relative"
>
{
/* Header */
}
{
/* Header */
}
<
div
className=
"border-b border-border bg-card px-6 py-3"
>
<
div
className=
"border-b border-border bg-card px-6 py-3"
>
...
@@ -169,6 +180,180 @@ export function ProjectDetail() {
...
@@ -169,6 +180,180 @@ export function ProjectDetail() {
);
);
}
}
function
ProjectScriptView
({
project
,
outline
,
episodes
,
characters
,
projectId
,
onNavigate
,
}:
{
project
:
import
(
"../../lib/api/projects"
).
ProjectDTO
|
undefined
;
outline
:
import
(
"../../lib/api/ai"
).
Outline
|
undefined
;
episodes
:
import
(
"../../lib/api/ai"
).
Episode
[];
characters
:
import
(
"../../lib/api/ai"
).
Character
[];
projectId
?:
string
;
onNavigate
:
(
path
:
string
)
=>
void
;
})
{
const
orderedEpisodes
=
[...
episodes
].
sort
(
(
a
,
b
)
=>
(
a
.
episodeNumber
||
0
)
-
(
b
.
episodeNumber
||
0
)
);
const
episodeTotal
=
Math
.
max
(
outline
?.
episodeCount
??
0
,
orderedEpisodes
.
length
,
orderedEpisodes
.
length
===
0
?
60
:
0
);
const
[
selectedEpisodeNo
,
setSelectedEpisodeNo
]
=
useState
(
1
);
const
selectedEpisode
=
orderedEpisodes
.
find
((
ep
)
=>
ep
.
episodeNumber
===
selectedEpisodeNo
)
??
null
;
const
selectedNo
=
selectedEpisodeNo
;
const
uniqueTags
=
Array
.
from
(
new
Set
(
[
outline
?.
genre
,
project
?.
style
,
"爽文"
,
"高燃"
,
"悬疑"
,
"玄幻"
].
filter
(
Boolean
)
as
string
[]
)
).
slice
(
0
,
6
);
const
defaultSynopsis
=
"一场意外让主角卷入仙门与妖族的暗潮之中。看似平凡的手艺成为破局关键,少年在一次次试炼里识破阴谋、守住同伴,也逐渐揭开自己身世与旧日神工之间的联系。"
;
const
fallbackCharacters
=
[
{
id
:
"fallback-1"
,
name
:
"夏母"
,
roleType
:
"关键人物"
,
gender
:
""
,
age
:
"中年"
,
personality
:
"温和坚韧,遇事有主见"
,
costume
:
"朴素家常服"
,
},
{
id
:
"fallback-2"
,
name
:
"萧医师"
,
roleType
:
"推动剧情"
,
gender
:
""
,
age
:
"青年"
,
personality
:
"理性克制,暗藏锋芒"
,
costume
:
"青色长衫"
,
},
];
const
displayCharacters
=
characters
.
length
>
0
?
characters
.
slice
(
0
,
4
)
:
fallbackCharacters
;
const
nextPath
=
episodes
.
length
===
0
?
`/project/
${
projectId
}
/outline`
:
`/project/
${
projectId
}
/characters`
;
return
(
<
div
className=
"relative h-full overflow-auto bg-[#f7f8fb] text-[#111827]"
>
<
main
className=
"mx-auto max-w-[860px] px-6 pb-32 pt-12"
>
<
section
className=
"flex flex-col gap-8 sm:flex-row sm:items-start"
>
<
div
className=
"h-[218px] w-[154px] flex-shrink-0 overflow-hidden rounded-[3px] bg-[#dfe3ea] shadow-[0_18px_48px_rgba(15,23,42,0.14)]"
>
{
project
?.
coverUrl
?
(
<
img
src=
{
project
.
coverUrl
}
alt=
"项目封面"
className=
"h-full w-full object-cover"
/>
)
:
(
<
div
className=
"flex h-full w-full flex-col items-center justify-center bg-[linear-gradient(145deg,#262b36,#10131a)] text-white"
>
<
Film
className=
"mb-3 h-9 w-9 opacity-70"
/>
<
span
className=
"px-5 text-center text-sm font-semibold leading-5"
>
{
project
?.
name
??
outline
?.
title
??
"剧本封面"
}
</
span
>
</
div
>
)
}
</
div
>
<
div
className=
"min-w-0 flex-1 pt-1"
>
<
h1
className=
"mb-4 text-[28px] font-semibold leading-[1.22] tracking-normal text-[#0b1220]"
>
{
outline
?.
title
??
project
?.
name
??
"未命名项目"
}
</
h1
>
<
div
className=
"mb-5 grid grid-cols-2 gap-x-8 gap-y-2 text-[13px] leading-6 text-[#6b7280] sm:grid-cols-4"
>
<
span
>
集数:
{
outline
?.
episodeCount
??
episodeTotal
}
集
</
span
>
<
span
>
比例:
{
project
?.
aspectRatio
??
"16:9"
}
</
span
>
<
span
>
清晰度:
{
project
?.
resolution
??
"720p"
}
</
span
>
<
span
>
风格:
{
project
?.
style
??
outline
?.
genre
??
"待生成"
}
</
span
>
</
div
>
<
div
className=
"flex flex-wrap gap-2"
>
{
uniqueTags
.
map
((
tag
)
=>
(
<
span
key=
{
tag
}
className=
"rounded-full border border-[#e4e7ef] bg-white px-3 py-1 text-xs font-medium text-[#536079]"
>
{
tag
}
</
span
>
))
}
</
div
>
</
div
>
</
section
>
<
div
className=
"my-10 h-px bg-[#e6e8ef]"
/>
<
section
className=
"mb-11"
>
<
h2
className=
"mb-4 text-[17px] font-semibold text-[#0b1220]"
>
大纲
</
h2
>
<
p
className=
"text-[14px] leading-8 text-[#384152]"
>
{
outline
?.
synopsis
??
project
?.
description
??
defaultSynopsis
}
</
p
>
</
section
>
<
section
className=
"mb-12"
>
<
h2
className=
"mb-5 text-[17px] font-semibold text-[#0b1220]"
>
人物小传
</
h2
>
<
div
className=
"grid gap-x-12 gap-y-6 md:grid-cols-2"
>
{
displayCharacters
.
map
((
char
)
=>
(
<
article
key=
{
char
.
id
}
className=
"text-[14px] leading-7 text-[#384152]"
>
<
h3
className=
"mb-1 font-semibold text-[#111827]"
>
{
char
.
name
}
</
h3
>
<
p
>
{
char
.
roleType
?
`${char.roleType},`
:
""
}
{
char
.
gender
?
`${char.gender},`
:
""
}
{
char
.
age
?
`${char.age}。`
:
""
}
{
char
.
personality
||
"性格鲜明,与主线冲突和情感推进紧密相关。"
}
{
char
.
costume
?
` 服饰:${char.costume}。`
:
""
}
</
p
>
</
article
>
))
}
</
div
>
</
section
>
<
section
>
<
h2
className=
"mb-5 text-[17px] font-semibold text-[#0b1220]"
>
分集剧情
</
h2
>
<
div
className=
"mb-7 grid grid-cols-8 gap-3 sm:grid-cols-10 md:grid-cols-12"
>
{
Array
.
from
({
length
:
episodeTotal
},
(
_
,
index
)
=>
index
+
1
).
map
((
num
)
=>
(
<
button
key=
{
num
}
type=
"button"
onClick=
{
()
=>
setSelectedEpisodeNo
(
num
)
}
className=
{
`h-9 rounded-full text-sm font-medium transition-all ${
num === selectedNo
? "bg-[#111827] text-white shadow-[0_8px_18px_rgba(17,24,39,0.2)]"
: "bg-white text-[#4b5563] ring-1 ring-[#e3e6ee] hover:bg-[#f1f3f7]"
}`
}
>
{
num
}
</
button
>
))
}
</
div
>
<
article
className=
"text-[14px] leading-8 text-[#384152]"
>
<
h3
className=
"mb-2 font-semibold text-[#111827]"
>
第
{
selectedNo
}
集
{
selectedEpisode
?.
title
?
`:${selectedEpisode.title}`
:
""
}
</
h3
>
<
p
className=
"mb-4"
>
{
selectedEpisode
?.
summary
??
"本集承接上一段危机,主角在新的线索中发现关键异常,并由此推动人物关系、外部冲突和最终选择进一步升级。"
}
</
p
>
<
p
className=
"whitespace-pre-wrap"
>
{
selectedEpisode
?.
script
??
"镜头从喧闹的街巷切入,主角察觉到细微异动。几句试探之后,暗处的矛盾浮出水面,众人的立场开始分化。主角在压力下做出决定,为下一集的分镜和视觉呈现留下清晰的动作线。"
}
</
p
>
</
article
>
</
section
>
</
main
>
<
div
className=
"pointer-events-none fixed bottom-8 left-0 right-0 z-20 flex justify-center px-6"
>
<
div
className=
"pointer-events-auto flex w-full max-w-[520px] items-center justify-center"
>
<
button
type=
"button"
onClick=
{
()
=>
onNavigate
(
nextPath
)
}
className=
"h-12 min-w-[260px] rounded-full bg-[#0b0f17] px-10 text-[15px] font-semibold text-white shadow-[0_18px_46px_rgba(17,24,39,0.3)] transition-transform hover:scale-[1.01] active:scale-[0.99]"
>
{
episodes
.
length
===
0
?
"生成分集"
:
"进入下一步"
}
</
button
>
</
div
>
</
div
>
</
div
>
);
}
// ---- Outline Tab ----
// ---- Outline Tab ----
function
OutlineTab
({
function
OutlineTab
({
outline
,
outline
,
...
...
doc/html/src/app/pages/StoryboardWorkspace.tsx
View file @
fbe3f0e7
This diff is collapsed.
Click to expand it.
doc/html/src/styles/theme.css
View file @
fbe3f0e7
...
@@ -3,42 +3,42 @@
...
@@ -3,42 +3,42 @@
:root
{
:root
{
--font-size
:
14px
;
--font-size
:
14px
;
--background
:
#f8f9fa
;
--background
:
#f8f9fa
;
--foreground
:
#1
a1a1a
;
--foreground
:
#1
5131f
;
--card
:
#ffffff
;
--card
:
#ffffff
;
--card-foreground
:
#1
a1a1a
;
--card-foreground
:
#1
5131f
;
--popover
:
#ffffff
;
--popover
:
#ffffff
;
--popover-foreground
:
#1
a1a1a
;
--popover-foreground
:
#1
5131f
;
--primary
:
#5
b5ff9
;
--primary
:
#5
337c7
;
--primary-foreground
:
#ffffff
;
--primary-foreground
:
#ffffff
;
--secondary
:
#f
1f3f5
;
--secondary
:
#f
3eefe
;
--secondary-foreground
:
#
1a1a1a
;
--secondary-foreground
:
#
262238
;
--muted
:
#f8f9fa
;
--muted
:
#f8f9fa
;
--muted-foreground
:
#
6c757d
;
--muted-foreground
:
#
4b5864
;
--accent
:
#
e7f5ff
;
--accent
:
#
f3eefe
;
--accent-foreground
:
#
1971c2
;
--accent-foreground
:
#
5337c7
;
--destructive
:
#f03e3e
;
--destructive
:
#f03e3e
;
--destructive-foreground
:
#ffffff
;
--destructive-foreground
:
#ffffff
;
--border
:
rgba
(
0
,
0
,
0
,
0.06
)
;
--border
:
#e6eaed
;
--input
:
rgba
(
0
,
0
,
0
,
0.06
)
;
--input
:
#d8dae9
;
--input-background
:
#ffffff
;
--input-background
:
#ffffff
;
--switch-background
:
#dee2e6
;
--switch-background
:
#dee2e6
;
--font-weight-medium
:
500
;
--font-weight-medium
:
500
;
--font-weight-normal
:
400
;
--font-weight-normal
:
400
;
--ring
:
#5
b5ff9
;
--ring
:
#5
337c7
;
--chart-1
:
#5
b5ff9
;
--chart-1
:
#5
337c7
;
--chart-2
:
#
22b8cf
;
--chart-2
:
#
36bffa
;
--chart-3
:
#
51cf66
;
--chart-3
:
#
12b76a
;
--chart-4
:
#f
f6b6b
;
--chart-4
:
#f
04438
;
--chart-5
:
#f
cc419
;
--chart-5
:
#f
db022
;
--radius
:
0.
7
5rem
;
--radius
:
0.
62
5rem
;
--sidebar
:
#ffffff
;
--sidebar
:
#ffffff
;
--sidebar-foreground
:
#1
a1a1a
;
--sidebar-foreground
:
#1
5131f
;
--sidebar-primary
:
#5
b5ff9
;
--sidebar-primary
:
#5
337c7
;
--sidebar-primary-foreground
:
#ffffff
;
--sidebar-primary-foreground
:
#ffffff
;
--sidebar-accent
:
#f
8f9fa
;
--sidebar-accent
:
#f
3eefe
;
--sidebar-accent-foreground
:
#
1a1a1a
;
--sidebar-accent-foreground
:
#
5337c7
;
--sidebar-border
:
rgba
(
0
,
0
,
0
,
0.06
)
;
--sidebar-border
:
#e6eaed
;
--sidebar-ring
:
#5
b5ff9
;
--sidebar-ring
:
#5
337c7
;
}
}
.dark
{
.dark
{
...
@@ -188,3 +188,26 @@
...
@@ -188,3 +188,26 @@
line-height
:
1.5
;
line-height
:
1.5
;
}
}
}
}
@layer
components
{
.dramix-surface
{
background
:
#f8f9fa
;
}
.dramix-panel
{
border
:
1px
solid
#e6eaed
;
background
:
#ffffff
;
box-shadow
:
0
10px
30px
rgba
(
37
,
31
,
63
,
0.05
);
}
.dramix-primary
{
background
:
#8064f1
;
color
:
#ffffff
;
box-shadow
:
0
10px
22px
rgba
(
83
,
55
,
199
,
0.18
);
}
.dramix-primary
:hover
{
background
:
#7357e8
;
box-shadow
:
0
14px
28px
rgba
(
83
,
55
,
199
,
0.24
);
}
}
docker-compose.yml
View file @
fbe3f0e7
...
@@ -8,7 +8,7 @@ services:
...
@@ -8,7 +8,7 @@ services:
MYSQL_PASSWORD
:
${MYSQL_PASSWORD:-yaoai123}
MYSQL_PASSWORD
:
${MYSQL_PASSWORD:-yaoai123}
TZ
:
${TZ:-Asia/Shanghai}
TZ
:
${TZ:-Asia/Shanghai}
ports
:
ports
:
-
"
${MYSQL_PORT:-
2
3306}:3306"
-
"
${MYSQL_PORT:-
1
3306}:3306"
command
:
command
:
-
--character-set-server=utf8mb4
-
--character-set-server=utf8mb4
-
--collation-server=utf8mb4_unicode_ci
-
--collation-server=utf8mb4_unicode_ci
...
@@ -26,7 +26,7 @@ services:
...
@@ -26,7 +26,7 @@ services:
redis
:
redis
:
image
:
docker.m.daocloud.io/library/redis:7.2-alpine
image
:
docker.m.daocloud.io/library/redis:7.2-alpine
ports
:
ports
:
-
"
${REDIS_PORT:-
2
6379}:6379"
-
"
${REDIS_PORT:-
1
6379}:6379"
volumes
:
volumes
:
-
redis-data:/data
-
redis-data:/data
healthcheck
:
healthcheck
:
...
@@ -43,8 +43,7 @@ services:
...
@@ -43,8 +43,7 @@ services:
environment
:
environment
:
TZ
:
${TZ:-Asia/Shanghai}
TZ
:
${TZ:-Asia/Shanghai}
SPRING_PROFILES_ACTIVE
:
${SPRING_PROFILES_ACTIVE:-prod}
SPRING_PROFILES_ACTIVE
:
${SPRING_PROFILES_ACTIVE:-prod}
SERVER_PORT
:
8081
SERVER_PORT
:
8080
BILLING_MODE
:
${BILLING_MODE:-STRICT}
DB_URL
:
jdbc:mysql://mysql:3306/${MYSQL_DATABASE:-yaoai_comic}?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8
DB_URL
:
jdbc:mysql://mysql:3306/${MYSQL_DATABASE:-yaoai_comic}?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8
DB_USERNAME
:
${MYSQL_USER:-yaoai}
DB_USERNAME
:
${MYSQL_USER:-yaoai}
DB_PASSWORD
:
${MYSQL_PASSWORD:-yaoai123}
DB_PASSWORD
:
${MYSQL_PASSWORD:-yaoai123}
...
@@ -66,7 +65,7 @@ services:
...
@@ -66,7 +65,7 @@ services:
redis
:
redis
:
condition
:
service_healthy
condition
:
service_healthy
ports
:
ports
:
-
"
${BACKEND_PORT:-
28081}:8081
"
-
"
${BACKEND_PORT:-
8080}:8080
"
restart
:
unless-stopped
restart
:
unless-stopped
frontend
:
frontend
:
...
@@ -78,19 +77,7 @@ services:
...
@@ -78,19 +77,7 @@ services:
depends_on
:
depends_on
:
-
backend
-
backend
ports
:
ports
:
-
"
${FRONTEND_PORT:-3000}:3000"
-
"
${FRONTEND_PORT:-3000}:80"
restart
:
unless-stopped
admin-web
:
build
:
context
:
./yaoai-admin-web
dockerfile
:
Dockerfile
environment
:
ADMIN_API_UPSTREAM
:
http://backend:8081
depends_on
:
-
backend
ports
:
-
"
${ADMIN_WEB_PORT:-3001}:3001"
restart
:
unless-stopped
restart
:
unless-stopped
volumes
:
volumes
:
...
...
docs/superpowers/specs/2026-05-25-doc-html-figma-handoff.md
0 → 100644
View file @
fbe3f0e7
# doc/html Figma 对齐交接记录
日期:2026-05-25
## 项目范围
-
前端项目:
`doc/html`
-
Figma 文件:
`https://www.figma.com/design/sRNoRpGZrFlmC2Du8SOIXw/AI%E6%BC%AB%E5%89%A7%E5%B9%B3%E5%8F%B0`
-
主要目标:把
`doc/html`
的 AI 漫剧工作流页面尽量对齐 Figma 设计稿。
-
本地后端接口可用时,前端
`.env.local`
指向
`http://localhost:8080`
。
## Figma 覆盖的主要页面
Figma MCP 已确认主工作流 section 内包含:
-
`首页`
-
`剧本1`
、
`剧本2`
、
`剧本3`
、
`剧本4`
-
`角色库`
-
`新增角色`
-
`场景库`
-
`场景库-空`
-
`道具库`
-
`分镜1`
、
`分镜2`
、
`分镜3`
、
`分镜4`
、
`分镜5`
-
`视频`
-
`余量统计`
本地导入参考图位于:
-
`doc/html/src/imports/1项目入口.png`
-
`doc/html/src/imports/2选择风格.png`
-
`doc/html/src/imports/3生成大纲.png`
-
`doc/html/src/imports/3生成大纲2.png`
-
`doc/html/src/imports/4生成分集.png`
-
`doc/html/src/imports/5生成角色.png`
-
`doc/html/src/imports/6生成场景.png`
-
`doc/html/src/imports/7生成道具.png`
-
`doc/html/src/imports/8生成每集分镜.png`
-
`doc/html/src/imports/10生成视频.png`
## 已完成或已接近 Figma 的页面
### Layout
文件:
`doc/html/src/app/components/Layout.tsx`
-
顶部品牌改为
`Dramix.ai`
。
-
项目页工作流导航已改成 Figma 风格:
`剧本 / 设定 / 分镜 / 视频`
。
-
右侧加入类似 Figma 的优化建议和余额区域。
### 项目入口 / 新建项目
文件:
`doc/html/src/app/pages/NewProject.tsx`
-
已接近 Figma 的项目入口方向。
-
上传剧本区域、创建中遮罩、模式切换已有较完整结构。
### 剧本展示页
文件:
`doc/html/src/app/pages/ProjectDetail.tsx`
-
`/project/:projectId`
已改成接近
`3生成大纲.png`
和
`4生成分集.png`
的阅读式页面。
-
结构包括:封面、标题信息、标签、大纲、人物小传、分集剧情按钮网格、底部黑色 CTA。
-
分集按钮可切换显示当前集内容。
### 分镜工作台
文件:
`doc/html/src/app/pages/StoryboardWorkspace.tsx`
-
已按 Figma
`分镜1-5`
做过主结构改造。
-
已修复视频预览区第一条分镜
`001`
横向滚动看不到的问题。
-
已取消无数据时的“垫图/假分镜”:
-
没有分集时显示“暂无分集”
-
没有分镜时显示“暂无分镜数据”
-
视频生成区没有真实分镜时显示空状态
-
视频预览没有真实视频时显示空状态
-
缩略图只在真实
`storyboards`
存在时显示
### 角色编辑弹窗
文件:
`doc/html/src/app/pages/CharacterGeneration.tsx`
-
角色图片已从正面/侧面/背面三个上传框改成单张角色图。
-
编辑弹窗新增
`角色图片提示词`
。
-
提示词支持中文显示和一键生成中文提示词。
-
保存仍写回后端
`imagePrompt`
字段。
-
上传单张图时仍走现有
`front`
兼容接口,后端会同步主图字段。
## 还没优化到位的页面
### 选择风格页
文件:
`doc/html/src/app/pages/StyleSelection.tsx`
-
对应
`2选择风格.png`
。
-
当前仍是旧的大卡片网格 + 紫色渐变按钮。
-
需要对齐 Figma 的工作流页头和轻量选择布局。
### 大纲生成过程页
文件:
`doc/html/src/app/pages/OutlineGeneration.tsx`
-
对应 Figma
`剧本1 / 剧本2`
的生成过程。
-
当前是功能型处理页,有大 loading 卡、进度卡、手动编辑区。
-
视觉还未完全贴 Figma。
### 分集独立页
文件:
`doc/html/src/app/pages/EpisodeGeneration.tsx`
-
对应
`剧本4 / 生成分集`
。
-
`/project/:projectId`
已经做了 Figma 风格分集展示,但这个独立路由仍是旧列表页。
### 角色库列表页
文件:
`doc/html/src/app/pages/CharacterGeneration.tsx`
-
只优化了编辑弹窗。
-
角色列表本体仍有旧统计条、旧卡片和旧 tab。
-
需要对齐 Figma
`角色库`
与
`新增角色`
。
### 场景库页
文件:
`doc/html/src/app/pages/SceneGeneration.tsx`
-
对应 Figma 多个
`场景库 / 场景库-空`
节点。
-
当前仍是旧的“项目设定 + 统计条 + 三列卡片”结构。
-
这是下一轮建议最高优先级页面。
### 道具库页
文件:
`doc/html/src/app/pages/PropsGeneration.tsx`
-
对应
`道具库`
。
-
当前仍是旧资产库风格,并且接的是 global assets。
-
需要按 Figma 道具库重新组织布局。
### 视频页
文件:
`doc/html/src/app/pages/VideoGeneration.tsx`
-
对应
`10生成视频.png`
/ Figma
`视频`
。
-
当前还是旧的左选集 + 播放器 + 分镜列表结构。
-
需要整体对齐 Figma 视频管理/预览页。
## 推荐下一步顺序
1.
优先优化
`SceneGeneration.tsx`
2.
优化
`CharacterGeneration.tsx`
的角色列表主体
3.
优化
`VideoGeneration.tsx`
4.
优化
`StyleSelection.tsx`
和
`OutlineGeneration.tsx`
5.
优化
`PropsGeneration.tsx`
6.
最后处理
`EpisodeGeneration.tsx`
,或考虑让它跳转/复用
`ProjectDetail.tsx`
的分集区域
## 注意事项
-
不要回滚这些已有改动:
-
`doc/html/src/app/components/Layout.tsx`
-
`doc/html/src/app/pages/ProjectDetail.tsx`
-
`doc/html/src/app/pages/StoryboardWorkspace.tsx`
-
`doc/html/src/app/pages/CharacterGeneration.tsx`
-
`doc/html/src/app/pages/NewProject.tsx`
-
`doc/html/src/app/pages/Dashboard.tsx`
-
`doc/html/src/styles/theme.css`
-
当前工作区里有用户已有修改,继续开发前应先看
`git status --short`
。
-
验证命令:在
`doc/html`
下执行
`npm run build`
。
-
不要把账号密码写进仓库文件。需要登录时由用户重新提供,或使用当前会话上下文。
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment