auto-save 2026-05-14 03:26 (~3)
This commit is contained in:
@@ -3037,6 +3037,19 @@
|
||||
"type": "session-heartbeat",
|
||||
"message": "Codex 会话活跃 · 最近命令:codex · 3 项未提交变更 · 最近提交:auto-save 2026-05-14 03:14 (~2)",
|
||||
"files_changed": 3
|
||||
},
|
||||
{
|
||||
"ts": "2026-05-14T03:20:46+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-05-14 03:20 (~4)",
|
||||
"hash": "2144c37",
|
||||
"files_changed": 4
|
||||
},
|
||||
{
|
||||
"ts": "2026-05-13T19:23:12Z",
|
||||
"type": "session-heartbeat",
|
||||
"message": "Claude 会话活跃 · 最近命令:claude · 1 项未提交变更 · 最近提交:auto-save 2026-05-14 03:20 (~4)",
|
||||
"files_changed": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -817,6 +817,18 @@ api/main.py
|
||||
<h2>变更记录</h2>
|
||||
<p>这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。</p>
|
||||
<div class="changelog">
|
||||
<article class="change">
|
||||
<header>
|
||||
<h3>2026-05-14 · 吸附工作面板贴近视口边缘</h3>
|
||||
<span class="tag violet">Canvas Panel</span>
|
||||
<span class="tag blue">Dock</span>
|
||||
</header>
|
||||
<div class="body">
|
||||
<p><strong>问题:</strong>视频抽帧面板吸附左侧 / 右侧时顶部仍留出明显空白;这不是实际板块遮挡,而是面板吸附样式里硬编码了 <code>top: 72</code> 和对应的高度预留。关键帧面板也保留了旧 storyboard 顶栏避让逻辑。</p>
|
||||
<p><strong>改动:</strong>新增统一吸附边距常量,视频抽帧面板和关键帧详情面板吸附时都贴近视口边缘,仅保留 8px 安全边距;移除关键帧面板对旧 <code>data-storyboard-dock</code> / <code>data-storyboard-bar</code> 的避让查询。</p>
|
||||
<p><strong>影响:</strong><code>web/components/nodes/index.tsx</code>、<code>docs/source-analysis.html</code>。后续画布工作面板的吸附语义统一为“贴边”,不是为顶部旧板块预留空间。</p>
|
||||
</div>
|
||||
</article>
|
||||
<article class="change">
|
||||
<header>
|
||||
<h3>2026-05-14 · 输入视频双击改为画布抽帧面板</h3>
|
||||
|
||||
@@ -115,6 +115,7 @@ function clamp(value: number, min: number, max: number) {
|
||||
}
|
||||
|
||||
const THUMBNAIL_HEIGHT = 176
|
||||
const FLOATING_PANEL_EDGE_INSET = 8
|
||||
|
||||
function canvasThumbnailAnchor(root: HTMLDivElement | null, target: HTMLElement) {
|
||||
if (!root) return { x: 160, y: 0 }
|
||||
@@ -727,7 +728,7 @@ export function VideoFramePanelNode({ data }: any) {
|
||||
width: panelWidth,
|
||||
height: panelHeight,
|
||||
maxWidth: "calc(100vw - 32px)",
|
||||
maxHeight: "calc(100vh - 84px)",
|
||||
maxHeight: `calc(100vh - ${FLOATING_PANEL_EDGE_INSET * 2}px)`,
|
||||
boxShadow: "0 30px 80px -20px rgba(0,0,0,0.75), 0 0 0 1px rgba(255,255,255,0.05)",
|
||||
}}
|
||||
>
|
||||
@@ -858,10 +859,10 @@ export function VideoFramePanelNode({ data }: any) {
|
||||
if (docked && typeof document !== "undefined") {
|
||||
const fixedStyle =
|
||||
dock === "left"
|
||||
? { left: 16, top: 72 }
|
||||
? { left: FLOATING_PANEL_EDGE_INSET, top: FLOATING_PANEL_EDGE_INSET }
|
||||
: dock === "right"
|
||||
? { right: 16, top: 72 }
|
||||
: { left: "50%", bottom: 16, transform: "translateX(-50%)" }
|
||||
? { right: FLOATING_PANEL_EDGE_INSET, top: FLOATING_PANEL_EDGE_INSET }
|
||||
: { left: "50%", bottom: FLOATING_PANEL_EDGE_INSET, transform: "translateX(-50%)" }
|
||||
return createPortal(
|
||||
<div className="fixed z-[240]" style={fixedStyle}>
|
||||
{panel}
|
||||
@@ -1486,39 +1487,16 @@ export function KeyframePanelNode({ data }: any) {
|
||||
const d: NodeData = data
|
||||
const { getZoom } = useReactFlow()
|
||||
const panelRef = useRef<HTMLDivElement>(null)
|
||||
const [pinRect, setPinRect] = useState<{ left: number; top: number }>({ left: 24, top: 72 })
|
||||
const [pinRect, setPinRect] = useState<{ left: number; top: number }>({
|
||||
left: FLOATING_PANEL_EDGE_INSET,
|
||||
top: FLOATING_PANEL_EDGE_INSET,
|
||||
})
|
||||
const scale = d.framePanelScale ?? 1
|
||||
const pinned = d.framePanelPinned ?? false
|
||||
|
||||
const getStoryboardDockTop = () => {
|
||||
if (typeof window === "undefined") return 64
|
||||
const dock = document.querySelector<HTMLElement>('[data-storyboard-dock="true"]')
|
||||
const bar = document.querySelector<HTMLElement>('[data-storyboard-bar="true"]')
|
||||
const bottom = (dock ?? 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-dock="true"]')
|
||||
?? 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)
|
||||
}
|
||||
if (!pinned) return
|
||||
setPinRect({ left: FLOATING_PANEL_EDGE_INSET, top: FLOATING_PANEL_EDGE_INSET })
|
||||
}, [pinned])
|
||||
|
||||
if (!d.job || d.expandedFrame === null) return null
|
||||
@@ -1535,7 +1513,7 @@ export function KeyframePanelNode({ data }: any) {
|
||||
if (!pinned) {
|
||||
const zoom = getZoom()
|
||||
setScale(scale * zoom)
|
||||
setPinRect({ left: 16, top: getStoryboardDockTop() })
|
||||
setPinRect({ left: FLOATING_PANEL_EDGE_INSET, top: FLOATING_PANEL_EDGE_INSET })
|
||||
}
|
||||
d.onFramePanelPinnedChange?.(!pinned)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user