Files
story-edit-web/web/static/style.css
邓雨鹏 e6ec743564 feat(timeline): 交互式分支演出 + 从任意节点开始 + 时间轴分面板/自适应
- 交互分支:播到选择/战斗/随机节点暂停,在镜头下方弹按钮手选,点完接对应分支续演
- 从任意节点开始:单击节点选中→「从选中处开始」或双击节点,立即从该处播放;
  起始位置按从开头到该节点途中的走位重放算出(角色站在应有位置),已离线验证
- 布局分两块(上舞台/下时间轴),时间轴 playhead 仅播放时居中、暂停可自由滚
- 时间轴自适应宽度按钮(整条压进可视区)+鼠标滚轮横向滚动,解决长时间轴够不着节点
- 演示事件 QY_TLDEMO 扩充两条分支(和解→随机夜晚/拔剑→战斗胜败),去掉对焦NP1
2026-06-13 18:54:32 +08:00

293 lines
18 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

* { box-sizing: border-box; }
body { margin:0; font-family:"Microsoft YaHei","PingFang SC",sans-serif;
background:#161310; color:#e8e0d4; height:100vh; overflow:hidden;
display:flex; flex-direction:column; }
/* ---- header / toolbar ---- */
header { padding:10px 18px; background:#1f1a15; border-bottom:1px solid #3a322a;
display:flex; align-items:center; gap:18px; flex:none; }
header h1 { margin:0; font-size:18px; color:#e6c878; white-space:nowrap; }
header h1 .ver { font-size:12px; color:#9a8f7e; }
.toolbar { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.toolbar .sep { width:1px; height:20px; background:#3a322a; margin:0 4px; }
.toolbar .who { margin-left:10px; font-size:12px; color:#9a8f7e; }
button { background:#3a3024; color:#e6c878; border:1px solid #5a4a32;
padding:6px 13px; border-radius:5px; cursor:pointer; font-size:13px; }
button:hover:not(:disabled) { background:#4a3d2c; }
button:disabled { opacity:.4; cursor:not-allowed; }
button.primary { background:#5a4a26; border-color:#8a7038; color:#f3dca0; }
button.mini { padding:2px 8px; font-size:12px; }
/* ---- layout ---- */
#wrap { display:flex; flex:1; min-height:0; }
#list-pane { width:250px; background:#19150f; border-right:1px solid #3a322a;
display:flex; flex-direction:column; flex:none; }
.filters { padding:10px; display:flex; gap:6px; border-bottom:1px solid #3a322a; }
.filters select, .filters input, #login input, #import-text {
background:#241f18; color:#e8e0d4; border:1px solid #4a4030; border-radius:4px;
padding:6px 8px; font-size:13px; }
.filters select { flex:none; }
.filters input { flex:1; min-width:0; }
#event-list { overflow:auto; flex:1; }
.ev { padding:9px 12px; border-bottom:1px solid #241f18; cursor:pointer; }
.ev:hover { background:#221d16; }
.ev.sel { background:#2a2316; border-left:3px solid #e6c878; padding-left:9px; }
.ev .t { font-size:13.5px; color:#ddd3c2; }
.ev .g { font-size:11px; color:#7a7264; margin-top:2px; }
.ev .badge { float:right; font-size:11px; padding:1px 7px; border-radius:9px; }
.b-pending { background:#3a3320; color:#d8c060; }
.b-confirmed{ background:#1f3a24; color:#7ad88a; }
.b-discarded{ background:#3a2020; color:#d88; }
#graph-pane { flex:1; min-width:0; display:flex; flex-direction:column; position:relative; }
#graph-tools { flex:none; display:flex; align-items:center; gap:8px; padding:6px 10px;
background:#19150f; border-bottom:1px solid #3a322a; }
#graph-tools .tip { font-size:11.5px; color:#7a7264; }
#graph-tools .gsep { width:1px; height:16px; background:#3a322a; margin:0 3px; }
#drawflow { position:relative; flex:1; min-height:0; background:#161310;
background-image:radial-gradient(#2a2419 1.1px, transparent 1.1px);
background-size:22px 22px; }
.empty-center { position:absolute; inset:0; display:flex; align-items:center;
justify-content:center; color:#6a6256; font-size:15px; pointer-events:none; }
#edit-pane { width:370px; background:#1c1813; border-left:1px solid #3a322a;
overflow:auto; flex:none; }
.sec-title { padding:10px 14px; font-size:13px; color:#9a8f7e;
border-bottom:1px solid #3a322a; background:#19150f; letter-spacing:1px;
display:flex; justify-content:space-between; align-items:center; }
#meta-edit, #node-edit { padding:12px 14px; }
.empty { color:#6a6256; font-size:13px; padding:8px 0; }
/* ---- nodes (tree) ---- */
.node { background:#262019; border:1.5px solid #4a4030; border-radius:9px;
padding:9px 12px; width:188px; cursor:pointer; transition:.15s;
box-shadow:0 2px 6px rgba(0,0,0,.4); position:absolute; }
.node:hover { border-color:#e6c878; transform:translateY(-2px); }
.node.sel { border-color:#e6c878; box-shadow:0 0 0 2px rgba(230,200,120,.4); z-index:20; }
.node-acts { position:absolute; display:flex; gap:4px; z-index:30; transform:translateX(-100%); }
.nact { border:none; border-radius:5px; font-size:11px; line-height:1; padding:4px 6px;
cursor:pointer; color:#161310; font-weight:bold; box-shadow:0 1px 3px rgba(0,0,0,.5); }
.nact.add { background:#e6c878; }
.nact.add:hover { background:#f0d68a; }
.nact.del { background:#d87878; color:#fff; }
.nact.del:hover { background:#e89090; }
.node .k { font-size:11px; color:#b89a5a; font-weight:bold; }
.node .t { font-size:13px; margin-top:3px; line-height:1.4; color:#ddd3c2; }
.node.kind-ending { background:#3a2a17; border-color:#e0a850; }
.node.kind-ending .k { color:#f2c463; }
.node.kind-ending .rw { font-size:11.5px; color:#c9a86a; margin-top:4px;
border-top:1px dashed #6a5630; padding-top:4px; }
.node.kind-fight { border-color:#7a4a4a; background:#2a1c1c; }
.node.kind-fight .k { color:#d87878; }
.node.kind-out_ref { border-style:dashed; border-color:#7a7ad8; background:#1d1d2a; }
.node.kind-out_ref .k { color:#9e9ef0; }
.node.kind-choice, .node.kind-choice_once {
background:#1d2840; border:none; padding-left:26px; padding-right:26px;
clip-path: polygon(16px 0, calc(100% - 16px) 0, 100% 50%, calc(100% - 16px) 100%, 16px 100%, 0 50%); }
.node.kind-choice .k, .node.kind-choice_once .k { color:#9ec0f0; }
/* ---- Drawflow 节点(拖拽连线版) ---- */
#drawflow .drawflow-node { background:#262019; border:1.5px solid #4a4030; border-radius:9px;
padding:8px 12px; width:192px; color:#e8e0d4; box-shadow:0 2px 6px rgba(0,0,0,.4); }
#drawflow .drawflow-node:hover { border-color:#e6c878; }
#drawflow .drawflow-node.selected { border-color:#e6c878; box-shadow:0 0 0 2px rgba(230,200,120,.45); }
#drawflow .drawflow_content_node { width:100%; }
/* 顶边框标牌legend边框在文字处断开 */
#drawflow .drawflow_content_node { position:relative; }
.drawflow-node .nlabel { position:absolute; top:-18px; left:50%; transform:translateX(-50%);
background:#161310; padding:0 7px; font-size:11.5px; font-weight:bold; white-space:nowrap; color:#b89a5a; }
.drawflow-node .nbody { padding-top:3px; }
.drawflow-node .who { font-size:11px; color:#b89a5a; margin-bottom:2px; }
.drawflow-node .t { font-size:12.5px; line-height:1.35; color:#ddd3c2; word-break:break-all; }
.drawflow-node .rw { font-size:11px; color:#c9a86a; margin-top:4px;
border-top:1px dashed #6a5630; padding-top:4px; }
#drawflow .kind-ending { background:#3a2a17; border-color:#e0a850; }
#drawflow .kind-ending .nlabel { color:#f2c463; }
#drawflow .kind-fight { border-color:#7a4a4a; background:#2a1c1c; }
#drawflow .kind-fight .nlabel { color:#d87878; }
#drawflow .kind-out_ref { border-style:dashed; border-color:#7a7ad8; background:#1d1d2a; }
#drawflow .kind-out_ref .nlabel { color:#9e9ef0; }
#drawflow .kind-choice, #drawflow .kind-choice_once { background:#1d2840; border-color:#3a527a; }
#drawflow .kind-choice .nlabel, #drawflow .kind-choice_once .nlabel { color:#9ec0f0; }
#drawflow .kind-random .nlabel { color:#c0a0e0; }
/* 端口圆点 */
#drawflow .drawflow-node .input, #drawflow .drawflow-node .output {
background:#e6c878; border:2px solid #161310; width:15px; height:15px; }
#drawflow .drawflow-node .input:hover, #drawflow .drawflow-node .output:hover { background:#f0d68a; }
/* 输出端口纵向行距=选项行距:默认是普通块流,相邻端口 margin-bottom/margin-top 会“外边距折叠”→行距仍是 20px
与 .ch-opt 的 25px 错位(越往下越偏)。把 .outputs 改成 flex 纵向列flex 项目外边距不折叠),
再给每个端口 margin:5px 0 → 端口格子 5+15+5=25px、圆点居中与各选项行逐行平齐任意选项数通用。 */
#drawflow .drawflow-node .outputs { display:flex; flex-direction:column; }
#drawflow .drawflow-node .output { margin:5px 0; }
/* 连线 */
#drawflow .connection .main-path { stroke:#7a96c8; stroke-width:2.4px; }
#drawflow .connection .main-path:hover { stroke:#e6c878; }
/* 开头节点标识 */
#drawflow .drawflow-node.isstart { border-color:#7ad88a; }
.drawflow-node .startflag { color:#7ad88a; font-weight:bold; }
/* 多出口节点每出口一行唯一流内容→垂直居中→与居中黄点逐行平齐top:2px 对齐端口偏移 */
.ch-opts { position:relative; top:2px; }
.ch-opt { height:25px; line-height:25px; text-align:right; font-size:12px; color:#dfe7f2;
overflow:hidden; text-overflow:ellipsis; white-space:nowrap; padding-right:3px; }
/* 战斗节点:左右两栏——左栏战斗描述(填满左侧大空间、可换行、垂直居中),右栏胜/败出口行 */
.drawflow-node .fbody { display:flex; align-items:flex-end; gap:10px; }
.drawflow-node .fbody .fdesc { flex:1; min-width:0; align-self:center; font-size:12px;
color:#e6b8a0; line-height:1.45; text-align:left; word-break:break-all; }
.drawflow-node .fbody .ch-opts { flex:none; }
/* 描述使内容变高,故让胜/败两个黄点贴底部两行,与右栏出口行逐行对齐 */
#drawflow .kind-fight .outputs { align-self:stretch; justify-content:flex-end; }
/* ---- toast ---- */
#toast { position:fixed; left:50%; bottom:38px; transform:translateX(-50%) translateY(10px);
background:#2a2316; color:#f3dca0; border:1px solid #6a5630; border-radius:7px;
padding:10px 18px; font-size:13.5px; box-shadow:0 4px 16px rgba(0,0,0,.5);
opacity:0; pointer-events:none; transition:.25s; z-index:9999; }
#toast.show { opacity:1; transform:translateX(-50%) translateY(0); }
/* ---- form ---- */
.fld { margin:9px 0; }
.fld > label { display:block; font-size:12px; color:#9a8f7e; margin-bottom:3px; }
.fld input, .fld select, .fld textarea {
width:100%; background:#241f18; color:#e8e0d4; border:1px solid #4a4030;
border-radius:4px; padding:6px 8px; font-size:13px; }
.fld textarea { min-height:60px; resize:vertical; font-family:inherit; }
.fld.inline { display:flex; gap:6px; align-items:center; }
.fld.inline > label { margin:0; flex:none; }
.subbox { border:1px solid #3a322a; border-radius:6px; padding:8px; margin:8px 0;
background:#19150f; }
.subbox .hd { display:flex; justify-content:space-between; align-items:center;
font-size:12px; color:#b89a5a; margin-bottom:6px; }
.row2 { display:flex; gap:6px; }
.row2 > * { flex:1; }
.tag-pick { display:flex; flex-wrap:wrap; gap:5px; }
.tag-pick label { font-size:12px; background:#241f18; border:1px solid #4a4030;
padding:3px 8px; border-radius:11px; cursor:pointer; }
.tag-pick input { margin-right:4px; }
.node-id { color:#7a7264; font-size:11px; }
.optdet { border:1px solid #3a322a; border-radius:6px; margin:6px 0; background:#19150f; }
.optdet > summary { cursor:pointer; padding:7px 10px; font-size:12.5px; color:#cdbf9a; user-select:none;
overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.optdet[open] > summary { border-bottom:1px solid #3a322a; color:#e6c878; margin-bottom:4px; }
.optbody { padding:0 10px 8px; }
/* ---- overlays / modals ---- */
.overlay { position:fixed; inset:0; background:rgba(0,0,0,.72); z-index:100;
display:flex; align-items:center; justify-content:center; }
.overlay.hidden { display:none; }
.login-box, .modal { background:#221d16; border:1px solid #4a4030; border-radius:10px;
padding:24px; width:380px; box-shadow:0 8px 30px rgba(0,0,0,.6); }
.modal { width:520px; max-height:82vh; overflow:auto; }
.modal.wide { width:760px; }
.login-box h2, .modal h3 { margin:0 0 12px; color:#e6c878; }
.login-box .hint { color:#9a8f7e; font-size:13px; margin:0 0 14px; }
.login-box input { width:100%; margin-bottom:10px; }
.login-box button { width:100%; }
.err { color:#e08a7a; font-size:13px; margin-top:8px; min-height:18px; }
.modal textarea { width:100%; min-height:160px; background:#19150f; color:#e8e0d4;
border:1px solid #4a4030; border-radius:5px; padding:8px;
font-family:monospace; font-size:12px; }
.modal-actions { margin-top:10px; display:flex; gap:8px; }
.drop { border:1.5px dashed #4a4030; border-radius:8px; padding:22px 12px; text-align:center;
cursor:pointer; color:#bdb29c; transition:border-color .15s, background .15s; }
.drop:hover { border-color:#6a5d40; background:#1c1810; }
.drop.over { border-color:#e6c878; background:#241d10; color:#e6c878; }
.drop p { margin:0; }
.drop .hint { margin-top:6px; font-size:12px; color:#8a8068; }
.filelist { margin-top:8px; }
.fileitem { display:flex; align-items:center; gap:8px; padding:5px 8px; margin-top:5px;
background:#19150f; border:1px solid #3a3326; border-radius:5px; font-size:13px; }
.fileitem .fn { flex:1; color:#e8e0d4; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.fileitem .fsz { color:#8a8068; font-size:12px; }
.fileitem .rm { background:none; border:none; color:#e08a7a; cursor:pointer; font-size:13px; padding:0 4px; }
.import-paste { margin-top:10px; }
.import-paste summary { cursor:pointer; color:#bdb29c; font-size:13px; user-select:none; }
.import-paste textarea { margin-top:8px; min-height:110px; }
.v-err { color:#e08a7a; margin:4px 0; font-size:13px; }
.v-warn { color:#d8c060; margin:4px 0; font-size:13px; }
.v-ok { color:#7ad88a; font-size:14px; }
/* ---- playtest ---- */
#pt-layout { display:flex; gap:14px; }
#pt-flow { flex:1; max-height:62vh; overflow:auto; }
#pt-ledger { width:230px; flex:none; background:#19150f; border:1px solid #3a322a;
border-radius:6px; padding:10px; font-size:13px; align-self:flex-start; }
#pt-ledger h4 { margin:0 0 8px; color:#e6c878; font-size:13px; }
#pt-ledger .lg { margin:4px 0; }
.pt-step { background:#241f18; border-left:3px solid #4a4030; padding:7px 10px;
margin:7px 0; border-radius:4px; font-size:13px; }
.pt-step.spk { color:#cdddf0; }
.pt-step.sys { color:#9a8f7e; font-style:italic; }
.pt-step.end { border-left-color:#e0a850; color:#eccf95; }
.pt-choices button { display:block; width:100%; text-align:left; margin:5px 0; }
.pt-choices button.locked { opacity:.55; }
.pt-q { color:#b89a5a; font-size:12px; margin:8px 0 4px; }
/* ---- 模式切换 ---- */
.mode-switch { display:flex; gap:0; border:1px solid #5a4a32; border-radius:6px; overflow:hidden; }
.mode-btn { background:#241f18; color:#9a8f7e; border:none; border-radius:0; padding:6px 14px; }
.mode-btn:hover:not(:disabled) { background:#2e2820; }
.mode-btn.active { background:#5a4a26; color:#f3dca0; }
header .who { margin-left:auto; font-size:12px; color:#9a8f7e; }
/* ---- 演出配置页 ---- */
#perform-wrap { display:flex; flex:1; min-height:0; }
#wrap.hidden, #perform-wrap.hidden { display:none; }
#perform-list-pane { width:250px; background:#19150f; border-right:1px solid #3a322a;
display:flex; flex-direction:column; flex:none; }
.perform-listhead { padding:10px 12px; font-size:12px; color:#9a8f7e; letter-spacing:1px;
border-bottom:1px solid #3a322a; background:#1f1a15; }
#perform-list { overflow:auto; flex:1; }
#perform-main { flex:1; min-width:0; display:flex; flex-direction:column; padding:14px 16px; min-height:0; }
#perform-main .empty-center { position:static; inset:auto; min-height:240px; }
/* ---- 演出预览:上=舞台面板 / 下=时间轴面板(分开两块)---- */
.tl-stagepanel { flex:none; }
.tl-mapinfo { font-size:12px; color:#9a8f7e; margin-bottom:8px; }
.tl-stagewrap { position:relative; background:#15130d; border:1px solid #3a322a; border-radius:6px;
overflow:hidden; line-height:0; max-width:640px; }
.tl-stage { width:100%; height:auto; display:block; }
.tl-controls { display:flex; align-items:center; gap:10px; margin:10px 0; flex-wrap:wrap; }
.tl-controls .tip { font-size:11.5px; color:#7a7264; }
.tl-time { font-size:13px; color:#e6c878; font-variant-numeric:tabular-nums; min-width:90px; }
.tl-startbtn { max-width:300px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.tl-fitbtn.on { background:#5a4a26; border-color:#8a7038; color:#f3dca0; }
.tl-tracks .tl-clip.startable { cursor:pointer; }
.tl-tracks .tl-clip.sel { box-shadow:0 0 0 2px #ff5a4a, 0 1px 3px rgba(0,0,0,.5); z-index:3; }
/* 选项浮层:镜头下方(舞台底部居中),模拟玩家看到的位置 */
.tl-choices { position:absolute; left:0; right:0; bottom:12px; display:flex; flex-direction:column;
align-items:center; gap:6px; padding:0 12px; line-height:normal; z-index:5; }
.tl-choices.hidden { display:none; }
.tl-choices-q { font-size:12px; color:#e6c878; background:rgba(20,18,12,.72); padding:2px 10px; border-radius:10px; }
.tl-choice-btn { min-width:240px; max-width:92%; background:rgba(36,31,24,.96); color:#f0e6c8;
border:1px solid #8a7038; border-radius:6px; padding:8px 14px; font-size:13px;
cursor:pointer; box-shadow:0 2px 8px rgba(0,0,0,.55); }
.tl-choice-btn:hover { background:#5a4a26; border-color:#e6c878; }
/* 时间轴面板:独立、占满剩余高度、自己横向滚动 */
.tl-timelinepanel { flex:1; min-height:120px; margin-top:8px; display:flex; }
.tl-tracks { position:relative; flex:1; overflow-x:auto; overflow-y:auto;
background:#19150f; border:1px solid #3a322a; border-radius:6px; padding-top:20px; }
.tl-ruler { position:relative; height:16px; border-bottom:1px solid #2a2419; }
.tl-tick { position:absolute; top:0; height:16px; border-left:1px solid #2a2419; }
.tl-tick span { font-size:9px; color:#6a6256; padding-left:3px; }
.tl-lane { position:relative; border-bottom:1px solid #211c15; }
.tl-lane-label { position:sticky; left:0; z-index:3; display:inline-block; min-width:54px;
padding:2px 6px; font-size:11px; color:#b89a5a; background:#211c15;
border-right:1px solid #2a2419; height:100%; box-sizing:border-box; line-height:26px; }
.tl-clip { position:absolute; top:3px; height:22px; line-height:22px; padding:0 5px;
font-size:11px; color:#1a1710; border-radius:3px; overflow:hidden; white-space:nowrap;
text-overflow:ellipsis; cursor:pointer; box-sizing:border-box; box-shadow:0 1px 2px rgba(0,0,0,.4);
user-select:none; -webkit-user-select:none; }
.tl-tracks .tl-clip.active { outline:2px solid #fff; outline-offset:-1px; z-index:2; }
.tl-clip.k-dialogue { background:#7ec8e3; }
.tl-clip.k-move { background:#e6c878; }
.tl-clip.k-anim { background:#9ee37e; }
.tl-clip.k-camera { background:#c89ee3; }
.tl-clip.k-branch { background:#9ec0f0; }
.tl-clip.k-fight { background:#e38f7e; }
.tl-clip.k-reward { background:#e0c060; }
.tl-clip.k-ending { background:#e0a850; }
.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); }