diff --git a/.memory/worklog.json b/.memory/worklog.json
index bf9fc0a..f7df35a 100644
--- a/.memory/worklog.json
+++ b/.memory/worklog.json
@@ -2255,6 +2255,19 @@
"message": "auto-save 2026-05-13 19:50 (~4)",
"hash": "a471f89",
"files_changed": 4
+ },
+ {
+ "ts": "2026-05-13T19:56:22+08:00",
+ "type": "commit",
+ "message": "auto-save 2026-05-13 19:56 (~4)",
+ "hash": "28de936",
+ "files_changed": 4
+ },
+ {
+ "ts": "2026-05-13T11:59:29Z",
+ "type": "session-heartbeat",
+ "message": "Codex 会话活跃 · 最近命令:codex · 6 项未提交变更 · 最近提交:auto-save 2026-05-13 19:56 (~4)",
+ "files_changed": 6
}
]
}
diff --git a/docs/source-analysis.html b/docs/source-analysis.html
index d2641da..a1107e8 100644
--- a/docs/source-analysis.html
+++ b/docs/source-analysis.html
@@ -573,7 +573,7 @@
web/components/nodes/index.tsx | DAG 节点定义:Input、Keyframe、ASR、Translate、Rewrite、Storyboard、VideoGen、Compose。 |
web/components/lightbox.tsx | 镜头拆解和元素提取的主工作面板:清洗、识别、元素编辑、区域提取、抠图。 |
web/components/storyboard-bar.tsx | 顶部已选分镜条:展示选入编排的关键帧,点击进入工作台。 |
- web/components/storyboard-workbench.tsx | 全屏分镜编排工作台:4 图槽、改造目标、时长、自动保存。 |
+ web/components/storyboard-workbench.tsx | 顶部分镜编排内嵌面板:4 图槽、改造目标、时长、自动保存。 |
web/lib/api.ts | 前端类型和 API client,是前后端数据契约镜像。 |
@@ -636,7 +636,7 @@ api/main.py
适合怎么描述“选好的分镜如何按时间序组织,以及如何进入具体分镜编排”。
-
你看到的区域分镜头编排工作台
+
你看到的区域顶部分镜头编排下拉面板
主要源码StoryboardWorkbench;保存到 frame.storyboard;接口 PUT /storyboard。
适合怎么描述“每个分镜需要哪些图片槽、哪些改造说明,如何为视频生成做准备”。
@@ -813,7 +813,7 @@ api/main.py
改分镜工作台
-
“我在全屏分镜编排工作台,每个分镜需要哪些槽位、字段如何命名、保存后如何传给后续生成视频。”
+
“我在顶部分镜头编排下拉面板,每个分镜需要哪些槽位、字段如何命名、保存后如何传给后续生成视频。”
改数据/接口
@@ -830,6 +830,18 @@ api/main.py
变更记录
这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。
+
+
+ 2026-05-13 · 分镜头编排工作台改为内嵌下拉
+ StoryboardWorkbench
+ StoryboardBar
+
+
+
问题:元素改造节点等入口仍会打开 fixed inset-0 的全屏 StoryboardWorkbench,用户感觉像跳转页面。
+
改动:移除 StoryboardWorkbench 的 portal 全屏承载方式,改为渲染在顶部分镜栏下方;所有“打开编排”入口只展开这个内嵌区域。
+
影响:web/components/storyboard-workbench.tsx、web/components/storyboard-bar.tsx、web/app/page.tsx、web/components/nodes/index.tsx。
+
+
2026-05-13 · 钉住面板停靠到分镜头编排边缘
@@ -849,8 +861,8 @@ api/main.py
问题:顶部 StoryboardBar 的“进入编排”和分镜缩略图点击会打开全屏 StoryboardWorkbench,打断当前画布流程。
-
改动:顶部按钮改为“展开编排”,只下拉展示当前分镜列表;缩略图点击只聚焦该分镜,不再触发全屏跳转。
-
影响:web/components/storyboard-bar.tsx、web/app/page.tsx;全屏工作台仍保留给其他明确入口。
+
改动:顶部按钮改为“展开编排”,只下拉展示当前分镜列表;缩略图点击只聚焦该分镜,不再触发全屏跳转。后续已把工作台整体改成内嵌下拉,见上方最新记录。
+
影响:web/components/storyboard-bar.tsx、web/app/page.tsx。
diff --git a/web/app/page.tsx b/web/app/page.tsx
index 5bd2cc3..78e2972 100644
--- a/web/app/page.tsx
+++ b/web/app/page.tsx
@@ -408,12 +408,27 @@ export default function Home() {
{/* 右区:顶部 storyboard bar + DAG 节点流图 */}
-
+
+ {
+ if (typeof idx === "number") setStoryboardFrame(idx)
+ setWorkbenchOpen(true)
+ }}
+ />
+ setWorkbenchOpen(false)}
+ onJobUpdate={setJob as any}
+ clipboard={clipboard}
+ focusedFrame={storyboardFrame}
+ />
+
- {/* 分镜头编排工作台 — 全屏覆盖 DAG */}
-
setWorkbenchOpen(false)}
- onJobUpdate={setJob as any}
- clipboard={clipboard}
- focusedFrame={storyboardFrame}
- />
-
>
)
diff --git a/web/components/nodes/index.tsx b/web/components/nodes/index.tsx
index 45f20d1..196b7ce 100644
--- a/web/components/nodes/index.tsx
+++ b/web/components/nodes/index.tsx
@@ -37,8 +37,8 @@ export interface NodeData {
onDeleteFrame?: (idx: number) => void // 删整张关键帧
onDeleteGenerated?: (frameIdx: number, genId: string) => void // 删单张生成图
onOpenStoryboard?: (frameIdx: number) => void // 打开分镜头编排专属面板
- onOpenWorkbench?: (frameIdx?: number) => void // 打开全屏分镜编排工作台
- onCopyImage?: (ref: ImageRef) => void // 复制图片到全局剪贴板(粘贴到分镜头编排工作台插槽)
+ onOpenWorkbench?: (frameIdx?: number) => void // 展开顶部分镜编排内嵌面板
+ onCopyImage?: (ref: ImageRef) => void // 复制图片到全局剪贴板(粘贴到分镜头编排插槽)
}
/* ---- 状态映射工具 ---- */
@@ -522,8 +522,9 @@ export function KeyframePanelNode({ data }: any) {
const getStoryboardDockTop = () => {
if (typeof window === "undefined") return 64
+ const dock = document.querySelector('[data-storyboard-dock="true"]')
const bar = document.querySelector('[data-storyboard-bar="true"]')
- const bottom = bar?.getBoundingClientRect().bottom ?? 52
+ const bottom = (dock ?? bar)?.getBoundingClientRect().bottom ?? 52
return Math.max(56, Math.min(window.innerHeight - 120, bottom + 10))
}
@@ -535,7 +536,8 @@ export function KeyframePanelNode({ data }: any) {
}
syncDock()
- const bar = document.querySelector('[data-storyboard-bar="true"]')
+ const bar = document.querySelector('[data-storyboard-dock="true"]')
+ ?? document.querySelector('[data-storyboard-bar="true"]')
let observer: ResizeObserver | null = null
if (bar && "ResizeObserver" in window) {
observer = new ResizeObserver(syncDock)
diff --git a/web/components/storyboard-bar.tsx b/web/components/storyboard-bar.tsx
index a21b18f..8fdc8fc 100644
--- a/web/components/storyboard-bar.tsx
+++ b/web/components/storyboard-bar.tsx
@@ -9,9 +9,10 @@ interface Props {
selectedFrames: Set
focusedFrame: number | null
onFocusFrame: (idx: number | null) => void
+ onOpenWorkbench?: (frameIdx?: number) => void
}
-export function StoryboardBar({ job, selectedFrames, focusedFrame, onFocusFrame }: Props) {
+export function StoryboardBar({ job, selectedFrames, focusedFrame, onFocusFrame, onOpenWorkbench }: Props) {
const [collapsed, setCollapsed] = useState(false)
const [mounted, setMounted] = useState(false)
useEffect(() => setMounted(true), [])
@@ -64,8 +65,10 @@ export function StoryboardBar({ job, selectedFrames, focusedFrame, onFocusFrame
,
- document.body,
+
)
}