初始化提交

This commit is contained in:
chenxin committed 2026-08-21 16:00:13 +08:00
commit 887c1f449e
963 files changed
+166869

No files matched your search

+230
View File
@@ -0,0 +1,230 @@
<!doctype html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>魂镜 · 符文引擎设计预览</title>
<link rel="stylesheet" href="tokens.css" />
<link rel="stylesheet" href="_base.css" />
<link rel="stylesheet" href="_chrome.css" />
<style>
/* ============================================================
* 铜镜鉴心 v2.1 — 符文引擎(Macros · 占位符显化)
* 功能融合 §8.4:{{char}}/{{user}}/{{original}} 等占位符
* 在组装管线末端显化为实际内容。核心隐喻——
* 符文形态(mono · 未显化的符箓)→ 显化(宋体 · 激活的文字)
* ============================================================ */
/* ── 主区 ── */
.page-header .spacer { flex: 1; }
.section-label .glyph { color: var(--gilt); opacity: 0.85; font-family: "Songti SC", serif; font-size: 14px; }
.section-label .sub { color: var(--fg-dim); opacity: 0.7; letter-spacing: 0.04em; }
/* ═══ 符文卡(签名:mono 符文形态 → 宋体显化)═══ */
.rune-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(248px, 1fr)); gap: 12px; }
.rune-card { border: 1px solid var(--rule); border-radius: 10px; background: var(--surface); padding: 14px 16px; transition: border-color 0.15s, transform 0.15s; position: relative; overflow: hidden; }
.rune-card:hover { border-color: var(--patina-dim); transform: translateY(-1px); }
.rune-card::after { content: ""; position: absolute; inset: 0; background-image: var(--sweep-gradient); background-size: 220% 100%; background-position: -40% 0; opacity: 0; transition: opacity 0.4s; pointer-events: none; }
.rune-card:hover::after { opacity: 0.5; }
.rune-form { font-family: "JetBrains Mono", monospace; font-size: 15px; color: var(--gilt); letter-spacing: 0.02em; }
.rune-arrow { color: var(--fg-faint); font-family: "JetBrains Mono", monospace; font-size: 11px; margin: 7px 0 5px; display: flex; align-items: center; gap: 5px; }
.rune-arrow .line { flex: 1; height: 1px; background: var(--rule); }
.rune-val { font-family: "Songti SC", serif; font-size: 17px; color: var(--patina); font-weight: 600; }
.rune-val.empty { color: var(--fg-faint); font-style: italic; font-weight: 400; }
.rune-desc { font-size: 11.5px; color: var(--muted); margin-top: 6px; line-height: 1.45; }
.rune-card.custom { border-style: dashed; }
.rune-card.custom .rune-form { color: var(--cinnabar); }
/* ═══ 显化沙盒 ═══ */
.sandbox { border: 1px solid var(--gilt-dim); border-radius: 10px; background: var(--face-sheen), var(--surface); box-shadow: inset 0 1px 0 var(--face-in-top), inset 0 -1px 0 var(--face-in-bot), var(--shadow); overflow: hidden; }
.sandbox-head { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid var(--rule); }
.sandbox-head .t { font-family: "Songti SC", serif; font-size: 14px; font-weight: 600; color: var(--fg); display: flex; align-items: center; gap: 7px; }
.ctx-select { font-family: inherit; font-size: 12px; color: var(--fg-dim); background: var(--glass); border: 1px solid var(--rule); border-radius: 7px; padding: 5px 10px; cursor: pointer; }
.ctx-select:focus { outline: none; border-color: var(--patina-dim); }
.sandbox-body { padding: 16px; display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 760px) { .sandbox-body { grid-template-columns: 1fr; } }
.sb-col .lb { font-family: "JetBrains Mono", monospace; font-size: 10px; letter-spacing: 0.12em; color: var(--fg-faint); text-transform: uppercase; margin-bottom: 6px; }
.sb-input { width: 100%; min-height: 96px; resize: vertical; font-family: "JetBrains Mono", monospace; font-size: 13px; line-height: 1.7; color: var(--fg); background: var(--ink); border: 1px solid var(--rule); border-radius: 7px; padding: 10px 12px; }
.sb-input:focus { outline: none; border-color: var(--patina-dim); }
.sb-output { width: 100%; min-height: 96px; font-family: "Songti SC", serif; font-size: 15px; line-height: 1.8; color: var(--fg); background: var(--patina-wash); border: 1px solid var(--patina-dim); border-radius: 7px; padding: 10px 12px; white-space: pre-wrap; }
.sb-output .hl { color: var(--patina); font-weight: 600; }
.sb-output .unk { color: var(--cinnabar); font-family: "JetBrains Mono", monospace; font-size: 12px; }
/* ═══ 管线位置(符文在组装末端)═══ */
.pipeline { border: 1px solid var(--rule); border-radius: 10px; background: var(--surface); padding: 18px 20px; }
.pipeline-track { display: flex; align-items: center; gap: 0; flex-wrap: wrap; margin-top: 6px; }
.pl-node { font-family: "Songti SC", serif; font-size: 13px; color: var(--fg-dim); padding: 8px 14px; border: 1px solid var(--rule); border-radius: 7px; background: var(--ink); }
.pl-node.dim { color: var(--fg-faint); }
.pl-arrow { color: var(--fg-faint); font-family: "JetBrains Mono", monospace; font-size: 13px; padding: 0 6px; }
.pl-node.terminal { color: var(--patina); border-color: var(--patina-dim); background: var(--patina-wash); font-weight: 600; position: relative; }
.pl-node.terminal::after { content: "符文显化"; position: absolute; top: -17px; left: 50%; transform: translateX(-50%); font-family: "JetBrains Mono", monospace; font-size: 9px; letter-spacing: 0.1em; color: var(--patina); white-space: nowrap; }
.pipeline-note { font-size: 12.5px; color: var(--fg-dim); margin-top: 16px; line-height: 1.6; }
.pipeline-note .em { color: var(--fg); }
.pipeline-note .warn { color: var(--cinnabar); }
.foot-note { font-family: "JetBrains Mono", monospace; font-size: 11px; color: var(--fg-faint); letter-spacing: 0.04em; margin-top: 4px; }
@media (max-width: 900px) { .sidebar-wrap { display: none; } .page-body { padding: 18px 16px 40px; } }
</style>
</head>
<body>
<div class="app-shell">
<!-- ════════ 侧栏 ════════ -->
<div class="sidebar-mount" data-active="符文引擎"></div>
<!-- ════════ 主区 ════════ -->
<div class="main-area">
<div class="page-header">
<div>
<div class="page-title">符文引擎</div>
<div class="page-sub">Macros · 占位符于组装末端显化</div>
</div>
<div class="spacer"></div>
<span class="stat-pill">12 内置 · 3 自定义 · 全部就绪</span>
</div>
<div class="page-body">
<!-- 内置符文 -->
<section>
<div class="section-label"><span class="glyph">符</span>内置符文 <span class="sub">— 系统上下文自动取值</span></div>
<div class="rune-grid" id="builtinGrid"></div>
</section>
<!-- 自定义符文 -->
<section>
<div class="section-label"><span class="glyph">刻</span>自定义符文 <span class="sub">— 创作者定义,全局可用</span></div>
<div class="rune-grid" id="customGrid"></div>
</section>
<!-- 显化沙盒 -->
<section>
<div class="section-label"><span class="glyph">显</span>显化沙盒 <span class="sub">— 左侧符文形态,右侧显化结果</span></div>
<div class="sandbox">
<div class="sandbox-head">
<span class="t">实时显化</span>
<label style="font-size:12px;color:var(--fg-dim);display:flex;align-items:center;gap:8px">
上下文
<select class="ctx-select" id="ctxSelect">
<option value="qingwu">青梧 · 暮色书房</option>
<option value="zhaoyin">照影 · 竹林雨夜</option>
</select>
</label>
</div>
<div class="sandbox-body">
<div class="sb-col">
<div class="lb">符文形态 · Input</div>
<textarea class="sb-input" id="sbInput">{{char}}在{{scenario}}对{{user}}低语,{{tone}}。{{idle}}</textarea>
</div>
<div class="sb-col">
<div class="lb">显化 · Resolved</div>
<div class="sb-output" id="sbOutput"></div>
</div>
</div>
</div>
</section>
<!-- 管线位置 -->
<section>
<div class="section-label"><span class="glyph">位</span>组装管线位置 <span class="sub">— 符文引擎是末端,所有占位符在此显化</span></div>
<div class="pipeline">
<div class="pipeline-track">
<span class="pl-node">道体</span><span class="pl-arrow">→</span>
<span class="pl-node">世谱</span><span class="pl-arrow">→</span>
<span class="pl-node">眉批</span><span class="pl-arrow">→</span>
<span class="pl-node">刻符</span><span class="pl-arrow">→</span>
<span class="pl-node terminal">符文引擎</span><span class="pl-arrow">→</span>
<span class="pl-node dim">最终 prompt</span>
</div>
<p class="pipeline-note">
<span class="em">末端替换</span>:符文引擎不生成内容,只把上游所有占位符(<code style="font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--gilt)">{{char}}</code> / <code style="font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--gilt)">{{user}}</code> / 自定义等)替换为实际值。
<span class="warn">循环引用</span>(如 <code style="font-family:'JetBrains Mono',monospace;font-size:12px">{{a}}→{{b}}→{{a}}</code>)由引擎检测并截断,标记为未显化。
</p>
<p class="foot-note">参考 docs/contracts/contract-glossary.md §8.4 · 导入角色卡前必须先有符文引擎,否则卡内占位符是死的</p>
</div>
</section>
</div>
</div>
</div>
<script>
/* 上下文:符文显化的取值源(真应用接运行时上下文,此处为示意) */
const CTX = {
qingwu: {
char: "青梧", user: "辰", scenario: "暮色书房", tone: "沉静温厚",
time: "申时三刻", date: "甲辰年 七月初一", idle: "已 2 时辰未语",
personality: "沉静·好奇", original: "(变换前原文)",
},
zhaoyin: {
char: "照影", user: "辰", scenario: "竹林雨夜", tone: "清冷疏淡",
time: "亥时", date: "甲辰年 七月廿三", idle: "方才分别",
personality: "清冷·机敏", original: "(变换前原文)",
},
};
/* 内置符文清单 */
const BUILTIN = [
["{{char}}", "char", "当前角色名"],
["{{user}}", "user", "用户名"],
["{{original}}", "original", "变换前原文(刻符/ujj 用)"],
["{{scenario}}", "scenario", "当前场景"],
["{{personality}}", "personality", "人格摘要"],
["{{tone}}", "tone", "语气基调(自定义回退)"],
["{{time}}", "time", "当前时辰"],
["{{date}}", "date", "当前日期"],
["{{idle}}", "idle", "距上次对话"],
];
/* 自定义符文 */
const CUSTOM = [
["{{weather}}", "细雨", "场景天气(创作者定义)"],
["{{mood}}", "微醺", "角色即时心境"],
["{{hook}}", "未完的旧诺", "本幕钩子"],
];
let ctxKey = "qingwu";
function renderCards() {
const ctx = CTX[ctxKey];
const card = (form, valOrKey, desc, custom) => {
const isCustom = custom;
const val = isCustom ? valOrKey : ctx[valOrKey];
const empty = !val;
return `<div class="rune-card ${isCustom ? "custom" : ""}">
<div class="rune-form">${form}</div>
<div class="rune-arrow"><span>显化</span><span class="line"></span><span>↓</span></div>
<div class="rune-val ${empty ? "empty" : ""}">${empty ? "(待上下文)" : val}</div>
<div class="rune-desc">${desc}</div>
</div>`;
};
document.getElementById("builtinGrid").innerHTML =
BUILTIN.map(([f, k, d]) => card(f, k, d, false)).join("");
document.getElementById("customGrid").innerHTML =
CUSTOM.map(([f, v, d]) => card(f, v, d, true)).join("") +
`<div class="rune-card custom" style="display:flex;align-items:center;justify-content:center;color:var(--fg-faint);font-size:13px;cursor:pointer;border-style:dashed">+ 刻新符文</div>`;
}
/* 沙盒:把 {{xxx}} 替换为显化值,未知标红 */
function renderSandbox() {
const ctx = Object.assign({}, CTX[ctxKey]);
CUSTOM.forEach(([f, v]) => { ctx[f.slice(2, -2)] = v; });
const input = document.getElementById("sbInput").value;
const out = input.replace(/\{\{([a-z_]+)\}\}/g, (m, key) =>
ctx[key] !== undefined
? `<span class="hl">${ctx[key]}</span>`
: `<span class="unk">${m}?</span>`
);
document.getElementById("sbOutput").innerHTML = out;
}
document.getElementById("sbInput").addEventListener("input", renderSandbox);
document.getElementById("ctxSelect").addEventListener("change", (e) => {
ctxKey = e.target.value; renderCards(); renderSandbox();
});
renderCards();
renderSandbox();
</script>
<script src="_theme.js" defer></script>
</body>
</html>