diff --git a/.memory/worklog.json b/.memory/worklog.json index 5258033..501ac95 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -2927,6 +2927,19 @@ "type": "session-heartbeat", "message": "Claude 会话活跃 · 最近命令:claude · 1 项未提交变更 · 最近提交:auto-save 2026-05-14 02:30 (+2, ~4)", "files_changed": 1 + }, + { + "ts": "2026-05-14T02:36:34+08:00", + "type": "commit", + "message": "auto-save 2026-05-14 02:36 (~2)", + "hash": "2b7eb00", + "files_changed": 2 + }, + { + "ts": "2026-05-13T18:38:48Z", + "type": "session-heartbeat", + "message": "Codex 会话活跃 · 最近命令:codex · 4 项未提交变更 · 最近提交:auto-save 2026-05-14 02:36 (~2)", + "files_changed": 4 } ] } diff --git a/docs/source-analysis.html b/docs/source-analysis.html index f4dc303..e041afa 100644 --- a/docs/source-analysis.html +++ b/docs/source-analysis.html @@ -815,6 +815,18 @@ api/main.py

变更记录

这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。

+
+
+

2026-05-14 · 缩略图滑动条改为大号可拖轨道

+ Canvas + Thumbnail +
+
+

问题:节点上方缩略图横排内容多时,原生横向滚动条太细且在画布缩放下不容易点中,用户很难拖动。

+

改动:新增 FloatingThumbnailStrip / ThumbnailScrollRail,在缩略图下方显示明显的大号紫色拖动轨道;轨道支持点击跳转、按住拖动和键盘左右移动,并用 nodrag nopan 避免误触发画布拖拽。

+

影响:web/components/nodes/index.tsxweb/app/globals.cssdocs/source-analysis.html。Input、VisualLab 以及保留的旧视觉节点缩略图条共用同一交互。

+
+

2026-05-14 · 三个视觉节点合并为画面工作台

diff --git a/web/app/globals.css b/web/app/globals.css index d550dd8..ea5bbf9 100644 --- a/web/app/globals.css +++ b/web/app/globals.css @@ -409,3 +409,23 @@ .react-flow__node .overflow-x-auto::-webkit-scrollbar-thumb:active { background: rgba(217, 70, 239, 0.95); } + +.react-flow__node .thumbnail-strip { + scrollbar-width: none; + overscroll-behavior-x: contain; +} + +.react-flow__node .thumbnail-strip::-webkit-scrollbar { + width: 0; + height: 0; + display: none; +} + +.react-flow__node .thumbnail-scroll-rail { + touch-action: none; +} + +.react-flow__node .thumbnail-scroll-rail:hover > div, +.react-flow__node .thumbnail-scroll-rail:focus-visible > div { + background: rgb(216, 180, 254); +} diff --git a/web/components/nodes/index.tsx b/web/components/nodes/index.tsx index 2bedfd9..4fc4a4d 100644 --- a/web/components/nodes/index.tsx +++ b/web/components/nodes/index.tsx @@ -149,7 +149,7 @@ function ThumbnailScrollRail({ const max = Math.max(0, el.scrollWidth - el.clientWidth) const visible = max > 2 const widthPct = visible - ? Math.max(18, Math.min(92, (el.clientWidth / Math.max(el.scrollWidth, 1)) * 100)) + ? Math.max(26, Math.min(92, (el.clientWidth / Math.max(el.scrollWidth, 1)) * 100)) : 100 const leftPct = visible ? (el.scrollLeft / max) * (100 - widthPct) : 0 const next = { @@ -207,10 +207,10 @@ function ThumbnailScrollRail({ aria-valuemax={rail.max} aria-valuenow={rail.now} tabIndex={0} - className={`thumbnail-scroll-rail nodrag nopan relative mt-2 h-5 rounded-full border shadow-[0_10px_24px_rgba(0,0,0,0.28)] outline-none transition ${ + className={`thumbnail-scroll-rail nodrag nopan relative mt-2.5 h-9 rounded-full border shadow-[0_14px_28px_rgba(0,0,0,0.34),0_0_0_1px_rgba(255,255,255,0.08)] outline-none transition ${ dragging - ? "cursor-grabbing border-violet-200/80 bg-violet-950/80 ring-2 ring-violet-300/70" - : "cursor-grab border-white/20 bg-black/50 hover:border-violet-300/75 hover:bg-violet-950/60 focus-visible:border-violet-200 focus-visible:ring-2 focus-visible:ring-violet-300/70" + ? "cursor-grabbing border-violet-100/95 bg-violet-500/55 ring-2 ring-violet-100/80" + : "cursor-grab border-violet-200/70 bg-violet-500/32 hover:border-violet-100/90 hover:bg-violet-400/45 focus-visible:border-violet-100 focus-visible:ring-2 focus-visible:ring-violet-100/80" }`} onPointerDown={(e) => { const el = scrollRef.current @@ -282,7 +282,7 @@ function ThumbnailScrollRail({ }} >
@@ -1537,10 +1537,7 @@ export function StoryboardNode({ data, selected }: any) {
{/* 节点上方:所有元素 crop 图(编排输入素材)— 视觉类节点统一样板:单行横滚 + 左上复制 + 右上删除 + hover/click pin 大预览 */} {elementCrops.length > 0 && job && ( -
+ {elementCrops.map((p) => { const key = `${p.frameIdx}_${p.elementId}` return ( @@ -1606,7 +1603,7 @@ export function StoryboardNode({ data, selected }: any) {
) })} -
+ )} {(() => { @@ -1686,10 +1683,7 @@ export function VideoGenNode({ data, selected }: any) { return (
{videos.length > 0 && ( -
+ {videos.map((v, i) => { const videoSrc = apiAssetUrl(v.url) const posterSrc = apiAssetUrl(v.poster_url) @@ -1771,7 +1765,7 @@ export function VideoGenNode({ data, selected }: any) {
)})} -
+ )} {(() => { if (!hoverPreviewVideo) return null