chore: add reference docs, scripts, tests, demo, prototypes

- Reference docs: known issues, user journey, location redesign, mission merge plan
- Scripts: fill_location_state, merge_missions, migrate_location, migrate_state
- Tests: e2e test suite, asset entries unit tests
- Demo: location board, hifi prototypes, test screenshots
- Data backup: asset log entries and state nodes
- Update .gitignore

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-01 15:29:47 +08:00
co-authored by Claude Opus 4.7
parent f4909765af
commit e1d601a774
190 changed files with 11464 additions and 0 deletions
@@ -0,0 +1,59 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Asset Entry Edit Hifi 04 - Card Workbench</title>
<link rel="stylesheet" href="./shared.css">
</head>
<body>
<main class="shell">
<header class="topbar"><a class="brand" href="./card-workbench.html"><span>Asset Log Editor</span><strong>Card Workbench</strong></a><nav class="nav"><a href="./command-console.html">Console</a><a href="./vertical-rail.html">Rail</a><a href="./dense-ops.html">Dense</a><a class="active" href="./card-workbench.html">Cards</a><a href="./review-split.html">Review</a></nav></header>
<section class="hero"><div><p class="kicker">Grouped Workbench</p><h1>把编辑页拆成 Entry、Window、Context、Nodes 四个工作区</h1><p class="sub">适合后续在真实模板里渐进替换:字段按语义分组,State Node 卡片强调“时间 + 事件 + 详情”。</p></div><div class="hero-meta"><span class="chip blue">Semantic groups</span><span class="chip green">Node cards</span></div></section>
<form class="layout-cards">
<section class="panel col-5">
<div class="panel-head"><div><p class="kicker">Entry</p><h2>Identity</h2></div></div>
<div class="panel-body grid-2">
<input type="hidden" name="sim_time" value="2060-03-12T09:00">
<label class="field span-2"><span class="label">Asset</span><input value="XH-02 Taibai" disabled></label>
<label class="field"><span class="label">Entry Type</span><select name="entry_kind"><option selected>state</option><option>event</option></select></label>
<label class="field"><span class="label">State</span><select name="state_label"><option selected>In Transit</option><option>Transfer</option><option>Exploration</option><option>Maintenance</option><option>Docked</option></select></label>
<label class="field span-2"><span class="label">Title</span><input name="title" value="Support mission transfer to Mars One Campus"></label>
</div>
</section>
<section class="panel col-7">
<div class="panel-head"><div><p class="kicker">Window</p><h2>Time and Place</h2></div><span class="chip amber">Active at sim time</span></div>
<div class="panel-body grid-3">
<label class="field"><span class="label">Start At</span><input name="start_at" type="datetime-local" value="2060-02-26T06:00"></label>
<label class="field"><span class="label">End At</span><input name="end_at" type="datetime-local" value="2060-03-29T18:00"></label>
<label class="field"><span class="label">Location</span><input name="location" value="Earth to Mars Transfer"></label>
<label class="field span-3"><span class="label">Mission Label</span><input name="mission_label" value="Support Mars One Campus"></label>
</div>
</section>
<section class="panel col-4">
<div class="panel-head"><div><p class="kicker">Summary</p><h2>Short Description</h2></div></div>
<div class="panel-body"><label class="field"><span class="label">Summary</span><textarea name="summary">Carrying structural truss and greenhouse kit; 105 km/s, 31-day transition.</textarea></label></div>
</section>
<section class="panel col-4">
<div class="panel-head"><div><p class="kicker">Note</p><h2>Internal Note</h2></div></div>
<div class="panel-body"><label class="field"><span class="label">Note</span><textarea name="note">Delta-v reserve remains above plan. Crew not aboard.</textarea></label></div>
</section>
<section class="panel col-4">
<div class="panel-head"><div><p class="kicker">Derived Preview</p><h2>What boards will show</h2></div></div>
<div class="panel-body mini-map"><div class="summary-item"><span>Current Location</span><strong>Earth to Mars Transfer</strong></div><div class="summary-item"><span>Mission</span><strong>Support Mars One Campus</strong></div><div class="summary-item"><span>State</span><strong>In Transit</strong></div></div>
</section>
<section class="panel col-12">
<div class="panel-head"><div><p class="kicker">State Nodes</p><h2>Node Cards</h2></div><button class="btn soft" type="button" onclick="addCard()">+ Add Node</button></div>
<div class="panel-body grid-3" id="card-nodes">
<article class="node-card good"><div class="node-top"><span class="time-badge">Feb 26<small>06:00</small></span><div class="node-title"><strong>Departure burn completed</strong><span>State starts here</span></div><button class="btn danger" type="button">Delete</button></div><div class="node-fields"><label class="field"><span class="label">Title</span><input name="state_node_title" value="Departure burn completed"></label><label class="field"><span class="label">At Time</span><input name="state_node_at" type="datetime-local" value="2060-02-26T06:00"></label><label class="field node-detail"><span class="label">Detail</span><textarea name="state_node_detail">TMI burn complete.</textarea></label></div></article>
<article class="node-card"><div class="node-top"><span class="time-badge">Mar 04<small>14:30</small></span><div class="node-title"><strong>Mid-course correction 1</strong><span>Trajectory refined</span></div><button class="btn danger" type="button">Delete</button></div><div class="node-fields"><label class="field"><span class="label">Title</span><input name="state_node_title" value="Mid-course correction 1"></label><label class="field"><span class="label">At Time</span><input name="state_node_at" type="datetime-local" value="2060-03-04T14:30"></label><label class="field node-detail"><span class="label">Detail</span><textarea name="state_node_detail">Correction burn completed.</textarea></label></div></article>
<article class="node-card warn"><div class="node-top"><span class="time-badge">Mar 18<small>09:15</small></span><div class="node-title"><strong>Thermal inspection flagged</strong><span>Watch item</span></div><button class="btn danger" type="button">Delete</button></div><div class="node-fields"><label class="field"><span class="label">Title</span><input name="state_node_title" value="Thermal inspection flagged"></label><label class="field"><span class="label">At Time</span><input name="state_node_at" type="datetime-local" value="2060-03-18T09:15"></label><label class="field node-detail"><span class="label">Detail</span><textarea name="state_node_detail">Radiator loop B above baseline.</textarea></label></div></article>
</div>
</section>
</form>
<div class="footer-actions"><button class="btn primary">保存条目</button><button class="btn">返回</button><span class="spacer"></span><button class="btn danger">删除条目</button></div>
</main>
<script>function addCard(){const list=document.getElementById('card-nodes'); const item=list.querySelector('.node-card').cloneNode(true); item.querySelectorAll('input,textarea').forEach(el=>el.value=''); list.appendChild(item);}</script>
</body>
</html>
@@ -0,0 +1,97 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Asset Entry Edit Hifi 01 - Command Console</title>
<link rel="stylesheet" href="./shared.css">
</head>
<body>
<main class="shell">
<header class="topbar">
<a class="brand" href="./command-console.html"><span>Asset Log Editor</span><strong>Command Console</strong></a>
<nav class="nav">
<a class="active" href="./command-console.html">Console</a>
<a href="./vertical-rail.html">Rail</a>
<a href="./dense-ops.html">Dense</a>
<a href="./card-workbench.html">Cards</a>
<a href="./review-split.html">Review</a>
</nav>
</header>
<section class="hero">
<div>
<p class="kicker">Edit State Interval</p>
<h1>Support mission transfer to Mars One Campus</h1>
<p class="sub">主信息在左侧保持完整编辑能力,右侧把 State Node 渲染成任务控制台式事件串,一眼能看出节点时间、标题和备注。</p>
</div>
<div class="hero-meta">
<span class="chip blue">Asset <strong>XH-02 Taibai</strong></span>
<span class="chip green">State <strong>In Transit</strong></span>
<span class="chip amber">Sim <strong>2060-03-12 09:00</strong></span>
</div>
</section>
<form class="layout-command">
<section class="panel">
<div class="panel-head">
<div><p class="kicker">Required Fields</p><h2>Entry Details</h2></div>
<span class="chip blue">State Interval</span>
</div>
<div class="panel-body">
<input type="hidden" name="sim_time" value="2060-03-12T09:00">
<div class="summary-strip">
<div class="summary-item"><span>Window</span><strong>31d 12h</strong></div>
<div class="summary-item"><span>Location</span><strong>Earth to Mars</strong></div>
<div class="summary-item"><span>Nodes</span><strong>4 events</strong></div>
<div class="summary-item"><span>Status</span><strong>Active now</strong></div>
</div>
<div class="grid-3">
<label class="field"><span class="label">Asset</span><input type="text" value="XH-02 Taibai" disabled></label>
<label class="field"><span class="label">Entry Type</span><select name="entry_kind"><option selected>state</option><option>event</option></select></label>
<label class="field"><span class="label">State</span><select name="state_label"><option selected>In Transit</option><option>Transfer</option><option>Exploration</option><option>Maintenance</option><option>Docked</option></select></label>
<label class="field span-3"><span class="label">Title</span><input name="title" type="text" value="Support mission transfer to Mars One Campus"></label>
<label class="field"><span class="label">Start At</span><input name="start_at" type="datetime-local" value="2060-02-26T06:00"></label>
<label class="field"><span class="label">End At</span><input name="end_at" type="datetime-local" value="2060-03-29T18:00"></label>
<label class="field"><span class="label">Location</span><input name="location" type="text" value="Earth to Mars Transfer"></label>
<label class="field span-3"><span class="label">Mission Label</span><input name="mission_label" type="text" value="Support Mars One Campus"></label>
<label class="field span-3"><span class="label">Summary</span><textarea name="summary">Carrying structural truss and greenhouse kit; 105 km/s, 31-day transition.</textarea></label>
<label class="field span-3"><span class="label">Note</span><textarea name="note">Delta-v reserve remains above plan. Crew not aboard.</textarea></label>
</div>
</div>
</section>
<aside class="panel">
<div class="panel-head">
<div><p class="kicker">State Nodes</p><h2>What Happened When</h2></div>
<div class="node-controls"><span class="node-count">4 nodes</span><button class="btn soft" type="button" onclick="addNode(this)">+ Add Node</button></div>
</div>
<div class="panel-body timeline-rail" data-node-list>
<article class="rail-node">
<div class="rail-time">Feb 26<br>06:00</div><div class="rail-line"><span class="rail-dot"></span></div>
<div class="rail-copy"><div class="node-card good"><div class="node-top"><span class="time-badge">2060-02-26<small>06:00</small></span><div class="node-title"><strong>Departure burn completed</strong><span>Transfer state begins</span></div><button class="btn danger" type="button">Delete</button></div><div class="node-fields"><label class="field"><span class="label">Node Title</span><input name="state_node_title" value="Departure burn completed"></label><label class="field"><span class="label">Node At</span><input name="state_node_at" type="datetime-local" value="2060-02-26T06:00"></label><label class="field node-detail"><span class="label">Detail</span><textarea name="state_node_detail">TMI burn complete, all tanks within expected residuals.</textarea></label></div></div></div>
</article>
<article class="rail-node">
<div class="rail-time">Mar 04<br>14:30</div><div class="rail-line"><span class="rail-dot"></span></div>
<div class="rail-copy"><div class="node-card"><div class="node-top"><span class="time-badge">2060-03-04<small>14:30</small></span><div class="node-title"><strong>Mid-course correction 1</strong><span>Trajectory refined</span></div><button class="btn danger" type="button">Delete</button></div><div class="node-fields"><label class="field"><span class="label">Node Title</span><input name="state_node_title" value="Mid-course correction 1"></label><label class="field"><span class="label">Node At</span><input name="state_node_at" type="datetime-local" value="2060-03-04T14:30"></label><label class="field node-detail"><span class="label">Detail</span><textarea name="state_node_detail">Correction burn shortened Mars arrival error ellipse.</textarea></label></div></div></div>
</article>
<article class="rail-node">
<div class="rail-time">Mar 18<br>09:15</div><div class="rail-line"><span class="rail-dot"></span></div>
<div class="rail-copy"><div class="node-card warn"><div class="node-top"><span class="time-badge">2060-03-18<small>09:15</small></span><div class="node-title"><strong>Thermal inspection flagged</strong><span>Watch item</span></div><button class="btn danger" type="button">Delete</button></div><div class="node-fields"><label class="field"><span class="label">Node Title</span><input name="state_node_title" value="Thermal inspection flagged"></label><label class="field"><span class="label">Node At</span><input name="state_node_at" type="datetime-local" value="2060-03-18T09:15"></label><label class="field node-detail"><span class="label">Detail</span><textarea name="state_node_detail">Radiator loop B is 3 C above baseline, no mission impact.</textarea></label></div></div></div>
</article>
</div>
</aside>
</form>
<div class="footer-actions">
<button class="btn primary" type="button">保存条目</button>
<button class="btn" type="button">返回</button>
<span class="spacer"></span>
<button class="btn danger" type="button">删除条目</button>
</div>
</main>
<script>
function addNode(btn){const list=document.querySelector('[data-node-list]'); const item=list.querySelector('.rail-node').cloneNode(true); item.querySelectorAll('input,textarea').forEach(el=>{if(!el.type||el.type!=='datetime-local')el.value='';}); list.appendChild(item);}
</script>
</body>
</html>
@@ -0,0 +1,61 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Asset Entry Edit Hifi 03 - Dense Ops</title>
<link rel="stylesheet" href="./shared.css">
</head>
<body>
<main class="shell">
<header class="topbar"><a class="brand" href="./dense-ops.html"><span>Asset Log Editor</span><strong>Dense Ops</strong></a><nav class="nav"><a href="./command-console.html">Console</a><a href="./vertical-rail.html">Rail</a><a class="active" href="./dense-ops.html">Dense</a><a href="./card-workbench.html">Cards</a><a href="./review-split.html">Review</a></nav></header>
<section class="hero"><div><p class="kicker">Operational Dense Form</p><h1>高信息密度,但 State Node 不再是难读输入堆</h1><p class="sub">这版更接近后台运维工具:主字段紧凑排布,State Node 用表格维护,同时右侧提供按时间生成的阅读摘要。</p></div><div class="hero-meta"><span class="chip">Mode <strong>state</strong></span><span class="chip blue">Edit URL ready</span></div></section>
<form class="layout-dense">
<section class="panel">
<div class="panel-head"><div><p class="kicker">All Current Fields</p><h2>Entry Form</h2></div><div class="button-row"><button class="btn primary" type="button">保存条目</button><button class="btn" type="button">返回</button></div></div>
<div class="panel-body grid-3">
<input type="hidden" name="sim_time" value="2060-03-12T09:00">
<label class="field"><span class="label">Asset</span><input value="XH-02 Taibai" disabled></label>
<label class="field"><span class="label">Entry Type</span><select name="entry_kind"><option selected>state</option><option>event</option></select></label>
<label class="field"><span class="label">State</span><select name="state_label"><option selected>In Transit</option><option>Transfer</option><option>Exploration</option><option>Maintenance</option><option>Docked</option></select></label>
<label class="field span-3"><span class="label">Title</span><input name="title" value="Support mission transfer to Mars One Campus"></label>
<label class="field"><span class="label">Start At</span><input name="start_at" type="datetime-local" value="2060-02-26T06:00"></label>
<label class="field"><span class="label">End At</span><input name="end_at" type="datetime-local" value="2060-03-29T18:00"></label>
<label class="field"><span class="label">Location</span><input name="location" value="Earth to Mars Transfer"></label>
<label class="field span-3"><span class="label">Mission Label</span><input name="mission_label" value="Support Mars One Campus"></label>
<label class="field span-3"><span class="label">Summary</span><textarea name="summary">Carrying structural truss and greenhouse kit; 105 km/s, 31-day transition.</textarea></label>
<label class="field span-3"><span class="label">Note</span><textarea name="note">Delta-v reserve remains above plan. Crew not aboard.</textarea></label>
</div>
<div class="panel-head">
<div><p class="kicker">State Nodes</p><h2>Editable Node Table</h2></div>
<button class="btn soft" type="button" onclick="addRow()">+ Add Node</button>
</div>
<div class="panel-body">
<table class="node-table" id="node-table">
<thead><tr><th>When</th><th>What happened</th><th>Detail</th><th></th></tr></thead>
<tbody>
<tr><td><input name="state_node_at" type="datetime-local" value="2060-02-26T06:00"></td><td><input name="state_node_title" value="Departure burn completed"></td><td><textarea name="state_node_detail">TMI burn complete, all tanks within expected residuals.</textarea></td><td><button class="btn danger" type="button">Delete</button></td></tr>
<tr><td><input name="state_node_at" type="datetime-local" value="2060-03-04T14:30"></td><td><input name="state_node_title" value="Mid-course correction 1"></td><td><textarea name="state_node_detail">Correction burn shortened Mars arrival error ellipse.</textarea></td><td><button class="btn danger" type="button">Delete</button></td></tr>
<tr><td><input name="state_node_at" type="datetime-local" value="2060-03-18T09:15"></td><td><input name="state_node_title" value="Thermal inspection flagged"></td><td><textarea name="state_node_detail">Radiator loop B is 3 C above baseline, no mission impact.</textarea></td><td><button class="btn danger" type="button">Delete</button></td></tr>
</tbody>
</table>
</div>
</section>
<aside class="panel">
<div class="panel-head"><div><p class="kicker">Readable Summary</p><h2>Node Sequence</h2></div><span class="chip green">Auto sorted</span></div>
<div class="panel-body mini-map">
<div class="map-step"><span></span><div><strong>2060-02-26 06:00</strong><small>Departure burn completed</small></div></div>
<div class="map-step"><span></span><div><strong>2060-03-04 14:30</strong><small>Mid-course correction 1</small></div></div>
<div class="map-step"><span></span><div><strong>2060-03-18 09:15</strong><small>Thermal inspection flagged</small></div></div>
<div class="map-step"><span></span><div><strong>2060-03-29 18:00</strong><small>Mars capture window</small></div></div>
<div class="callout">表格负责高效率编辑,右侧摘要负责让用户确认“什么时间发生了什么”。两者可以由同一组 State Node 数据驱动。</div>
</div>
</aside>
</form>
<div class="footer-actions"><button class="btn primary">保存条目</button><button class="btn">返回</button><span class="spacer"></span><button class="btn danger">删除条目</button></div>
</main>
<script>function addRow(){const tbody=document.querySelector('#node-table tbody'); const row=tbody.rows[0].cloneNode(true); row.querySelectorAll('input,textarea').forEach(el=>el.value=''); tbody.appendChild(row);}</script>
</body>
</html>
@@ -0,0 +1,59 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Asset Entry Edit Hifi 05 - Review Split</title>
<link rel="stylesheet" href="./shared.css">
</head>
<body>
<main class="shell">
<header class="topbar"><a class="brand" href="./review-split.html"><span>Asset Log Editor</span><strong>Review Split</strong></a><nav class="nav"><a href="./command-console.html">Console</a><a href="./vertical-rail.html">Rail</a><a href="./dense-ops.html">Dense</a><a href="./card-workbench.html">Cards</a><a class="active" href="./review-split.html">Review</a></nav></header>
<section class="hero"><div><p class="kicker">Edit and Read Side by Side</p><h1>左边编辑,右边实时审阅这条日志会如何被理解</h1><p class="sub">这版适合减少误填:用户编辑所有原始字段,同时右侧以可读时间线预览当前日志、状态窗口和节点含义。</p></div><div class="hero-meta"><span class="chip green">Preserves all controls</span><span class="chip blue">Clear state nodes</span></div></section>
<form class="layout-review">
<section class="panel">
<div class="panel-head"><div><p class="kicker">Raw Form</p><h2>Editable Source Data</h2></div></div>
<div class="panel-body grid-2">
<input type="hidden" name="sim_time" value="2060-03-12T09:00">
<label class="field span-2"><span class="label">Asset</span><input value="XH-02 Taibai" disabled></label>
<label class="field"><span class="label">Entry Type</span><select name="entry_kind"><option selected>state</option><option>event</option></select></label>
<label class="field"><span class="label">State</span><select name="state_label"><option selected>In Transit</option><option>Transfer</option><option>Exploration</option><option>Maintenance</option><option>Docked</option></select></label>
<label class="field span-2"><span class="label">Title</span><input name="title" value="Support mission transfer to Mars One Campus"></label>
<label class="field"><span class="label">Start At</span><input name="start_at" type="datetime-local" value="2060-02-26T06:00"></label>
<label class="field"><span class="label">End At</span><input name="end_at" type="datetime-local" value="2060-03-29T18:00"></label>
<label class="field span-2"><span class="label">Location</span><input name="location" value="Earth to Mars Transfer"></label>
<label class="field span-2"><span class="label">Mission Label</span><input name="mission_label" value="Support Mars One Campus"></label>
<label class="field span-2"><span class="label">Summary</span><textarea name="summary">Carrying structural truss and greenhouse kit; 105 km/s, 31-day transition.</textarea></label>
<label class="field span-2"><span class="label">Note</span><textarea name="note">Delta-v reserve remains above plan. Crew not aboard.</textarea></label>
<div class="span-2 button-row"><span class="spacer"></span><button class="btn soft" type="button" onclick="addReviewNode()">+ Add Node</button></div>
<div class="span-2" id="review-node-list">
<article class="node-card good"><div class="node-top"><span class="time-badge">Node 1<small>start</small></span><div class="node-title"><strong>Departure burn completed</strong><span>2060-02-26 06:00</span></div><button class="btn danger" type="button">Delete</button></div><div class="node-fields"><label class="field"><span class="label">Node Title</span><input name="state_node_title" value="Departure burn completed"></label><label class="field"><span class="label">Node At</span><input name="state_node_at" type="datetime-local" value="2060-02-26T06:00"></label><label class="field node-detail"><span class="label">Detail</span><textarea name="state_node_detail">TMI burn complete, all tanks within expected residuals.</textarea></label></div></article>
<article class="node-card"><div class="node-top"><span class="time-badge">Node 2<small>course</small></span><div class="node-title"><strong>Mid-course correction 1</strong><span>2060-03-04 14:30</span></div><button class="btn danger" type="button">Delete</button></div><div class="node-fields"><label class="field"><span class="label">Node Title</span><input name="state_node_title" value="Mid-course correction 1"></label><label class="field"><span class="label">Node At</span><input name="state_node_at" type="datetime-local" value="2060-03-04T14:30"></label><label class="field node-detail"><span class="label">Detail</span><textarea name="state_node_detail">Correction burn shortened Mars arrival error ellipse.</textarea></label></div></article>
</div>
</div>
</section>
<aside class="panel">
<div class="panel-head"><div><p class="kicker">Interpretation Preview</p><h2>How Users Read It</h2></div><span class="chip amber">Current clock inside interval</span></div>
<div class="panel-body">
<div class="summary-strip">
<div class="summary-item"><span>Asset</span><strong>XH-02</strong></div>
<div class="summary-item"><span>State</span><strong>In Transit</strong></div>
<div class="summary-item"><span>Location</span><strong>Transfer</strong></div>
<div class="summary-item"><span>Nodes</span><strong>4</strong></div>
</div>
<div class="timeline-rail">
<article class="rail-node"><div class="rail-time">Feb 26 06:00</div><div class="rail-line"><span class="rail-dot"></span></div><div class="rail-copy"><div class="node-card good"><div class="node-title"><strong>State interval begins</strong><span>Departure burn completed. From this moment, boards should show XH-02 in Earth to Mars Transfer.</span></div></div></div></article>
<article class="rail-node"><div class="rail-time">Mar 04 14:30</div><div class="rail-line"><span class="rail-dot"></span></div><div class="rail-copy"><div class="node-card"><div class="node-title"><strong>Mid-course correction 1</strong><span>Trajectory refined while remaining in the same parent state.</span></div></div></div></article>
<article class="rail-node"><div class="rail-time">Mar 12 09:00</div><div class="rail-line"><span class="rail-dot"></span></div><div class="rail-copy"><div class="node-card warn"><div class="node-title"><strong>Current simulation time</strong><span>This marker is not saved; it explains why this interval is active right now.</span></div></div></div></article>
<article class="rail-node"><div class="rail-time">Mar 29 18:00</div><div class="rail-line"><span class="rail-dot"></span></div><div class="rail-copy"><div class="node-card good"><div class="node-title"><strong>Expected interval end</strong><span>Mars capture window closes this state and hands off to Mars Orbit.</span></div></div></div></article>
</div>
</div>
</aside>
</form>
<div class="footer-actions"><button class="btn primary">保存条目</button><button class="btn">返回</button><span class="spacer"></span><button class="btn danger">删除条目</button></div>
</main>
<script>function addReviewNode(){const list=document.getElementById('review-node-list'); const item=list.querySelector('.node-card').cloneNode(true); item.querySelectorAll('input,textarea').forEach(el=>el.value=''); list.appendChild(item);}</script>
</body>
</html>
+188
View File
@@ -0,0 +1,188 @@
:root {
--bg: #0b0d12;
--panel: #141821;
--panel-2: #1b2130;
--panel-3: #222a3a;
--line: rgba(226, 232, 240, .12);
--line-strong: rgba(226, 232, 240, .2);
--text: #eef4ff;
--muted: #93a4bb;
--faint: #65758a;
--blue: #4f8cff;
--cyan: #35c7d8;
--green: #39d98a;
--amber: #f3b64a;
--red: #ff6575;
--purple: #a98bff;
--shadow: 0 24px 70px rgba(0, 0, 0, .28);
--radius: 8px;
--mono: "Cascadia Code", "SF Mono", Consolas, monospace;
--sans: "Segoe UI", "Microsoft YaHei UI", system-ui, sans-serif;
}
* { box-sizing: border-box; }
html, body { margin: 0; min-height: 100%; }
body {
color: var(--text);
font-family: var(--sans);
background:
radial-gradient(circle at 20% -10%, rgba(79, 140, 255, .22), transparent 32%),
radial-gradient(circle at 85% 5%, rgba(53, 199, 216, .14), transparent 30%),
linear-gradient(180deg, #0b0d12 0%, #111622 100%);
}
a { color: inherit; text-decoration: none; }
.shell { width: min(1480px, calc(100vw - 32px)); margin: 0 auto; padding: 18px 0 34px; }
.topbar {
position: sticky; top: 12px; z-index: 5;
display: flex; align-items: center; justify-content: space-between; gap: 16px;
padding: 11px 14px; margin-bottom: 16px;
border: 1px solid var(--line); border-radius: var(--radius);
background: rgba(20, 24, 33, .88); backdrop-filter: blur(16px);
box-shadow: var(--shadow);
}
.brand { display: grid; gap: 2px; }
.brand span { color: var(--muted); font: 700 11px/1 var(--mono); letter-spacing: .16em; text-transform: uppercase; }
.brand strong { font: 700 18px/1.1 var(--mono); }
.nav { display: flex; gap: 6px; flex-wrap: wrap; }
.nav a {
padding: 7px 10px; border: 1px solid var(--line); border-radius: 6px;
color: var(--muted); background: rgba(255, 255, 255, .025); font-size: 12px; font-weight: 700;
}
.nav a.active { color: #fff; border-color: rgba(79, 140, 255, .5); background: rgba(79, 140, 255, .18); }
.hero {
display: flex; justify-content: space-between; gap: 18px; align-items: flex-end;
padding: 18px; margin-bottom: 14px;
border: 1px solid var(--line); border-radius: var(--radius);
background: linear-gradient(135deg, rgba(20, 24, 33, .95), rgba(27, 33, 48, .82));
}
.kicker, .label {
color: var(--muted); font: 700 11px/1 var(--mono); letter-spacing: .14em; text-transform: uppercase;
}
h1, h2, h3, p { margin: 0; }
h1 { margin-top: 6px; font: 700 clamp(24px, 3vw, 38px)/1.06 var(--mono); letter-spacing: 0; }
h2 { font: 700 18px/1.2 var(--mono); }
h3 { font: 700 14px/1.35 var(--sans); }
.sub { color: var(--muted); margin-top: 7px; line-height: 1.55; font-size: 13px; max-width: 760px; }
.hero-meta { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.chip {
display: inline-flex; align-items: center; gap: 7px;
min-height: 28px; padding: 0 9px; border-radius: 6px;
border: 1px solid var(--line); background: rgba(255,255,255,.035);
color: var(--muted); font: 700 12px/1 var(--mono);
}
.chip strong { color: var(--text); }
.chip.blue { color: #cfe0ff; border-color: rgba(79,140,255,.38); background: rgba(79,140,255,.12); }
.chip.green { color: #ceffe6; border-color: rgba(57,217,138,.34); background: rgba(57,217,138,.1); }
.chip.amber { color: #ffe7b7; border-color: rgba(243,182,74,.32); background: rgba(243,182,74,.1); }
.panel {
border: 1px solid var(--line); border-radius: var(--radius);
background: rgba(20, 24, 33, .9); box-shadow: 0 14px 40px rgba(0,0,0,.16);
}
.panel-head {
display: flex; justify-content: space-between; gap: 12px; align-items: flex-start;
padding: 14px 15px; border-bottom: 1px solid var(--line);
}
.panel-body { padding: 15px; }
.grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 11px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 11px; }
.span-2 { grid-column: span 2; }
.span-3 { grid-column: span 3; }
label.field { display: grid; gap: 6px; min-width: 0; }
.field input, .field select, .field textarea {
width: 100%; min-height: 38px; padding: 9px 10px;
border: 1px solid var(--line); border-radius: 6px;
background: rgba(11, 13, 18, .66); color: var(--text);
font: 500 13px/1.3 var(--sans);
}
.field textarea { min-height: 82px; resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus { outline: none; border-color: rgba(79,140,255,.72); box-shadow: 0 0 0 3px rgba(79,140,255,.12); }
.field input[disabled] { color: var(--muted); background: rgba(255,255,255,.04); }
.button-row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.spacer { flex: 1; }
.btn {
min-height: 34px; padding: 0 12px; border-radius: 6px; border: 1px solid var(--line);
background: rgba(255,255,255,.04); color: var(--muted);
font: 700 12px/1 var(--sans); cursor: default;
}
button.btn { cursor: pointer; }
.btn.primary { color: white; border-color: rgba(79,140,255,.7); background: linear-gradient(135deg, #4f8cff, #2d63d8); }
.btn.danger { color: #ffd5da; border-color: rgba(255,101,117,.45); background: rgba(255,101,117,.1); }
.btn.soft { color: #dbe7ff; border-color: rgba(79,140,255,.28); background: rgba(79,140,255,.1); }
.node-controls { display: flex; gap: 7px; align-items: center; flex-wrap: wrap; }
.node-count { color: var(--cyan); font: 700 12px/1 var(--mono); }
.node-card {
position: relative; display: grid; gap: 9px; padding: 11px;
border: 1px solid var(--line); border-radius: 7px; background: rgba(27, 33, 48, .75);
}
.node-card::before { content: ""; position: absolute; left: -1px; top: 10px; bottom: 10px; width: 3px; border-radius: 3px; background: var(--cyan); }
.node-card.warn::before { background: var(--amber); }
.node-card.good::before { background: var(--green); }
.node-top { display: grid; grid-template-columns: 132px minmax(0, 1fr) auto; gap: 8px; align-items: center; }
.time-badge {
display: inline-grid; gap: 2px; justify-items: start;
padding: 7px 8px; border-radius: 6px; background: rgba(53,199,216,.1); border: 1px solid rgba(53,199,216,.24);
color: #c8f8ff; font: 700 11px/1.1 var(--mono);
}
.time-badge small { color: var(--muted); font: 700 10px/1 var(--mono); }
.node-title { min-width: 0; }
.node-title strong { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.node-title span { color: var(--muted); font-size: 12px; }
.node-fields { display: grid; grid-template-columns: minmax(0, 1fr) 170px; gap: 8px; }
.node-detail { grid-column: 1 / -1; }
.timeline-rail { display: grid; gap: 0; }
.rail-node { display: grid; grid-template-columns: 150px 30px minmax(0, 1fr); gap: 9px; min-height: 82px; }
.rail-time { color: var(--cyan); font: 700 12px/1.3 var(--mono); padding-top: 4px; text-align: right; }
.rail-line { position: relative; display: flex; justify-content: center; }
.rail-line::before { content: ""; position: absolute; top: 0; bottom: 0; width: 2px; background: rgba(226,232,240,.12); }
.rail-dot { position: relative; z-index: 1; width: 14px; height: 14px; margin-top: 2px; border-radius: 50%; background: var(--cyan); box-shadow: 0 0 0 4px rgba(53,199,216,.14); }
.rail-copy { padding-bottom: 16px; }
.rail-copy .node-card { box-shadow: none; }
.summary-strip {
display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 9px; margin-bottom: 14px;
}
.summary-item { padding: 11px; border: 1px solid var(--line); border-radius: 7px; background: rgba(255,255,255,.035); }
.summary-item span { display: block; color: var(--muted); font: 700 10px/1 var(--mono); letter-spacing: .12em; text-transform: uppercase; }
.summary-item strong { display: block; margin-top: 6px; font: 700 15px/1.2 var(--mono); }
.callout { padding: 11px; border: 1px solid rgba(243,182,74,.3); border-radius: 7px; background: rgba(243,182,74,.08); color: #ffe7b7; font-size: 13px; line-height: 1.5; }
.layout-command { display: grid; grid-template-columns: minmax(0, 1.15fr) 430px; gap: 14px; align-items: start; }
.layout-rail { display: grid; grid-template-columns: 390px minmax(0, 1fr); gap: 14px; align-items: start; }
.layout-dense { display: grid; grid-template-columns: minmax(0, 1fr) 360px; gap: 14px; align-items: start; }
.layout-cards { display: grid; grid-template-columns: repeat(12, 1fr); gap: 14px; align-items: start; }
.layout-review { display: grid; grid-template-columns: minmax(0, .96fr) minmax(0, 1.04fr); gap: 14px; align-items: start; }
.col-4 { grid-column: span 4; }
.col-5 { grid-column: span 5; }
.col-7 { grid-column: span 7; }
.col-8 { grid-column: span 8; }
.col-12 { grid-column: span 12; }
.node-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.node-table th, .node-table td { padding: 9px 8px; border-bottom: 1px solid var(--line); text-align: left; vertical-align: top; }
.node-table th { color: var(--muted); font: 700 10px/1 var(--mono); letter-spacing: .12em; text-transform: uppercase; }
.node-table td:first-child { color: var(--cyan); font-family: var(--mono); white-space: nowrap; }
.node-table input, .node-table textarea {
width: 100%; min-height: 34px; padding: 7px 8px;
border: 1px solid var(--line); border-radius: 6px;
background: rgba(11,13,18,.66); color: var(--text);
font: 500 12px/1.35 var(--sans);
}
.node-table textarea { min-height: 54px; resize: vertical; }
.mini-map { display: grid; gap: 8px; }
.map-step { display: flex; gap: 8px; align-items: center; padding: 9px; border: 1px solid var(--line); border-radius: 7px; background: rgba(255,255,255,.035); }
.map-step span { width: 8px; height: 8px; border-radius: 50%; background: var(--cyan); box-shadow: 0 0 0 4px rgba(53,199,216,.1); flex: 0 0 auto; }
.map-step strong { display: block; font-size: 13px; }
.map-step small { color: var(--muted); }
.footer-actions {
position: sticky; bottom: 10px; z-index: 4;
display: flex; gap: 8px; align-items: center; margin-top: 14px; padding: 10px;
border: 1px solid var(--line); border-radius: var(--radius);
background: rgba(20,24,33,.9); backdrop-filter: blur(14px);
}
@media (max-width: 1080px) {
.layout-command, .layout-rail, .layout-dense, .layout-review { grid-template-columns: 1fr; }
.layout-cards { grid-template-columns: 1fr; }
.col-4, .col-5, .col-7, .col-8, .col-12 { grid-column: auto; }
.grid-2, .grid-3, .summary-strip { grid-template-columns: 1fr; }
.span-2, .span-3 { grid-column: auto; }
.node-top, .node-fields, .rail-node { grid-template-columns: 1fr; }
.rail-time { text-align: left; }
.rail-line { display: none; }
}
@@ -0,0 +1,335 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>编辑日志 · XH-02 Taibai</title>
<link rel="stylesheet" href="./shared.css">
<style>
.app-nav a { min-width: 76px; text-align: center; }
.crumbs { display: flex; gap: 7px; align-items: center; color: var(--muted); font: 700 11px/1 var(--mono); letter-spacing: .08em; text-transform: uppercase; }
.crumbs span { color: var(--faint); }
.hero.entry-hero { align-items: stretch; }
.entry-title-block { display: grid; align-content: center; }
.entry-status-board {
display: grid; grid-template-columns: repeat(2, minmax(138px, 1fr)); gap: 9px;
min-width: 420px;
}
.entry-stat {
padding: 12px; border: 1px solid var(--line); border-radius: 7px;
background: rgba(255,255,255,.035);
}
.entry-stat span { display: block; color: var(--muted); font: 700 10px/1 var(--mono); letter-spacing: .12em; text-transform: uppercase; }
.entry-stat strong { display: block; margin-top: 7px; font: 700 15px/1.25 var(--mono); }
.entry-stat.current { border-color: rgba(57,217,138,.34); background: rgba(57,217,138,.09); }
.entry-stat.current strong { color: #caffdf; }
.section-note { margin-top: 6px; color: var(--muted); font-size: 12px; line-height: 1.45; }
.form-stack { display: grid; gap: 13px; }
.context-card {
display: grid; gap: 10px; padding: 12px; border: 1px solid var(--line); border-radius: 7px;
background: rgba(255,255,255,.035);
}
.context-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.context-row span { color: var(--muted); font: 700 10px/1 var(--mono); letter-spacing: .12em; text-transform: uppercase; }
.context-row strong { text-align: right; font-size: 13px; }
.entry-type-switch { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; }
.entry-type-option {
display: flex; align-items: center; justify-content: center; gap: 8px;
min-height: 38px; border: 1px solid var(--line); border-radius: 6px;
color: var(--muted); background: rgba(255,255,255,.025); font-weight: 800; font-size: 12px;
}
.entry-type-option.active { color: white; border-color: rgba(79,140,255,.55); background: rgba(79,140,255,.16); }
.node-summary-bar {
display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
padding: 10px 15px; border-bottom: 1px solid var(--line); background: rgba(255,255,255,.025);
}
.node-summary-bar .spacer { flex: 1; }
.rail-copy .node-card { background: linear-gradient(180deg, rgba(27,33,48,.92), rgba(20,24,33,.92)); }
.node-status {
display: inline-flex; align-items: center; justify-content: center; min-height: 24px; padding: 0 8px;
border-radius: 999px; font: 800 10px/1 var(--mono); letter-spacing: .08em; text-transform: uppercase;
border: 1px solid var(--line);
}
.node-status.start { color: #ceffe6; border-color: rgba(57,217,138,.32); background: rgba(57,217,138,.1); }
.node-status.mid { color: #c8f8ff; border-color: rgba(53,199,216,.28); background: rgba(53,199,216,.09); }
.node-status.watch { color: #ffe7b7; border-color: rgba(243,182,74,.32); background: rgba(243,182,74,.1); }
.node-status.end { color: #d9d1ff; border-color: rgba(169,139,255,.32); background: rgba(169,139,255,.1); }
.rail-node:last-child { min-height: 0; }
.rail-node:last-child .rail-line::before { bottom: calc(100% - 14px); }
.compact-actions { display: flex; gap: 7px; align-items: center; justify-content: flex-end; }
.field-help { color: var(--faint); font-size: 11px; line-height: 1.4; }
@media (max-width: 1080px) {
.entry-status-board { min-width: 0; grid-template-columns: 1fr; }
.hero.entry-hero { align-items: flex-start; }
}
</style>
</head>
<body>
<main class="shell">
<header class="topbar">
<a class="brand" href="./vertical-rail.html">
<span>KSP 管理后台</span>
<strong>任务资产日志</strong>
</a>
<nav class="nav app-nav">
<a href="#">任务总览</a>
<a href="#">状态板</a>
<a href="#">地点板</a>
<a href="#">时间线</a>
<a class="active" href="#">任务资产</a>
</nav>
</header>
<section class="hero entry-hero">
<div class="entry-title-block">
<div class="crumbs">任务资产 <span>/</span> XH-02 Taibai <span>/</span> 编辑日志</div>
<h1>Support mission transfer to Mars One Campus</h1>
<p class="sub">XH-02 Taibai · Exploration Mothership · Mars Expedition Program</p>
</div>
<div class="entry-status-board">
<div class="entry-stat current"><span>Current at Sim Time</span><strong>Active State</strong></div>
<div class="entry-stat"><span>Simulation Time</span><strong>2060-03-12 09:00</strong></div>
<div class="entry-stat"><span>State Window</span><strong>2060-02-26 → 2060-03-29</strong></div>
<div class="entry-stat"><span>State Nodes</span><strong>4 timeline events</strong></div>
</div>
</section>
<form class="layout-rail">
<aside class="form-stack">
<section class="panel">
<div class="panel-head">
<div>
<p class="kicker">Entry</p>
<h2>日志信息</h2>
<p class="section-note">这些字段会决定资产在状态板、时间线和地点板上的当前状态。</p>
</div>
</div>
<div class="panel-body form-stack">
<input type="hidden" name="sim_time" value="2060-03-12T09:00">
<div class="context-card">
<div class="context-row"><span>Asset</span><strong>XH-02 Taibai</strong></div>
<div class="context-row"><span>Current Location</span><strong>Earth to Mars Transfer</strong></div>
<div class="context-row"><span>Current Mission</span><strong>Support Mars One Campus</strong></div>
</div>
<label class="field">
<span class="label">Asset</span>
<input value="XH-02 Taibai" disabled>
</label>
<label class="field">
<span class="label">Entry Type</span>
<select name="entry_kind">
<option selected value="state">State Interval</option>
<option value="event">Event Point</option>
</select>
</label>
<div class="entry-type-switch" aria-hidden="true">
<div class="entry-type-option active">State Interval</div>
<div class="entry-type-option">Event Point</div>
</div>
<label class="field">
<span class="label">Title</span>
<input name="title" value="Support mission transfer to Mars One Campus">
</label>
<div class="grid-2">
<label class="field">
<span class="label">Start At</span>
<input name="start_at" type="datetime-local" value="2060-02-26T06:00">
</label>
<label class="field">
<span class="label">End At</span>
<input name="end_at" type="datetime-local" value="2060-03-29T18:00">
</label>
</div>
<label class="field">
<span class="label">State</span>
<select name="state_label">
<option selected>In Transit</option>
<option>Transfer</option>
<option>Exploration</option>
<option>Maintenance</option>
<option>Docked</option>
</select>
</label>
<label class="field">
<span class="label">Location</span>
<input name="location" value="Earth to Mars Transfer">
<span class="field-help">资产详情页中的当前位置会从当前模拟时间覆盖的日志中派生。</span>
</label>
<label class="field">
<span class="label">Mission Label</span>
<input name="mission_label" value="Support Mars One Campus">
</label>
<label class="field">
<span class="label">Summary</span>
<textarea name="summary">Carrying structural truss and greenhouse kit; 105 km/s, 31-day transition.</textarea>
</label>
<label class="field">
<span class="label">Note</span>
<textarea name="note">Delta-v reserve remains above plan. Crew not aboard.</textarea>
</label>
</div>
</section>
<section class="panel">
<div class="panel-head">
<div>
<p class="kicker">Validation</p>
<h2>保存前检查</h2>
</div>
<span class="chip green">Ready</span>
</div>
<div class="panel-body mini-map">
<div class="map-step"><span></span><div><strong>时间区间有效</strong><small>Start At 早于 End At</small></div></div>
<div class="map-step"><span></span><div><strong>节点按时间排序</strong><small>4 个节点都落在状态区间内</small></div></div>
<div class="map-step"><span></span><div><strong>地点将参与派生</strong><small>Earth to Mars Transfer</small></div></div>
</div>
</section>
</aside>
<section class="panel">
<div class="panel-head">
<div>
<p class="kicker">State Nodes</p>
<h2>状态节点时间轴</h2>
<p class="section-note">每个节点代表这段状态中的一个具体时刻。时间、事件和备注会一起保存。</p>
</div>
<div class="compact-actions">
<span class="chip blue">按 Node At 升序</span>
<button class="btn soft" type="button" onclick="addRailNode()">+ Add Node</button>
</div>
</div>
<div class="node-summary-bar">
<span class="node-status start">Start</span>
<span class="node-status mid">Course</span>
<span class="node-status watch">Watch</span>
<span class="node-status end">Arrival</span>
<span class="spacer"></span>
<span class="node-count">4 nodes</span>
</div>
<div class="panel-body timeline-rail" id="rail-list">
<article class="rail-node">
<div class="rail-time">2060-02-26<br>06:00</div>
<div class="rail-line"><span class="rail-dot"></span></div>
<div class="rail-copy">
<div class="node-card good">
<div class="node-top">
<span class="time-badge">Start<small>state begins</small></span>
<div class="node-title">
<strong>Departure burn completed</strong>
<span>TMI burn complete, all tanks within expected residuals.</span>
</div>
<button class="btn danger" type="button">Delete</button>
</div>
<div class="node-fields">
<label class="field"><span class="label">Node Title</span><input name="state_node_title" value="Departure burn completed"></label>
<label class="field"><span class="label">Node At</span><input name="state_node_at" type="datetime-local" value="2060-02-26T06:00"></label>
<label class="field node-detail"><span class="label">Detail</span><textarea name="state_node_detail">TMI burn complete, all tanks within expected residuals.</textarea></label>
</div>
</div>
</div>
</article>
<article class="rail-node">
<div class="rail-time">2060-03-04<br>14:30</div>
<div class="rail-line"><span class="rail-dot"></span></div>
<div class="rail-copy">
<div class="node-card">
<div class="node-top">
<span class="time-badge">MCC-1<small>trajectory</small></span>
<div class="node-title">
<strong>Mid-course correction 1</strong>
<span>Correction burn shortened Mars arrival error ellipse.</span>
</div>
<button class="btn danger" type="button">Delete</button>
</div>
<div class="node-fields">
<label class="field"><span class="label">Node Title</span><input name="state_node_title" value="Mid-course correction 1"></label>
<label class="field"><span class="label">Node At</span><input name="state_node_at" type="datetime-local" value="2060-03-04T14:30"></label>
<label class="field node-detail"><span class="label">Detail</span><textarea name="state_node_detail">Correction burn shortened Mars arrival error ellipse.</textarea></label>
</div>
</div>
</div>
</article>
<article class="rail-node">
<div class="rail-time">2060-03-18<br>09:15</div>
<div class="rail-line"><span class="rail-dot"></span></div>
<div class="rail-copy">
<div class="node-card warn">
<div class="node-top">
<span class="time-badge">Watch<small>thermal</small></span>
<div class="node-title">
<strong>Thermal inspection flagged</strong>
<span>Radiator loop B is above baseline, continue monitoring.</span>
</div>
<button class="btn danger" type="button">Delete</button>
</div>
<div class="node-fields">
<label class="field"><span class="label">Node Title</span><input name="state_node_title" value="Thermal inspection flagged"></label>
<label class="field"><span class="label">Node At</span><input name="state_node_at" type="datetime-local" value="2060-03-18T09:15"></label>
<label class="field node-detail"><span class="label">Detail</span><textarea name="state_node_detail">Radiator loop B is above baseline, continue monitoring.</textarea></label>
</div>
</div>
</div>
</article>
<article class="rail-node">
<div class="rail-time">2060-03-29<br>18:00</div>
<div class="rail-line"><span class="rail-dot"></span></div>
<div class="rail-copy">
<div class="node-card good">
<div class="node-top">
<span class="time-badge">End<small>arrival</small></span>
<div class="node-title">
<strong>Mars capture window</strong>
<span>Planned capture burn and cargo handoff.</span>
</div>
<button class="btn danger" type="button">Delete</button>
</div>
<div class="node-fields">
<label class="field"><span class="label">Node Title</span><input name="state_node_title" value="Mars capture window"></label>
<label class="field"><span class="label">Node At</span><input name="state_node_at" type="datetime-local" value="2060-03-29T18:00"></label>
<label class="field node-detail"><span class="label">Detail</span><textarea name="state_node_detail">Planned capture burn and cargo handoff.</textarea></label>
</div>
</div>
</div>
</article>
</div>
</section>
</form>
<div class="footer-actions">
<button class="btn primary" type="button">保存条目</button>
<button class="btn" type="button">返回</button>
<span class="spacer"></span>
<button class="btn danger" type="button">删除条目</button>
</div>
</main>
<script>
function addRailNode() {
const list = document.getElementById('rail-list');
const item = list.querySelector('.rail-node').cloneNode(true);
item.querySelectorAll('input, textarea').forEach((el) => { el.value = ''; });
item.querySelector('.rail-time').innerHTML = 'New node';
const title = item.querySelector('.node-title strong');
const caption = item.querySelector('.node-title span');
if (title) title.textContent = 'Untitled node';
if (caption) caption.textContent = 'Fill in time, title, and detail.';
list.appendChild(item);
}
</script>
</body>
</html>