新增预设工作台:编辑、版本树、结构检查、用例试跑放在同一页。

改预设要能一边写一边看装出来的 messages,还要多模型对照,所以把编辑与试跑拆成两个视图塞进一个页面;版本存完整快照、可分叉,用例全局共用一份、运行记录按预设存。顺带让读预设文件先剥 BOM,静态资源改成不缓存,免得改了脚本还要强刷。
This commit is contained in:
2026-10-26 01:09:40 +08:00
parent 98e51f9c6c
commit f3303038fe
20 changed files with 6263 additions and 6 deletions
+182
View File
@@ -0,0 +1,182 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>预设工作台 · Writing Agent</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;600;700&family=Noto+Serif+SC:wght@500;600;700&display=swap"
rel="stylesheet"
/>
<script src="/theme-init.js"></script>
<link rel="stylesheet" href="/settings.css" />
<link rel="stylesheet" href="/history-editor.css" />
<link rel="stylesheet" href="/presets.css" />
</head>
<body class="settings-page px-page" data-preview="on" data-outline="off">
<header class="st-top">
<a class="st-brand" href="/">Writing Agent</a>
<nav class="st-nav" aria-label="主导航">
<a href="/">对话</a>
<a href="/chat.html">直聊</a>
<a href="/modules.html">技能</a>
<a href="/libraries.html">库</a>
<a href="/stats.html">Token</a>
<a href="/presets.html" aria-current="page">预设</a>
<a href="/settings.html">设置</a>
</nav>
</header>
<!-- 预设身份条:唯一脏状态 + 唯一主行动 -->
<div class="px-idbar">
<div class="px-id-main">
<select id="preset-select" class="px-select" aria-label="选择预设"></select>
<button type="button" id="btn-new-preset" class="px-btn px-btn-mini" title="新建预设">+</button>
<input
id="preset-name"
class="px-input px-name"
type="text"
aria-label="预设名称"
placeholder="预设名称"
/>
<span class="px-idmeta" id="preset-meta"></span>
<span class="px-dirty" id="dirty-pill" data-dirty="0">已同步</span>
</div>
<div class="px-id-actions">
<span class="px-notice" id="notice" aria-live="polite"></span>
<button type="button" class="px-btn" id="btn-reload" title="从磁盘重读预设文件,未保存的草稿会丢">重新载入</button>
<button type="button" class="px-btn" id="btn-run-entry">
<span class="px-btn-label">试跑</span><span class="px-btn-meta" id="run-entry-meta"></span>
</button>
<button type="button" class="px-btn" id="btn-versions" title="打开版本树">版本树</button>
<button type="button" class="px-btn" id="btn-save-current" title="覆盖当前版本节点,不新建(Ctrl+Shift+S)">
保存至当前版本
</button>
<button type="button" class="px-btn" id="btn-save-new" title="在当前节点下追加一个新版本(Ctrl+S)">
保存为新版本
</button>
<button type="button" class="px-btn px-btn-primary" id="btn-apply" title="先保存为新版本,再覆盖预设文件——对话页读的是那个文件">应用到对话页</button>
</div>
</div>
<div class="px-alert" id="alert" hidden></div>
<!-- 视图 A:编辑 -->
<div class="px-view px-view-edit" id="view-edit">
<nav class="px-outline" id="outline" aria-label="发送顺序"></nav>
<main class="px-editor" id="editor"></main>
<aside class="px-preview" id="preview-pane" aria-label="实际发送内容">
<div class="px-preview-head">
<select id="preview-case" class="px-select" aria-label="预览用例"></select>
<button type="button" class="px-btn px-btn-mini" id="btn-preview" title="Ctrl+Enter">重算</button>
<button type="button" class="px-btn px-btn-mini" id="btn-toggle-preview" title="显示 / 收起预览">
收起
</button>
</div>
<div class="px-band" id="issues-band" data-level="ok"></div>
<p class="px-preview-meta" id="preview-meta"></p>
<div class="px-messages" id="preview-out"></div>
</aside>
</div>
<!-- 视图 B:试跑 -->
<div class="px-view px-view-run" id="view-run" hidden>
<div class="px-runbar">
<div class="px-runbar-row">
<span class="px-label">当前用例</span>
<span class="px-runbar-case" id="run-case-label">先在左侧选一个用例</span>
<span class="px-runbar-sep" aria-hidden="true"></span>
<span class="px-label">模型</span>
<div class="px-picker">
<button
type="button"
class="px-btn px-btn-mini px-picker-trigger"
id="model-picker-btn"
aria-haspopup="listbox"
aria-expanded="false"
>
选择模型<span class="px-picker-count" id="model-picker-count"></span>
</button>
<div
class="px-picker-menu"
id="model-picker-menu"
role="listbox"
aria-multiselectable="true"
hidden
></div>
</div>
<div class="px-picked" id="picked-models" aria-label="已选模型"></div>
<button type="button" class="px-btn px-btn-primary px-runbar-run" id="btn-run">运行此用例</button>
</div>
</div>
<nav class="px-caselist" id="cases" aria-label="用例" title="用例全局共用:任何预设都能拿这些用例去跑"></nav>
<main class="px-results-pane">
<div class="px-results-head">
<select id="run-select" class="px-select" aria-label="历史运行"></select>
<button type="button" class="px-btn px-btn-mini" id="btn-refresh-runs">刷新记录</button>
<p class="px-hint px-results-hint">不自动评分,逐条标记成功 / 失败;标记会落盘。</p>
</div>
<div class="px-results" id="results"></div>
</main>
</div>
<!-- 版本抽屉(低频,从身份条进入) -->
<div class="px-scrim" id="scrim" hidden></div>
<aside class="px-drawer" id="version-drawer" hidden aria-label="版本">
<div class="px-drawer-head">
<h2>版本</h2>
<button type="button" class="px-btn px-btn-mini" id="btn-close-versions">关闭</button>
</div>
<div class="px-drawer-body">
<div id="version-tree" role="tree"></div>
<p class="px-hint" id="version-hint">还没有版本。</p>
<label class="px-field">
<span>本次修改说明</span>
<input type="text" id="note-input" placeholder="例:收紧叙事视角" />
</label>
<div class="px-row">
<button type="button" class="px-btn px-btn-primary" id="btn-save-version">保存为新版本</button>
<button type="button" class="px-btn" id="btn-save-version-current">保存至当前版本</button>
</div>
<div class="px-row">
<button type="button" class="px-btn" id="btn-load-version">载入选中</button>
<button type="button" class="px-btn" id="btn-fork-version">从选中分叉</button>
</div>
<p class="px-hint" id="dirty-hint"></p>
<p class="px-hint">
点节点弹菜单:「读取」换进编辑器,「从它分叉」用当前草稿在它下面开新分支。
圆点是当前编辑位置,分叉会另开一条线。
</p>
<p class="px-hint">
「保存为新版本」(Ctrl+S)在当前节点下追加子节点;「保存至当前版本」直接覆盖当前节点。
「应用到对话页」会先保存为新版本,再覆盖 <code>presets/&lt;id&gt;.json</code>——对话页读的是那个文件。
「重新载入」从磁盘重读预设文件,未保存的草稿会丢。
</p>
</div>
</aside>
<!-- 用例浮窗(字段多、还要写历史,用大浮窗而不是窄侧栏) -->
<section
class="px-modal"
id="case-modal"
role="dialog"
aria-modal="true"
aria-labelledby="case-drawer-title"
hidden
>
<div class="px-modal-head">
<h2 id="case-drawer-title">用例</h2>
<button type="button" class="px-btn px-btn-mini" id="btn-close-case">完成</button>
</div>
<div class="px-modal-body" id="case-form"></div>
</section>
<script type="module" src="/presets.js"></script>
</body>
</html>