auto-save 2026-05-13 19:56 (~4)
This commit is contained in:
@@ -2248,6 +2248,13 @@
|
||||
"type": "session-heartbeat",
|
||||
"message": "Codex 会话活跃 · 最近命令:codex · 4 项未提交变更 · 最近提交:auto-save 2026-05-13 19:45 (~3)",
|
||||
"files_changed": 4
|
||||
},
|
||||
{
|
||||
"ts": "2026-05-13T19:50:51+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-05-13 19:50 (~4)",
|
||||
"hash": "a471f89",
|
||||
"files_changed": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -830,6 +830,18 @@ api/main.py
|
||||
<h2>变更记录</h2>
|
||||
<p>这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。</p>
|
||||
<div class="changelog">
|
||||
<article class="change">
|
||||
<header>
|
||||
<h3>2026-05-13 · 钉住面板停靠到分镜头编排边缘</h3>
|
||||
<span class="tag orange">KeyframePanelNode</span>
|
||||
<span class="tag violet">StoryboardBar</span>
|
||||
</header>
|
||||
<div class="body">
|
||||
<p><strong>问题:</strong>关键帧详情钉住在浏览器左侧固定位置时,会遮挡顶部分镜头编排栏展开后的缩略图区域。</p>
|
||||
<p><strong>改动:</strong>给 <code>StoryboardBar</code> 增加稳定 DOM 标记;钉住面板实时读取该区域下边缘,并吸附到其下方。展开 / 折叠分镜头编排时,钉住面板自动让位。</p>
|
||||
<p><strong>影响:</strong><code>web/components/storyboard-bar.tsx</code>、<code>web/components/nodes/index.tsx</code>。</p>
|
||||
</div>
|
||||
</article>
|
||||
<article class="change">
|
||||
<header>
|
||||
<h3>2026-05-13 · 顶部分镜头编排不再跳转全屏工作台</h3>
|
||||
|
||||
@@ -517,10 +517,40 @@ export function KeyframePanelNode({ data }: any) {
|
||||
const { getZoom } = useReactFlow()
|
||||
const panelRef = useRef<HTMLDivElement>(null)
|
||||
const [pinRect, setPinRect] = useState<{ left: number; top: number }>({ left: 24, top: 72 })
|
||||
if (!d.job || d.expandedFrame === null) return null
|
||||
const active = d.job.frames.find((f) => f.index === d.expandedFrame)
|
||||
const scale = d.framePanelScale ?? 1
|
||||
const pinned = d.framePanelPinned ?? false
|
||||
|
||||
const getStoryboardDockTop = () => {
|
||||
if (typeof window === "undefined") return 64
|
||||
const bar = document.querySelector<HTMLElement>('[data-storyboard-bar="true"]')
|
||||
const bottom = bar?.getBoundingClientRect().bottom ?? 52
|
||||
return Math.max(56, Math.min(window.innerHeight - 120, bottom + 10))
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!pinned || typeof window === "undefined") return
|
||||
|
||||
const syncDock = () => {
|
||||
setPinRect({ left: 16, top: getStoryboardDockTop() })
|
||||
}
|
||||
|
||||
syncDock()
|
||||
const bar = document.querySelector<HTMLElement>('[data-storyboard-bar="true"]')
|
||||
let observer: ResizeObserver | null = null
|
||||
if (bar && "ResizeObserver" in window) {
|
||||
observer = new ResizeObserver(syncDock)
|
||||
observer.observe(bar)
|
||||
}
|
||||
window.addEventListener("resize", syncDock)
|
||||
|
||||
return () => {
|
||||
observer?.disconnect()
|
||||
window.removeEventListener("resize", syncDock)
|
||||
}
|
||||
}, [pinned])
|
||||
|
||||
if (!d.job || d.expandedFrame === null) return null
|
||||
const active = d.job.frames.find((f) => f.index === d.expandedFrame)
|
||||
const panelWidth = Math.round(760 * scale)
|
||||
const panelHeight = Math.round(746 * scale)
|
||||
const bodyHeight = Math.max(520, panelHeight - 27)
|
||||
@@ -533,7 +563,7 @@ export function KeyframePanelNode({ data }: any) {
|
||||
if (!pinned) {
|
||||
const zoom = getZoom()
|
||||
setScale(scale * zoom)
|
||||
setPinRect({ left: 16, top: 64 })
|
||||
setPinRect({ left: 16, top: getStoryboardDockTop() })
|
||||
}
|
||||
d.onFramePanelPinnedChange?.(!pinned)
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ export function StoryboardBar({ job, selectedFrames, focusedFrame, onFocusFrame
|
||||
|
||||
|
||||
return (
|
||||
<div className="relative z-20 flex-shrink-0 border-b border-white/5 bg-black/30 backdrop-blur-xl">
|
||||
<div data-storyboard-bar="true" className="relative z-20 flex-shrink-0 border-b border-white/5 bg-black/30 backdrop-blur-xl">
|
||||
{/* header */}
|
||||
<div className="flex items-center justify-between px-4 py-2">
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
|
||||
Reference in New Issue
Block a user