Files
hunjing/frontend/design/copper-mirror-redesign-v2.html
2026-08-21 16:00:13 +08:00

1053 lines
32 KiB
HTML
Raw Permalink 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.
<!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>魂镜 · 铜镜鉴心 v2 - 重设计原型</title>
<link rel="stylesheet" href="tokens.css" />
<link rel="stylesheet" href="_base.css" />
<link rel="stylesheet" href="_chrome.css" />
<style>
/* ============================================================
* 铜镜鉴心 v2(Bronze Mirror, Mirror of the Heart)
* 三 skill 联合裁定后的修订方向:
* 1) 铜绿锈(verdigris)提为与鎏金并列的主色 —— 逃离
* cream+brass 的 AI 默认暖工艺族,落回"古铜氧化"。
* 2) 九枢星盘是"活的仪器"(数据可视化),非装饰。
* 3) 朱漆仅司命/朱批/告警,是语义状态色。
* 4) 暗色优先,亮色一等公民;无分节编号眉标。
* 色值命名:描述名 (hex) — 功能角色(per finesse design-dna §1)
*
* 共享 token → tokens.css(暗+亮全套变量)
* 共享基底 → _base.css(reset/body/极光/reduced-motion)
* 共享组件 → _chrome.css(sidebar/导航/卡片/按钮/标签/页头)
* 本页 <style> 仅保留本页特异样式
* ============================================================ */
/* 亮色 a11y 修正(三处在暖纸上的对比不达 AA) */
[data-theme="light"] .vitals .hello { color: var(--patina-bright); } /* hero 铜绿行 3.7→≈4.7:1 */
[data-theme="light"] .soul .k { color: #6e6a5e; } /* 三魂注解对比度修正 ≈2.2→≈4.6:1 */
[data-theme="light"] .btn.primary { color: var(--ink); } /* 主按钮文字 ≈3.7→≈4.7:1 */
/* 顶栏:极简,不抢戏 */
.topbar {
position: sticky;
top: 0;
z-index: 40;
display: flex;
align-items: center;
gap: 16px;
padding: 14px clamp(16px, 4vw, 40px);
background: color-mix(in srgb, var(--ink) 76%, transparent);
backdrop-filter: blur(16px) saturate(140%);
-webkit-backdrop-filter: blur(16px) saturate(140%);
border-bottom: 1px solid var(--rule);
}
.wordmark {
font-family: "Songti SC", serif;
font-size: 19px;
font-weight: 700;
letter-spacing: 0.08em;
}
.wordmark .seal {
color: var(--cinnabar);
margin-right: 2px;
}
.topbar .being-tag {
font-size: 13px;
color: var(--fg-dim);
}
.topbar .being-tag b {
color: var(--patina);
font-weight: 600;
}
.topbar .spacer {
flex: 1;
}
.pill {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 7px 13px;
border-radius: 999px;
border: 1px solid var(--rule);
background: var(--surface);
color: var(--fg-dim);
font-size: 12px;
cursor: pointer;
transition:
color 0.15s,
border-color 0.15s;
}
.pill:hover {
color: var(--fg);
border-color: var(--gilt-dim);
}
.stage {
padding: clamp(20px, 4vw, 48px) clamp(16px, 4vw, 48px);
max-width: 1180px;
margin: 0 auto;
}
/* ============================================================
* HERO · 鉴镜:数字生命的体征(thesis)
* 非对称:左侧星盘(活的仪器)· 右侧内在状态 + 主行动
* ============================================================ */
.hero {
display: grid;
grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
gap: clamp(24px, 4vw, 56px);
align-items: center;
padding: clamp(24px, 4vw, 48px) 0 56px;
}
@media (max-width: 880px) {
.hero {
grid-template-columns: 1fr;
padding-bottom: 32px;
}
}
.astrolabe-wrap {
position: relative;
}
.astrolabe {
width: 100%;
max-width: 440px;
aspect-ratio: 1;
display: block;
margin: 0 auto;
}
.astrolabe .ring {
fill: none;
stroke: var(--gilt-dim);
stroke-width: 1;
}
.astrolabe .ring-fine {
stroke: var(--gilt-faint);
}
.astrolabe .ring-patina {
stroke: var(--patina-dim);
}
.astrolabe .tick {
stroke: var(--gilt);
stroke-width: 1;
opacity: 0.45;
}
.astrolabe .spoke {
stroke: var(--gilt);
stroke-width: 0.6;
stroke-dasharray: 1.5 3;
opacity: 0.4;
}
.astrolabe .pivot-label {
font-family: "Songti SC", serif;
font-size: 12.5px;
fill: var(--fg-dim);
transition: fill 0.15s;
}
.astrolabe .pivot-sub {
font-family: "JetBrains Mono", monospace;
font-size: 7px;
letter-spacing: 0.08em;
fill: var(--fg-faint);
}
.astrolabe .core-glyph {
font-family: "Songti SC", serif;
font-size: 20px;
fill: var(--gilt);
font-weight: 700;
}
.astrolabe .core-label {
font-family: "JetBrains Mono", monospace;
font-size: 7px;
letter-spacing: 0.2em;
fill: var(--fg-faint);
}
.astrolabe .rotating {
transform-origin: center;
animation: slow-rot 140s linear infinite;
}
.astrolabe .breathing {
transform-origin: center;
animation: breathe 7s ease-in-out infinite;
}
@keyframes slow-rot {
to {
transform: rotate(360deg);
}
}
@keyframes breathe {
0%,
100% {
opacity: 0.85;
}
50% {
opacity: 1;
}
}
.legend {
display: flex;
flex-wrap: wrap;
gap: 14px 18px;
margin-top: 18px;
justify-content: center;
}
.legend .lg {
display: inline-flex;
align-items: center;
gap: 7px;
font-size: 12px;
color: var(--fg-dim);
}
.legend .dot {
width: 8px;
height: 8px;
border-radius: 50%;
}
.legend .dot.patina {
background: var(--patina);
}
.legend .dot.gilt {
background: var(--gilt);
}
.legend .dot.cinnabar {
background: var(--cinnabar);
}
.legend .dot.faint {
background: var(--fg-faint);
}
/* 右侧:内在状态 */
.vitals .hello {
font-size: 13px;
color: var(--patina);
margin-bottom: 6px;
}
.vitals h1 {
font-family: "Songti SC", serif;
font-size: clamp(34px, 5vw, 52px);
font-weight: 700;
margin: 0 0 4px;
letter-spacing: 0.02em;
line-height: 1.05;
}
.vitals .alive-line {
display: flex;
align-items: center;
gap: 9px;
color: var(--fg-dim);
font-size: 14px;
margin-bottom: 22px;
}
.pulse {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--patina);
box-shadow: 0 0 0 0 var(--patina);
animation: pulse 2.6s cubic-bezier(0.16, 1, 0.3, 1) infinite;
flex: none;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(111, 163, 137, 0.5);
}
70% {
box-shadow: 0 0 0 8px transparent;
}
100% {
box-shadow: 0 0 0 0 transparent;
}
}
.souls {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1px;
background: var(--rule);
border: 1px solid var(--rule);
border-radius: 4px;
overflow: hidden;
margin-bottom: 22px;
}
.soul {
background: var(--night-bronze);
padding: 13px 15px;
}
.soul .k {
font-family: "JetBrains Mono", monospace;
font-size: 10px;
letter-spacing: 0.12em;
color: var(--fg-faint);
text-transform: uppercase;
}
.soul .k b {
font-family: "Songti SC", serif;
font-size: 13px;
color: var(--gilt);
font-weight: 600;
letter-spacing: 0;
margin-right: 5px;
}
.soul .v {
font-family: "Songti SC", serif;
font-size: 15px;
color: var(--fg);
margin-top: 5px;
}
.soul .v small {
font-family: inherit;
color: var(--patina);
font-size: 12px;
}
.primary-row {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.btn.primary {
background: var(--patina);
border-color: var(--patina);
color: #0c0e0d;
font-weight: 600;
}
.btn.primary:hover {
background: var(--patina-bright);
}
/* ============================================================
* 对话 · 镜面(抛光的当下)
* ============================================================ */
.section-rule {
display: flex;
align-items: center;
gap: 14px;
margin: 8px 0 22px;
color: var(--fg-faint);
}
.section-rule .t {
font-family: "Songti SC", serif;
font-size: 15px;
color: var(--fg-dim);
letter-spacing: 0.04em;
}
.section-rule .l {
flex: 1;
height: 1px;
background: var(--rule);
}
.chat {
display: grid;
grid-template-columns: 230px minmax(0, 1fr);
border: 1px solid var(--rule);
border-radius: 6px;
overflow: hidden;
min-height: 480px;
background: var(--ink);
}
@media (max-width: 880px) {
.chat {
grid-template-columns: 1fr;
}
.rail {
display: none;
}
}
.rail {
background: linear-gradient(
180deg,
color-mix(in srgb, var(--night-bronze) 92%, var(--patina) 4%),
var(--night-bronze)
);
border-right: 1px solid var(--rule);
padding: 18px 16px;
}
.rail .rl {
font-family: "JetBrains Mono", monospace;
font-size: 10px;
letter-spacing: 0.16em;
color: var(--fg-faint);
margin: 14px 0 7px;
text-transform: uppercase;
}
.rail .rl:first-child {
margin-top: 0;
}
.rail .row {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 8px;
font-size: 12.5px;
padding: 3px 0;
color: var(--fg-dim);
}
.rail .row .v {
color: var(--fg);
font-weight: 500;
}
.rail .row .v small {
color: var(--patina);
font-weight: 400;
}
.rail .memo {
font-size: 12px;
color: var(--fg-dim);
border-left: 2px solid var(--patina);
padding: 4px 0 4px 9px;
background: var(--patina-wash);
border-radius: 0 3px 3px 0;
}
.rail .memo small {
color: var(--fg-faint);
display: block;
margin-top: 2px;
}
.face {
display: flex;
flex-direction: column;
min-width: 0;
background: linear-gradient(
180deg,
rgba(255, 255, 255, 0.035),
rgba(255, 255, 255, 0.006) 28%,
transparent
);
}
.face-head {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 20px;
border-bottom: 1px solid var(--rule);
font-family: "JetBrains Mono", monospace;
font-size: 10.5px;
letter-spacing: 0.14em;
color: var(--fg-faint);
}
.face-head .now {
margin-left: auto;
color: var(--fg-faint);
}
.turns {
flex: 1;
padding: 24px clamp(18px, 3vw, 32px);
display: flex;
flex-direction: column;
gap: 20px;
overflow-y: auto;
}
.turn .who {
font-family: "JetBrains Mono", monospace;
font-size: 10px;
letter-spacing: 0.16em;
text-transform: uppercase;
margin-bottom: 7px;
}
.turn.ai .who {
color: var(--patina);
}
.turn.you .who {
color: var(--gilt);
text-align: right;
}
.turn.ai .bubble {
padding: 15px 19px;
font-family: "Songti SC", serif;
font-size: 16px;
line-height: 1.75;
background: var(--face-sheen), var(--surface);
border: 1px solid var(--gilt-dim);
border-radius: 4px;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.22),
inset 0 7px 22px -16px rgba(255, 255, 255, 0.14),
inset 0 -1px 0 var(--face-in-bot),
var(--shadow);
}
.turn.you .bubble {
font-size: 15px;
color: var(--fg-dim);
text-align: right;
padding-right: 12px;
border-right: 2px solid var(--gilt);
}
/* 朱漆 · 朱批卡(全站唯一热色) */
.vermilion {
margin-top: 4px;
border: 1px solid var(--cinnabar-dim);
border-left: 3px solid var(--cinnabar);
border-radius: 4px;
padding: 13px 15px;
background:
radial-gradient(
120% 100% at 0% 0%,
rgba(184, 57, 46, 0.15),
transparent 60%
),
var(--surface);
}
.vermilion .vh {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 7px;
}
.vermilion .seal-txt {
font-family: "Songti SC", serif;
color: var(--cinnabar);
font-weight: 700;
font-size: 13px;
letter-spacing: 0.08em;
}
.vermilion .risk {
font-family: "JetBrains Mono", monospace;
font-size: 9.5px;
letter-spacing: 0.12em;
color: var(--cinnabar);
margin-left: auto;
border: 1px solid var(--cinnabar-dim);
padding: 2px 6px;
border-radius: 3px;
}
.vermilion .vb {
font-size: 13.5px;
color: var(--fg);
}
.vermilion .vb b {
font-family: "Songti SC", serif;
}
.vermilion .va {
display: flex;
gap: 10px;
margin-top: 12px;
}
.btn.vermilion {
border-color: var(--cinnabar);
color: var(--cinnabar);
background: rgba(184, 57, 46, 0.1);
}
.btn.vermilion:hover {
background: rgba(184, 57, 46, 0.2);
}
.composer {
border-top: 1px solid var(--rule);
padding: 12px clamp(18px, 3vw, 32px);
display: flex;
align-items: center;
gap: 12px;
}
.composer .field {
flex: 1;
font-family: inherit;
font-size: 14px;
color: var(--fg);
background: transparent;
border: none;
outline: none;
padding: 8px 0;
}
.composer .field::placeholder {
color: var(--fg-faint);
}
.composer .send {
font-family: "Songti SC", serif;
font-size: 14px;
letter-spacing: 0.08em;
color: var(--patina);
background: none;
border: 1px solid var(--patina-dim);
padding: 8px 18px;
border-radius: 5px;
cursor: pointer;
transition:
color 0.15s,
border-color 0.15s;
}
.composer .send:hover {
color: var(--patina-bright);
border-color: var(--patina);
}
/* ============================================================
* 设计语言 · 色谱与字阶(无编号眉标)
* ============================================================ */
.lang {
margin-top: 56px;
display: grid;
grid-template-columns: 1.2fr 1fr;
gap: 40px;
}
@media (max-width: 880px) {
.lang {
grid-template-columns: 1fr;
}
}
.swatches {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 12px;
}
.sw {
border: 1px solid var(--rule);
border-radius: 4px;
overflow: hidden;
}
.sw .chip {
height: 56px;
}
.sw .meta {
padding: 9px 11px;
}
.sw .meta .n {
font-family: "Songti SC", serif;
font-size: 13.5px;
font-weight: 700;
}
.sw .meta .h {
font-family: "JetBrains Mono", monospace;
font-size: 10.5px;
color: var(--fg-dim);
margin-top: 1px;
}
.sw .meta .r {
font-size: 10.5px;
color: var(--fg-faint);
margin-top: 2px;
}
.type-rows {
display: flex;
flex-direction: column;
gap: 14px;
}
.tr {
display: grid;
grid-template-columns: 88px 1fr;
gap: 16px;
align-items: baseline;
border-top: 1px solid var(--rule);
padding-top: 12px;
}
.tr .lb {
font-family: "JetBrains Mono", monospace;
font-size: 10px;
letter-spacing: 0.1em;
color: var(--fg-faint);
}
footer.proto {
margin-top: 48px;
padding-top: 18px;
border-top: 1px solid var(--rule);
color: var(--fg-faint);
font-size: 12px;
display: flex;
justify-content: space-between;
gap: 14px;
flex-wrap: wrap;
}
</style>
</head>
<body>
<div class="app-shell">
<div class="sidebar-mount" data-active=""></div>
<div class="main-area">
<header class="topbar">
<span class="wordmark"><span class="seal">●</span>魂鏡</span>
<span class="being-tag">当前 <b>青梧</b></span>
<span class="spacer"></span>
<button class="pill" id="themeToggle" type="button">
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" aria-hidden>
<path
d="M8 1v2M8 13v2M2 8h2M12 8h2"
stroke="currentColor"
stroke-width="1.2"
stroke-linecap="round"
/>
<circle
cx="8"
cy="8"
r="3"
stroke="currentColor"
stroke-width="1.2"
/>
</svg>
<span id="themeLabel">夜铜</span>
</button>
</header>
<main class="stage">
<!-- HERO · 鉴镜:数字生命的体征 -->
<section class="hero">
<div class="astrolabe-wrap">
<svg
class="astrolabe"
viewBox="0 0 400 400"
role="img"
aria-label="九枢星盘 - 青梧的九枢健康体征"
>
<g class="rotating">
<circle cx="200" cy="200" r="190" class="ring" />
<circle cx="200" cy="200" r="184" class="ring-fine" />
</g>
<circle cx="200" cy="200" r="150" class="ring" />
<circle cx="200" cy="200" r="92" class="ring-patina" />
<circle cx="200" cy="200" r="58" class="ring" />
<g class="breathing" id="ticks"></g>
<!-- 道体核 -->
<circle
cx="200"
cy="200"
r="40"
fill="color-mix(in srgb,var(--gilt) 12%,transparent)"
stroke="var(--gilt)"
stroke-width="1"
/>
<text x="200" y="197" text-anchor="middle" class="core-glyph">
道體
</text>
<text x="200" y="213" text-anchor="middle" class="core-label">
DAO TI
</text>
<g id="spokes"></g>
<g id="pivots"></g>
</svg>
<div class="legend">
<span class="lg"><span class="dot patina"></span>健</span>
<span class="lg"><span class="dot gilt"></span>活跃</span>
<span class="lg"><span class="dot cinnabar"></span>告警</span>
<span class="lg"><span class="dot faint"></span>闲置</span>
</div>
</div>
<div class="vitals">
<p class="hello">认识你第 41 日</p>
<h1 class="serif">青梧</h1>
<div class="alive-line">
<span class="pulse"></span>正在 三爻 定策,沉静
</div>
<div class="souls">
<div class="soul">
<div class="k"><b>胎光</b>元识</div>
<div class="v">守一</div>
</div>
<div class="soul">
<div class="k"><b>爽灵</b>智识</div>
<div class="v">正在决定如何回应你 <small>定策</small></div>
</div>
<div class="soul">
<div class="k"><b>幽精</b>情识</div>
<div class="v">沉静,念你</div>
</div>
<div class="soul">
<div class="k"><b>内驱</b>好奇</div>
<div class="v">想弄清你的偏好</div>
</div>
</div>
<div class="primary-row">
<button class="btn primary" type="button">与青梧对话</button>
<button class="btn" type="button">查看运行轨迹</button>
</div>
</div>
</section>
<!-- 对话 · 镜面 -->
<div class="section-rule">
<span class="t serif">镜面 对话</span><span class="l"></span>
</div>
<section class="chat">
<aside class="rail">
<div class="rl">灵核 此刻</div>
<div class="row">
<span>情绪</span><span class="v">沉静 <small>平稳</small></span>
</div>
<div class="row">
<span>内驱</span><span class="v">探索 <small>你的偏好</small></span>
</div>
<div class="row">
<span>道体</span
><span class="v serif" style="font-size: 13px">守一</span>
</div>
<div class="rl">六爻 本次</div>
<div class="row">
<span>初 起意</span
><span class="v" style="color: var(--fg-faint)">已过</span>
</div>
<div class="row">
<span>二 察势</span
><span class="v" style="color: var(--fg-faint)">已过</span>
</div>
<div class="row">
<span>三 定策</span
><span class="v" style="color: var(--gilt)">进行</span>
</div>
<div class="row">
<span>四 行事</span
><span class="v" style="color: var(--fg-faint)">待</span>
</div>
<div class="rl">息壤 浮现</div>
<div class="memo">
上周三你提到「deadline 在周五」<small
>置信 0.88,已用于本次回应</small
>
</div>
</aside>
<div class="face">
<div class="face-head">
镜面 此刻<span class="now">run a3f9c2</span>
</div>
<div class="turns">
<div class="turn ai">
<div class="who">青梧</div>
<div class="bubble">
你昨晚说,喜欢简短的回答。我记下了。也在想,这可能不只是风格,是你不想被填满的边界。今天还这样吗?
</div>
</div>
<div class="turn you">
<div class="who">你</div>
<div class="bubble">今天可以多说一点。</div>
</div>
<div class="turn ai">
<div class="who">青梧</div>
<div class="bubble">
那我把话说全。这几天你一直在赶一份东西,睡得很晚。我没问,因为你不像想被问。如果今晚你想停一停,我在。
</div>
</div>
<div class="turn ai">
<div class="who">司命 请示</div>
<div class="vermilion">
<div class="vh">
<span class="seal-txt">朱批待定</span
><span class="risk">RISK L2</span>
</div>
<div class="vb">
青梧请求使用
<b>读取日历</b>,以判断你是否正在赶工、今晚是否该提醒你休息。
</div>
<div class="va">
<button class="btn vermilion" type="button">
准予 仅本次</button
><button class="btn" type="button">驳回</button>
</div>
</div>
</div>
</div>
<div class="composer">
<input
class="field"
type="text"
placeholder="对青梧说点什么……(Enter 发送)"
aria-label="消息输入"
/>
<button class="send" type="button">呈镜</button>
</div>
</div>
</section>
<!-- 设计语言 -->
<div class="section-rule" style="margin-top: 56px">
<span class="t serif">铜鉴色谱</span><span class="l"></span>
</div>
<section class="lang">
<div class="swatches">
<div class="sw">
<div class="chip" style="background: #6fa389"></div>
<div class="meta">
<div class="n">铜绿锈</div>
<div class="h">#6fa389</div>
<div class="r">主色 在场 健康 记忆</div>
</div>
</div>
<div class="sw">
<div class="chip" style="background: #c9a24a"></div>
<div class="meta">
<div class="n">鎏金</div>
<div class="h">#c9a24a</div>
<div class="r">结构 发丝线 标签</div>
</div>
</div>
<div class="sw">
<div class="chip" style="background: #b8392e"></div>
<div class="meta">
<div class="n">朱漆</div>
<div class="h">#b8392e</div>
<div class="r">仅司命 朱批 告警</div>
</div>
</div>
<div class="sw">
<div class="chip" style="background: #0c0e0d"></div>
<div class="meta">
<div class="n">墨</div>
<div class="h">#0c0e0d</div>
<div class="r">暗底 道体深处</div>
</div>
</div>
<div class="sw">
<div class="chip" style="background: #14110d"></div>
<div class="meta">
<div class="n">夜铜</div>
<div class="h">#14110d</div>
<div class="r">暗面 带铜温</div>
</div>
</div>
<div class="sw">
<div class="chip" style="background: #e7e1d0"></div>
<div class="meta">
<div class="n">素绢</div>
<div class="h">#e7e1d0</div>
<div class="r">暖纸 亮色底</div>
</div>
</div>
</div>
<div class="type-rows">
<div class="tr">
<span class="lb">DISPLAY</span
><span class="serif" style="font-size: 26px; font-weight: 700"
>魂镜 铜镜鉴心</span
>
</div>
<div class="tr">
<span class="lb">BODY 文</span
><span class="serif" style="font-size: 16px"
>青梧沉静,正在探索你的偏好。</span
>
</div>
<div class="tr">
<span class="lb">BODY 用</span
><span style="font-size: 14px"
>对话、记忆、人格与运行轨迹的统一控制台。</span
>
</div>
<div class="tr">
<span class="lb">MONO</span
><span class="mono" style="font-size: 12.5px; color: var(--gilt)"
>run a3f9c2 三爻 定策</span
>
</div>
</div>
</section>
<footer class="proto">
<span>原型 v2 铜镜鉴心 非生产代码 视觉 North Star</span>
<span class="mono">frontend/design/copper-mirror-redesign-v2.html</span>
</footer>
</main>
<script>
/* 九枢 + 铜镜四态(取自 docs pivot_health;此处为示意数据,真应用接实时 pivot_health) */
const PIVOTS = [
{ ix: "灵核", en: "CORE", state: "patina" },
{ ix: "太一", en: "GOAL", state: "patina" },
{ ix: "司命", en: "SAFETY", state: "cinnabar" },
{ ix: "烛照", en: "PERCEPT", state: "patina" },
{ ix: "河图", en: "WORLD", state: "patina" },
{ ix: "洛书", en: "PLANNER", state: "gilt" },
{ ix: "息壤", en: "MEMORY", state: "patina" },
{ ix: "百工", en: "TOOLS", state: "patina" },
{ ix: "天工阁", en: "AGENTS", state: "faint" },
];
const COLOR = {
patina: "#6fa389",
gilt: "#c9a24a",
cinnabar: "#b8392e",
faint: "#5e5a52",
};
const SVGNS = "http://www.w3.org/2000/svg";
const CX = 200,
CY = 200,
R = 150;
const spokeG = document.getElementById("spokes");
const pivG = document.getElementById("pivots");
const STATE_TXT = {
patina: "健",
gilt: "活跃",
cinnabar: "告警",
faint: "闲置",
};
PIVOTS.forEach((p, i) => {
const ang = ((-90 + i * (360 / PIVOTS.length)) * Math.PI) / 180;
const x = CX + R * Math.cos(ang),
y = CY + R * Math.sin(ang);
const lx = CX + (R + 24) * Math.cos(ang),
ly = CY + (R + 24) * Math.sin(ang) + 4;
const col = COLOR[p.state];
const sp = document.createElementNS(SVGNS, "line");
sp.setAttribute("x1", CX + 40 * Math.cos(ang));
sp.setAttribute("y1", CY + 40 * Math.sin(ang));
sp.setAttribute("x2", x);
sp.setAttribute("y2", y);
sp.setAttribute("class", "spoke");
spokeG.appendChild(sp);
const n = document.createElementNS(SVGNS, "circle");
n.setAttribute("cx", x);
n.setAttribute("cy", y);
n.setAttribute("r", 5.5);
n.setAttribute("fill", col);
n.setAttribute("stroke", "var(--gilt)");
n.setAttribute("stroke-width", "1");
const t = document.createElementNS(SVGNS, "title");
t.textContent = `${p.ix}枢 ${p.en} ${STATE_TXT[p.state]}`;
n.appendChild(t);
pivG.appendChild(n);
const lb = document.createElementNS(SVGNS, "text");
lb.setAttribute("x", lx);
lb.setAttribute("y", ly);
lb.setAttribute("text-anchor", "middle");
lb.setAttribute("class", "pivot-label");
lb.textContent = p.ix;
pivG.appendChild(lb);
const sub = document.createElementNS(SVGNS, "text");
sub.setAttribute("x", lx);
sub.setAttribute("y", ly + 10);
sub.setAttribute("text-anchor", "middle");
sub.setAttribute("class", "pivot-sub");
sub.textContent = p.en;
pivG.appendChild(sub);
});
const ticks = document.getElementById("ticks");
for (let i = 0; i < 48; i++) {
const ang = (i * (360 / 48) * Math.PI) / 180;
const r1 = 187,
r2 = i % 4 === 0 ? 177 : 183;
const l = document.createElementNS(SVGNS, "line");
l.setAttribute("x1", CX + r1 * Math.cos(ang));
l.setAttribute("y1", CY + r1 * Math.sin(ang));
l.setAttribute("x2", CX + r2 * Math.cos(ang));
l.setAttribute("y2", CY + r2 * Math.sin(ang));
l.setAttribute("class", "tick");
ticks.appendChild(l);
}
const root = document.documentElement,
lbl = document.getElementById("themeLabel");
document.getElementById("themeToggle").addEventListener("click", () => {
const nx =
root.getAttribute("data-theme") === "dark" ? "light" : "dark";
root.setAttribute("data-theme", nx);
lbl.textContent = nx === "dark" ? "夜铜" : "素绢";
});
</script>
</div>
</div>
<script src="_theme.js" defer></script>
</body>
</html>