feat(timeline): P2 并行编排——scene 多轨编辑器 + 白模重叠预览

剧情 Timeline P2 前端 + 共享内核(与 SGame 源真同步):
- ir_core/IR_SCHEMA/样张:scene v0.3 + scene 校验 + 导出 gate(D3),与 SGame 仓逐字一致
- timeline.js:appendScene 按 authored start 铺多轨 clip(自然重叠预览),move from 同 actor 跨轨续连(D4);
  drawStage 改逐 actor 查对话→多人气泡同时计时;导出 _clipDur 纯函数;show() 加 startId 参;常量加 CAMERA_DUR
- scene_edit.js(新):演出段编辑模态——拖 clip 改 start(吸附 0.1s)、拖右缘改 dur、增删 clip/轨道、
  选中属性条精确编辑、客户端轻量 lint(镜像 validate.py)、▶ 预览此段(复用播放核)
- graph.js:scene 节点(KIND_CN/summary/nodeInner 列轨道)+双击进编辑模态
- form.js:右栏 renderScene 精确数值编辑(轨道/clip 的 start/dur/kind/目标)+打开编辑器按钮
- app.py export:捕获 CompileError 并入 report(scene 被拦时不再 500)
- test_scene.js:离线 10 断言全过(重叠确凿/晚 1.5s 起步/from 续连);gitignore 忽略本地 _localdemo.db

待浏览器目测拖拽编辑落 IR + 白模重叠演出。
This commit is contained in:
2026-06-13 22:34:29 +08:00
parent 06e639f0df
commit 021080dd56
14 changed files with 841 additions and 16 deletions

View File

@ -325,3 +325,62 @@ body.perform-mode .mode-switch { border-color:#2f7a60; }
.tl-clip.k-stop { background:#d87878; color:#fff; }
.tl-playhead { position:absolute; top:0; bottom:0; width:2px; background:#ff5a4a;
pointer-events:none; z-index:4; box-shadow:0 0 4px rgba(255,90,74,.7); }
/* ===== 演出段 scene 节点(分支图)===== */
#drawflow .kind-scene { background:#16241f; border-color:#3a7a64; }
#drawflow .kind-scene .nlabel { color:#7ee3c8; }
.drawflow-node .scenebody { padding-top:2px; }
.drawflow-node .scenebody .screl { font-size:11px; color:#bfe6d8; line-height:1.5; }
.drawflow-node .scenebody .screl.more { color:#7a8a82; }
.drawflow-node .scenebody .schint { font-size:10px; color:#5a8a7a; margin-top:3px; font-style:italic; }
/* ===== 演出段编辑模态 ===== */
.scene-edit { width:92vw; max-width:1280px; max-height:92vh; }
.se-psinfo { font-size:12px; color:#9a907e; font-weight:normal; }
.se-toolbar { display:flex; align-items:center; gap:10px; margin:4px 0 8px; flex-wrap:wrap; }
.se-toolbar .se-tip { font-size:11px; color:#8a8275; }
.se-toolbar .se-total { margin-left:auto; font-size:12px; color:#e6c878; }
.se-trackswrap { overflow-x:auto; background:#15130d; border:1px solid #2a2419; border-radius:6px; padding-bottom:4px; }
.se-ruler { position:relative; height:16px; border-bottom:1px solid #2a2419; min-width:100%; }
.se-tick { position:absolute; top:0; height:16px; border-left:1px solid #2a2419; }
.se-tick span { font-size:9px; color:#6a6256; padding-left:3px; }
.se-lanes { position:relative; min-width:100%; }
.se-lane { position:relative; border-bottom:1px solid #211c15; }
.se-lane-label { position:sticky; left:0; z-index:5; display:inline-flex; align-items:center; gap:3px;
width:54px; height:100%; background:#1d1810; color:#d8cda0; font-size:11px; padding:0 3px;
border-right:1px solid #2a2419; box-sizing:border-box; }
.se-lane-label button { background:none; border:none; color:#9a8f78; cursor:pointer; font-size:11px; padding:0 1px; }
.se-lane-label .se-addclip:hover { color:#7ee3c8; }
.se-lane-label .se-deltrack:hover { color:#e38f7e; }
.se-clip { position:absolute; top:5px; height:24px; line-height:24px; padding:0 6px; border-radius:4px;
font-size:11px; color:#1a1710; white-space:nowrap; overflow:hidden; cursor:grab; user-select:none;
box-shadow:0 1px 3px rgba(0,0,0,.5); }
.se-clip.sel { box-shadow:0 0 0 2px #ff5a4a, 0 1px 3px rgba(0,0,0,.5); z-index:3; }
.se-clip.k-dialogue { background:#7ec8e3; }
.se-clip.k-move { background:#e6c878; }
.se-clip.k-anim { background:#9ee37e; }
.se-clip.k-camera { background:#c89ee3; }
.se-clip.k-wait { background:#9a9488; color:#fff; }
.se-resize { position:absolute; top:0; right:0; width:7px; height:100%; cursor:ew-resize; background:rgba(0,0,0,.18); border-radius:0 4px 4px 0; }
.se-empty, .se-prop-empty { padding:14px; color:#8a8275; font-size:12px; text-align:center; }
.se-lint { margin:8px 0 4px; font-size:11.5px; padding:5px 8px; border-radius:5px; }
.se-lint.ok { color:#7ee3a0; background:rgba(60,120,80,.12); }
.se-lint.bad { color:#f0b070; background:rgba(160,90,40,.14); }
.se-prop { background:#19150f; border:1px solid #2a2419; border-radius:6px; padding:8px; min-height:40px; }
.se-prop-row { display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.se-prop-kind { font-size:12px; color:#e6c878; font-weight:bold; }
.se-f { font-size:11px; color:#a89e88; display:flex; align-items:center; gap:4px; }
.se-f input, .se-f select { width:auto; min-width:64px; background:#221d16; color:#e8e0d4; border:1px solid #3a3326; border-radius:4px; padding:2px 4px; font-size:12px; }
.se-f input[type=number] { width:64px; }
.se-delclip { margin-left:auto; color:#e38f7e !important; }
.se-preview { margin-top:10px; }
.se-prev-head { font-size:12px; color:#7ee3c8; margin-bottom:4px; }
.se-prev-host { height:560px; background:#100e09; border:1px solid #2a2419; border-radius:6px; overflow:hidden; position:relative; }
/* scene 右栏 clip 行form.js 精确数值编辑) */
.se-cliprow { display:flex; align-items:flex-end; gap:5px; flex-wrap:wrap; padding:5px 0; border-top:1px dashed #2a2419; }
.se-cliprow .fld { margin:0; }
.se-cliprow .fld label { font-size:10px; }
.se-cliprow input[type=number] { width:52px; }
.se-cliprow select { min-width:60px; }
#edit-pane .optdet-like { border:1px solid #2a2419; border-radius:5px; padding:6px; margin-bottom:6px; }