dsh-metaphysics 0.46.5 → 0.50.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,17 +14,16 @@
14
14
  <meta name="twitter:card" content="summary_large_image">
15
15
  <style>
16
16
  :root{--bg:#f3eee4;--panel:#fffdf8;--panel2:#f8f3e9;--line:#d6cbb7;--ink:#1a1916;--ink2:#3f3a33;--dim:#6f6a60;--gold:#a72c23;--gold-deep:#7d1f18;--good:#1f6f3f;--bad:#a72c23;--warn:#8a5a00;
17
+ /* 强调面(选中 tab、主按钮等实心底)与压在上面的文字色,两套配色各给一组 */
18
+ --accent-bg:#a72c23;--accent-bg2:#7d1f18;--on-accent:#fff;--on-accent-soft:rgba(255,255,255,.84);
17
19
  --radius:12px;--shadow:0 1px 2px rgba(35,28,18,.05),0 18px 40px -30px rgba(35,28,18,.42);--ring:0 0 0 3px rgba(167,44,35,.16)}
18
20
  /* 深色:跟随系统。只换变量,元素规则一条不改。@media screen 限定 → 打印永远浅色 */
19
21
  @media screen and (prefers-color-scheme:dark){
20
22
  :root{--bg:#12110d;--panel:#1b1913;--panel2:#221f18;--line:#3c372e;--ink:#e7e2d6;--ink2:#d5cfc2;--dim:#9b9488;--gold:#e2755f;--gold-deep:#b8503f;--good:#67b98a;--bad:#e2755f;--warn:#d9a052;
23
+ /* 深色下 --gold 是浅珊瑚,实心底若沿用原色,白字与深字都够不着 4.5:1;
24
+ 所以强调面单独取一档更亮的珊瑚,压深色字(见 test/theme.test.mjs 的对比度复算) */
25
+ --accent-bg:#f0a38e;--accent-bg2:#e2755f;--on-accent:#14130f;--on-accent-soft:rgba(20,19,15,.84);
21
26
  --shadow:0 1px 2px rgba(0,0,0,.5),0 18px 40px -30px rgba(0,0,0,.9);--ring:0 0 0 3px rgba(226,117,95,.2)}
22
- /* 深色下强调色是浅色,这些实心底上的文字得反过来 */
23
- .tab.on,.seg button.on,.go{color:#14130f}
24
- .tool.on,.tool.on b{color:#14130f}
25
- .tool.on span{color:rgba(20,19,15,.72)}
26
- .mb-o.on b,.mb-o.on span,.mb-btn.primary{color:#14130f}
27
- .err{background:rgba(226,117,95,.14)}
28
27
  }
29
28
  *{box-sizing:border-box}
30
29
  html{-webkit-text-size-adjust:100%}
@@ -42,7 +41,7 @@ header{display:flex;flex-direction:column;align-items:stretch;gap:12px;margin-bo
42
41
  .brand-row{display:flex;align-items:center;justify-content:space-between;gap:10px 16px;flex-wrap:wrap}
43
42
  .brand{display:flex;align-items:center;gap:12px;font-size:22px;font-weight:700;letter-spacing:.14em;color:var(--gold);line-height:1.3}
44
43
  .brand .seal{display:inline-flex;align-items:center;justify-content:center;flex:none;width:40px;height:40px;border-radius:11px;
45
- background:linear-gradient(145deg,var(--gold),var(--gold-deep));color:#fff;font:700 20px/1 "Songti SC","SimSun",serif;letter-spacing:0;
44
+ background:linear-gradient(145deg,var(--accent-bg),var(--accent-bg2));color:var(--on-accent);font:700 20px/1 "Songti SC","SimSun",serif;letter-spacing:0;
46
45
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.22),0 8px 18px -10px var(--gold)}
47
46
  .brand-txt{display:block;min-width:0}
48
47
  .brand-txt small{display:block;font-size:12.5px;letter-spacing:.02em;color:var(--dim);font-weight:400;margin-top:3px}
@@ -53,7 +52,7 @@ header{display:flex;flex-direction:column;align-items:stretch;gap:12px;margin-bo
53
52
  .tab{display:flex;align-items:center;justify-content:center;text-align:center;padding:7px 10px;border-radius:999px;border:1px solid transparent;background:color-mix(in srgb,var(--panel) 62%,transparent);color:var(--dim);cursor:pointer;font-size:13.5px;font-family:inherit;white-space:nowrap;
54
53
  transition:background .16s ease,color .16s ease,border-color .16s ease,box-shadow .16s ease}
55
54
  .tab:hover{background:var(--panel);border-color:var(--line);color:var(--ink)}
56
- .tab.on{background:linear-gradient(145deg,var(--gold),var(--gold-deep));border-color:transparent;color:#fff;font-weight:600;box-shadow:0 8px 18px -12px var(--gold)}
55
+ .tab.on{background:linear-gradient(145deg,var(--accent-bg),var(--accent-bg2));border-color:transparent;color:var(--on-accent);font-weight:600;box-shadow:0 8px 18px -12px var(--gold)}
57
56
  /* 首屏按「想干什么」分组:19 个 tab 平铺时,第一次来的人得先猜「梅花」「称骨」是什么。
58
57
  卡片只是另一套入口,点击仍走同一个 setTab;熟练用户可一键回到平铺(19 个 tab 原样保留)。 */
59
58
  .view-switch{display:flex;align-items:center;gap:8px}
@@ -83,9 +82,9 @@ header{display:flex;flex-direction:column;align-items:stretch;gap:12px;margin-bo
83
82
  .tool b{font-size:13.5px;font-weight:600;letter-spacing:.02em}
84
83
  .tool span{font-size:11.5px;line-height:1.4;color:var(--dim)}
85
84
  .tool:hover{border-color:var(--gold);background:var(--panel)}
86
- .tool.on{border-color:transparent;background:linear-gradient(145deg,var(--gold),var(--gold-deep));box-shadow:0 8px 18px -12px var(--gold)}
87
- .tool.on b{color:#fff}
88
- .tool.on span{color:rgba(255,255,255,.84)}
85
+ .tool.on{border-color:transparent;background:linear-gradient(145deg,var(--accent-bg),var(--accent-bg2));box-shadow:0 8px 18px -12px var(--gold)}
86
+ .tool.on b{color:var(--on-accent)}
87
+ .tool.on span{color:var(--on-accent-soft)}
89
88
  /* 奇数个的那一组:末项铺满整行,避免右下留一个孤格 */
90
89
  .tool.wide{grid-column:1/-1}
91
90
  /* 手机端每组先露 4 个,其余折在「还有 N 个」后面:三组全展开会把表单挤到第二屏 */
@@ -130,15 +129,15 @@ body.view-flat .start{display:none}
130
129
  .mb-o b{font-size:14px;font-weight:600;color:var(--ink2);font-variant-numeric:tabular-nums}
131
130
  .mb-o span{font-size:11.5px;line-height:1.3}
132
131
  .mb-o:hover{border-color:color-mix(in srgb,var(--line) 40%,var(--gold))}
133
- .mb-o.on{border-color:transparent;background:linear-gradient(145deg,var(--gold),var(--gold-deep));box-shadow:0 8px 16px -12px var(--gold)}
134
- .mb-o.on b,.mb-o.on span{color:#fff}
132
+ .mb-o.on{border-color:transparent;background:linear-gradient(145deg,var(--accent-bg),var(--accent-bg2));box-shadow:0 8px 16px -12px var(--gold)}
133
+ .mb-o.on b,.mb-o.on span{color:var(--on-accent)}
135
134
  .mb-o:focus-within{outline:2px solid var(--gold);outline-offset:2px}
136
135
  .mb-nav{display:flex;align-items:center;gap:8px;margin-top:12px;flex-wrap:wrap}
137
136
  .mb-btn{padding:8px 16px;border-radius:9px;border:1px solid var(--line);background:var(--panel2);color:var(--ink);font-family:inherit;font-size:13.5px;cursor:pointer;
138
137
  transition:border-color .14s ease,background .14s ease,color .14s ease}
139
138
  .mb-btn:hover:not(:disabled){border-color:var(--gold)}
140
139
  .mb-btn:disabled{opacity:.42;cursor:not-allowed}
141
- .mb-btn.primary{border-color:transparent;background:linear-gradient(145deg,var(--gold),var(--gold-deep));color:#fff;font-weight:600;box-shadow:0 10px 20px -14px var(--gold)}
140
+ .mb-btn.primary{border-color:transparent;background:linear-gradient(145deg,var(--accent-bg),var(--accent-bg2));color:var(--on-accent);font-weight:600;box-shadow:0 10px 20px -14px var(--gold)}
142
141
  .mb-btn.primary:hover:not(:disabled){filter:brightness(1.05)}
143
142
  .mb-hint{margin-left:auto;font-size:12.5px;color:var(--dim);line-height:1.6}
144
143
  .mb-hint.warn{color:var(--bad);font-weight:600}
@@ -146,8 +145,8 @@ body.view-flat .start{display:none}
146
145
  /* 必填未填:边框转红,提示复用 .err 的口径 */
147
146
  .field.invalid textarea{border-color:var(--bad);background:rgba(224,102,95,.06)}
148
147
  .field.check input{min-width:0;width:18px;height:18px;accent-color:var(--gold);padding:0}
149
- .go{margin-left:0;grid-column:4;justify-self:end;align-self:end;padding:11px 24px;border-radius:9px;border:none;color:#fff;font-weight:600;cursor:pointer;font-family:inherit;font-size:15px;
150
- background:linear-gradient(145deg,var(--gold),var(--gold-deep));box-shadow:0 10px 22px -14px var(--gold),inset 0 1px 0 rgba(255,255,255,.18);
148
+ .go{margin-left:0;grid-column:4;justify-self:end;align-self:end;padding:11px 24px;border-radius:9px;border:none;color:var(--on-accent);font-weight:600;cursor:pointer;font-family:inherit;font-size:15px;
149
+ background:linear-gradient(145deg,var(--accent-bg),var(--accent-bg2));box-shadow:0 10px 22px -14px var(--gold),inset 0 1px 0 rgba(255,255,255,.18);
151
150
  transition:transform .14s ease,filter .14s ease,box-shadow .14s ease}
152
151
  .go:hover{filter:brightness(1.06);transform:translateY(-1px);box-shadow:0 14px 26px -14px var(--gold),inset 0 1px 0 rgba(255,255,255,.22)}
153
152
  .go:active{transform:translateY(0);filter:brightness(.98)}
@@ -167,7 +166,7 @@ h3::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--li
167
166
  .seg{display:inline-flex;border:1px solid var(--line);border-radius:9px;overflow:hidden;background:var(--panel2)}
168
167
  .seg button{padding:7px 14px;background:transparent;border:none;color:var(--dim);font-size:13.5px;font-family:inherit;cursor:pointer;transition:background .14s ease,color .14s ease}
169
168
  .seg button:hover{color:var(--ink)}
170
- .seg button.on{background:linear-gradient(145deg,var(--gold),var(--gold-deep));color:#fff;font-weight:600}
169
+ .seg button.on{background:linear-gradient(145deg,var(--accent-bg),var(--accent-bg2));color:var(--on-accent);font-weight:600}
171
170
  /* 结果入场:只做一次轻微上浮,不做循环动画 */
172
171
  #out{animation:mp-rise .3s cubic-bezier(.2,.7,.3,1) both}
173
172
  @keyframes mp-rise{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
@@ -190,7 +189,7 @@ h3::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--li
190
189
  .askbox{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin:16px 0 0;padding:11px 13px;border:1px dashed var(--line);border-radius:10px;background:color-mix(in srgb,var(--panel) 55%,transparent)}
191
190
  .askbox input{flex:1 1 240px;min-width:0;background:var(--panel2);border:1px solid var(--line);color:var(--ink);border-radius:8px;padding:8px 12px;font-size:14px;font-family:inherit;outline:none}
192
191
  .askbox input:focus{border-color:var(--gold);box-shadow:var(--ring)}
193
- .askbox button{padding:8px 14px;border:1px solid transparent;border-radius:8px;background:var(--gold);color:#fff;font:inherit;font-size:13px;cursor:pointer}
192
+ .askbox button{padding:8px 14px;border:1px solid transparent;border-radius:8px;background:var(--accent-bg);color:var(--on-accent);font:inherit;font-size:13px;cursor:pointer}
194
193
  .askbox button:hover{filter:brightness(1.08)}
195
194
  .askbox .hint{font-size:12px;color:var(--dim)}
196
195
  .method{max-width:860px;margin:0 auto;padding:24px 18px 40px;font-size:14px;line-height:1.9;color:var(--ink2)}
@@ -205,7 +204,7 @@ h3::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--li
205
204
  .mp-verify-row{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:13px;padding:3px 0}
206
205
  .mp-verify-btns{display:flex;gap:4px}
207
206
  .mp-chip{font-size:12px;padding:2px 9px;border-radius:999px;border:1px solid var(--line);background:transparent;color:var(--dim);cursor:pointer;font-family:inherit}
208
- .mp-chip.on{background:var(--gold);color:#fff;border-color:transparent}
207
+ .mp-chip.on{background:var(--accent-bg);color:var(--on-accent);border-color:transparent}
209
208
  .mp-verify-foot{display:flex;align-items:flex-start;gap:8px;margin-top:8px;flex-wrap:wrap}
210
209
  .mp-verify-fit{font-size:12.5px;color:var(--dim);line-height:1.7;flex:1;min-width:0}
211
210
  .mp-rect-row{display:flex;gap:6px;margin-bottom:6px}
@@ -264,8 +263,15 @@ h3::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--li
264
263
  .bar{grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;padding:14px}
265
264
  .field{width:100%;min-width:0}
266
265
  .field input,.field select,.field textarea,#addr,#changing,#direction{width:100%;min-width:0;padding:11px 12px;font-size:16px;min-height:44px}
266
+ /* 农历:子表单独占整行,方向分段按钮不再把「农历 → 公历」折成两行;
267
+ 闰月改用 .field.check 的小号勾选框,不再被 .field input 的 width:100% 拉成大方块 */
268
+ #qk-convert{grid-column:1/-1}
269
+ #qk-convert .seg button{white-space:nowrap}
267
270
  #for-ask{flex-direction:column;align-items:stretch}
268
- .field.check{padding-bottom:0}
271
+ /* .field input 的 44px 高度是给文本框的;勾选框继承后会变成 18×44 的长条,
272
+ 触摸区改由外层 label 撑(点文字也能勾),方块本身保持 18px */
273
+ .field.check{min-height:44px;padding-bottom:0;gap:10px}
274
+ .field.check input{min-height:0;height:18px}
269
275
  #mh-num{flex-wrap:wrap}
270
276
  #mh-num input{flex:1 1 30%;width:auto}
271
277
  .seg{display:flex;width:100%}
@@ -338,7 +344,7 @@ h3::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--li
338
344
  <div class="wrap">
339
345
  <header>
340
346
  <div class="brand-row">
341
- <div class="brand"><span class="seal" aria-hidden="true">命</span><span class="brand-txt">命理排盘<span class="ver">v0.46.5</span><small>八字 · 紫微 · 六爻 · 梅花 · 六壬 · 奇门 · 合盘 · 塔罗 全部在本机计算</small></span></div>
347
+ <div class="brand"><span class="seal" aria-hidden="true">命</span><span class="brand-txt">命理排盘<span class="ver">v0.50.1</span><small>八字 · 紫微 · 六爻 · 梅花 · 六壬 · 奇门 · 合盘 · 塔罗 全部在本机计算</small></span></div>
342
348
  <div class="view-switch">
343
349
  <span class="hint">熟练了可以换回平铺</span>
344
350
  <span class="seg" role="group" aria-label="首屏样式">
@@ -527,9 +533,7 @@ h3::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--li
527
533
  </div>
528
534
  <div id="qk-convert-lunar" style="display:none">
529
535
  <label class="field">农历日期<input id="lunardate" placeholder="如 2026-9-17"></label>
530
- <label class="field" style="flex-direction:row;gap:6px;align-items:center">
531
- <input id="leapm" type="checkbox"> 闰月
532
- </label>
536
+ <label class="field check">闰月<input id="leapm" type="checkbox"></label>
533
537
  </div>
534
538
  </div>
535
539
  <div id="qk-xiaoliuren" style="display:none">
@@ -607,7 +611,7 @@ h3::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--li
607
611
  <h3>对拍:把典籍当期望值</h3>
608
612
  <ul><li>六爻:京房八宫卦序与世应,64 卦全表;旬空六旬表</li><li>六壬:日干寄宫、中气换将;紫微:纳音定五行局、紫微天府相照</li><li>卦例:《增删卜易》9 条 +《卜筮正宗》128 条(93 成对 + 35 静卦),核本变卦 / 动爻数 / 卦宫 / 缺爻 / 旬空</li></ul>
609
613
  <h3>工程验证</h3>
610
- <ul><li>测试:94 个测试文件 / 3643 条断言;七道闸(回归 · 断言基线 · 测试可失败自检 · HTML 注入面 · 工程约定 · 产物宿主形态 · 随机扫射)</li><li>可复现:seed / asOf 显式;同一输入同一输出</li><li>口径:晚子时、真太阳时、奇门三流派、闰月归月都可切换,并有字段级差异对照</li></ul>
614
+ <ul><li>测试:102 个测试文件 / 4406 条断言;七道闸(回归 · 断言基线 · 测试可失败自检 · HTML 注入面 · 工程约定 · 产物宿主形态 · 随机扫射)</li><li>可复现:seed / asOf 显式;同一输入同一输出</li><li>口径:晚子时、真太阳时、奇门三流派、闰月归月都可切换,并有字段级差异对照</li></ul>
611
615
  <h3>已知边界(不猜)</h3>
612
616
  <ul><li>六爻应期(具体哪天应验)不做;卦例只录动爻个数,不录爻位</li><li>财富/健康只给结构判断与档位,不给金额区间、不作诊断</li><li>紫微飞星已做「单一支化逐宫追到成环」与「三方四正 16 条按落宫归堆」,但都未叠大限流年四化、不折吉凶;八字交运日已能算到日/时,但未与第三方排盘案例批量对拍</li></ul>
613
617
  <p class="note">内容用于传统文化学习与自我探索,不构成医疗、投资、法律或人事决策依据。</p>
@@ -3549,6 +3553,16 @@ const CLASSICS = [
3549
3553
  plain: '成格不等于一路顺:成中带忌(官逢财又逢伤、官被合之类)就打了折;败格也未必无救——印来解伤、合来清煞,败中还能转成。',
3550
3554
  url: 'https://www.diancang.xyz/xuanxuewushu/zipingzhenquan/53629.html',
3551
3555
  },
3556
+ {
3557
+ id: 'zp-zaji',
3558
+ book: 'ziping',
3559
+ chapter: '十六、论杂气如何取用',
3560
+ scene: '用神',
3561
+ tags: [],
3562
+ quote: '何谓透干?如甲生辰月,透戊则用偏财,透癸则用正印,透乙则用月劫是也。何谓会支?如甲生辰月,逢申与子会局,则用水印是也。一透则一用,兼透则兼用,透而又会,则透与会并用。',
3563
+ plain: '四墓(辰戌丑未)所藏不止一气,所以叫杂气:取用先看哪一藏干透到天干,透一个用一个;没有透干,再看地支有没有与月支会成三合局,会成什么局就取局中那一气。透干与会支同时成立时两样并用。原文对「兼透」只给原则,没有给取舍次序。',
3564
+ url: 'https://www.diancang.xyz/xuanxuewushu/zipingzhenquan/53636.html',
3565
+ },
3552
3566
  {
3553
3567
  id: 'zp-bianhua',
3554
3568
  book: 'ziping',
@@ -3825,6 +3839,20 @@ const CLASSICS = [
3825
3839
  plain: '从财格要财会成局、日干无气;一见根气就破格,原文用「命损无猜」这种重话写明。以丁生酉月、柱多庚辛为例:只能弃命相从,行北方财官旺地为入格,南行则灾。',
3826
3840
  url: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7652.html',
3827
3841
  },
3842
+ // —— 三命通会 · 总论岁运:岁运提示一节的原文出处。
3843
+ // 原文的「返吟 / 伏吟」按「岁运与日柱相对 / 岁运压日」说,与本插件「干支相同为伏吟、天克地冲为反吟」
3844
+ // 不是同一套定义;口径照本插件,原文照录不改写,两者在 plain 里并列说明。
3845
+ // 这一条不进五条上限的取条表:它只在盘面有岁运信号时随「岁运提示」一起出现。
3846
+ {
3847
+ id: 'smt-suiyun',
3848
+ book: 'sanming',
3849
+ chapter: '卷二·总论岁运',
3850
+ scene: '岁运',
3851
+ tags: [],
3852
+ quote: '行运以生月为运元,最怕行运与太岁冲克,若岁运冲月必祸;若岁运与日相对,谓之返吟,岁运压日,谓之伏吟,二者不利六亲,非横破财,不为吉兆。',
3853
+ plain: '大运以生月为起点,最怕与流年(太岁)冲克:原文说岁运冲月为祸,又把「岁运与日柱相对」叫返吟、「岁运压日」叫伏吟。这两句是按「对日柱」说的,与本插件「干支相同为伏吟、天克地冲为反吟」不是同一套定义(本插件取后者);末句「不利六亲、破财」是旧说的重话,只作变动提示,不作断语。',
3854
+ url: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7539.html',
3855
+ },
3828
3856
  ];
3829
3857
 
3830
3858
  const BY_ID = CLASSICS.reduce((m, e) => { m[e.id] = e; return m; }, {});
@@ -3861,6 +3889,14 @@ function classicOf(id) {
3861
3889
  return BY_ID[id] || null;
3862
3890
  }
3863
3891
 
3892
+ // 岁运提示的原文:盘面出现岁运并临 / 运年反吟 / 流年伏吟反吟时随该节给出,不占五条上限。
3893
+ // 没有信号就不给——这段原文讲的正是冲克,没冲没克时挂出来只会误导。
3894
+ function suiyunClassicOf(r) {
3895
+ const liunian = r && Array.isArray(r.liunian) ? r.liunian : [];
3896
+ const hit = liunian.some((l) => l && l.suiyun && Array.isArray(l.suiyun.signals) && l.suiyun.signals.length);
3897
+ return hit ? classicOf('smt-suiyun') : null;
3898
+ }
3899
+
3864
3900
  function tagOf(gejuName) {
3865
3901
  const name = String(gejuName || '');
3866
3902
  if (GEJU_KEY[name]) return GEJU_KEY[name];
@@ -3879,7 +3915,8 @@ function chapterTitle(e) {
3879
3915
  }
3880
3916
 
3881
3917
  // 根据一张八字结果挑原文:特殊结构(专旺/从格)先取单章,再月令格(子平真诠 + 三命通会对照),
3882
- // 再调候(寒暖/燥湿),最后「用神总纲」一位按盘取:有化气格取论用神变化、见组合取论用神成败救应、其余取通用总纲;去重、限量。
3918
+ // 再调候(寒暖/燥湿),最后「用神总纲」一位按盘四选一:有化气格取论用神变化、辰戌丑未月取论杂气如何取用、
3919
+ // 见组合取论用神成败救应、其余取通用总纲;去重、限量。
3883
3920
  function classicsFor(r, limit = 5) {
3884
3921
  if (!r) return [];
3885
3922
  const out = [];
@@ -3917,12 +3954,16 @@ function classicsFor(r, limit = 5) {
3917
3954
  if (tgEntry) {
3918
3955
  push(tgEntry, `本盘忌${tg.foe}克用${tg.use},局中${tg.bridge}${tg.weight}分可作通关,故取${CLASSIC_BOOKS.ditian.name}「通关」一条`);
3919
3956
  }
3920
- // 用神总纲位只放一条:同一个格,化气看「变化」、有组合看「成败救应」,都没有才回到四吉四凶总纲。
3921
- // 不另加位次,是因为格局两条 + 气候两条正好占满 5 条上限;总纲与具体结构二选一,避免泛论挤掉针对这张盘的话。
3957
+ // 用神总纲位只放一条,按盘四选一:化气看「变化」、四墓杂气看「杂气取用」、有组合看「成败救应」,
3958
+ // 都没有才回到四吉四凶总纲。不另加位次,是因为格局两条 + 气候两条正好占满 5 条上限。
3922
3959
  const huagi = gejuList.find((g) => g && g.kind === '化气格');
3923
3960
  const combo = gejuList.find((g) => g && g.kind === '组合');
3961
+ const monthBranch = r.monthBranch || (r.frame && r.frame.monthBranch) || (r.pillars && r.pillars.month && r.pillars.month.branch);
3962
+ const zaMonth = ['辰', '未', '戌', '丑'].includes(monthBranch);
3924
3963
  if (huagi) {
3925
3964
  push(classicOf('zp-bianhua'), `本盘列有「${huagi.name}」:用神由月令合化而变,总纲一位改取${CLASSIC_BOOKS.ziping.name}论用神变化`);
3965
+ } else if (zaMonth) {
3966
+ push(classicOf('zp-zaji'), `月令${monthBranch}属四墓杂气,取用先看透干、再看会支,总纲一位取${CLASSIC_BOOKS.ziping.name}论杂气如何取用`);
3926
3967
  } else if (combo) {
3927
3968
  push(classicOf('zp-chengbai'), `本盘另有「${combo.name}」组合:成格还要看带忌与救应,总纲一位改取${CLASSIC_BOOKS.ziping.name}论用神成败救应`);
3928
3969
  } else {
@@ -3936,7 +3977,7 @@ function classicsText(rows) {
3936
3977
  return rows.map((e) => `《${CLASSIC_BOOKS[e.book].name}·${e.chapter}》「${e.quote}」`).join('\n');
3937
3978
  }
3938
3979
 
3939
- return { CLASSIC_BOOKS, CLASSICS, classicOf, classicsFor, classicsText };
3980
+ return { CLASSIC_BOOKS, CLASSICS, classicOf, suiyunClassicOf, classicsFor, classicsText };
3940
3981
  })();
3941
3982
 
3942
3983
  __mods.geju = (function () {
@@ -3951,6 +3992,7 @@ __mods.geju = (function () {
3951
3992
 
3952
3993
  const { judgeClimate, arbitrate } = __mods.tiaohou;
3953
3994
  const { qtOf } = __mods.qiongtong;
3995
+ const { sanheGroupOf } = __mods.sanhe;
3954
3996
 
3955
3997
  const STEMS = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'];
3956
3998
  const BRANCHES = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'];
@@ -4029,6 +4071,25 @@ function elementRoles(dayStem) {
4029
4071
 
4030
4072
  // ── 月令格 ───────────────────────────────────────────────────
4031
4073
 
4074
+ // 四墓(杂气)月:辰戌丑未所藏多、用神不一,取用先看透干,再看会支。
4075
+ // 会支只认三合局——子平真诠十六「逢申与子会局」的例子即三合,三会方与兼透兼用仍不纳入。
4076
+ const ZA_MONTHS = ['辰', '未', '戌', '丑'];
4077
+
4078
+ function huiCandidate(pillars, monthBranch, cands) {
4079
+ if (!ZA_MONTHS.includes(monthBranch)) return null;
4080
+ const group = sanheGroupOf(monthBranch);
4081
+ if (!group) return null;
4082
+ const others = ['year', 'day', 'hour'];
4083
+ const need = group.branches.filter((b) => b !== monthBranch);
4084
+ if (!need.every((b) => others.some((k) => pillars[k].branch === b))) return null;
4085
+ const cand = cands.find((c) => STEM_ELEMENT[c.stem] === group.element);
4086
+ if (!cand) return null;
4087
+ const where = others
4088
+ .filter((k) => need.includes(pillars[k].branch))
4089
+ .map((k) => `${({ year: '年', day: '日', hour: '时' })[k]}支${pillars[k].branch}`);
4090
+ return { ...cand, group, where };
4091
+ }
4092
+
4032
4093
  function monthPattern(pillars, dayStem, monthBranch) {
4033
4094
  const hidden = HIDDEN[monthBranch] || [];
4034
4095
  const cands = hidden.map((stem, i) => ({
@@ -4040,10 +4101,13 @@ function monthPattern(pillars, dayStem, monthBranch) {
4040
4101
  const others = ['year', 'month', 'hour'];
4041
4102
  const exposedKey = others.find((k) => cands.some((c) => c.stem === pillars[k].stem));
4042
4103
  const base = cands[0];
4043
- const use = exposedKey ? cands.find((c) => c.stem === pillars[exposedKey].stem) : base;
4104
+ const hui = exposedKey ? null : huiCandidate(pillars, monthBranch, cands);
4105
+ const use = exposedKey ? cands.find((c) => c.stem === pillars[exposedKey].stem) : (hui || base);
4106
+ const usedBy = exposedKey ? '透干' : (hui ? '会支' : '本气');
4044
4107
  const exposedPos = exposedKey ? { year: '年', month: '月', hour: '时' }[exposedKey] : null;
4045
4108
 
4046
- if (base.god === '比肩') {
4109
+ // 建禄只认日干的禄位;戊土辰月、己土未月这类「本气比肩但不在禄位」的杂气月不再冒称建禄
4110
+ if (base.god === '比肩' && LUSHEN_BRANCH[dayStem] === monthBranch) {
4047
4111
  return {
4048
4112
  name: '建禄格',
4049
4113
  kind: '月令格',
@@ -4060,10 +4124,31 @@ function monthPattern(pillars, dayStem, monthBranch) {
4060
4124
  }
4061
4125
  return { name: '月劫格', kind: '月令格', evidence: `月令${monthBranch}本气${base.stem}为劫财` };
4062
4126
  }
4127
+ if (usedBy === '本气' && base.god === '比肩') {
4128
+ return {
4129
+ name: '比肩格',
4130
+ kind: '月令格',
4131
+ evidence: `月令${monthBranch}藏${hidden.join('')},本气${base.stem}为比肩,但${dayStem}禄在${LUSHEN_BRANCH[dayStem]}、不在${monthBranch},属杂气月比肩当权`,
4132
+ };
4133
+ }
4134
+ // 子平十六:「透乙则用月劫」——杂气月由透干或会支取到劫财时,仍作月劫论
4135
+ if (usedBy !== '本气' && use.god === '劫财') {
4136
+ const via = exposedKey ? `透于${exposedPos}干` : `${hui.where.join('、')}会成${hui.group.element}局`;
4137
+ return {
4138
+ name: '月劫格',
4139
+ kind: '月令格',
4140
+ evidence: `月令${monthBranch}藏${hidden.join('')},${use.pos}${use.stem}(劫财)${via},劫财为用,作月劫论`,
4141
+ };
4142
+ }
4063
4143
  const name = `${use.god}格`;
4064
- const ex = exposedKey
4065
- ? `月令${monthBranch}藏${hidden.join('')},${use.pos}${use.stem}(${use.god})透于${exposedPos}干`
4066
- : `月令${monthBranch}藏${hidden.join('')},本气${base.stem}(${base.god})未透干,以月令本气取格`;
4144
+ let ex;
4145
+ if (exposedKey) {
4146
+ ex = `月令${monthBranch}藏${hidden.join('')},${use.pos}${use.stem}(${use.god})透于${exposedPos}干`;
4147
+ } else if (hui) {
4148
+ ex = `月令${monthBranch}藏${hidden.join('')},本气${base.stem}(${base.god})未透干,而${hui.where.join('、')}会成${hui.group.element}局,取${use.pos}${use.stem}(${use.god})为用`;
4149
+ } else {
4150
+ ex = `月令${monthBranch}藏${hidden.join('')},本气${base.stem}(${base.god})未透干,以月令本气取格`;
4151
+ }
4067
4152
  return { name, kind: '月令格', evidence: ex };
4068
4153
  }
4069
4154
 
@@ -4382,6 +4467,24 @@ const HIDDEN_STEMS = {
4382
4467
  亥: ['壬', '甲'],
4383
4468
  };
4384
4469
 
4470
+ // 人元司权的 30 日表:余气 → 中气 → 本气,按「节」当日起算。
4471
+ // 各家对日数分法不一(另有 7/7/16、9/3/18 等),这里固定通行 30 日表;
4472
+ // 只作解释层的一层细节,不参与取格。亥月的余气戊不在本柱藏干表中,两表各照原样。
4473
+ const RENYUAN_TABLE = {
4474
+ 寅: [['戊', 7], ['丙', 7], ['甲', 16]],
4475
+ 卯: [['甲', 10], ['乙', 20]],
4476
+ 辰: [['乙', 9], ['癸', 3], ['戊', 18]],
4477
+ 巳: [['戊', 5], ['庚', 9], ['丙', 16]],
4478
+ 午: [['丙', 10], ['己', 9], ['丁', 11]],
4479
+ 未: [['丁', 9], ['乙', 3], ['己', 18]],
4480
+ 申: [['戊', 7], ['壬', 3], ['庚', 20]],
4481
+ 酉: [['庚', 10], ['辛', 20]],
4482
+ 戌: [['辛', 9], ['丁', 3], ['戊', 18]],
4483
+ 亥: [['戊', 7], ['甲', 5], ['壬', 18]],
4484
+ 子: [['壬', 10], ['癸', 20]],
4485
+ 丑: [['癸', 9], ['辛', 3], ['己', 18]],
4486
+ };
4487
+
4385
4488
  const STEM_ELEMENT = {
4386
4489
  甲: '木', 乙: '木', 丙: '火', 丁: '火', 戊: '土',
4387
4490
  己: '土', 庚: '金', 辛: '金', 壬: '水', 癸: '水',
@@ -4558,6 +4661,45 @@ function applyTrueSolar(jd, longitude) {
4558
4661
  };
4559
4662
  }
4560
4663
 
4664
+ /**
4665
+ * 人元司权:生在本月第几日、哪一藏干当权。
4666
+ * 日数按「节令日」与出生日的日差计,节当日为第 1 日;固定 30 日表,
4667
+ * 第 31 日以后不再硬分摊,按本气算并标 beyond。
4668
+ * @param {string} monthBranch 月支(寅…丑)
4669
+ * @param {number} birthJdn 出生日的儒略日数(含真太阳时修正后的日)
4670
+ * @param {number} termJd 本月「节」的儒略日
4671
+ */
4672
+ function renyuanOf(monthBranch, birthJdn, termJd) {
4673
+ const table = RENYUAN_TABLE[monthBranch];
4674
+ if (!table) return null;
4675
+ const segments = [];
4676
+ let from = 1;
4677
+ for (const [stem, days] of table) {
4678
+ segments.push({ stem, element: STEM_ELEMENT[stem], from, to: from + days - 1 });
4679
+ from += days;
4680
+ }
4681
+ const tableDays = from - 1;
4682
+ const day = Math.max(1, Math.floor(birthJdn) - Math.floor(termJd + 0.5) + 1);
4683
+ const beyond = day > tableDays;
4684
+ const idx = beyond ? segments.length - 1 : segments.findIndex((s) => day >= s.from && day <= s.to);
4685
+ const seg = segments[idx] || segments[segments.length - 1];
4686
+ const next = segments[idx + 1] || null;
4687
+ return {
4688
+ month: monthBranch,
4689
+ day,
4690
+ tableDays,
4691
+ segments,
4692
+ active: seg.stem,
4693
+ activeElement: seg.element,
4694
+ segment: { from: seg.from, to: seg.to },
4695
+ next: next ? { stem: next.stem, element: next.element, from: next.from, inDays: next.from - day } : null,
4696
+ // 司权表与本柱藏干表口径不同:亥月的余气戊只出现在司权表里
4697
+ inHidden: (HIDDEN_STEMS[monthBranch] || []).includes(seg.stem),
4698
+ beyond,
4699
+ note: '人元司权按「节」当日起算,节当日为第 1 日,日差用本盘所用时刻(含真太阳时)所在日计;本插件固定 30 日表(余气 → 中气 → 本气),第 31 日以后按本气算。只作解释层的一层细节,不改取格。',
4700
+ };
4701
+ }
4702
+
4561
4703
  /**
4562
4704
  * 排四柱八字
4563
4705
  * @param {object} input { year,month,day,hour,minute, gender:'male'|'female', longitude?, lateZi?:boolean, trueSolar?:boolean }
@@ -4880,6 +5022,10 @@ function buildBazi(input) {
4880
5022
  const bodyStemIdx = (yinStem + ((bodyIdx - 2 + 12) % 12)) % 10;
4881
5023
  const shengong = ganzhiName(sexagenaryIndex(bodyStemIdx, bodyIdx));
4882
5024
 
5025
+ // 人元司权:按节令到出生日的日差定当权藏干(节当日为第 1 日)
5026
+ const renyuan = renyuanOf(BRANCHES[monthBranchIdx], jdnBase, prevJieTerm.jd);
5027
+ if (renyuan) renyuan.term = { name: prevJieTerm.name, time: formatJd(prevJieTerm.jd) };
5028
+
4883
5029
  return {
4884
5030
  system: 'bazi',
4885
5031
  relations: buildRelations(pillars),
@@ -4912,6 +5058,7 @@ function buildBazi(input) {
4912
5058
  // 与 NAOJ 历要项对拍实测节气最多偏 13.5 分钟,20 分钟留出余量。
4913
5059
  nearBoundaryMinutes: Math.round(Math.min(jd - prevJieTerm.jd, nextJieTerm.jd - jd) * 1440),
4914
5060
  },
5061
+ renyuan,
4915
5062
  pillars,
4916
5063
  dayMaster: { stem: dayStem, element: STEM_ELEMENT[dayStem], yinYang: isYangStem(dayStem) ? '阳' : '阴' },
4917
5064
  wuxing,
@@ -4944,7 +5091,7 @@ function buildBazi(input) {
4944
5091
  };
4945
5092
  }
4946
5093
 
4947
- return { HIDDEN_STEMS, STEM_ELEMENT, BRANCH_ELEMENT, buildRelations, tenGod, nayin, applyTrueSolar, buildBazi };
5094
+ return { HIDDEN_STEMS, RENYUAN_TABLE, STEM_ELEMENT, BRANCH_ELEMENT, buildRelations, tenGod, nayin, applyTrueSolar, renyuanOf, buildBazi };
4948
5095
  })();
4949
5096
 
4950
5097
  __mods.liuyao = (function () {
@@ -5016,6 +5163,7 @@ const LIUSHEN_START = { 甲: 0, 乙: 0, 丙: 1, 丁: 1, 戊: 2, 己: 3, 庚: 4,
5016
5163
  const ZHI_CHONG_PAIRS = [['子', '午'], ['丑', '未'], ['寅', '申'], ['卯', '酉'], ['辰', '戌'], ['巳', '亥']];
5017
5164
  const ZHI_HE_PAIRS = [['子', '丑'], ['寅', '亥'], ['卯', '戌'], ['辰', '酉'], ['巳', '申'], ['午', '未']];
5018
5165
  const CHONG_OF = Object.fromEntries(ZHI_CHONG_PAIRS.flatMap(([a, b]) => [[a, b], [b, a]]));
5166
+ const HE_OF = Object.fromEntries(ZHI_HE_PAIRS.flatMap(([a, b]) => [[a, b], [b, a]]));
5019
5167
  const TRIGRAM_OPPOSITE = { 乾: '巽', 巽: '乾', 坎: '离', 离: '坎', 艮: '坤', 坤: '艮', 震: '兑', 兑: '震' };
5020
5168
 
5021
5169
  // ── 化进神 / 化退神 ────────────────────────────────────────────────
@@ -5881,7 +6029,106 @@ function jinTuiPower(result, opts = {}) {
5881
6029
  };
5882
6030
  }
5883
6031
 
5884
- return { jinTuiOf, hexBody, bodyRelations, bodyLabels, HEXAGRAM_COUNT, hexagramFromBits, YONGSHEN_RULES, yongshenOf, tossLines, buildLiuyao, zhiElementOf, monthStateOf, dayRelationOf, yongshenStrength, FUSHEN_VERDICTS, fushenAccess, hexagramStrength, jinTuiPower };
6032
+ // ── 应期线索:只列「该留意哪一支」,不给日期 ─────────────────────────
6033
+ // 知识卡 liuyao-yingqi 的承诺是「只作时间窗口的提示」:传统取用神逢值、
6034
+ // 逢冲、逢合之期,旬空另等出空、冲空。这里只把这些地支列出来,再按月建给
6035
+ // 一个快慢方向;不换算成日期、不排先后、不判忌神受制,也不叠动变连环。
6036
+ const YINGQI_TEMPO = ['偏速', '偏缓', '待解', '未定'];
6037
+ const TEMPO_SAY = {
6038
+ 偏速: '用神得月令之气,应期偏近',
6039
+ 偏缓: '用神失月令之气,应期偏远',
6040
+ 待解: '有月破或旬空的用神,先等条件解除',
6041
+ 未定: '快慢不定(缺月建,或用神两现而旺衰不一)',
6042
+ };
6043
+ const TRIGGER_ORDER = ['逢值', '逢冲', '逢合', '出空填实', '冲空'];
6044
+
6045
+ /**
6046
+ * 应期线索:用神逢值、逢冲、逢合,旬空另标出空与冲空。
6047
+ * @param {object} result buildLiuyao 的结果
6048
+ * @param {object} useGod yongshenOf 的结果
6049
+ * @param {{monthBranch?: string}} opts
6050
+ */
6051
+ function yingqiWindow(result, useGod, opts = {}) {
6052
+ if (!useGod || !useGod.liuqin) return null;
6053
+ const found = Array.isArray(useGod.found) ? useGod.found : [];
6054
+ const fushen = Array.isArray(useGod.fushen) ? useGod.fushen : [];
6055
+ const viaFushen = !found.length && fushen.length > 0;
6056
+ const source = found.length ? found : fushen;
6057
+ if (!source.length) return null;
6058
+ const monthBranch = opts.monthBranch || null;
6059
+ const monthElement = monthBranch ? zhiElementOf(monthBranch) : null;
6060
+ const kong = Array.isArray(result && result.xunKong)
6061
+ ? result.xunKong
6062
+ : (result && typeof result.xunKong === 'string' ? result.xunKong.split('') : []);
6063
+
6064
+ const items = source.map((l) => {
6065
+ const branch = l.branch;
6066
+ const element = l.element || zhiElementOf(branch);
6067
+ const monthState = monthStateOf(element, monthElement);
6068
+ const isEmpty = kong.includes(branch);
6069
+ const monthChong = !!(monthBranch && CHONG_OF[branch] === monthBranch);
6070
+ const tempo = monthChong ? '待解'
6071
+ : isEmpty ? '待解'
6072
+ : monthState === 'unknown' ? '未定'
6073
+ : (monthState === '旺' || monthState === '相') ? '偏速' : '偏缓';
6074
+ const tempoSay = monthChong ? `遭${monthBranch}冲(月破),先待出月或填实`
6075
+ : isEmpty ? '用神旬空,先待出空填实'
6076
+ : monthState === 'unknown' ? '缺月建,快慢不定'
6077
+ : `月建${monthState},${tempo === '偏速' ? '应期偏近' : '应期偏远'}`;
6078
+ const triggers = [
6079
+ { branch, labels: ['逢值'] },
6080
+ { branch: CHONG_OF[branch] || null, labels: ['逢冲'] },
6081
+ { branch: HE_OF[branch] || null, labels: ['逢合'] },
6082
+ ];
6083
+ if (isEmpty) {
6084
+ triggers[0].labels.push('出空填实');
6085
+ triggers[1].labels.push('冲空');
6086
+ }
6087
+ return {
6088
+ position: l.position,
6089
+ branch,
6090
+ element,
6091
+ monthState,
6092
+ monthChong,
6093
+ kong: isEmpty,
6094
+ tempo,
6095
+ tempoSay,
6096
+ triggers,
6097
+ };
6098
+ });
6099
+
6100
+ // 用神两现时各爻的支可能不同:按支归并标签,不替谁选主用
6101
+ const merged = new Map();
6102
+ for (const it of items) {
6103
+ for (const t of it.triggers) {
6104
+ if (!t.branch) continue;
6105
+ const cur = merged.get(t.branch) || { branch: t.branch, labels: [] };
6106
+ for (const lb of t.labels) if (!cur.labels.includes(lb)) cur.labels.push(lb);
6107
+ merged.set(t.branch, cur);
6108
+ }
6109
+ }
6110
+ const triggers = [...merged.values()].map((t) => ({
6111
+ branch: t.branch,
6112
+ labels: [...t.labels].sort((a, b) => TRIGGER_ORDER.indexOf(a) - TRIGGER_ORDER.indexOf(b)),
6113
+ }));
6114
+ const tempos = items.map((x) => x.tempo);
6115
+ const tempo = tempos.includes('待解') ? '待解'
6116
+ : tempos.every((t) => t === '偏速') ? '偏速'
6117
+ : tempos.every((t) => t === '偏缓') ? '偏缓' : '未定';
6118
+
6119
+ return {
6120
+ month: monthBranch ? { branch: monthBranch, element: monthElement } : null,
6121
+ viaFushen,
6122
+ tempo,
6123
+ tempoSay: TEMPO_SAY[tempo],
6124
+ items,
6125
+ triggers,
6126
+ labels: [`应期${tempo}`, ...triggers.map((t) => `${t.labels.join('/')}${t.branch}`)],
6127
+ note: '应期只给「该留意哪一支」与快慢方向:用神逢值、逢冲、逢合,旬空另标出空与冲空。不换算成日期、不排先后、不判忌神受制;近事以日计、远事以月计,用哪把尺由事情远近决定,本插件不代选。',
6128
+ };
6129
+ }
6130
+
6131
+ return { jinTuiOf, hexBody, bodyRelations, bodyLabels, HEXAGRAM_COUNT, hexagramFromBits, YONGSHEN_RULES, yongshenOf, tossLines, buildLiuyao, zhiElementOf, monthStateOf, dayRelationOf, yongshenStrength, FUSHEN_VERDICTS, fushenAccess, hexagramStrength, jinTuiPower, YINGQI_TEMPO, yingqiWindow };
5885
6132
  })();
5886
6133
 
5887
6134
  __mods.ziwei = (function () {
@@ -11050,9 +11297,10 @@ __mods.almanac = (function () {
11050
11297
  // 提供:农历日期、年/月/日干支(年按立春界,另列春节界生肖)、节气、建除十二神、
11051
11298
  // 冲煞(冲什么生肖 / 煞在何方)、财神与喜神方位、建除简表宜忌、每日一卦。
11052
11299
  //
11053
- // 诚实边界:本插件不依赖 lunar-javascript,逐日神煞(吉神凶煞)、二十八宿值日、
11054
- // 彭祖百忌、胎神等全量黄历数据未收录——输出里如实标注「未收录」,不臆造。
11055
- // 宜忌只按「建除十二神」通行简表给出(传统择日的第一层判据),不是逐日全量黄历。
11300
+ // 诚实边界:本插件不依赖 lunar-javascript;逐日全量吉神凶煞与彭祖百忌未收录——
11301
+ // 输出里如实标注「未收录」,不臆造。值日十二神、二十八宿、胎神、吉神方位按通行表收录
11302
+ // (见返回值的 scope)。宜忌只按「建除十二神」通行简表给出(传统择日的第一层判据),
11303
+ // 不是逐日全量黄历。
11056
11304
  const { solarToLunar } = __mods.lunar;
11057
11305
  const { nayinOf } = __mods.nayin;
11058
11306
  const { toJDN, fromJDN, dayGanzhiIndex, ganzhiName, termWindow, termInstantJd, partsToJd, STEMS, BRANCHES, TERM_NAMES } = __mods.calendar;
@@ -11155,7 +11403,7 @@ function tianShenOf(branch, anchorBranch) {
11155
11403
  return { name, type, luck: type === '黄道' ? '吉' : '凶' };
11156
11404
  }
11157
11405
 
11158
- /** 二十八宿:值日宿按「日支 + 星期」轮转(四组各七宿)。 */
11406
+ /** 二十八宿:值日宿按「日支 + 星期」轮转(四组各七宿)。宿名按通行「宿·五行·动物」写法(壁水貐、胃土雉)。 */
11159
11407
  const MANSIONS = {
11160
11408
  角: { luck: '吉', zheng: '木', animal: '蛟', gong: '东' }, 亢: { luck: '凶', zheng: '金', animal: '龙', gong: '东' },
11161
11409
  氐: { luck: '凶', zheng: '土', animal: '貉', gong: '东' }, 房: { luck: '吉', zheng: '日', animal: '兔', gong: '东' },
@@ -11163,9 +11411,9 @@ const MANSIONS = {
11163
11411
  箕: { luck: '吉', zheng: '水', animal: '豹', gong: '东' }, 斗: { luck: '吉', zheng: '木', animal: '獬', gong: '北' },
11164
11412
  牛: { luck: '凶', zheng: '金', animal: '牛', gong: '北' }, 女: { luck: '凶', zheng: '土', animal: '蝠', gong: '北' },
11165
11413
  虚: { luck: '凶', zheng: '日', animal: '鼠', gong: '北' }, 危: { luck: '凶', zheng: '月', animal: '燕', gong: '北' },
11166
- 室: { luck: '吉', zheng: '火', animal: '猪', gong: '北' }, 壁: { luck: '吉', zheng: '水', animal: '獝', gong: '北' },
11414
+ 室: { luck: '吉', zheng: '火', animal: '猪', gong: '北' }, 壁: { luck: '吉', zheng: '水', animal: '貐', gong: '北' },
11167
11415
  奎: { luck: '凶', zheng: '木', animal: '狼', gong: '西' }, 娄: { luck: '吉', zheng: '金', animal: '狗', gong: '西' },
11168
- 胃: { luck: '吉', zheng: '土', animal: '彘', gong: '西' }, 昴: { luck: '凶', zheng: '日', animal: '鸡', gong: '西' },
11416
+ 胃: { luck: '吉', zheng: '土', animal: '雉', gong: '西' }, 昴: { luck: '凶', zheng: '日', animal: '鸡', gong: '西' },
11169
11417
  毕: { luck: '吉', zheng: '月', animal: '乌', gong: '西' }, 觜: { luck: '凶', zheng: '火', animal: '猴', gong: '西' },
11170
11418
  参: { luck: '吉', zheng: '水', animal: '猿', gong: '西' }, 井: { luck: '吉', zheng: '木', animal: '犴', gong: '南' },
11171
11419
  鬼: { luck: '凶', zheng: '金', animal: '羊', gong: '南' }, 柳: { luck: '凶', zheng: '土', animal: '獐', gong: '南' },
@@ -12634,6 +12882,19 @@ function jinTuiPlain(r) {
12634
12882
  return `动爻的进退还要看时令:${jp.lines.map((l) => `${l.position}爻${l.from}→${l.to}${l.type},${sayOf(l)}`).join(';')}。`;
12635
12883
  }
12636
12884
 
12885
+ // 应期线索 → 一句白话:只给快慢与可留意的支,不换算日期
12886
+ function yingqiPlain(r) {
12887
+ const yq = r && r.yingqi;
12888
+ if (!yq || !Array.isArray(yq.triggers) || !yq.triggers.length) return '';
12889
+ const when = yq.tempo === '偏速' ? '大体偏近,近事更容易先见分晓'
12890
+ : yq.tempo === '偏缓' ? '大体偏远,别指望立刻见分晓'
12891
+ : yq.tempo === '待解' ? '眼下还有月破或旬空,得先等条件解除'
12892
+ : '缺月建,快慢暂不定';
12893
+ const branches = yq.triggers.map((t) => t.branch).join('、');
12894
+ const fu = yq.viaFushen ? '(用神是伏神,先要出得来)' : '';
12895
+ return `应期上${when};传统会留意${branches}这几个地支${fu}——这只是提示,不换算成具体哪天。`;
12896
+ }
12897
+
12637
12898
  // 全卦逐爻旺衰 → 一句白话:整张卦的气偏在哪一头(与用神那一句分开说)
12638
12899
  function hexStrengthPlain(hs) {
12639
12900
  if (!hs || !hs.counts) return '';
@@ -12744,7 +13005,7 @@ function buildAnswer(r, ask, ctx = {}) {
12744
13005
  head + `先说结论:本卦是${s(ben.name, '—')}` +
12745
13006
  `${ben.palace ? `(${ben.palace})` : ''}` +
12746
13007
  `${moving.length ? `,第 ${moving.join('、')} 爻动,变为${s(bian.name, '—')}` : ',六爻安静没有动爻'}。` +
12747
- `${plainBody}${usePlainOf(r.useGod, r.fushenAccess)}${useStrengthPlain(r.useGodStrength)}${jinTuiPlain(r)}${hexStrengthPlain(r.hexStrength)}`,
13008
+ `${plainBody}${usePlainOf(r.useGod, r.fushenAccess)}${useStrengthPlain(r.useGodStrength)}${jinTuiPlain(r)}${yingqiPlain(r)}${hexStrengthPlain(r.hexStrength)}`,
12748
13009
  '断卦的落点在「世爻」(代表你)和「动爻」(事情的推动点),下面「看盘顺序」就按这个次序展开。',
12749
13010
  ].join('');
12750
13011
  }
@@ -12812,7 +13073,7 @@ function buildPlain(r, ask, ctx = {}) {
12812
13073
  const plainBody = bodyPlainOf(labels);
12813
13074
  return `${s(ben.name, '这一卦')}讲的是一个「${moving.length ? '正在动、还会变' : '暂时不动、先看清'}」的局面——` +
12814
13075
  `${moving.length ? '有动爻,说明事情还有转圜空间,不是死局。' : '六爻安静,说明当下更该观察而不是硬推。'}` +
12815
- `${plainBody}${usePlainOf(r.useGod, r.fushenAccess)}${useStrengthPlain(r.useGodStrength)}${jinTuiPlain(r)}${hexStrengthPlain(r.hexStrength)}`;
13076
+ `${plainBody}${usePlainOf(r.useGod, r.fushenAccess)}${useStrengthPlain(r.useGodStrength)}${jinTuiPlain(r)}${yingqiPlain(r)}${hexStrengthPlain(r.hexStrength)}`;
12816
13077
  }
12817
13078
  return '';
12818
13079
  }
@@ -12931,10 +13192,10 @@ const KNOWLEDGE = [
12931
13192
  id: 'bazi-yueling-siquan',
12932
13193
  system: 'bazi', dims: ['overall', 'career'],
12933
13194
  gist: '月令不是「一个月」,是当月最有权的那一个字。',
12934
- detail: '月令藏干按本气、中气、余气分日用事,本气当权的时间最长。所以判旺衰先看日主在月令是得令还是失令——得令者未看全局,已有三分底气。',
13195
+ detail: '月令藏干按本气、中气、余气分日用事,本气当权的时间最长。所以判旺衰先看日主在月令是得令还是失令——得令者未看全局,已有三分底气。本插件按 30 日表算出「生在本月第几日、哪一干当权」,给的是月内轮值这一层细节;旺衰与取格仍以月令本气、透干与会支为主,不随轮值改判。',
12935
13196
  source: '《子平真诠》论用神;人元司权分日之说见《渊海子平》',
12936
13197
  status: 'variant',
12937
- note: '各家对中气、余气各主几日分法不同(有 7/7/16、9/3/18 等多说),本插件只取「本气当权」这一层,未细分日数。',
13198
+ note: '各家对中气、余气各主几日分法不同(有 7/7/16、9/3/18 等多说);本插件固定通行 30 日表(如寅月戊7丙7甲16),节当日为第 1 日,第 31 日以后按本气算、不硬分摊。司权表与本柱藏干表口径不同(子月壬、卯月甲、午月丙、酉月庚、亥月戊只出现在司权表里),两表各照原样;取格不随轮值改判。',
12938
13199
  },
12939
13200
  {
12940
13201
  id: 'bazi-tonggen-tougan',
@@ -12982,8 +13243,8 @@ const KNOWLEDGE = [
12982
13243
  id: 'bazi-suiyun',
12983
13244
  system: 'bazi', dims: ['overall', 'career'],
12984
13245
  gist: '大运管十年基调,流年管当年触发。',
12985
- detail: '大运像换了条路,流年像这条路上某一段的天气。岁运并临(当值大运与流年干支相同)、运年反吟(大运与流年天克地冲)、流年伏吟(流年与原局某柱干支相同)与流年反吟(流年与原局某柱天克地冲),是传统上标记「变动较大」的年份,属于提示而非断语。本插件逐年标出四类信号:当值大运按该年立春到次年立春的区间取,交运落在年内时前后两步并列;只标干支重复与对冲,不折分数。',
12986
- source: '岁运并临、伏吟反吟见《三命通会》《渊海子平》',
13246
+ detail: '大运像换了条路,流年像这条路上某一段的天气。岁运并临(当值大运与流年干支相同)、运年反吟(大运与流年天克地冲)、流年伏吟(流年与原局某柱干支相同)与流年反吟(流年与原局某柱天克地冲),是传统上标记「变动较大」的年份,属于提示而非断语。本插件逐年标出四类信号:当值大运按该年立春到次年立春的区间取,交运落在年内时前后两步并列;只标干支重复与对冲,不折分数。原文里「返吟」指岁运与日柱相对、「伏吟」指岁运压日,与本插件「干支相同为伏吟、天克地冲为反吟」不是同一套定义,两者并列、不合并。',
13247
+ source: '《三命通会》卷二·总论岁运(https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7539.html);岁运并临之说出自《渊海子平》',
12987
13248
  status: 'variant',
12988
13249
  note: '「岁运并临主大凶」是流传最广也最被滥用的说法,本插件只作变动提示,不断吉凶;大运与原局之间的伏吟反吟未逐年标注,四柱十神层面的引化也未叠入。',
12989
13250
  },
@@ -13041,6 +13302,15 @@ const KNOWLEDGE = [
13041
13302
  status: 'variant',
13042
13303
  note: '化气格成立条件各家宽严不一(是否要求月令为化神、日主是否允许有根),本插件只列条件、附原文,不据此断定化气已成。',
13043
13304
  },
13305
+ {
13306
+ id: 'bazi-zaji-quyong',
13307
+ system: 'bazi', dims: ['overall', 'career'],
13308
+ gist: '辰戌丑未是杂气月:先看透干,再看会支。',
13309
+ detail: '四墓(辰戌丑未)每个字都藏着不止一气,用神不一,所以叫杂气。子平真诠的取法是:藏干透到天干的先用(甲生辰月,透戊用偏财、透癸用正印、透乙用月劫);没有透干,再看地支有没有与月支会成三合局(辰月逢申子会水,就取水印)。本插件按「透干 → 三合会支 → 本气」的次序取格,依据里写明走的是哪一条,并把《论杂气如何取用》附在结果里。',
13310
+ source: '《子平真诠》十六、论杂气如何取用',
13311
+ status: 'variant',
13312
+ note: '原文说「兼透则兼用」,但只给一个格名时总要落到一个藏干;本插件只取最先透出的那一干,会支也只认三合、不认三会方,且不判原文所说的「有情 / 无情」,取舍次序各家另有说法。',
13313
+ },
13044
13314
  {
13045
13315
  id: 'bazi-zhuanwang-yuanwen',
13046
13316
  system: 'bazi', dims: ['overall', 'career'],
@@ -13206,10 +13476,10 @@ const KNOWLEDGE = [
13206
13476
  id: 'liuyao-yingqi',
13207
13477
  system: 'liuyao', dims: ['overall', 'career'],
13208
13478
  gist: '应期是「什么时候见分晓」,不是「哪天」。',
13209
- detail: '近事以日计、远事以月计;常见取法是等用神逢值、逢冲、逢合之期,或等忌神受制之期。应期法各家互有出入,同一卦不同取法可以差出很远,所以只作时间窗口的提示。',
13479
+ detail: '近事以日计、远事以月计;常见取法是等用神逢值、逢冲、逢合之期,或等忌神受制之期。本插件只做前一半:把用神(不上卦则取伏神)的逢值、逢冲、逢合三支列出,旬空另标出空与冲空,再按月建五态给「偏速 / 偏缓 / 待解 / 未定」一个快慢方向;忌神受制与具体日期仍未做。应期法各家互有出入,同一卦不同取法可以差出很远,所以只作时间窗口的提示。',
13210
13480
  source: '《卜筮正宗》应期章;《增删卜易》',
13211
13481
  status: 'variant',
13212
- note: '应期取法(值日、冲、合、填实、旬空出空)各家优先级不同,本插件不做具体日期推算。',
13482
+ note: '应期取法(值日、冲、合、填实、旬空出空)各家优先级不同,本插件只并列触发支、不排先后,也不换算日期;近事以日计、远事以月计,用哪把尺由事情远近决定,不代选。',
13213
13483
  },
13214
13484
  {
13215
13485
  id: 'liuyao-liushen',
@@ -13366,10 +13636,10 @@ const KNOWLEDGE = [
13366
13636
  id: 'liuyao-yingqi-exact',
13367
13637
  system: 'liuyao', dims: [],
13368
13638
  gist: '具体应期(哪一天应验)本插件尚未推算。',
13369
- detail: '应期需要综合用神旺衰、动变、旬空出空、日月冲合多层条件,且各家取法不同。当前只给时间窗口的方向性提示,不给具体日期——与其给一个看起来精确、实则拍脑袋的日子,不如留白。',
13639
+ detail: '应期需要综合用神旺衰、动变、旬空出空、日月冲合多层条件,且各家取法不同。当前只给时间窗口的方向性提示(逢值 / 逢冲 / 逢合的触发支,加一个快慢方向),不给具体日期——与其给一个看起来精确、实则拍脑袋的日子,不如留白。',
13370
13640
  source: '待补:《卜筮正宗》应期章逐条实现 + 已录 128 条卦例对拍(源 138 条)',
13371
13641
  status: 'todo',
13372
- note: '扩展方向:用《卜筮正宗》卦例里有明确应验日期的条目做回归,先验证再输出。',
13642
+ note: '扩展方向:用《卜筮正宗》卦例里有明确应验日期的条目做回归,先验证再输出。0.50.0 已给出触发支与快慢方向,仍不输出日期。',
13373
13643
  },
13374
13644
  ];
13375
13645
 
@@ -14213,6 +14483,19 @@ function baziInterpret(r) {
14213
14483
  `⑤ 落到维度:工作看喜用五行与十神权重;感情看配偶星与日支;健康看最旺与最弱的五行。五维度都只是这套推导的应用,不是另一套算法。`,
14214
14484
  ].join('\n'),
14215
14485
  },
14486
+ ...(r.renyuan ? [(() => {
14487
+ const ry = r.renyuan;
14488
+ const table = ry.segments.map((s) => `${s.stem}${s.from}–${s.to}`).join(' / ');
14489
+ const nextSeg = ry.next ? `,第 ${ry.next.from} 日起交 ${ry.next.stem}${ry.next.element}` : '';
14490
+ const outside = ry.inHidden ? '' : ` 表里的 ${ry.active} 不在本柱藏干表中(属余气口径),两表各照原样,不互相改写。`;
14491
+ const beyond = ry.beyond ? ' 你生在第 31 日以后,已超出 30 日表,按本气算,不硬分摊。' : '';
14492
+ return {
14493
+ key: 'renyuan', label: '人元司权',
14494
+ text: `生于${ry.month}月第 ${ry.day} 日(从${ry.term ? ry.term.name : '节'}起算,节当日为第 1 日,日差按本盘所用时刻所在日计):这一日由 **${ry.active}${ry.activeElement}** 司权,第 ${ry.segment.from}–${ry.segment.to} 日属它${nextSeg}。\n`
14495
+ + '月令这一个月里,余气、中气、本气轮值,不是整月同一个五行当值——所以「月令是什么」,还要看生在第几日。\n'
14496
+ + `**口径**:本插件固定 30 日表(${table});另有 7/7/16、9/3/18 等分法,各家不一。分日只作解释层的一层细节,**不改取格**:取格仍按本气、透干与会支判定。${outside}${beyond}`,
14497
+ };
14498
+ })()] : []),
14216
14499
  {
14217
14500
  key: 'timeline', label: '时间窗',
14218
14501
  text: (() => {
@@ -14233,6 +14516,8 @@ function baziInterpret(r) {
14233
14516
  { key: 'advice', label: '专业建议', text: adviceText },
14234
14517
  (() => {
14235
14518
  const hits = (r.liunian || []).filter((l) => l.suiyun && Array.isArray(l.suiyun.signals) && l.suiyun.signals.length);
14519
+ const e = suiyunClassicOf(r);
14520
+ const src = e ? `\n原文:《${CLASSIC_BOOKS[e.book].name}·${e.chapter}》「${e.quote}」\n白话:${e.plain}` : '';
14236
14521
  return {
14237
14522
  key: 'suiyun', label: '岁运提示',
14238
14523
  text: hits.length
@@ -14240,7 +14525,7 @@ function baziInterpret(r) {
14240
14525
  const stem = (l.suiyun.dayun || []).map((d) => `第${d.step}步${d.ganzhi}`).join('、');
14241
14526
  return `${l.year} ${l.ganzhi}(${stem || '未到起运'}):${l.suiyun.signals.map((s) => s.detail).join(';')}`;
14242
14527
  }).join('\n') +
14243
- '\n读法:岁运并临、运年反吟与流年伏吟/反吟只说明干支层面重复或对冲,传统上当作「变动感较强」的信号;本插件不据此判吉凶,要落到具体年份,仍看该年是否动到用神。'
14528
+ '\n读法:岁运并临、运年反吟与流年伏吟/反吟只说明干支层面重复或对冲,传统上当作「变动感较强」的信号;本插件不据此判吉凶,要落到具体年份,仍看该年是否动到用神。' + src
14244
14529
  : '近十年没有岁运并临、运年反吟或流年伏吟/反吟。「岁运并临主大凶」这类说法流传很广,本插件只把它当变动提示,没有命中就不作额外解读。',
14245
14530
  };
14246
14531
  })(),
@@ -15025,10 +15310,32 @@ function liuyaoInterpret(r, ask) {
15025
15310
  useText.push(`时令对用神:${head},日辰 ${strength.day ? `${strength.day.branch}(${strength.day.element})` : '—'}。${body.join(';')}。`);
15026
15311
  useText.push('月建看这一段时令站在哪一边,日辰看当天那股力量是扶还是克;两处同时看,只报状态,不折成分数。');
15027
15312
  }
15028
- useText.push('这一层只把用神、卦体与时令对起来,应期与成败还要看动爻变化与月建日辰的进退节奏,所以这里不折成分数、不断成败。');
15313
+ useText.push('这一层只把用神、卦体与时令对起来,不折成分数、不断成败;应期另在「应期线索」一节只给触发支与快慢。');
15029
15314
  sections.push({ key: 'yongshen', label: '用神落位与卦体', text: useText.join('\n') });
15030
15315
  }
15031
15316
 
15317
+ // 应期线索:只给触发支与快慢方向,不给日期
15318
+ {
15319
+ const yq = r.yingqi;
15320
+ if (yq && Array.isArray(yq.items) && yq.items.length) {
15321
+ const rows = yq.items.map((it) => {
15322
+ const flags = [
15323
+ it.monthState && it.monthState !== 'unknown' ? `月建${it.monthState}` : '',
15324
+ it.monthChong ? '月破' : '',
15325
+ it.kong ? '旬空' : '',
15326
+ ].filter(Boolean).join('、');
15327
+ return `${it.position}爻${it.branch}${it.element}${flags ? `(${flags})` : ''}:${it.tempoSay}`;
15328
+ });
15329
+ sections.push({
15330
+ key: 'yingqi',
15331
+ label: '应期线索',
15332
+ text: `应期快慢:${yq.tempoSay}。${rows.join(';')}。可留意的地支:${yq.triggers.map((t) => `${t.branch}(${t.labels.join('、')})`).join(';')}。` +
15333
+ `${yq.viaFushen ? '用神不上卦,这里的支取自伏神;伏神要先得出才算数。' : ''}` +
15334
+ '这一节只把传统的「逢值、逢冲、逢合」与旬空的出空、冲空摆出来,不换算成具体日期、不排先后、也不判忌神受制;近事以日计、远事以月计,用哪把尺取决于事情本身。',
15335
+ });
15336
+ }
15337
+ }
15338
+
15032
15339
  // 全卦逐爻旺衰:不挑用神,六爻都过一遍,看整张卦的气压在哪一头
15033
15340
  {
15034
15341
  const hs = r.hexStrength;
@@ -15058,7 +15365,7 @@ function liuyaoInterpret(r, ask) {
15058
15365
 
15059
15366
  sections.push({
15060
15367
  key: 'limits', label: '未纳入',
15061
- text: '本卦没有计算:用神两现时该取哪一爻的权重、伏神出伏的应期(出伏条件已判,应期仍未做)。化进/化退的力重只按月建五态给「得令/失令」这类档位,不折成加权分数,也不叠动变连环;这些都会改变"旺不旺"的判断,所以这里只给取象与方向。',
15368
+ text: '本卦没有计算:用神两现时该取哪一爻的权重、应期的具体日期(只给逢值/逢冲/逢合的触发支与快慢方向)、忌神受制的时点、动变连环。化进/化退的力重只按月建五态给「得令/失令」这类档位,不折成加权分数;这些都会改变"旺不旺"的判断,所以这里只给取象与方向。',
15062
15369
  });
15063
15370
 
15064
15371
  const dimensions = [
@@ -15484,7 +15791,7 @@ const BUILDERS = {
15484
15791
  const { interpretQimen } = __mods['qimen-interpret'];
15485
15792
  const { buildAnswer, buildPlain, buildGlossary, buildFollowups, dimensionPlain } = __mods['interpret-plain'];
15486
15793
  const { knowledgeFor, caveatsFor, STATUS_LABEL } = __mods['interpret-knowledge'];
15487
- const { CLASSIC_BOOKS } = __mods.classics;
15794
+ const { CLASSIC_BOOKS, suiyunClassicOf } = __mods.classics;
15488
15795
 
15489
15796
  /**
15490
15797
  * 给任意体系的解析结果补上「白话四层」:直答 / 说人话 / 每维一句话 / 追问建议。
@@ -16012,16 +16319,33 @@ function crossCheck(bazi, zw) {
16012
16319
  const bad = ['忌', '大忌'];
16013
16320
  const baziGood = good.includes(t);
16014
16321
  const baziBad = bad.includes(t);
16322
+ const baziKnown = t !== '—';
16015
16323
  const ziweiHeavy = sha >= 2;
16324
+ const ziweiKnown = !!d;
16016
16325
  let stance = '有张力';
16017
- let note = '一边顺、一边压,按「大方向跟顺的一边、具体动作避开压的年份」处理';
16018
- if ((baziGood && !ziweiHeavy) || (baziBad && ziweiHeavy) || (!baziGood && !baziBad && !ziweiHeavy)) {
16326
+ let note = '';
16327
+ if (!baziKnown || !ziweiKnown) {
16328
+ // 一边没取到就谈不上「同向 / 有张力」——只并列现有的一侧,并写清缺的是哪边
16329
+ stance = '互补';
16330
+ const missing = !baziKnown && !ziweiKnown
16331
+ ? '八字当前大运与紫微当前大限都没取到'
16332
+ : !baziKnown
16333
+ ? `八字当前大运未取到(${t}),只有紫微一侧可参考`
16334
+ : '紫微当前大限未取到,只有八字一侧可参考';
16335
+ note = `${missing}:这一项只作提醒,不下两边对照的结论`;
16336
+ } else if (baziGood && ziweiHeavy) {
16337
+ note = '八字这边顺、紫微大限煞星偏多:大方向可参考八字,具体动作按紫微标出的位置收一收';
16338
+ } else if (baziBad && !ziweiHeavy) {
16339
+ note = '八字这边偏紧、紫微大限没见到明显煞星:先按八字收着走,紫微一侧的平静不等于没有压力';
16340
+ } else if (!baziGood && !baziBad && ziweiHeavy) {
16341
+ note = '八字大运为平、紫微大限煞星偏多:只把紫微这一侧当作提醒,不据此判断阶段好坏';
16342
+ } else {
16019
16343
  stance = '同向';
16020
16344
  note = '两边对当前阶段的松紧判断接近';
16021
16345
  }
16022
16346
  push('rhythm', '当前节奏',
16023
16347
  `大运档位 ${t}`,
16024
- `${d ? `大限在${d.palace}` : '大限未取到'}${sha ? `;煞星 ${sha} 颗` : ';无煞星'}`,
16348
+ d ? `大限在${d.palace}${sha ? `;煞星 ${sha} 颗` : ';无煞星'}` : '大限未取到',
16025
16349
  stance, note);
16026
16350
  }
16027
16351
 
@@ -16036,8 +16360,17 @@ function crossText(cross) {
16036
16360
  .map((i) => `· ${i.label}:八字——${i.bazi};紫微——${i.ziwei};判断:${i.stance}(${i.note})`)
16037
16361
  .join('\n');
16038
16362
  }
16363
+
16364
+ /**
16365
+ * 交叉验证分节。文本给 Markdown/工具输出,items 给面板画对照卡——
16366
+ * 同一份数据两个去处,免得面板再去拆文本。
16367
+ */
16368
+ function crossSection(cross) {
16369
+ const items = cross && Array.isArray(cross.items) ? cross.items : [];
16370
+ return { key: 'crossSystem', label: '八字 × 紫微 交叉验证', text: crossText(cross), items };
16371
+ }
16039
16372
 
16040
- return { crossCheck, crossText };
16373
+ return { crossCheck, crossText, crossSection };
16041
16374
  })();
16042
16375
 
16043
16376
  __mods.verify = (function () {
@@ -16246,7 +16579,7 @@ return { matchEvent, rectifyBirth, RECTIFY_KINDS };
16246
16579
  __mods.index = (function () {
16247
16580
  // 统一入口:解析参数、分派到各体系、生成可读摘要
16248
16581
  const { buildBazi } = __mods.bazi;
16249
- const { buildLiuyao, tossLines, bodyLabels, yongshenOf, yongshenStrength, fushenAccess, hexagramStrength, jinTuiPower } = __mods.liuyao;
16582
+ const { buildLiuyao, tossLines, bodyLabels, yongshenOf, yongshenStrength, fushenAccess, hexagramStrength, jinTuiPower, yingqiWindow } = __mods.liuyao;
16250
16583
  const { buildZiwei, BRIGHTNESS, flowOf, asOfParts, ZIWEI_SCHOOL_DEFAULTS } = __mods.ziwei;
16251
16584
  const { drawTarot } = __mods.tarot;
16252
16585
  const { buildMeihua } = __mods.meihua;
@@ -16259,7 +16592,7 @@ const { toJDN, ganzhiName, dayGanzhiIndex } = __mods.calendar;
16259
16592
  const { randomOf, isSeed } = __mods.random;
16260
16593
  const { interpret, DIRECTIONS, DIRECTION_DEFAULT } = __mods.interpret;
16261
16594
  const { followup, askPrompt } = __mods.followup;
16262
- const { crossCheck, crossText } = __mods.crosscheck;
16595
+ const { crossCheck, crossText, crossSection } = __mods.crosscheck;
16263
16596
  const { pastSignals, verifyFit } = __mods.verify;
16264
16597
  const { rectifyBirth } = __mods.rectify;
16265
16598
  const { buildAlmanac } = __mods.almanac;
@@ -16270,7 +16603,7 @@ const { buildStar } = __mods.star;
16270
16603
  const { buildNumerology } = __mods.numerology;
16271
16604
  const { buildMbti, mbtiPaper } = __mods.mbti;
16272
16605
  const { buildConvert } = __mods.convert;
16273
- const { classicsFor, classicsText } = __mods.classics;
16606
+ const { classicsFor, classicsText, suiyunClassicOf, CLASSIC_BOOKS } = __mods.classics;
16274
16607
  const { buildTrend } = __mods.trend;
16275
16608
  const { buildFengshui } = __mods.fengshui;
16276
16609
  const { buildName } = __mods.name;
@@ -16372,6 +16705,10 @@ function baziText(r) {
16372
16705
  ? `神煞(${r.shensha.count} 见):` + r.shensha.groups.map((g) =>
16373
16706
  `${g.cat}|` + g.items.map((i) => `${i.name}(${i.pillars.map((k) => SHORT[k]).join('')})`).join('、')).join(';')
16374
16707
  : '神煞:—';
16708
+ const ry = r.renyuan;
16709
+ const renyuanLine = ry
16710
+ ? `人元:${ry.month}月第${ry.day}日,${ry.active}${ry.activeElement}司权(${ry.segments.map((s) => `${s.stem}${s.from}–${s.to}`).join(' ')})${ry.beyond ? ';已过 30 日表,按本气算' : ''}`
16711
+ : '';
16375
16712
  const gejuLine = r.frame ? [
16376
16713
  `格局:${r.frame.primary ? `${r.frame.primary.name}(${r.frame.primary.evidence})` : '—'}`,
16377
16714
  `   ${r.frame.geju.map((g) => `${g.name}[${g.kind}]`).join(' ')}`,
@@ -16394,6 +16731,7 @@ function baziText(r) {
16394
16731
  shenshaLine,
16395
16732
  `真太阳时:${r.solarTime.trueSolarText}(${r.input.geo ? r.input.geo.note + ';' : ''}${r.solarTime.note}) 晚子时:${r.input.lateZi ? '23:00 后算次日' : '23:00 后仍算当日'}`,
16396
16733
  `节气:上一节 ${r.jieqi.previous.name} ${r.jieqi.previous.time} 下一节 ${r.jieqi.next.name} ${r.jieqi.next.time}`,
16734
+ ...(renyuanLine ? [renyuanLine] : []),
16397
16735
  `大运:${r.dayun.rule}(${r.dayun.direction}),${r.dayun.startAge.years}岁${r.dayun.startAge.months}个月起运${jiao}`,
16398
16736
  ` ${dayun}`,
16399
16737
  `流年:${r.liunian.slice(0, 10).map((l) => `${l.year}${l.ganzhi}(${l.stemTenGod})${l.luck ? `·${l.luck.tier}` : ''}`).join(' ')}`,
@@ -16401,8 +16739,10 @@ function baziText(r) {
16401
16739
  const hits = r.liunian.filter((l) => l.suiyun && l.suiyun.signals && l.suiyun.signals.length);
16402
16740
  if (!hits.length) return '岁运提示:近十年无岁运并临、运年反吟或流年伏吟/反吟(只标干支,不判吉凶)';
16403
16741
  const label = (s) => `${s.type}${s.type.startsWith('流年') && s.target ? `(${s.target})` : ''}`;
16742
+ const e = suiyunClassicOf(r);
16743
+ const src = e ? `|古籍见《${CLASSIC_BOOKS[e.book].name}·${e.chapter}》` : '';
16404
16744
  return '岁运提示(只标干支变动,不判吉凶):' + hits
16405
- .map((l) => `${l.year}${l.ganzhi} ${l.suiyun.signals.map(label).join('/')}`).join(';');
16745
+ .map((l) => `${l.year}${l.ganzhi} ${l.suiyun.signals.map(label).join('/')}`).join(';') + src;
16406
16746
  })(),
16407
16747
  '(格局、用神、行运吉凶为扶抑+简化调候的量化提示,非断语。)',
16408
16748
  ].join('\n') + near;
@@ -16654,6 +16994,11 @@ function liuyaoText(r) {
16654
16994
  if (jt && Array.isArray(jt.lines) && jt.lines.length) {
16655
16995
  out.push(`进退力重:${jt.lines.map((l) => `${l.position}爻${l.from}→${l.to}${l.type}·${l.verdict}`).join(' · ')}`);
16656
16996
  }
16997
+ // 应期线索:只列地支与快慢,不给日期
16998
+ const yq = r.yingqi;
16999
+ if (yq && Array.isArray(yq.triggers) && yq.triggers.length) {
17000
+ out.push(`应期线索:${yq.tempo} · ${yq.triggers.map((t) => `${t.labels.join('/')}${t.branch}`).join(' · ')}(只列地支,不出日期)`);
17001
+ }
16657
17002
  return out.filter(Boolean).join('\n');
16658
17003
  }
16659
17004
 
@@ -16674,6 +17019,9 @@ function compute(args = {}) {
16674
17019
  r.classics = classicsFor(r);
16675
17020
  const ct = classicsText(r.classics);
16676
17021
  if (ct) r.summary = `${r.summary}\n【古籍】\n${ct}`;
17022
+ // 岁运提示的原文不占五条上限:只在盘面真有岁运信号时挂到结果上,摘要、解析层与面板各自取用
17023
+ const syc = suiyunClassicOf(r);
17024
+ if (syc) r.suiyunClassic = { ...syc, bookName: CLASSIC_BOOKS[syc.book].name };
16677
17025
  }
16678
17026
  // 超出 ΔT 覆盖范围时把精度提示顶到摘要最前,别让远期盘悄悄出错
16679
17027
  if (r && r.precision && r.precision.level === 'approx' && r.summary) {
@@ -16708,7 +17056,7 @@ function compute(args = {}) {
16708
17056
  : buildBazi({ ...common, lateZi: args.lateZi !== false, nowYear: asOf ? asOf.year : undefined });
16709
17057
  r.cross = crossCheck(r.system === 'bazi' ? r : other, r.system === 'ziwei' ? r : other);
16710
17058
  if (r.interpret) {
16711
- r.interpret.sections.push({ key: 'crossSystem', label: '八字 × 紫微 交叉验证', text: crossText(r.cross) });
17059
+ r.interpret.sections.push(crossSection(r.cross));
16712
17060
  }
16713
17061
  }
16714
17062
  // 验前事:把过去若干年的流年信号摊开(只读,不改盘)
@@ -16991,6 +17339,9 @@ function computeRaw(args = {}) {
16991
17339
  // 伏神出伏条件:飞伏生克叠月建日辰,只判条件、不出应期
16992
17340
  const chufu = fushenAccess(result, ug, { monthBranch, dayBranch: result.dayGanZhi.branch });
16993
17341
  if (chufu) result.fushenAccess = chufu;
17342
+ // 应期线索:只列逢值/逢冲/逢合与旬空的出空/冲空,再给快慢方向,不换算日期
17343
+ const yq = yingqiWindow(result, ug, { monthBranch });
17344
+ if (yq) result.yingqi = yq;
16994
17345
  }
16995
17346
  if (args.seed !== undefined && args.seed !== null && args.seed !== '') result.seed = Number(args.seed);
16996
17347
  return { ...result, ...(asOf ? { asOf } : {}), summary: liuyaoText(result) };
@@ -17455,7 +17806,7 @@ function renderText(result) {
17455
17806
 
17456
17807
 
17457
17808
 
17458
- return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAddress, resolveAddress, buildBazi, buildLiuyao, tossLines, followup, askPrompt, pastSignals, verifyFit, rectifyBirth, crossCheck, crossText, buildAlmanac, buildZeri, buildChenggu, buildXiaoliuren, buildStar, buildNumerology, buildConvert, buildTrend, buildFengshui, buildName, parseDateTime, compute, interpretText, renderText };
17809
+ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAddress, resolveAddress, buildBazi, buildLiuyao, tossLines, followup, askPrompt, pastSignals, verifyFit, rectifyBirth, crossCheck, crossText, crossSection, buildAlmanac, buildZeri, buildChenggu, buildXiaoliuren, buildStar, buildNumerology, buildConvert, buildTrend, buildFengshui, buildName, parseDateTime, compute, interpretText, renderText };
17459
17810
  })();
17460
17811
 
17461
17812
  window.Engine = __mods.index;
@@ -17623,6 +17974,8 @@ window.Engine = __mods.index;
17623
17974
  .pp-str b{color:var(--red);font-size:15px;margin:0 4px}
17624
17975
  .pp-drel{font-style:normal;color:var(--red);font-size:12px}
17625
17976
  .pp-dim{color:var(--muted)}
17977
+ /* 岁运提示下的原文:比正文再轻一档,只做出处,不抢结论 */
17978
+ .pp-sy-src{margin-top:6px;font-size:11.5px;line-height:1.55}
17626
17979
  .pp-r{display:none}
17627
17980
  .pp-modes{display:flex;align-items:center;border-bottom:1px solid var(--line);font-family:system-ui,sans-serif}
17628
17981
  /* min-height:44px —— 视图切换是本盘唯一的主要控件,触摸目标必须够。
@@ -17936,7 +18289,8 @@ window.Engine = __mods.index;
17936
18289
  /* —— 解析与详解 —— */
17937
18290
  /* 变量由上面的 .pp,.mp-it 共享块提供。这里原先写过 --ink:var(--ink) 这类自引用声明,
17938
18291
  自引用会让变量变成 guaranteed-invalid,反而把继承值也挡掉了(--card 因此彻底失效)。 */
17939
- .mp-it{margin-top:14px;border:1px solid var(--line);background:var(--bg);color:var(--ink);border-radius:12px;overflow:hidden;box-shadow:var(--shadow);
18292
+ /* container-type:dsh 侧栏是「窗口宽、报告窄」,@media 不触发;下面 ≤640 的表格/目录规则另挂一份 @container */
18293
+ .mp-it{margin-top:14px;border:1px solid var(--line);background:var(--bg);color:var(--ink);border-radius:12px;overflow:hidden;box-shadow:var(--shadow);container-type:inline-size;
17940
18294
  font-family:"Songti SC","SimSun","Microsoft YaHei",serif;font-size:13px;line-height:1.75}
17941
18295
  .mp-it *{box-sizing:border-box}
17942
18296
  /* 标题栏即折叠开关:整栏可点,箭头旋转表示状态 */
@@ -18037,6 +18391,19 @@ window.Engine = __mods.index;
18037
18391
  .mp-it-conf.c-强{color:var(--lu);border-color:rgba(var(--lu-rgb),.4)}
18038
18392
  .mp-it-conf.c-中{color:var(--label);border-color:var(--line)}
18039
18393
  .mp-it-conf.c-弱{color:var(--red);border-color:rgba(var(--red-rgb),.4)}
18394
+ /* 交叉验证:一个方面一张对照卡,八字与紫微左右并列,判断做成语气标签 */
18395
+ .mp-cross{display:flex;flex-direction:column;gap:8px;font-family:system-ui,sans-serif}
18396
+ .mp-cross-row{border:1px solid var(--line);border-radius:8px;background:var(--card);padding:9px 11px}
18397
+ .mp-cross-h{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
18398
+ .mp-cross-l{font-size:13px;font-weight:700;color:var(--ink);letter-spacing:.02em}
18399
+ .mp-cross-tag{font-size:11px;line-height:1.5;padding:1px 8px;border:1px solid currentColor;border-radius:999px;color:var(--muted)}
18400
+ .mp-cross-tag.t-good{color:var(--lu)}
18401
+ .mp-cross-tag.t-watch{color:var(--quan)}
18402
+ .mp-cross-tag.t-alt{color:var(--ke)}
18403
+ .mp-cross-b{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:4px 14px;margin-top:7px}
18404
+ .mp-cross-cell{font-size:12.5px;line-height:1.75;color:var(--ink2)}
18405
+ .mp-cross-cell b{display:block;font-size:10.5px;letter-spacing:.1em;color:var(--muted);font-weight:600;margin-bottom:1px}
18406
+ .mp-cross-note{margin-top:7px;padding-top:6px;border-top:1px dashed var(--line);font-size:12px;line-height:1.7;color:var(--muted)}
18040
18407
  /* 三张表:横向可滚动,避免窄栏撑破 */
18041
18408
  .mp-tables{border-top:1px solid var(--line);padding:12px 16px 4px;background:var(--card)}
18042
18409
  .mp-table-wrap{margin-bottom:12px}
@@ -18044,7 +18411,8 @@ window.Engine = __mods.index;
18044
18411
  .mp-table-scroll{overflow-x:auto;border:1px solid var(--line);border-radius:8px}
18045
18412
  .mp-table{width:100%;border-collapse:collapse;font-size:12px;line-height:1.6;color:var(--ink2);font-family:system-ui,sans-serif}
18046
18413
  .mp-table th,.mp-table td{padding:5px 9px;text-align:left;vertical-align:top;border-bottom:1px solid var(--line);white-space:nowrap}
18047
- .mp-table th{background:var(--band);color:var(--label);font-weight:600;font-size:11px;letter-spacing:.06em}
18414
+ /* 表头 11px 小字:用 ink2 而不是 label,浅色下 label 对 band 只有 3.5:1,读不清 */
18415
+ .mp-table th{background:var(--band);color:var(--ink2);font-weight:600;font-size:11px;letter-spacing:.06em}
18048
18416
  .mp-table tbody tr:last-child td{border-bottom:none}
18049
18417
  .mp-table tbody tr:nth-child(even) td{background:rgba(0,0,0,.02)}
18050
18418
  /* 打印:表格不撕页、目录隐藏、折叠的内容要展开 */
@@ -18056,6 +18424,56 @@ window.Engine = __mods.index;
18056
18424
  .mp-table-wrap,.mp-it-sec,.mp-it-dim{break-inside:avoid}
18057
18425
  .mp-table-wrap{page-break-inside:avoid}
18058
18426
  }
18427
+ /* —— ≤640px:大运表 6 列排到 748px,窄屏只能看到左边一段且没有「还能滑」的提示,
18428
+ 这里改成一列一张卡;目录横滑成胶囊、证据摘要与名词解释的点击区抬到 44px —— */
18429
+ @media (max-width:640px){
18430
+ .mp-tables{padding:10px 11px 2px}
18431
+ .mp-table-wrap{margin-bottom:14px}
18432
+ .mp-table-scroll{overflow:visible;border:0;border-radius:0}
18433
+ .mp-table{display:block;width:100%}
18434
+ .mp-table thead{display:none}
18435
+ .mp-table tbody{display:block}
18436
+ .mp-table tr{display:block;border:1px solid var(--line);border-radius:8px;background:var(--card);padding:7px 10px;margin:0 0 8px}
18437
+ .mp-table td{display:grid;grid-template-columns:56px minmax(0,1fr);gap:8px;border-bottom:0;padding:2px 0;white-space:normal;font-size:12.5px;line-height:1.65}
18438
+ .mp-table td::before{content:attr(data-label);color:var(--muted);font-size:11px;line-height:1.9}
18439
+ .mp-table td:first-child{display:block;padding-bottom:3px;font-size:13px;font-weight:700;color:var(--ink)}
18440
+ .mp-table td:first-child::before{content:none}
18441
+ .mp-table tbody tr:nth-child(even) td{background:none}
18442
+ .mp-table tbody tr:last-child{margin-bottom:0}
18443
+ /* 对照卡:两列并排在 342px 里每边只剩 164px,竖排后标签跟正文走 */
18444
+ .mp-cross-b{grid-template-columns:1fr;gap:6px}
18445
+ .mp-cross-cell b{display:inline;margin-right:6px;letter-spacing:.06em}
18446
+ .mp-toc{flex-wrap:nowrap;overflow-x:auto;gap:8px;margin:10px 0 0;padding:6px 11px;border:0;border-bottom:1px solid var(--line);border-radius:0;scrollbar-width:none}
18447
+ .mp-toc::-webkit-scrollbar{display:none}
18448
+ .mp-toc a{flex:0 0 auto;display:inline-flex;align-items:center;min-height:44px;padding:0 12px;border:1px solid var(--line);border-radius:999px;color:var(--lu);text-decoration:none}
18449
+ .mp-toc a:hover{color:var(--ink)}
18450
+ .mp-it-ev>summary{display:flex;align-items:center;min-height:44px;padding:0 9px}
18451
+ .mp-it-gloss summary{display:flex;align-items:center;min-height:44px;margin-bottom:2px}
18452
+ }
18453
+ /* 容器查询版(dsh 侧栏 420px、窗口 1400px 时 @media 不会触发):与上面同规则,不另立一套口径 */
18454
+ @container (max-width:640px){
18455
+ .mp-tables{padding:10px 11px 2px}
18456
+ .mp-table-wrap{margin-bottom:14px}
18457
+ .mp-table-scroll{overflow:visible;border:0;border-radius:0}
18458
+ .mp-table{display:block;width:100%}
18459
+ .mp-table thead{display:none}
18460
+ .mp-table tbody{display:block}
18461
+ .mp-table tr{display:block;border:1px solid var(--line);border-radius:8px;background:var(--card);padding:7px 10px;margin:0 0 8px}
18462
+ .mp-table td{display:grid;grid-template-columns:56px minmax(0,1fr);gap:8px;border-bottom:0;padding:2px 0;white-space:normal;font-size:12.5px;line-height:1.65}
18463
+ .mp-table td::before{content:attr(data-label);color:var(--muted);font-size:11px;line-height:1.9}
18464
+ .mp-table td:first-child{display:block;padding-bottom:3px;font-size:13px;font-weight:700;color:var(--ink)}
18465
+ .mp-table td:first-child::before{content:none}
18466
+ .mp-table tbody tr:nth-child(even) td{background:none}
18467
+ .mp-table tbody tr:last-child{margin-bottom:0}
18468
+ .mp-cross-b{grid-template-columns:1fr;gap:6px}
18469
+ .mp-cross-cell b{display:inline;margin-right:6px;letter-spacing:.06em}
18470
+ .mp-toc{flex-wrap:nowrap;overflow-x:auto;gap:8px;margin:10px 0 0;padding:6px 11px;border:0;border-bottom:1px solid var(--line);border-radius:0;scrollbar-width:none}
18471
+ .mp-toc::-webkit-scrollbar{display:none}
18472
+ .mp-toc a{flex:0 0 auto;display:inline-flex;align-items:center;min-height:44px;padding:0 12px;border:1px solid var(--line);border-radius:999px;color:var(--lu);text-decoration:none}
18473
+ .mp-toc a:hover{color:var(--ink)}
18474
+ .mp-it-ev>summary{display:flex;align-items:center;min-height:44px;padding:0 9px}
18475
+ .mp-it-gloss summary{display:flex;align-items:center;min-height:44px;margin-bottom:2px}
18476
+ }
18059
18477
  /* 追问结果 */
18060
18478
  .mp-fu{margin:12px 16px;border:1px solid var(--line);border-left:3px solid var(--lu);border-radius:8px;background:var(--card);padding:10px 12px;font-size:13px;line-height:1.8;color:var(--ink2)}
18061
18479
  .mp-fu.miss{border-left-color:var(--red)}
@@ -18931,6 +19349,14 @@ window.Engine = __mods.index;
18931
19349
  return `<span class="pp-chip ${cls}">伏神${esc(l)}</span>`;
18932
19350
  }).join('')}</div>`
18933
19351
  : '';
19352
+ // 应期线索:只摆触发支与快慢方向,不写日期
19353
+ const yq = r.yingqi && Array.isArray(r.yingqi.labels) ? r.yingqi : null;
19354
+ const yingqiRow = yq && yq.labels.length
19355
+ ? `<div class="pp-chips">${yq.labels.map((l) => {
19356
+ const cls = /应期偏速/.test(l) ? 'ok' : /应期待解/.test(l) ? 'warn' : '';
19357
+ return `<span class="pp-chip ${cls}" title="只列地支,不出日期">${esc(l)}</span>`;
19358
+ }).join('')}</div>`
19359
+ : '';
18934
19360
  // 化进/化退的力重:方向之外再叠月建日辰,单列一排,免与卦体标签混读
18935
19361
  const jp = r.jinTuiPower && Array.isArray(r.jinTuiPower.labels) ? r.jinTuiPower : null;
18936
19362
  const jinRow = jp && jp.labels.length
@@ -18955,6 +19381,7 @@ window.Engine = __mods.index;
18955
19381
  ${useRow}
18956
19382
  ${strengthRow}
18957
19383
  ${fushenRow}
19384
+ ${yingqiRow}
18958
19385
  ${jinRow}
18959
19386
  ${hexRow}
18960
19387
  </div>`;
@@ -19730,8 +20157,10 @@ window.Engine = __mods.index;
19730
20157
  return `<span class="pp-chip ${lk ? tierCls(lk.tier) : ''}" title="${esc(lk ? `${lk.tier} ${lk.score}|${(lk.reasons || []).join(';')}${syTip}` : syTip.replace(/^|/, ''))}">${l.year} ${l.ganzhi}${lk ? ` ${lk.tier}` : ''}</span>`;
19731
20158
  }).join('')}</div>` : '';
19732
20159
  const sy = (r.liunian || []).filter((l) => l.suiyun && Array.isArray(l.suiyun.signals) && l.suiyun.signals.length);
20160
+ const syc = r.suiyunClassic;
20161
+ const sySrc = syc ? `<div class="pp-dim pp-sy-src" title="${esc(syc.plain || '')}">《${esc(syc.bookName || '')}·${esc(syc.chapter || '')}》「${esc(syc.quote || '')}」</div>` : '';
19733
20162
  const syRow = sy.length ? `<div class="pp-tl pp-dim">岁运提示(只标干支变动,不判吉凶)</div><div class="pp-chips">${sy.map((l) =>
19734
- `<span class="pp-chip" title="${esc(`${l.suiyun.signals.map((x) => x.detail).join(';')}|${l.suiyun.note || ''}`)}">${l.year} ${esc(l.suiyun.signals.map((x) => `${x.type}${x.type.startsWith('流年') && x.target ? `·${x.target}` : ''}`).join('/'))}</span>`).join('')}</div>` : '';
20163
+ `<span class="pp-chip" title="${esc(`${l.suiyun.signals.map((x) => x.detail).join(';')}|${l.suiyun.note || ''}`)}">${l.year} ${esc(l.suiyun.signals.map((x) => `${x.type}${x.type.startsWith('流年') && x.target ? `·${x.target}` : ''}`).join('/'))}</span>`).join('')}</div>${sySrc}` : '';
19735
20164
  const str = r.strength ? `<div class="pp-str">日主强弱:<b>${r.strength.level}</b>${r.strength.score} <span class="pp-dim">${r.strength.detail}</span></div>` : '';
19736
20165
  const ssGroups = (r.shensha && r.shensha.groups) || [];
19737
20166
  const ssx = ssGroups.length ? `
@@ -19772,6 +20201,21 @@ window.Engine = __mods.index;
19772
20201
  // ── 解析与详解:把 interpret() 的结构化结果渲染成卡片 ──
19773
20202
  const MT_LABEL = { natal: '命盘解析', divination: '卦象解析', card: '牌阵解析' };
19774
20203
 
20204
+ // 交叉验证的「判断」只有三档,各给一种语气色;未知值走中性色,不猜
20205
+ const STANCE_TONE = { 同向: 'good', 有张力: 'watch', 互补: 'alt' };
20206
+ // 一栏一个方面:标题带判断标签,八字与紫微并列,注解放最后一行,省得五段长句连读
20207
+ const crossHTML = (items) => `<div class="mp-cross">${items.map((c) => {
20208
+ const tone = STANCE_TONE[c.stance] || 'plain';
20209
+ return `<div class="mp-cross-row">
20210
+ <div class="mp-cross-h"><span class="mp-cross-l">${esc(c.label)}</span><span class="mp-cross-tag t-${tone}">${esc(c.stance || '—')}</span></div>
20211
+ <div class="mp-cross-b">
20212
+ <div class="mp-cross-cell"><b>八字</b><span>${esc(c.bazi || '—')}</span></div>
20213
+ <div class="mp-cross-cell"><b>紫微</b><span>${esc(c.ziwei || '—')}</span></div>
20214
+ </div>
20215
+ ${c.note ? `<div class="mp-cross-note">${esc(c.note)}</div>` : ''}
20216
+ </div>`;
20217
+ }).join('')}</div>`;
20218
+
19775
20219
  /**
19776
20220
  * @param {object} it interpret() 的结果
19777
20221
  * @param {object} [opt] { folded 是否收起 } —— 桌面默认展开、竖屏默认收起由调用方按视口决定
@@ -19803,8 +20247,8 @@ window.Engine = __mods.index;
19803
20247
  ? `<div class="mp-tables">${it.tables.filter((t) => Array.isArray(t.rows) && t.rows.length).map((t) => `<div class="mp-table-wrap">
19804
20248
  <div class="mp-table-h">${esc(t.title)}</div>
19805
20249
  <div class="mp-table-scroll"><table class="mp-table">
19806
- <thead><tr>${t.columns.map((c) => `<th>${esc(c)}</th>`).join('')}</tr></thead>
19807
- <tbody>${t.rows.map((row) => `<tr>${row.map((c) => `<td>${esc(c)}</td>`).join('')}</tr>`).join('')}</tbody>
20250
+ <thead><tr>${t.columns.map((c) => `<th scope="col">${esc(c)}</th>`).join('')}</tr></thead>
20251
+ <tbody>${t.rows.map((row) => `<tr>${row.map((c, ci) => `<td data-label="${esc(t.columns[ci] || '')}">${esc(c)}</td>`).join('')}</tr>`).join('')}</tbody>
19808
20252
  </table></div>
19809
20253
  </div>`).join('')}</div>`
19810
20254
  : '';
@@ -19816,7 +20260,7 @@ window.Engine = __mods.index;
19816
20260
  const secs = (it.sections && it.sections.length)
19817
20261
  ? `<div class="mp-it-secs">${it.sections.map((s) => `<div class="mp-it-sec${s.tone ? ` tone-${esc(s.tone)}` : ''}" id="mp-sec-${esc(s.key)}">
19818
20262
  <div class="mp-it-sh">${esc(s.label)}</div>
19819
- <div class="mp-it-stext">${richText(s.text)}</div>
20263
+ <div class="mp-it-stext">${(Array.isArray(s.items) && s.items.length) ? crossHTML(s.items) : richText(s.text)}</div>
19820
20264
  </div>`).join('')}</div>`
19821
20265
  : '';
19822
20266
  const tags = [it.systemLabel, MT_LABEL[it.moduleType] || it.moduleType].filter(Boolean);
@@ -20443,8 +20887,8 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
20443
20887
  it = window.Engine.interpret ? window.Engine.interpret(last.r, askArgs()) : (last.r.interpret || null);
20444
20888
  } catch (e) { it = null; }
20445
20889
  // 交叉验证结果由 compute 挂在盘面对象上,这里补进解析分节,保证与真实报告同一渲染路径
20446
- if (it && last.r && last.r.cross && window.Engine.crossText) {
20447
- it.sections = [...(it.sections || []), { key: 'crossSystem', label: '八字 × 紫微 交叉验证', text: window.Engine.crossText(last.r.cross) }];
20890
+ if (it && last.r && last.r.cross && window.Engine.crossSection) {
20891
+ it.sections = [...(it.sections || []), window.Engine.crossSection(last.r.cross)];
20448
20892
  }
20449
20893
  el('out').innerHTML = castAtHTML(last.tab, last.timed, last.date, last.time)
20450
20894
  + window.__PP.resultWithInterpret(last.html, it, { folded: defaultFolded() })
@@ -20672,7 +21116,7 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
20672
21116
  if (tab === 'convert') {
20673
21117
  args.mode = convMode;
20674
21118
  if (convMode === 'lunar2solar') {
20675
- const m = /^(d{4})-(d{1,2})-(d{1,2})$/.exec(String(el('lunardate').value).trim());
21119
+ const m = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(String(el('lunardate').value).trim());
20676
21120
  if (!m) { el('out').innerHTML = '<div class="err">农历转公历请按「农历年-月-日」填写,例如 2026-9-17;闰月单独勾选。</div>'; return; }
20677
21121
  args.lunarYear = Number(m[1]); args.lunarMonth = Number(m[2]); args.lunarDay = Number(m[3]);
20678
21122
  args.isLeapMonth = !!(el('leapm') && el('leapm').checked);
@@ -20820,12 +21264,14 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
20820
21264
  const fc = el('for-count');
20821
21265
  if (fc) fc.style.display = tab === 'tarot' ? '' : 'none';
20822
21266
  const castLabel = tab === 'qimen';
20823
- const dateLabel = tab === 'almanac' || tab === 'zeri' || tab === 'star' || tab === 'numerology';
21267
+ // 与 TIME_KIND 的 date 类共用一处口径:农历(convert)原先漏在名单外,标签错写成「起卦日期」
21268
+ const dateLabel = kindOf(tab) === 'date';
20824
21269
  el('lbl-date').textContent = birth ? '出生日期(公历)' : dateLabel ? '日期(公历)' : (castLabel ? '起局日期(公历)' : '起卦日期(公历)');
20825
21270
  el('lbl-time').textContent = birth ? '出生时刻' : (castLabel ? '起局时刻' : '起卦时刻');
20826
21271
  const qk = el('for-quick');
20827
21272
  if (qk) {
20828
21273
  el('qk-zeri').style.display = tab === 'zeri' ? '' : 'none';
21274
+ el('qk-convert').style.display = tab === 'convert' ? '' : 'none';
20829
21275
  el('qk-xiaoliuren').style.display = tab === 'xiaoliuren' ? '' : 'none';
20830
21276
  el('qk-trend').style.display = tab === 'trend' ? '' : 'none';
20831
21277
  el('qk-fengshui').style.display = tab === 'fengshui' ? '' : 'none';